ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2019-07-13 20:24:44  阅读:175  来源: 互联网

标签:android android-service android-syncadapter


我使用SyncAdapter时遇到问题.关于它的最奇怪的事情是它早先工作,但现在同步只有在我手动调用时才有效.

它也不适用于模拟器(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" />

如果它有用,我可以提供更多细节.我真的坚持这个问题并在stackoverflow中检查了一些答案.对我没有帮助.有没有办法让这项工作?周期性同步适用于仿真器,但不适用于实际设备.

更新:我读过可能是原因的打盹模式,但它不是我的情况,或者我只是配置错误.
基本上打盹模式及其电池优化可以禁用设备上的一些后台任务.

解决方法:

来自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年1月开始的.这解释了为什么它适用于API 19但不适用于25.

我进一步验证了此提交添加了将最小超时从60秒增加到1小时的代码.

然后你可能会问为什么谷歌开发人员会改变许多应用程序所依赖的工作API的语义而没有向开发人员发出适当的通知?答案和往常一样 – 因为他们可以.

标签:android,android-service,android-syncadapter
来源: https://codeday.me/bug/20190713/1453123.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有