3

My app includes a sync adapter; it has been working fine for years, but recently I've received a very strange crash report:

Samsung Galaxy S6 Edge+ (zenlte), 4096MB RAM, Android 7.0

java.lang.ClassCastException:
1. at com.myappid.android.ScormApplication.getApplication (ScormApplication.java:34)
2. at com.myappid.android.sync.SyncAdapter.onPerformSync (SyncAdapter.java:98)
3. at android.content.AbstractThreadedSyncAdapter$SyncThread.run (AbstractThreadedSyncAdapter.java:272)

ScormApplication.java:

public class ScormApplication extends Application {
    //...
    public static ScormApplication getApplication(Context context) {
        if (context instanceof ScormApplication) {
            return (ScormApplication) context;
        }
        return (ScormApplication) context.getApplicationContext(); // this is a line 34
    }
    //...
}

SyncAdapter.java:

public class SyncAdapter extends AbstractThreadedSyncAdapter {
    //...
    @Override
    public void onPerformSync(Account account,
                              Bundle extras,
                              String authority,
                              ContentProviderClient provider,
                              SyncResult syncResult) {
        // sync code here...
        ScormApplication.getApplication(getContext()).sendOttoEvent(ottoEvent); // this is a line 98
    }
}

Thus, it means that the returned object from the method getApplicationContext called on SyncAdapter's context is not an instance of my application class. But how can it be possible?

Here is a bit more of my code:

AndroidManifest.xml (no separate process is declared) :

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
    <application ...>
        ...
        <service
            android:name="com.myappid.android.sync.SyncService"
            android:exported="true">
            <intent-filter>
                <action android:name="android.content.SyncAdapter" />
            </intent-filter>
            <meta-data
                android:name="android.content.SyncAdapter"
                android:resource="@xml/syncadapter" />
        </service>
        ...

SyncService.java:

public class SyncService extends Service {

    private static SyncAdapter sSyncAdapter = null;
    private static final Object sSyncAdapterLock = new Object();

    @Override
    public void onCreate() {
        synchronized (sSyncAdapterLock) {
            if (sSyncAdapter == null) {
                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return sSyncAdapter.getSyncAdapterBinder();
    }
}

@xml/syncadapter:

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
          android:contentAuthority="@string/content_authority"
          android:accountType="@string/account_type"
          android:userVisible="true"
          android:supportsUploading="true"
          android:allowParallelSyncs="false"
          android:isAlwaysSyncable="true" />
Dima Kozhevin
  • 3,602
  • 9
  • 39
  • 52
Mikalai Daronin
  • 8,590
  • 2
  • 35
  • 47
  • Are you declaring the application class in your manifest? – Nick Shvelidze Nov 01 '17 at 11:47
  • @NickShvelidze of course – Mikalai Daronin Nov 01 '17 at 11:50
  • Application is a subclass of `ContextWrapper`, so I guess `getApplicationContext` returns the wrapped context. – Nick Shvelidze Nov 01 '17 at 11:54
  • Just store the Application instance in a private field at `onCreate` and return it. – Nick Shvelidze Nov 01 '17 at 11:55
  • @NickShvelidze well, it may be a workaround, but I'm interested what was returned from `getApplicationContext` in that case. – Mikalai Daronin Nov 01 '17 at 11:57
  • @NickShvelidze > _getApplicationContext returns the wrapped context_ It sounds quite odd since my code works fine on many other devices. Could you point me to any documentation? – Mikalai Daronin Nov 01 '17 at 11:59
  • It may be the context passed to Application at `attach(Context)` http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/java/android/app/Application.java#188 – Nick Shvelidze Nov 01 '17 at 12:04
  • Try `context == getBaseContext()` in `getApplication` – Nick Shvelidze Nov 01 '17 at 12:07
  • 1
    I'm also experiencing this problem, exactly as you said. Been working for years, but now I get these random ClassCastExceptions. My suspicion points to updating the buildSdk to 26, since that's one of the most obvious changes – Durican Radu Dec 05 '17 at 12:02
  • @DuricanRadu my app had `compileSdkVersion` = `23`, `buildToolsVersion` = `23.0.2`, and `targetSdkVersion` = `23` when I got this exception. – Mikalai Daronin Dec 05 '17 at 15:28
  • @NikolaiDoronin Ow okay. The fix that we're working on right now is, trying to replace getApplicationContext() with getApplication() for Activities and Services, and also if that's not possible, just make the Application be accessible from a Singleton. I know it's not pretty but as an experiment to see if we still get the crash reports – Durican Radu Dec 06 '17 at 11:43

0 Answers0