从网络获取数据(2)使用SyncAdapter

来源:互联网 发布:python下载安装 编辑:程序博客网 时间:2024/06/04 19:18

    之前我们讲的都是及时传输数据,这些随机的数据传输不利于管理,影响内存或者电池的使用,现在安卓提供了一个sync adapter框架,

帮助管理和自动进行数据传输,并且在不同app间合作进行同步。还有如下特色:

Plug-in architecture
允许我们添加数据传输的代码到系统以可调用组件的形式
Automated execution
允许我们基于一些标准的自动数据传输,包括数据改变、逝去时间或者一天中的时间(including data changes, elapsed time, or time of day)。此外,系统添加不能执行的传输到一个队列中,在可能时运行他们
Automated network checking
系统仅仅会执行数据传输当设备有网络连接(不用自己判断有没有网了)
Improved battery performance
允许我们集中应用的所有数据传输任务发生在一个地方,这样他们全部同时运行。我们的应用传输也和其他应用的数据传输一起协同调度。这些因素减少了系统开启网络的次数,减少电量使用
Account management and authentication
如果我们的应用要求用户授权或者服务登录,我们可以选择性的集成账户管理和权限认证到我们的数据传输中。

下面来学习如何使用Sync Adapter

Creating a Stub Authenticator

sync adapter假设我们的数据传输和一个账号关联并且服务器数据存储需要登录权限。

及时我们不需要账户,也要模拟添加一个。

(1)继承AbstractAccountAuthenticator,实现默认方法

(2)绑定Authenticator到框架

提供一个绑定服务,在服务的OnCreate方法实例化Authenticator, 在onBind方法返回mAuthenticator.getBinder().

返回的这个IBinder是在OnCreate方法中创建的Authenticator对象获得的。

(3)添加Authenticator Metadata文件

   在res/xml中定义一个描述文件:

<?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中声明Authenticator

为上面创建的服务添加声明:

   <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>

这个<intent-filter>元素设置了一个filter由android.accounts.AccountAuthenticator触发,由系统发送用来运行这个authenticator。当filter被触发,系统开启AuthenticatorService,我们提供的用来wrap Authenticator的bound service。
<meta-data>元素为Authenticator声明metadata。Android:name属性连接meta-data到Authentication框架。Android:resource元素指定我们之前创建的Authenticator metadata文件。

Creating a Stub Content Provider

如果没有内容提供器,还要创建一个提供器。这个不相信讲了,参看http://blog.csdn.net/qingziguanjun1/article/details/51330360

Creating a Sync Adapter

最重要的部分就是创建这个适配器了,这个适配器包裹了传输数据的代码。以我们在app中提供的调度和触发器为基础,sync Adapter

框架运行这些代码在sync adapter 组件中。(又是Adapter,这种设计方式很棒啊?)。我们需要添加下面的组成:

Sync adapter class.
A class that wraps your data transfer code in an interface compatible with the sync adapter framework.
Bound Service.
A component that allows the sync adapter framework to run the code in your sync adapter class.
Sync adapter XML metadata file.
A file containing information about your sync adapter. The framework reads this file to find out how to load and schedule your data transfer.
Declarations in the app manifest.
XML that declares the bound service and points to sync adapter-specific metadata.     

一共四个部分,一个Adapter类,一个绑定服务,一个xml,在nanifest声明服务,指向Adapter。

和Authenticator很像啊,我们只负责定义这些部分,然后在Manifest中声明一下如何使用他们,然后就系统会帮我们调用。

那我们是在哪里开始调用的呢?后面会讲到

下面开始讲解四个部分:

Create a Sync Adapter Class

(1)继承AbstractThreadedSyncAdapter,提供构造器,构造器执行设置,比如使用内容提供器,那么实例化一个ContentResolver对象。

下面是例子


(2)在onPerformSync中添加数据传输代码

这个方法接收的参数好几个,哪里来的,系统传递的?为什么sunshine的这个方法没有使用这几个参数?这些参数应该从下面

创建的绑定服务传来的。

(3)绑定sync adapter到框架

在服务的OnCreate方法,单例线程安全的创建SyncAdapter对象。饿汉式。


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)添加框架要求的账户

最好的地方的在开启活动的OnCreate方法,不过sunshine的就是在adapter中定义了一个getAccount方法。

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文件

<?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"/>

android:contentAuthority:内容提供器的权限

android:accountType:前面Authenticator定义的类型,也是第四步添加的账户类型

android:userVisible:同步账户类型的可见性

android:allowParallelSyncs 允许sync adapter的多个实例同时运行

android:isAlwaysSyncable: sync adapter可以在任何指定时间运行,设置为falst,调用requestSync方法.Sunshine设为

true,还是调用了这个requestSync方法啊,这不科学啊。


(6)定义adapter在Manifest中

添加权限:

网络权限,此外,应用需要请求读写sync adapter设置权限,这样可以在应用其他组件编程控制sync adapter。

还需要一个特殊的权限允许应用使用我们创建的Authenticator组件。

<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>


使用android.content.SyncAdapter action,系统发送去运行sync adapter。

然后android:process,告诉service在一个全局线程中执行(这个怎么知道这个线程),如果有多个sync adapter,可以分享

线程,减少开销。


Running a Sync Adapter



0 0