Neill Bowler
Neill Bowler

Reputation: 165

onBind not called when binding service in android

I have been going round in circles on this one for days. I am following a tutorial to create a music player service: part 2 of music player tutorial

I think I've followed it correctly, but I get an error when it tries to set the song ID because the service has not been started correctly. Checking back it never calls the onBind method of the MusicService class. Normally this is caused by not having the service set correctly in the android manifest. However, I have looked at many help pages, changed my service call accordingly, and I don't think it's the source of the error. Any ideas what I am doing wrong?

Android manifest:

    <uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

    <service android:name="mycompany.mediaplayer.MusicService">
    </service>

</manifest>

MainActivity.java

package mycompany.mediaplayer;

import android.provider.BaseColumns;
import android.provider.UserDictionary;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

import android.os.IBinder;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.MenuItem;
import android.view.View;
import mycompany.mediaplayer.MusicService.MusicBinder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import android.net.Uri;
import android.content.ContentResolver;
import android.database.Cursor;
import android.widget.ListView;

public class MainActivity extends ActionBarActivity {

    private ArrayList<Song> songList;
    private ListView songView;
    private MusicService musicSrv;
    private Intent playIntent;
    private boolean musicBound=false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        songView = (ListView)findViewById(R.id.song_list);
        songList = new ArrayList<Song>();
        getSongList();
        Collections.sort(songList, new Comparator<Song>(){
            public int compare(Song a, Song b){
                return a.getTitle().compareTo(b.getTitle());
            }
        });
        SongAdapter songAdt = new SongAdapter(this, songList);
        songView.setAdapter(songAdt);
    }

    //connect to the service
    private ServiceConnection musicConnection = new ServiceConnection(){

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            MusicBinder binder = (MusicBinder)service;
            //get service
            musicSrv = binder.getService();
            //pass list
            musicSrv.setList(songList);
            musicBound = true;
            Log.i("onServiceConnected", "Have called connection");
            Log.i("onServiceConnected", String.valueOf(musicSrv));
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            musicBound = false;
        }
    };

    @Override
    protected void onStart() {
        super.onStart();
        if(playIntent==null){
            Log.i("onStart", "Binding service");
            playIntent = new Intent(this, MusicService.class);
            Log.i("onStart", "Play intent "+String.valueOf(playIntent));
            Log.i("onStart", String.valueOf(bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE)));
            startService(playIntent);
        }
        Log.i("onStart", "Have started the activity");
        Log.i("onStart", String.valueOf(musicSrv));
    }

    @Override
    protected void onDestroy() {
        stopService(playIntent);
        musicSrv=null;
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

//        //noinspection SimplifiableIfStatement
//        if (id == R.id.action_settings) {
//            return true;
//        }
        switch (item.getItemId()) {
            case R.id.action_shuffle:
                //shuffle
                break;
            case R.id.action_end:
                stopService(playIntent);
                musicSrv=null;
                System.exit(0);
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    public void songPicked(View view){
        Log.i("songPicked", String.valueOf(musicSrv));
        Log.i("songPicked", String.valueOf(Integer.parseInt(view.getTag().toString())));
        int song_number = Integer.parseInt(view.getTag().toString());
        Log.i("songPicked", String.valueOf(song_number));
        musicSrv.setSong(song_number);
        musicSrv.playSong();
    }

    public void getSongList() {
        //retrieve song info
        ContentResolver musicResolver = getContentResolver();

        Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null);
        Log.i("MusicRetriever", "Query finished. " + (musicCursor == null ? "Returned NULL." : "Returned a cursor."));
        if(musicCursor!=null && musicCursor.moveToFirst()){
            //get columns
            int titleColumn = musicCursor.getColumnIndex
                    (android.provider.MediaStore.Audio.Media.TITLE);
            int idColumn = musicCursor.getColumnIndex
                    (android.provider.MediaStore.Audio.Media._ID);
            int artistColumn = musicCursor.getColumnIndex
                    (android.provider.MediaStore.Audio.Media.ARTIST);
            //add songs to list
            do {
                long thisId = musicCursor.getLong(idColumn);
                String thisTitle = musicCursor.getString(titleColumn);
                String thisArtist = musicCursor.getString(artistColumn);
                songList.add(new Song(thisId, thisTitle, thisArtist));
                //Log.i("MusicRetriever", "Added song" + thisTitle);
            }
            while (musicCursor.moveToNext());
        }
    }
}

