場景解析
信息同步場景很多,如電子郵件的收取欧漱、筆記應(yīng)用的云備份、天氣應(yīng)用的及時同步葬燎。核心訴求就是兩個:
- 把設(shè)備數(shù)據(jù)同步到服務(wù)器误甚。
- 把服務(wù)器數(shù)據(jù)同步設(shè)備。
解決方案
最簡單的解決方案谱净,就是終端直接發(fā)起網(wǎng)絡(luò)請求窑邦。但在Android設(shè)備上會存在 App被殺死,無法及時后臺同步壕探,導(dǎo)致再次啟動app時冈钦,數(shù)據(jù)沒有更新的情形。
有問題就需要解決方案李请,Android系統(tǒng)默認提供了一套方案——SyncAdapters瞧筛,其特點如下:
- 將所有的數(shù)據(jù)傳輸都放到同一個地方,以便操作系統(tǒng)智能地安排數(shù)據(jù)傳輸导盅,優(yōu)化電池性能较幌。
- 可以智能安排數(shù)據(jù)傳輸,如檢查網(wǎng)絡(luò)連接白翻、下載失敗后重試等乍炉。可以根據(jù)不同條件自動發(fā)起數(shù)據(jù)傳輸滤馍,如服務(wù)器數(shù)據(jù)變更岛琼、定時同步等。
- 使用SyncAdapter可以加快應(yīng)用的加載時間巢株、實現(xiàn)離線功能槐瑞,可以在數(shù)據(jù)及時同步和減少網(wǎng)絡(luò)調(diào)用以節(jié)約電池電量之間達到一種平衡局面。
如何配置SyncAdapter
首先纯续,我們要了解Android系統(tǒng)是如何理解同步這件事的随珠。對系統(tǒng)而言灭袁,同步的意思是某個賬戶
同步
某些數(shù)據(jù)
猬错。
所以,SyncAdapter需要上層應(yīng)用提供三類信息:賬戶茸歧、數(shù)據(jù)倦炒、同步動作。具體代碼如下:
1. 賬戶組件Authenticator
分為三個部分:賬戶認證软瞎、配置文件逢唤、賬戶認證服務(wù)拉讯。最后將賬戶認證服務(wù)注冊到Anroid系統(tǒng)中即可。
1.1 賬戶認證器 StubAuthenticator
系統(tǒng)設(shè)置→賬戶→添加賬戶
的時候會調(diào)用其 addAccount 方法鳖藕。下面是一個不需要賬戶認證的實現(xiàn)魔慷。
public class StubAuthenticator extends AbstractAccountAuthenticator {
public StubAuthenticator(Context context) {
super(context);
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
String s) {
throw new UnsupportedOperationException();
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, String s,
String s2, String[] strings, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
Account account, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
Account account, String s, Bundle bundle) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
@Override
public String getAuthTokenLabel(String s) {
throw new UnsupportedOperationException();
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
Account account, String s, Bundle bundle) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
Account account, String[] strings) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
}
1.2 配置文件為 authenticator.xml
放在 res/xml
目錄下,一般內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.eutechpro.syncadapterexample"
android:icon="@drawable/ic_launcher"
android:smallIcon="@drawable/ic_launcher"
android:label="@string/app_name" />
-
android:accountType
:賬戶類型著恩,系統(tǒng)唯一院尔,一般采用系統(tǒng)包名為前綴 -
android:icon
:圖標,顯示在“設(shè)置”應(yīng)用的“賬號”一項中喉誊。 -
android:smallIcon
:小圖標邀摆,根據(jù)屏幕尺寸可能在設(shè)置中代替icon屬性。 -
android:label
:標識賬戶類型伍茄,一般為應(yīng)用名栋盹,顯示在“設(shè)置”應(yīng)用的“賬號”一項中。
1.3 賬戶認證服務(wù) StubAuthenticatorService
溝通 SyncAdapter framework 和 Authenticator敷矫,提供一個遠程程序調(diào)用RPC 的 IBinder
public class StubAuthenticatorService extends Service {
private StubAuthenticator authenticator;
@Override
public void onCreate() {
authenticator = new StubAuthenticator(this);
}
/*
* When the system binds to this Service to make the RPC call
* return the authenticator’s IBinder.
*/
@Override
public IBinder onBind(Intent intent) {
return authenticator.getIBinder();
}
}
1.4 將Service注冊到系統(tǒng)中
<service android:name="ch.teleboy.sync_app_settings.StubAuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
2. 數(shù)據(jù)組件ContentProvider例获。
下面是一個空Provider的實現(xiàn)。
public class StubContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] columns, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
return null;
}
@Override
public int delete(Uri uri, String s, String[] strings) {
return 0;
}
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
}
同樣需要將其注冊到系統(tǒng)中:
<provider
android:name="ch.teleboy.sync_app_settings.StubContentProvider"
android:authorities="com.eutechpro.syncadapterexample.provider"
android:exported="false"
android:syncable="true"></provider>
3. 同步組件SyncAdapter沪饺。
氛圍三個部分:同步器躏敢、配置文件、同步服務(wù)整葡。最后將同步服務(wù)注冊到系統(tǒng)即可件余。
3.1 同步器SyncAdapter
最終同步相關(guān)代碼放在onPerformSync
方法里面。
public class SyncAdapter extends AbstractThreadedSyncAdapter {
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient contentProviderClient, SyncResult syncResult) {
System.out.println("******* onPerformSync *******");
// System.out.println("*******" + syncResult.syncAlreadyInProgress+" *******");
System.out.println("*****************************");
}
}
3.2 配置文件
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.eutechpro.syncadapterexample.provider"
android:accountType="com.eutechpro.syncadapterexample"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"
android:supportsUploading="false"
android:userVisible="true" />
-
android:contentAuthority
指定要同步的ContentProvider在其AndroidManifest.xml文件中有個android:authorities屬性遭居。 -
android:accountType
表示進行同步的賬號的類型啼器。 -
android:allowParallelSyncs
是否支持多賬號同時同步 -
android:isAlwaysSyncable
設(shè)置所有賬號的isSyncable為true -
android:supportsUploading
設(shè)置是否必須notifyChange通知才能同步 -
android:syncAdapterSettingsAction
指定一個可以設(shè)置同步的activity的Action。 -
android:userVisible
設(shè)置是否在“設(shè)置”中顯示
3.3 同步服務(wù)
public class SyncAdapterService extends Service {
private static SyncAdapter syncAdapter = null;
// Object to use as a thread-safe lock
private static final Object syncAdapterLock = new Object();
@Override
public void onCreate() {
super.onCreate();
/*
* Create the sync adapter as a singleton.
* Set the sync adapter as syncable
* Disallow parallel syncs
*/
synchronized (syncAdapterLock) {
if (syncAdapter == null) {
syncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
/**
* Return an object that allows the system to invoke the sync adapter.
*/
@Override
public IBinder onBind(Intent intent) {
/*
* Get the object that allows external processes
* to call onPerformSync(). The object is created
* in the base class code when the SyncAdapter
* constructors call super()
*/
return syncAdapter.getSyncAdapterBinder();
}
}
如何使用SyncAdapter
- 建立賬戶init俱萍。
- 主動調(diào)用forceRefresh 或者等待系統(tǒng)擇機調(diào)用端壳。
參考代碼如下:
public static void init(Context context) {
newAccount = new Account(ACCOUNT, ACCOUNT_TYPE);
AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
if (accountManager.addAccountExplicitly(newAccount, null, null)) {
System.out.println("添加 acc");
} else {
System.out.println("已經(jīng)添加過啦");
}
ContentResolver.setSyncAutomatically(newAccount, AUTHORITY, true);
}
public static void forceRefresh() {
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
ContentResolver.requestSync(newAccount, AUTHORITY, bundle);
}
注意:
xml中的各處的 android:accountType、android:contentAuthority 必須保持一直枪蘑。
參考:
- Demo:看其備注损谦,是斯洛文尼亞人寫的,非常簡潔岳颇。
- 在Android中使用SyncAdapter同步數(shù)據(jù)全攻略