Reputation: 22018
I have an Application class like this:
public class MyApplication extends Application {
}
It is registered in the Manifest:
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
...
</application>
I use this Application class for keeping Dagger components etc.
Now I have a BroadcastReceiver:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent){
MyApplication myApplication = (MyApplication) context.getApplicationContext();
}
}
It is registered in the Manifest as a InstallReferrerReceiver:
<receiver
android:name="my.package.MyReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER"/>
</intent-filter>
</receiver>
As you can see I cast the application context to my Application class, which works fine in Activities etc. and also usually here.
Via Crashlytics though I receive exception:
Unable to start receiver my.package.BroadcastReceiver: java.lang.ClassCastException: android.app.Application cannot be cast to my.package.MyApplication
My question being: am I not guaranteed to receive my Application object as the application Context in a BroadcastReceiver?
Upvotes: 3
Views: 1570
Reputation: 346
If you look at the implementation of ActivityThread.handleReceiver you'll see that the BroadcastReceiver.onReceived is invoked by passing ContextImpl.getReceiverRestrictedContext() to it. The context returned by this call doesn't actually wrap getApplicationContext so ti will be called on ContextImpl. Now if you look at ContextImpl.getApplicationContext() you'll see something like this
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ? mPackageInfo.getApplication() :
mMainThread.getApplication();
}
If you look at the last branch of the ternary operator you'll see that it will call back to ActivityThread.getApplication()
which will return it's mInitialApplication
member. mInitialApplication
is initialized by invoking LoadedApk.makeApplication()
which has a boolean parameter : forceDefaultAppClass
. If set to true android.app.Application
will instantiated instead the application defined in your manifest.
Based on AOSP source this happens when for e.g.:
If the app is being launched for full backup or restore, bring it up in a restricted environment with the base application class.
Upvotes: 2