I am experiencing a weird ClassCastException
exception when trying to cast an IBinder
to a local's service Binder. This happens once in a while, and I personally cannot reproduce it (only know about it thanks to Crashlytics). The stacktrace is as follows:
Fatal Exception: java.lang.ClassCastException: android.os.BinderProxy cannot be cast to gr.sport24.radio.MediaPlayerService$MediaPlayerBinder
at gr.sport24.radio.RadioManager$RadioServiceConnection.onServiceConnected(RadioManager.java:740)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1203)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1220)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5536)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1397)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1192)
This is the code that initiates the connection:
mApplicationContext.bindService(
new Intent(mApplicationContext, MediaPlayerService.class),
new RadioServiceConnection(),
Context.BIND_AUTO_CREATE
);
This is the RadioServiceConnection inner class:
private class RadioServiceConnection implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName className, IBinder serviceBinder) {
synchronized (RADIO_LOCK) {
// This is the line where ClassCastException sometimes occurs
MediaPlayerService.MediaPlayerBinder binder =
(MediaPlayerService.MediaPlayerBinder) serviceBinder;
mRadioService = binder.getService();
mRadioService.setClient(RadioManager.this);
}
mIsConnectingToRadioService.set(false);
mIsBoundToRadioService = true;
if (mAutoStartOnBind) startRadioPlayer();
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mIsConnectingToRadioService.set(false);
mIsBoundToRadioService = false;
stopRadioPlayer();
}
}
This is the MediaPlayerService class (slim version):
public class MediaPlayerService extends Service
{
private final Binder mBinder;
...
public MediaPlayerService() {
mBinder = new MediaPlayerBinder();
...
}
@Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
...
public class MediaPlayerBinder extends Binder
{
public MediaPlayerService getService() {
return MediaPlayerService.this;
}
}
}
This is the BroadcastReceiver that receives local notification button events (a local notification is created in order to play/pause radio):
private class LocalRadioNotificationEventBroadcastReceiver extends BroadcastReceiver
{
@Override
public void onReceive (Context context, Intent intent) {
if (Static.debug) Log.d(TAG, "Received local radio notification event.");
if (intent == null) return;
String action = (intent.getAction() != null) ? intent.getAction() : "";
if (RadioNotificationEventsReceiver.ACTION_PLAY_PAUSE.equals(action)) {
onRadioNotificationAction(ACTION_PLAY_PAUSE);
} else if (RadioNotificationEventsReceiver.ACTION_CLOSE.equals(action)) {
onRadioNotificationAction(ACTION_CLOSE);
} else {
if (Static.debug) {
Log.w(TAG, "Unknown radio notification action [" + action + "].");
}
}
}
}
Note: onRadioNotificationAction()
does not do anything fancy, it just checks what action was executed and acts accordingly. If for some reason connection to service is lost, bindService()
will be called indirectly from this method.
This is the Manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<permission
android:name="gr.sport24.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="gr.sport24.permission.C2D_MESSAGE" />
<application
android:name=".Application"
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="@drawable/sport24"
android:label="@string/app_name"
android:theme="@style/Theme.Sport24"
tools:replace="android:icon">
<meta-data
android:name="com.google.android.gms.analytics.globalConfigResource"
android:resource="@xml/global_tracker" />
<activity android:name=".activities.MainActivity"
android:launchMode="singleTop" />
<activity android:name=".activities.SplashActivity"
android:theme="@style/Theme.sport24.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activities.LiveViewerActivity"
android:launchMode="singleTask">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity android:name=".activities.WebViewerPlainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity android:name=".activities.WebViewURLActivity"
android:launchMode="singleTask">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity android:name=".activities.WebViewerActivity"
android:launchMode="singleTask">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity android:name=".activities.LoadingPlayerActivity"
android:theme="@style/Theme.sport24.Transparent.NoActionBar" >
</activity>
<activity android:name=".activities.TeamSelectorActivity" />
<activity android:name=".activities.PreferenceActivity"
android:windowSoftInputMode="stateAlwaysHidden" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity android:name=".activities.AboutActivity"
android:windowSoftInputMode="stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.PreferenceActivity" />
</activity>
<activity android:name=".activities.GalleryActivity"
android:theme="@style/Theme.sport24.TranslusentActionBar"
android:windowSoftInputMode="stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity android:name=".activities.RadioActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<service android:name="gr.sport24.radio.MediaPlayerService" />
<!-- Receiver used for listening to radio notification actions. -->
<receiver android:name="gr.sport24.radio.RadioNotificationEventsReceiver">
<intent-filter>
<action android:name="gr.sport24.radio.intent.action.ACTION_PLAY_PAUSE" />
<action android:name="gr.sport24.radio.intent.action.ACTION_CLOSE" />
</intent-filter>
</receiver>
<service android:name="com.pushmyapps.push.GcmMessageListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<service android:name="com.pushmyapps.push.GcmTokenRefreshListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID"/>
</intent-filter>
</service>
<service android:name="com.pushmyapps.location.GeofencesIntentService" />
<service android:name="com.pushmyapps.sync.SyncService"
android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
</intent-filter>
</service>
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="gr.sport24" />
</intent-filter>
</receiver>
<receiver
android:name="gr.sport24.PushManager$CustomPushBroadcastReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.pushmyapps.push.intent.RECEIVE" />
<action android:name="com.pushmyapps.push.intent.OPEN" />
<action android:name="com.pushmyapps.push.intent.CLOSE" />
</intent-filter>
</receiver>
<receiver
android:name="com.pushmyapps.BootUpBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
Usually, this crash happens when a service is declared with the android:process
attribute, which indicates that service will be started in a separate process, but this is clearly not my case.
Is it possible for the onServiceConnected()
to receive the "wrong" IBinder
? I haven't found any evidence of this in the docs though.
Does anyone know how to fix this ? Any suggestions are welcome!