优点:
1.自动同步数据(根据时间,数据变化),把不满足条件的同步操作加入队列,当满足条件时自动运行。
2.多个应用协作同步
3.同步代码插件化
4.自动网络连接检测 系统只会在联网状态下进行数据同步
5.节约电量 把数据同步操作放在一块,并且多个应用协作
6.账号管理和身份验证
一、创建身份验证者(authenticator)
因为同步适配器框架假设你需要在某账号下的本地数据和需要登录验证的服务器传递数据,所以同步适配器框架需要你有一个身份验证者。身份验证者为身份验证提供标准的接口,如登录等。
即便你不使用账号,你也需要提供一个身份验证者。并且和一个服务绑定(bound service),该服务用来让同步适配框架调用身份验证者的方法。
1.具体实现:
/*
* 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用来启动身份验证者。
二、创建一个Stub Content Provider
同步适配器框架使用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一致
三、创建 Sync Adapter
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
有很多方式可以触发sync adapter同步。
1.服务器数据变化,可通过推送告知客户端同步
2.客户端数据变化
3.有规律的计划同步时间
4.其他需要
计划同步:
可以设置一个同步间隔或在某个特定的时间点同步。
建议在服务器比较空闲的时候进行同步,如夜间,同步的时候要注意每个设备的同步的时间要稍微有写差别,避免服务器压力过大。
addPeriodicSync 不会 停用 setSyncAutomatically,也就是说可能同步会多次触发。