優勢:android
1.自動同步數據(根據時間,數據變化),把不知足條件的同步操做加入隊列,當知足條件時自動運行。服務器
2.多個應用協做同步網絡
3.同步代碼插件化app
4.自動網絡鏈接檢測 系統只會在聯網狀態下進行數據同步框架
5.節約電量 把數據同步操做放在一塊,而且多個應用協做dom
6.帳號管理和身份驗證async
由於同步適配器框架假設你須要在某帳號下的本地數據和須要登陸驗證的服務器傳遞數據,因此同步適配器框架須要你有一個身份驗證者。身份驗證者爲身份驗證提供標準的接口,如登陸等。ide
即使你不使用帳號,你也須要提供一個身份驗證者。而且和一個服務綁定(bound service),該服務用來讓同步適配框架調用身份驗證者的方法。this
1.具體實現:spa
/* * Implement AbstractAccountAuthenticator and stub out all * of its methods */ public class Authenticator extends AbstractAccountAuthenticator { // Simple constructor public Authenticator(Context context) { super(context); } // Editing properties is not supported @Override public Bundle editProperties( AccountAuthenticatorResponse r, String s) { throw new UnsupportedOperationException(); } // Don't add additional accounts @Override public Bundle addAccount( AccountAuthenticatorResponse r, String s, String s2, String[] strings, Bundle bundle) throws NetworkErrorException { return null; } // Ignore attempts to confirm credentials @Override public Bundle confirmCredentials( AccountAuthenticatorResponse r, Account account, Bundle bundle) throws NetworkErrorException { return null; } // Getting an authentication token is not supported @Override public Bundle getAuthToken( AccountAuthenticatorResponse r, Account account, String s, Bundle bundle) throws NetworkErrorException { throw new UnsupportedOperationException(); } // Getting a label for the auth token is not supported @Override public String getAuthTokenLabel(String s) { throw new UnsupportedOperationException(); } // Updating user credentials is not supported @Override public Bundle updateCredentials( AccountAuthenticatorResponse r, Account account, String s, Bundle bundle) throws NetworkErrorException { throw new UnsupportedOperationException(); } // Checking features for the account is not supported @Override public Bundle hasFeatures( AccountAuthenticatorResponse r, Account account, String[] strings) throws NetworkErrorException { throw new UnsupportedOperationException(); } }
2.建立綁定服務:
/** * A bound Service that instantiates the authenticator * when started. */ public class AuthenticatorService extends Service { ... // Instance field that stores the authenticator object private Authenticator mAuthenticator; @Override public void onCreate() { // Create a new authenticator object mAuthenticator = new Authenticator(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 mAuthenticator.getIBinder(); } }
3.建立身份驗證者的元數據(Metadata)文件
爲了描述身份驗證者的一些信息,須要向同步適配框架和帳號框架提供一個metadata文件,該文件放在res/xml/目錄下通常命名爲authenticator.xml
metadata主要描述兩方面信息:1.帳號類型 2.用戶界面元素(若是你想讓你的帳號類型展現給用戶)
字段:
accountType:同步適配框架須要每一個同步適配有一個帳號類型,做爲內部的身份確認。對於須要登陸的服務端,該值也和yoghurt帳號之前做爲身份驗證信息。它的值內容可任選
todo
例子:
<?xml version="1.0" encoding="utf-8"?> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="example.com" android:icon="@drawable/ic_launcher" android:smallIcon="@drawable/ic_launcher" android:label="@string/app_name"/>
4.在Manifest中聲明
<service android:name="com.example.android.syncadapter.AuthenticatorService"> <intent-filter> <action android:name="android.accounts.AccountAuthenticator"/> </intent-filter> <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" /> </service>
其中action用來啓動身份驗證者。
同步適配器框架使用content provider 框架管理數據,必須有一個cotent provider。即使是此content provider 不被使用。
具體代碼例子:
/* * Define an implementation of ContentProvider that stubs out * all methods */ public class StubProvider extends ContentProvider { /* * Always return true, indicating that the * provider loaded correctly. */ @Override public boolean onCreate() { return true; } /* * Return no type for MIME type */ @Override public String getType(Uri uri) { return null; } /* * query() always returns no results * */ @Override public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } /* * insert() always returns null (no URI) */ @Override public Uri insert(Uri uri, ContentValues values) { return null; } /* * delete() always returns "no rows affected" (0) */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } /* * update() always returns "no rows affected" (0) */ public int update( Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }
在manifest聲明provider:
syncable: 此provider是否能夠和sync adapter傳遞數據。但數據交換須要手動觸發。
其餘配置和普通provider一致
1.sync adapter作和服務端同步的邏輯,作同步時,sync adapter裏的方法被執行。
建立sync adapter 和 建立身份驗證者步驟相似。包括adapter class 、bound service 、xml metadaa file 、manifest聲明。
在Sync Adapter的構造方法裏作一些初始化操做。android3.0後構造方法增長了parallelSync參數,因此爲了兼容須要建立兩個構造方法。
sync adapter 應該是一個單例
建立具體代碼:
/** * Handle the transfer of data between a server and an * app, using the Android sync adapter framework. */ public class SyncAdapter extends AbstractThreadedSyncAdapter { ... // Global variables // Define a variable to contain a content resolver instance ContentResolver mContentResolver; /** * Set up the sync adapter */ public SyncAdapter(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(); } ... /** * Set up the sync adapter. This form of the * constructor maintains compatibility with Android 3.0 * and later platform versions */ public SyncAdapter( Context context, boolean autoInitialize, boolean allowParallelSyncs) { super(context, autoInitialize, allowParallelSyncs); /* * If your app uses a content resolver, get an instance of it * from the incoming Context */ mContentResolver = context.getContentResolver(); ... }
2.在onPerformSync方法中編寫數據同步代碼
/* * Specify the code you want to run in the sync adapter. The entire * sync adapter runs in a background thread, so you don't have to set * up your own background processing. */ @Override public void onPerformSync( Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { /* * Put the data transfer code here. */ ... }
3.把sync adapter綁定到sync adapter框架
建立一個bound service用例綁定到sync adapter框架
sync adapter 須要是單例,而且加鎖
package com.example.android.syncadapter; /** * Define a Service that returns an IBinder for the * sync adapter class, allowing the sync adapter framework to call * onPerformSync(). */ public class SyncService extends Service { // Storage for an instance of the sync adapter private static SyncAdapter sSyncAdapter = null; // Object to use as a thread-safe lock private static final Object sSyncAdapterLock = new Object(); /* * Instantiate the sync adapter object. */ @Override public void onCreate() { /* * Create the sync adapter as a singleton. * Set the sync adapter as syncable * Disallow parallel syncs */ synchronized (sSyncAdapterLock) { if (sSyncAdapter == null) { sSyncAdapter = 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 sSyncAdapter.getSyncAdapterBinder(); } }
4.添加框架須要的帳號
sync adapter 框架須要每一個sync adapter有一個帳戶類型,即在建立Authenticator是定義的。
調用addAccountExplicitly方法添加
public class MainActivity extends FragmentActivity { ... ... // Constants // The authority for the sync adapter's content provider public static final String AUTHORITY = "com.example.android.datasync.provider"; // An account type, in the form of a domain name public static final String ACCOUNT_TYPE = "example.com"; // The account name public static final String ACCOUNT = "dummyaccount"; // Instance fields Account mAccount; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // Create the dummy account mAccount = CreateSyncAccount(this); ... } ... /** * Create a new dummy account for the sync adapter * * @param context The application context */ public static Account CreateSyncAccount(Context context) { // Create the account type and default account Account newAccount = new Account( ACCOUNT, ACCOUNT_TYPE); // Get an instance of the Android account manager AccountManager accountManager = (AccountManager) context.getSystemService( ACCOUNT_SERVICE); /* * 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, null)) { /* * If you don't set android:syncable="true" in * in your <provider> element in the manifest, * then call context.setIsSyncable(account, AUTHORITY, 1) * here. */ } else { /* * The account exists or some other error occurred. Log this, report it, * or handle it internally. */ } } ... }
5.添加metadata
contentAuthority: contentprovider的authority
accountType:定義的accountType
userVisible:accountType是否可見。默認account icon 和 label是可見的。若是設置爲不可見,仍然能夠經過app界面讓用戶控制同步方式。
supportsUploading:是否支持上傳
allowParallelSync:是否容許有多個sync adapter同時容許
isalwaysSyncable:手動同步,仍是按照計劃自動同步
<?xml version="1.0" encoding="utf-8"?> <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:contentAuthority="com.example.android.datasync.provider" android:accountType="com.android.example.datasync" android:userVisible="false" android:supportsUploading="false" android:allowParallelSyncs="false" android:isAlwaysSyncable="true"/>
5.在manifest中配置
1.權限申請
android.permission.INTERNET 網絡
android.permission.READ_SYNC_SETTINGS android.permission.WRITE_SYNC_SETTINGS 若是不動態操做sync adapter能夠不申請
android.permission.AUTHENTICATE_ACCOUNTS 帳號
<manifest> ... <uses-permission android:name="android.permission.INTERNET"/> <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"/> ... </manifest>
<service android:name="com.example.android.datasync.SyncService" 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>
有不少方式能夠觸發sync adapter同步。
1.服務器數據變化,可經過推送告知客戶端同步
2.客戶端數據變化
3.有規律的計劃同步時間
4.其餘須要
計劃同步:
能夠設置一個同步間隔或在某個特定的時間點同步。
建議在服務器比較空閒的時候進行同步,如夜間,同步的時候要注意每一個設備的同步的時間要稍微有寫差異,避免服務器壓力過大。
addPeriodicSync 不會 停用 setSyncAutomatically,也就是說可能同步會屢次觸發。