Android SyncAdapter不会在我的设备上同步Samsung S6 Edge Plus Nougat(API <= 23时定期同步)

问题描述:

我在使用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有帮助。我真的坚持这个问题,并检查了一些在*答案。对我没有帮助。有什么方法可以完成这项工作?周期性同步适用于仿真器,但不适用于真实设备。

更新:我已阅读关于打盹模式,可能是原因,但它不是在我的情况下,或者我刚刚配置了错误的东西。 基本打盹模式与其电池优化可以禁用设备上的一些后台任务。

+0

真实设备的品牌是什么?我在Xiaomi设备中遇到类似这样的问题。 –

+0

我的设备是三星Galaxy 6边缘加,它以前工作,但不是现在 – Konrad

+0

我发现,这个问题只出现在我的手机到目前为止,我有三星Galaxy S6边缘加Android Nougat版本 – Konrad

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