MusicService.java

package mycompany.mediaplayer;

/**
 * Created by Neill on 28/02/2016.
 */

import java.util.ArrayList;

import android.app.Service;
import android.content.ContentUris;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;

public class MusicService extends Service implements
        MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
        MediaPlayer.OnCompletionListener {

    //media player
    private MediaPlayer player;
    //song list
    private ArrayList<Song> songs;
    //current position
    private int songPosn;
    private final IBinder musicBind = new MusicBinder();

    public void onCreate(){
        //create the service
        super.onCreate();
        //initialize position
        songPosn=0;
        //create player
        Log.i("onCreate", "Creating service");
        player = new MediaPlayer();
        initMusicPlayer();
    }

    public void onCompletion(MediaPlayer mp) {
    }

    public boolean onError(MediaPlayer mp, int a, int b) {
        return false;
    }

    public void initMusicPlayer(){
        //set player properties
        player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
        player.setAudioStreamType(AudioManager.STREAM_MUSIC);
        player.setOnPreparedListener(this);
        player.setOnCompletionListener(this);
        player.setOnErrorListener(this);
    }

    public void setList(ArrayList<Song> theSongs){
        songs=theSongs;
    }

    public class MusicBinder extends Binder {
        MusicService getService() {
            return MusicService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("onBind", "Returning bind");
        return musicBind;
    }

    @Override
    public boolean onUnbind(Intent intent){
        player.stop();
        player.release();
        return false;
    }
    public void playSong(){
        //play a song
        player.reset();
        //get song
        Song playSong = songs.get(songPosn);
        //get id
        long currSong = playSong.getID();
        //set uri
        Uri trackUri = ContentUris.withAppendedId(
                android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                currSong);
        try{
            player.setDataSource(getApplicationContext(), trackUri);
        }
        catch(Exception e){
            Log.e("MUSIC SERVICE", "Error setting data source", e);
        }
        player.prepareAsync();
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        //start playback
        mp.start();
    }

    public void setSong(int songIndex){
        songPosn=songIndex;
    }

}

And finally the logcat:

03-17 21:55:43.986 29835-29835/? I/art: Late-enabling -Xcheck:jni
03-17 21:55:44.028 29835-29835/? W/ResourceType: Found multiple library tables, ignoring...
03-17 21:55:44.034 29835-29835/? W/ResourceType: Found multiple library tables, ignoring...
03-17 21:55:44.038 29835-29835/? W/ResourceType: Found multiple library tables, ignoring...
03-17 21:55:44.243 29835-29835/mycompany.mediaplayer I/MusicRetriever: Query finished. Returned a cursor.
03-17 21:55:44.308 29835-29835/mycompany.mediaplayer I/onStart: Binding service
03-17 21:55:44.308 29835-29835/mycompany.mediaplayer I/onStart: Play intent Intent { cmp=mycompany.mediaplayer/.MusicService }
03-17 21:55:44.310 29835-29835/mycompany.mediaplayer I/onStart: false
03-17 21:55:44.310 29835-29835/mycompany.mediaplayer I/onStart: Have started the activity
03-17 21:55:44.310 29835-29835/mycompany.mediaplayer I/onStart: null
03-17 21:55:44.322 29835-29864/mycompany.mediaplayer D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
03-17 21:55:44.337 29835-29835/mycompany.mediaplayer D/Atlas: Validating map...
03-17 21:55:44.399 29835-29864/mycompany.mediaplayer I/Adreno-EGL: <qeglDrvAPI_eglInitialize:410>: EGL 1.4 QUALCOMM build: AU_LINUX_ANDROID_LA.BF.1.1.1_RB1.05.01.00.042.030_msm8226_LA.BF.1.1.1_RB1__release_AU ()
                                                                   OpenGL ES Shader Compiler Version: E031.25.03.06
                                                                   Build Date: 06/10/15 Wed
                                                                   Local Branch: 
                                                                   Remote Branch: quic/LA.BF.1.1.1_rb1.24
                                                                   Local Patches: NONE
                                                                   Reconstruct Branch: AU_LINUX_ANDROID_LA.BF.1.1.1_RB1.05.01.00.042.030 + 6151be1 +  NOTHING
03-17 21:55:44.401 29835-29864/mycompany.mediaplayer I/OpenGLRenderer: Initialized EGL, version 1.4
03-17 21:55:44.421 29835-29864/mycompany.mediaplayer D/OpenGLRenderer: Enabling debug mode 0
03-17 21:55:44.780 29835-29835/mycompany.mediaplayer I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@285dcbce time:12224185
03-17 21:55:46.802 29835-29835/mycompany.mediaplayer I/songPicked: null
03-17 21:55:46.802 29835-29835/mycompany.mediaplayer I/songPicked: 6
03-17 21:55:46.802 29835-29835/mycompany.mediaplayer I/songPicked: 6
03-17 21:55:46.803 29835-29835/mycompany.mediaplayer D/AndroidRuntime: Shutting down VM
03-17 21:55:46.804 29835-29835/mycompany.mediaplayer E/AndroidRuntime: FATAL EXCEPTION: main
                                                                       Process: mycompany.mediaplayer, PID: 29835
                                                                       java.lang.IllegalStateException: Could not execute method of the activity
                                                                           at android.view.View$1.onClick(View.java:4096)
                                                                           at android.view.View.performClick(View.java:4856)
                                                                           at android.view.View$PerformClick.run(View.java:19956)
                                                                           at android.os.Handler.handleCallback(Handler.java:739)
                                                                           at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                           at android.os.Looper.loop(Looper.java:211)
                                                                           at android.app.ActivityThread.main(ActivityThread.java:5371)
                                                                           at java.lang.reflect.Method.invoke(Native Method)
                                                                           at java.lang.reflect.Method.invoke(Method.java:372)
                                                                           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:945)
                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:740)
                                                                        Caused by: java.lang.reflect.InvocationTargetException
                                                                           at java.lang.reflect.Method.invoke(Native Method)
                                                                           at java.lang.reflect.Method.invoke(Method.java:372)
                                                                           at android.view.View$1.onClick(View.java:4091)
                                                                           at android.view.View.performClick(View.java:4856) 
                                                                           at android.view.View$PerformClick.run(View.java:19956) 
                                                                           at android.os.Handler.handleCallback(Handler.java:739) 
                                                                           at android.os.Handler.dispatchMessage(Handler.java:95) 
                                                                           at android.os.Looper.loop(Looper.java:211) 
                                                                           at android.app.ActivityThread.main(ActivityThread.java:5371) 
                                                                           at java.lang.reflect.Method.invoke(Native Method) 
                                                                           at java.lang.reflect.Method.invoke(Method.java:372) 
                                                                           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:945) 
                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:740) 
                                                                        Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void mycompany.mediaplayer.MusicService.setSong(int)' on a null object reference
                                                                           at mycompany.mediaplayer.MainActivity.songPicked(MainActivity.java:130)
                                                                           at java.lang.reflect.Method.invoke(Native Method) 
                                                                           at java.lang.reflect.Method.invoke(Method.java:372) 
                                                                           at android.view.View$1.onClick(View.java:4091) 
                                                                           at android.view.View.performClick(View.java:4856) 
                                                                           at android.view.View$PerformClick.run(View.java:19956) 
                                                                           at android.os.Handler.handleCallback(Handler.java:739) 
                                                                           at android.os.Handler.dispatchMessage(Handler.java:95) 
                                                                           at android.os.Looper.loop(Looper.java:211) 
                                                                           at android.app.ActivityThread.main(ActivityThread.java:5371) 
                                                                           at java.lang.reflect.Method.invoke(Native Method) 
                                                                           at java.lang.reflect.Method.invoke(Method.java:372) 
                                                                           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:945) 
                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:740) 
03-17 21:55:48.109 29835-29835/mycompany.mediaplayer I/Process: Sending signal. PID: 29835 SIG: 9

Thanks in advance for your help.

Upvotes: 1

Views: 3144

Answers (1)

Neill Bowler
Neill Bowler

Reputation: 165

Fixed it!

It took a while to track down, but my mistake was putting the call to "service" outside the "application" section in the android manifest. Moving the service line upwards fixes the problem.

Upvotes: 2

Related Questions