2017-06-06 22 views
3

我在使用SyncAdapter時遇到了問題。最奇怪的是,它是早期工作,但現在只有手動調用它才能同步。Android SyncAdapter不會在我的設備上同步Samsung S6 Edge Plus Nougat(API <= 23時定期同步)

這還不算在模擬器(API 24)

這裏工作是我的同步適配器的代碼:

public class SmogAppSyncAdapter extends AbstractThreadedSyncAdapter { 

private static final String LOG_TAG = SmogAppSyncAdapter.class.getSimpleName(); 

public static final int SYNC_INTERVAL = 60; // 60 * 60 = 1h to the nearest 20min. 
public static final int SYNC_FLEXTIME = SYNC_INTERVAL/3; 
public static final int POLLUTION_DISTANCE = 10000; //preferred distance between prefs location and nearest measurement point 
private static final int POLLUTION_NOTIFICATION_ID = 0; 

private ContentResolver mContentResolver; 
private SharedPreferences prefs; 
private Context syncContext; 
private int prefsPollutionLevel; 
private double prefsHomeLocationLatitude; 
private double prefsHomeLocationLongitude; 
private boolean prefsNewMessageNotification; 
private int currentApiPollutionLevel; 
private Float currentApiPollutionLevelLatitude; 
private Float currentApiPollutionLevelLongitude; 


/** 
* Set up the sync adapter 
*/ 
SmogAppSyncAdapter(Context context, boolean autoInitialize) { 
    super(context, autoInitialize); 
    /* 
    * If your app uses a content resolver, get an instance of it 
    * from the incoming Context 
    */ 
    mContentResolver = context.getContentResolver(); 
    prefs = PreferenceManager.getDefaultSharedPreferences(context); 
    syncContext = context; 
    prefsHomeLocationLatitude = prefs.getFloat(syncContext.getResources().getString(R.string.pref_key_home_latitude), 0f); 
    prefsHomeLocationLongitude = prefs.getFloat(syncContext.getResources().getString(R.string.pref_key_home_longitude), 0f); 
    prefsNewMessageNotification = prefs.getBoolean(syncContext.getResources().getString(R.string.pref_key_notification_new_message), true); 
    prefsPollutionLevel = Integer.valueOf(prefs.getString(syncContext.getResources().getString(R.string.pref_key_pollution_level_list), "0")); 
} 

@Override 
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { 

    // fetching remote data and insert some stuff 

    Log.d(LOG_TAG, "onPerformSync was called"); 

} 

/** 
* Helper method to schedule the sync adapter periodic execution 
*/ 
private static void configurePeriodicSync(Context context, int syncInterval, int flexTime) { 
    Account account = getSyncAccount(context); 
    String authority = context.getString(R.string.content_authority); 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 
     // we can enable inexact timers in our periodic sync 
     SyncRequest request = new SyncRequest.Builder(). 
       syncPeriodic(syncInterval, flexTime). 
       setSyncAdapter(account, authority). 
       setExtras(new Bundle()).build(); 
     ContentResolver.requestSync(request); 
    } else { 
    ContentResolver.addPeriodicSync(account, 
      authority, new Bundle(), syncInterval); 
    } 
} 

/** 
* Helper method to have the sync adapter sync immediately 
* 
* @param context The context used to access the account service 
*/ 
private static void syncImmediately(Context context) { 
    Bundle bundle = new Bundle(); 
    bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 
    bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 
    ContentResolver.requestSync(getSyncAccount(context), 
      context.getString(R.string.content_authority), bundle); 
} 

/** 
* Helper method to get the fake account to be used with SyncAdapter, or make a new one 
* if the fake account doesn't exist yet. If we make a new account, we call the 
* onAccountCreated method so we can initialize things. 
* 
* @param context The context used to access the account service 
* @return a fake account. 
*/ 
public static Account getSyncAccount(Context context) { 
    Log.d(LOG_TAG, "getSyncAccount"); 
    // Get an instance of the Android account manager 
    AccountManager accountManager = 
      (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); 

    // Create the account type and default account 
    Account newAccount = new Account(
      context.getString(R.string.app_name), context.getString(R.string.sync_account_type)); 

    // If the password doesn't exist, the account doesn't exist 
    if (null == accountManager.getPassword(newAccount)) { 

    /* 
    * Add the account and account type, no password or user data 
    * If successful, return the Account object, otherwise report an error. 
    */ 
     if (!accountManager.addAccountExplicitly(newAccount, "", null)) { 
      Log.d(LOG_TAG, "return null"); 
      return null; 
     } 
     /* 
     * If you don't set android:syncable="true" in 
     * in your <provider> element in the manifest, 
     * then call ContentResolver.setIsSyncable(account, AUTHORITY, 1) 
     * here. 
     */ 

     onAccountCreated(newAccount, context); 
    } 
    else { 
     Log.d(LOG_TAG, "If the password doesn't exist, the account doesn't exist"); 
    } 
    Log.d(LOG_TAG, "Account name: " + newAccount.name); 
    return newAccount; 
} 

