aycanadal aycanadal - 2 months ago 18
Android Question

Reference to service becomes null

I have an activity that starts and binds to a service. I enable the Don't Keep Activities option in Developer Options of the phone to test this.

When I run the app the first time both the activity and the service starts. I can use the app without problems. Then I hit the home button and both the activity and service are killed instantly. Then I launch the app again, the activity starts as normal, service starts and gets bound as normal, and the service instance is saved in the musicService variable as normal. Then I use the activity which tries to call a method on musicService and it crashes because musicService is null.

I am starting the service and binding to it in onStart of my activity:

@Override
protected void onStart() {
super.onStart();
initMusicService();
}

private void initMusicService() {

LocalBroadcastManager.getInstance(this).registerReceiver(onMediaPlayerPlayingReceiver,
new IntentFilter("MEDIA_PLAYER_PLAYING"));

if (musicServiceIntent == null) {

musicServiceIntent = new Intent(this, MusicService.class);
bindService(musicServiceIntent, musicServiceConnection, 0);
startService(musicServiceIntent);

}

}


And then I am saving a reference to the service when it's connected:

private ServiceConnection musicServiceConnection = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName name, IBinder service) {

MusicBinder binder = (MusicBinder) service;
musicService = binder.getService();
...

}

@Override
public void onServiceDisconnected(ComponentName name) {

tabPagerAdapter.getNowPlayingFragment().unbindController();

}
};


And this is the onDestroy method of my activity:

@Override
protected void onDestroy() {

unbindService(musicServiceConnection);
stopService(musicServiceIntent);
musicService = null;

super.onDestroy();

}


This is the error in logcat:

09-01 07:41:25.393: E/AndroidRuntime(20592): FATAL EXCEPTION: main
09-01 07:41:25.393: E/AndroidRuntime(20592): java.lang.NullPointerException
09-01 07:41:25.393: E/AndroidRuntime(20592): at com.zezo.music.MusicPlayerActivity$2.onReceive(MusicPlayerActivity.java:101)
09-01 07:41:25.393: E/AndroidRuntime(20592): at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
09-01 07:41:25.393: E/AndroidRuntime(20592): at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
09-01 07:41:25.393: E/AndroidRuntime(20592): at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
09-01 07:41:25.393: E/AndroidRuntime(20592): at android.os.Handler.dispatchMessage(Handler.java:99)
09-01 07:41:25.393: E/AndroidRuntime(20592): at android.os.Looper.loop(Looper.java:137)
09-01 07:41:25.393: E/AndroidRuntime(20592): at android.app.ActivityThread.main(ActivityThread.java:5103)
09-01 07:41:25.393: E/AndroidRuntime(20592): at java.lang.reflect.Method.invokeNative(Native Method)
09-01 07:41:25.393: E/AndroidRuntime(20592): at java.lang.reflect.Method.invoke(Method.java:525)
09-01 07:41:25.393: E/AndroidRuntime(20592): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
09-01 07:41:25.393: E/AndroidRuntime(20592): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
09-01 07:41:25.393: E/AndroidRuntime(20592): at dalvik.system.NativeStart.main(Native Method)


I use breakpoints after activity is relaunched and see that onServiceConnected is called,
musicService = binder.getService();
is run and musicService is not null.

In the same activity I have:

public void play(Song song) {

if (song != null && musicService.audioFocusGranted()) {
musicService.playSong(song);
Toast.makeText(this, "Playing.", Toast.LENGTH_SHORT).show();
}

}


and

private BroadcastReceiver onMediaPlayerPlayingReceiver = new BroadcastReceiver() {

@Override
public void onReceive(Context c, Intent i) {

if (i.getAction() != "MEDIA_PLAYER_PLAYING")
return;

Song song = musicService.getCurrentSong();

...

}
};


After relaunch, musicService in the play method is not null and the service assigned to it works fine but the one in onReceive is null. And the musicService is a member on the activity instance so it should have the same value in both methods. Somehow it is as if it first gets assigned but then gets unassigned between the calls or gets assigned in an activity instance but gets called in the destroyed instance of the same activity somehow.

The manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zezo.music"
android:versionCode="155"
android:versionName="1.55">

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />

<!-- Soft input delete key not working bug above target 13 and above min 8. -->
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="23" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/MyTheme">
<activity
android:name="com.zezo.music.MusicPlayerActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTop"
android:screenOrientation="fullSensor"
android:windowSoftInputMode="stateHidden">

<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />

<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.MAIN" />

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

<service android:name="com.zezo.music.MusicService">
<intent-filter>
<action android:name="com.zezo.zezomusicplayer.MusicService.BIND" />

<category android:name="android.intent.category.DEFAULT" />

<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
</intent-filter>
</service>

<receiver
android:name="com.zezo.music.MediaButtonReceiver"
android:enabled="true">
<intent-filter android:priority="2147483647">
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>

</application>



Answer

The only reason an Activity should register BroadcastReceivers is to use the events in some way on the current activity, to inform the User of an event. If onPause() has been called, then the Activity is no longer in the foreground, and therefore can't update the User.

You should register and unregister your receivers in onStart() and onStop(). onDestroy() is not guaranteed to be called, and you could continue receiving broadcasts for a long time, even when the Activity is no longer open.

Comments