CriminalDuck CriminalDuck - 1 month ago 8
Android Question

AbstractThreadedSyncAdapter doesn't fire right away

I've been wondering why the AbstractThreadedSyncAdapter doesn't perform Sync once i run the app but instead it waits like 5-10 mins then it starts to download the data using an API request and fills my tables ,which causes this error :

02-03 05:03:34.954 6061-6061/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.moviesapp.app, PID: 6061
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.example.android.moviesapp.app/com.example.android.moviesapp.app.MainActivity}:
java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2184)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
at com.example.android.moviesapp.app.DetailFragment.ReadTrailersDetailsFromDB(DetailFragment.java:70)
at com.example.android.moviesapp.app.DetailFragment.onCreateView(DetailFragment.java:114)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:1962)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1613)
at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:330)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:547)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1171)
at android.app.Activity.performStart(Activity.java:5241)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2157)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233) 
at android.app.ActivityThread.access$800(ActivityThread.java:135) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:136) 
at android.app.ActivityThread.main(ActivityThread.java:5001) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:515) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) 
at dalvik.system.NativeStart.main(Native Method)

Answer

The whole point of the Android SyncManager is that you let it decide when it wants to do a sync. This is for battery efficiency so that the system has the flexibility to batch sync operations from different apps together. Doing so means that the system can turn off the power hungry radio for longer periods of time when all apps have synced.

So, if you use the Android SyncManager (by implementing the SyncAdapter pattern) then you must expect it to call SyncAdapter.onPerformSync() at times that you cannot control.

From what I understand, your Activity crashes because it is reading empty tables? And you expected the tables to have been filled by your SyncAdapter already?

There is no way around it, you must make your Activities so that they can deal with empty tables. You will have to put in an extra check for that.

Note that you can trigger an immediate sync with this:

/**
 * Starts a manual sync operation, i.e. independent of the automatic sync settings.
 *
 * @param context   System's service locator
 * @param account   The account to sync
 * @param expedited Make this sync a priority system wide
 */
public static void requestSync(Context context, Account account, boolean expedited) {
    Bundle extras = new Bundle();
    extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
    extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, expedited);
    ContentResolver.requestSync(account, Contract.AUTHORITY, extras);
}

You provide the Account that you want to sync and set expedited to true to make it so that the Android SyncManager will sync your account before any other apps. Because this sync request is set to 'manual' it means the SyncManager will not wait for a convenient time, it will just turn on the radio and start syncing all apps, your's first.

But even if you do a requestSync, you should expect that the internet is slow and the results will take some time from milliseconds to seconds or even minutes before they are written to your database by your SyncAdapter. In the meantime your Activity has already started up and is reading empty tables...

So make sure you Activities do not crash on empty tables.

Comments