private static void onAccountCreated(Account newAccount, Context context) { 
    Log.d(LOG_TAG, "onAccountCreated"); 
    /* 
    * Since we've created an account 
    */ 
    SmogAppSyncAdapter.configurePeriodicSync(context, SYNC_INTERVAL, SYNC_FLEXTIME); 

    /* 
    * Without calling setSyncAutomatically, our periodic sync will not be enabled. 
    */ 
    ContentResolver.setIsSyncable(newAccount, context.getString(R.string.content_authority), 1); 
    ContentResolver.setSyncAutomatically(newAccount, context.getString(R.string.content_authority), true); 

    /* 
    * Finally, let's do a sync to get things started 
    */ 
    // syncImmediately(context); 
} 

public static void initializeSyncAdapter(Context context) { 
    Log.d(LOG_TAG, "inside initializeSyncAdapter"); 
    getSyncAccount(context); 
} 

}

我的服務:

public class SmogAppSyncService extends Service { 

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


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

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

}

在我的表現,我增加那些:

<service android:name=".services.sync.SmogAppAuthenticatorService"> 
     <intent-filter> 
      <action android:name="android.accounts.AccountAuthenticator" /> 
     </intent-filter> 

     <meta-data 
      android:name="android.accounts.AccountAuthenticator" 
      android:resource="@xml/authenticator" /> 
    </service> 
    <service 
     android:name=".services.sync.SmogAppSyncService" 
     android:exported="true" 
     android:process=":sync"> 
     <intent-filter> 
      <action android:name="android.content.SyncAdapter" /> 
     </intent-filter> 

     <meta-data 
      android:name="android.content.SyncAdapter" 
      android:resource="@xml/syncadapter" /> 
    </service> 

和權限:

<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> 
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> 
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> 

這裏是我的其他XML文件:

<?xml version="1.0" encoding="utf-8"?> 
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" 
android:contentAuthority="@string/content_authority" 
android:accountType="@string/sync_account_type" 
android:userVisible="false" 
android:supportsUploading="false" 
android:allowParallelSyncs="false" 
android:isAlwaysSyncable="true" /> 

Authetnicator.xml

<?xml version="1.0" encoding="utf-8"?> 
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" 
android:accountType="@string/sync_account_type" 
android:icon="@mipmap/ic_launcher" 
android:label="@string/app_name" 
android:smallIcon="@mipmap/ic_launcher" /> 

我可以提供更多的細節,如果它會b有幫助。我真的堅持這個問題,並檢查了一些在stackoverflow答案。對我沒有幫助。有什麼方法可以完成這項工作?週期性同步適用於仿真器,但不適用於真實設備。

更新:我已閱讀關於打盹模式,可能是原因,但它不是在我的情況下,或者我剛剛配置了錯誤的東西。 基本打盹模式與其電池優化可以禁用設備上的一些後臺任務。

+0

真實設備的品牌是什麼?我在Xiaomi設備中遇到類似這樣的問題。 –

+0

我的設備是三星Galaxy 6邊緣加,它以前工作,但不是現在 – Konrad

+0

我發現,這個問題只出現在我的手機到目前爲止,我有三星Galaxy S6邊緣加Android Nougat版本 – Konrad

回答

12

SyncRequest.Builder#syncPeriodic(long, long)的javadoc:

/** 
    * Build a periodic sync. 
    ... 
    * @param pollFrequency the amount of time in seconds that you wish 
    *   to elapse between periodic syncs. A minimum period of 1 hour is enforced. 
    ... 
    */ 
    public Builder syncPeriodic(long pollFrequency, long beforeSeconds) { 
     ... 
    } 

注意,它指出,1小時的最小週期同步超時強制執行。這可能是你的問題。

但是自從什麼時候?我從來沒有聽說過這麼久的超時。讓我們深入瞭解它。

我跑以下命令:

$ cd ~/aosp/frameworks/base 
$ find ./ -name SyncRequest.java | xargs git blame | grep "A minimum period of 1 hour is enforced" 

而得到這樣的結果:

e96c3b7eff52 (Shreyas Basarge 2016-01-29 19:25:51 +0000 310)   *   to elapse between periodic syncs. A minimum period of 1 hour is enforced. 

貌似提交從2016年一月這就解釋了爲什麼它適用於API 19而不是25

我進一步驗證了這個提交添加了將最小超時從60秒增加到1小時的代碼。

然後你可能會問,爲什麼谷歌開發人員會改變工作API的語義,而許多應用程序在沒有向開發人員提供適當通知的情況下依賴它?答案和往常一樣 - 因爲他們可以。

+0

不幸的是,即使我將同步週期更改爲1.5h,問題依然存在。 – Konrad