android学习(二十二) 传输数据使用Sync Adapters

来源:互联网 发布:儿童安全座椅 知乎 编辑:程序博客网 时间:2024/05/19 12:39

同步在android设备和web服务器之间的数据可以让你的应用更多的为用户提供实用和令人信服的体验。例如,例如,将数据传送到web服务器作为一个有用的备份,并且即使当设备离线时,从服务器传输数据也可以向用户提供数据。在某些情况下,用户可能会发现更容易进入和编辑他们的数据在Web界面,然后在他们的设备上获得这些数据,或者随着时间的推移他们可能要收集数据,然后将其上传到中央存储区。

虽然你可以设计自己的系统做数据传输在你的应用程序,你应该考虑使用Android的同步适配器框架。这个框架有助于管理和自动化数据传输,并协调跨不同应用程序的同步操作。当你自己设计的数据传输方案不可用时,使用这个框架,你可以利用几个特性:

插件架构:
允许您以可调用组件的形式向系统中添加数据传输代码。
自动执行:
允许你根据各种标准自动传输数据,包括数据改变,经过的时间或者一天的时间。此外,系统增加了无法运行到队列的传输,并在可能的时候运行它们。
自动的网络检查:
当网设备络连接时,系统仅运行你的数据传输。
改进电池性能:
允许您将所有应用程序的数据传输任务集中在一个地方,以便它们同时运行。您的数据传输也可以与其他应用程序的数据传输相结合。这些因素减少了系统在网络上切换的次数,从而减少了电池的使用。
账户管理和认证:
如果您的应用程序需要用户凭据或服务器登录,您可以选择账户管理和认证集成到您的数据传输。

这个类向您展示如何创建同步适配器和绑定它的绑定Service,如何提供帮助您将同步适配器插入框架的其他组件,以及如何用各种方式运行同步适配器。

注:同步适配器是异步运行的,所以你应该期望他们定期和有效地传输数据,但不是瞬间。如果你需要做数据实时传输,你应该在一个AsyncTask或intentservice使用。


创建一个创建存根验证器

同步适配器框架假定您的同步适配器将需要登录访问的帐户和服务器存储关联的设备存储之间传输数据。因此,框架希望你提供一个组件叫做验证器作为同步适配器部分。此组件添加到Android帐户和验证框架,并提供了一个标准的界面,用于处理用户凭据,如登录信息。

即使你的应用不使用账户,你仍需要提供一个验证器组件。如果你不需要账户或服务器登录,信息处理也不能被忽视所以你需要一个验证组件包含存根方法实现,你你也需要提供绑定Servicce允许同步适配器框架调用验证器的方法。

下面介绍如何定义可满足同步适配器框架的要求的存根认证的所有部分可满足同步适配器框架的要求。如果你需要提供一个真正的认证处理用户帐户,阅读abstractaccountauthenticator参考文档。


添加一个存根验证组件

为了添加一个存根验证器组件到你的应用,创建一个继承AbstractAccountAuthenticator类的类。取消所有方法,通过返回null或抛出一个异常。

public class Authenticator extends AbstractAccountAuthenticator {    public Authenticator(Context context) {        super(context);    }    //不支持编辑属性    @Override    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {        throw new UnsupportedOperationException();    }    //不增加额外的账户    @Override    public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {        return null;    }    //忽略尝试确认证书    @Override    public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {        return null;    }    //不支持获取认证令牌    @Override    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {        throw new UnsupportedOperationException();    }    //不支持获得令牌的标签    @Override    public String getAuthTokenLabel(String authTokenType) {        throw new UnsupportedOperationException();    }    //不支持更新使用的证书    @Override    public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {        throw new UnsupportedOperationException();    }    //不支持检查账户的特性    @Override    public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {        throw new UnsupportedOperationException();    }}

绑定认证框架

为了同步适配器框架进入你的认证器,你必须为它创建一个bound Service。这个Service提供一个android绑定对象允许这个框架调用你的认证器和在认证器和框架之间传递数据。

由于框架第一次开始这个Service它需要进入认证器,你也可以使用Service实例化这个认证器,通过调用这个认证器的构造函数在Service.onCreate()方法里。

public class AuthenticatorService extends Service {    //实例字段存储认证对象    private Authenticator mAuthenticator;    @Override    public void onCreate() {        super.onCreate();        mAuthenticator = new Authenticator(this);    }    //当系统绑定这个使用RPC调用Service    // 返回认证器的IBinder    @Nullable    @Override    public IBinder onBind(Intent intent) {        return mAuthenticator.getIBinder();    }}

添加认证器元数据文件

为了在同步适配器补充认证器组件和账户框架,你需要提供元数据描述的组件的框架。这个元数据声明了账户的类型,你为了同步适配器创建并且声明使用者系统显示的界面元素如果你想使用你的账户类型显示给使用者,声明这个在XML的元数据,在你的项目里存储/res/xml/ directory,通常使用文件名为authenticator.xml
这个XML文件包含一个单一的元素< account-authenticator > 下面是属性。
android:accountType
同步适配器框架需要每个同步适配器有一个账号类型以域名的形式。框架使用帐户类型作为同步适配器内部标识的一部分。对于需要登录的服务器,将帐户类型连同用户帐户作为登录凭据的一部分发送到服务器。

如果你的服务器不需要登录,你仍然可以提供账户类型。对于值,请使用您控制的域名。当框架使用它来管理你的同步适配器时,这个值不会被发送到你的服务器。

android:icon
指定一个Drawable资源包含一个图标。如果你通过指定的属性android:userVisible=”true” in res/xml/syncadapter.xml显示同步适配器。你必须提供图标资源。他出现在系统的设置应用里的账户选择。

android:smallIcon

android:label可本地化的字符串,标识用户的帐户类型。如果你通过指定的属性android:userVisible=”true” in res/xml/syncadapter.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"/>

在Manifest中声明认证器

在上一步中,您创建了一个绑定的服务链接认证器同步适配器框架。若要将此服务标识到系统中,请在“应用程序清单”中声明如下内容:将< servicce >元素添加为< application >的子元素:

<service android:name=".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 >元素设置一个过滤器,将由系统运行验证器通过intent触发android.accounts.AccountAuthenticator。当这个过滤器触发了,系统开始AuthenticatorService, bound Service 提供给外包的认证器。

< meta-data >元素声明在认证器的元数据。* android:name*属性连接这个目标数据为了认证器框架。android:resource元素指定认证器元数据的文件。

除了认证器之外,同步适配器还需要一个content provider。如果你的应用不准备使用content provider,下面可以学习创建一个存根content provider。


创建一个存根Content Provider

同步适配器框架设计是与灵活和高度安全的内容提理框架管理的数据一起工作。为此,同步适配器框架预计使用该框架的应用程序已经为其本地数据定义了内容提供程序。如果同步适配器框架试图运行你的同步适配器,而你的应用程序没有一个内容提供者,那么你的同步适配器就会崩溃。


添加一个存根Content Provider

继承ContentProvider取消所有必须的方法。

public class StubProvider extends ContentProvider {    //总是返回true,指示这个provider加载正确    @Override    public boolean onCreate() {        return true;    }    //query()总是返回没有结果    @Nullable    @Override    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {        return null;    }    //返回没有类型作为MIME type    @Nullable    @Override    public String getType(@NonNull Uri uri) {        return null;    }    //insert总是返回null(没有URI)    @Nullable    @Override    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {        return null;    }    //delete()总是返回"没有受影响的行"(0)    @Override    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {        return 0;    }    //update()总是返回"没有受影响的行"(0)    @Override    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {        return 0;    }}

**

在Manifest里声明Provider

在Manifest里声明Provider,同步适配器通过检查验证你的应用里的content provider。为了在Manifest里声明stub Provider,在< provider >添加以下元素。

android:name=”com.example.android.datasync.provider.StubProvider”
指定实现存根内容提供程序的类的完全限定名称。

android:authorities=”com.example.android.datasync.provider”
标识存根内容提供程序的URI权限。将此应用程序的包名称与附加到它的字符串“.provider”联系起来。即使您向系统声明存根提供程序,也没有东西尝试访问提供者本身。

android:exported=”false”
确定其他应用程序是否可以访问内容提供程序。对于存根内容提供程序,将值设置为false,因为不需要允许其他应用程序查看提供程序。此值不影响同步适配器框架和内容提供程序之间的交互。

android:syncable=”true”
设置一个标志,表明provider是syncable。如果你设置这个标志为true,你不能在你的代码中调用setissyncable()。该标志允许同步适配器框架与内容提供程序进行数据传输,但只有在显式地执行传输时才会发生转移。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.android.network.sync.BasicSyncAdapter"    android:versionCode="1"    android:versionName="1.0" >    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >    ...    <provider        android:name="com.example.android.datasync.provider.StubProvider"        android:authorities="com.example.android.datasync.provider"        android:exported="false"        android:syncable="true"/>    ...    </application></manifest>

创建一个Sync Adapters

应用程序中的同步适配器组件封装了在设备和服务器之间传输数据的任务的代码。基于在应用程序中提供的调度和触发器,同步适配器框架要在同步适配器组件中运行代码。要添加同步适配器组件到您的应用程序,您需要添加以下内容:

Sync adapter class
在兼容同步适配器框架的接口中封装数据传输代码的类。

Bound Service
允许同步适配器框架在同步适配器类中运行代码的组件。

Sync adapter XML metadata file.
包含同步适配器信息的文件。框架读取此文件以了解如何加载和调度数据传输。

**在manifest中声明
声明绑定服务并指向同步适配器特定元数据的xml。


创建一个Sync Adapter Class

为了创建Sync Adapter组件,首先需要继承* AbstractThreadedSyncAdapter*和重写构造函数。使用构造函数为了每次你的sync adapter组件从刚开始被创建是运行更新任务,正如你使用Activity.onCreate()为了设置一个activity。例如,如果你的应用使用content provider为了存储数据,使用构造函数获得一个ContentRsolver实例。由于第二个构造函数的形式是在Android平台的3版本加入的为了支持parallelsyncs产生的争论,你需要创建两种构造函数保持兼容性。

:sync adapter框架被设计为工作的sync adapter是单例。

//在服务器和引用之间发送传输数据使用sync adapter框架public class SyncAdapter extends AbstractThreadedSyncAdapter {    //全局变量    //定义一个变量包含一个content resolver实例    ContentResolver mContentResolver;    public SyncAdapter(Context context, boolean autoInitialize) {        super(context, autoInitialize);        //如果你的应用使用content resolver,        //从进入的context里获取实例        mContentResolver = context.getContentResolver();    }    //设置sync adapter。    // 这种形式的构造函数与Android 3.0和以后的平台版本保持兼容性。    public SyncAdapter(Context context,boolean autoInitialize,boolean allowParallelSyncs){        super(context,autoInitialize,allowParallelSyncs);        mContentResolver = context.getContentResolver();    }    ...}

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

sync adapter不会自动的进行传输数据。相反,它封装了您的数据传输代码,使同步适配器框架可以在后台运行数据传输,而不需要从应用程序中参与。当这个框架准备同步你的应用程序的数据,它会启动方法onPerformSync()。
onPerformSync()有以下的元素:

Account
与触发同步适配器的事件关联的帐户对象。如果您的服务器不使用帐户,则不需要使用此对象中的信息。

Extras
一个bundle包含设置触发同步适配器的事件的标签。

Authority
系统中内容提供者的权限。您的应用程序必须访问此提供程序。通常,权限对应于您自己的应用程序中的内容提供程序。

ContentProviderClient
作为content provider通过权限元素指向的ContentProviderClient。ContentProviderClient是一个轻量的content provider公共接口。它具有ContentResolver相同的基本功能。如果你使用content privider保存数据,你可以连接这个provider对象,否则你可以忽视他。

SyncResult
SyncResult对象是你为了发送信息到sync adapter框架使用的。

    //运行在sync adapter的具体代码。    //全部的sync adapter运行在后台线程中    //所以你不用设置你自己的后台进程。    @Override    public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {    }

onperformsync()实际执行特定于应用程序的数据同步需求和服务器连接的协议,然而也有一些通用任务的实施应执行:

连接一个服务器
尽管你认为当你的数据传输开始时网络已连接,但是sync adapter不会自动连接一个服务器。

下载和更新数据
sync adapter不会自动进行一些数据传输任务。如果你想要从服务器下载数据和在content provider保存数据,你可以提供请求数据的代码,下载和嵌入到provider。同样,如果你想要发送数据到服务器,你可需要从文件,数据库,或者provider读取它们,和发送必要的更新请求,你也要发送网络错误当你的数据传输运行时。

处理数据冲突或者决定当前数据
sync adapter不会自动的处理服务器和设备之间的数据冲突。还有它不会自动的检查如果在服务器的数据比设备的数据更新时,反之亦然。相反,你必须提供自己的算法来处理这种情况。

清理
关闭服务器的连接,并始终在数据传输结束时清除临时文件和缓存

除了你的同步相关的任务,你应该尽量把你的网络相关的任务的规则,并将它们添加到onperformsync()。通过将您的网络任务集中到该方法中,您可以节省启动和停止网络接口所需的电池电量。


绑定Sync Adapter框架

现在你已经把传输数据的代码封装在sync adapter组件中了,但是你还需要提供框用来进入你的代码。为此,你需要创建一个bound Service。这个绑定对象,框架能够调用onPerformSync()方法和传递数据。

在service的onCreate()方法中实例化sync adapter组件的单例。通过在onCreate()实例化组件,你可以延迟创建他直到当框架第一次尝试运行你的传输数据时service开始。你需要以线程安全的方式实例化组件,如果同步适配器框架响应触发器或调度队列,同步执行多个同步适配器。

例如,下面片段展示了你怎样创建一个class,实现绑定service,实例化你的sync adapter组件和获得android绑定对象。

//定义一个Service为sync adapter类返回一个IBinder    //允许sync adapter框架调用onPerformSync()public class SyncService extends Service {    //存储一个sync adapter实例    private static SyncAdapter sSyncAdapter = null;    //用作线程安全锁的对象    private static final Object sSyncAdapterLock = new Object();    @Override    public void onCreate() {        super.onCreate();        //创建单例sync adapter        //设置同步适配器作为syncable        // 禁止并行同步        synchronized (sSyncAdapterLock){            sSyncAdapter = new SyncAdapter(getApplicationContext(),true);        }    }    //返回一个对象允许系统调用sync adapter    @Nullable    @Override    public IBinder onBind(Intent intent) {        //获取对象允许外部进程调用onPerformSync()。        //当sync adapter构造函数调用super()时        //在对象在基本的类被创建        return sSyncAdapter.getSyncAdapterBinder();    }}

要查看同步适配器的绑定服务的详细示例,请参见示例应用程序


通过框架添加账户需求

sync adapter框架需要每个sync adapter有一个账户类型。你声明账户类型在一个认证元数据文件。现在你需要在android系统中设置账户类型。为了设置账户类型,添加虚拟账户,使用账户类型调用* addAccountExplicitly()*

要调用的方法的最好的地方是在你的应用程序的打开的activity的oncreate()方法。例如:

public class MainActivity extends FragmentActivity {    ...    ////sync adapter的content provider的权限    public static final String AUTHORITY = "com.example.android.datasync.provider";    //账户类型,来自域名    public static final String ACCOUNT_TYPE = "example.com";    //账户名字    public static final String ACCOUNT = "dummyaccount";    //实例域    Account mAccount;    ...    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ...        // Create the dummy account        mAccount = CreateSyncAccount(this);        ...    }    ...    //从sync adapter创建一个虚拟账号    public static Account CreateSyncAccount(Context context) {        //创建账户类型 默认账号        Account newAccount = new Account(ACCOUNT,ACCOUNT_TYPE);        //获取android账户管理实例        AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);        //添加账户和账户类型,没有密码和使用者数据        //如果成功,返回一个账户对象,否则报错        if(accountManager.addAccountExplicitly(newAccount,null,null)){            //你不需要设置android:syncable="true"在清单文件里            //可以在这里调用context.setIsSyncable(account, AUTHORITY, 1)        } else {            //账户错误进行处理        }    }    ...}

添加Sync Adapter元数据文件

要将同步适配器组件插入框架,需要提供给框架描述组件和提供额外的标签的元数据。元数据指定了你创建在sync adapter的账户类型,声明了content provider的相关权限,控制与同步适配器相关的系统用户界面的一部分,声明其他sync相关的标签。文件创建 /res/xml/ directory。通常名字为syncadapter.xml
该XML文件包含一个单一元素 < sync-adapter >下面是属性。

android:contentAuthority
这个URI权限来自你的content provider。如果你创建一个存根content provider在你的app,使用在manifest里的< provider >里指定android:authorities属性。如果你传输文件从content provider到服务器,这个值应该和你使用数据的content URI authority一样。

android:accountType
sync adapter框架的账户类型是必须的。这个值必须和你创建的authenticator元数据文件里的是一样的。

设置属性
android:userVisble
设置sync adapter的账户类型的是否可见。默认情况下账户图标和相关联标签的的账户类型是可见的。所以你应该使用你的sync adapter为不可见,除你有一个账户或者域名和应用关联的之外。如果你使你账户类型可见你甚至可以允许使用者控制你的sync adapter在你的应用activity里提供接口。
android:supportsUploading
允许你云更新数据。如果你的应用仅下载数据设置false。
android:allowParallelSyncs
允许多个sync adapter实例组件允许在相同时间里。使用这个如果你的应用支持多个使用者账户和你想允许多个使用者并行传输数据。如果你从不并行运行传输数据这个标签是不影响的。
android:isAlwaysSyncable
指示sync adapter框架在你指定的时间运行sync adapter。如果当sync adapter运行时你想程序化控制设置这个为false,并且调用requestSync()运行sync adapter。

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

在Manifest里声明Sync Adapter

需要以下权限

    <uses-permission android:name="android.permission.INTERNET" />    <!--允许你的应用读取当前的sync adapter设置-->    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>    <!--允许你的应用控制sync adapter设置,在使用addPeriodicSync()需要这个权限-->    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

然后声明绑定service

         <service                android:name="com.example.android.datasync.SyncService"                android:exported="true"                <!--告诉系统service在一个名为sync的全局共享的进程里-->                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. 在需要的时候


当服务器数据改变时

当你的服务器数据经常变化,你的应用从一个服务器传输数据。为了运行Sync Adapter你要一个BroadcastReceiver接收服务器发送的特殊数据。为了响应这些信息调用ContentResolver.requestSync()。

你可以使用Google Cloud Messaging (GCM)他要比轮询服务器效率要高。而且对电池负荷要小。

:当从服务器接收特殊数据后,多个sync adapter在同一时间内接收信息。这对服务器和网络造成了负担。所以你应该思考分配时段对于每个设备接受的信息。


当content provider数据改变时

为了创建一个观察者在你的content provider。继承ContentObserver和实现onChange()方法。在方法里调用requestSync();

为了注册观察者,调用registerContentObserver()。在调用里你需要设置你要观察的content URI。

public class MainActivity extends FragmentActivity {    ...    // Content provider scheme    public static final String SCHEME = "content://";    // Content provider 权限    public static final String AUTHORITY = "com.example.android.datasync.provider";    //content provider表的路径    public static final String TABLE_PATH = "data_table";    // 账号    public static final String ACCOUNT = "default_account";    Uri mUri;    ContentResolver mResolver;    ...    public class TableObserver extends ContentObserver {        //当在content provider观察到数据改变调用这个方法        @Override        public void onChange(boolean selfChange) {            onChange(selfChange, null);        }        @Override        public void onChange(boolean selfChange, Uri changeUri) {            ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);        }        ...    }    ...    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ...        mResolver = getContentResolver();        mUri = new Uri.Builder()                  .scheme(SCHEME)                  .authority(AUTHORITY)                  .path(TABLE_PATH)                  .build();        //provider不改变所以设置selfChange为false        TableObserver observer = new TableObserver(false);        //注册观察者        mResolver.registerContentObserver(mUri, true, observer);        ...    }    ...}

定期运行Sync Adapter

为了定期运行sync adapter调用 addPeriodicSync()。

public class MainActivity extends FragmentActivity {    ...    public static final String AUTHORITY = "com.example.android.datasync.provider";    public static final String ACCOUNT = "default_account";    public static final long SECONDS_PER_MINUTE = 60L;    public static final long SYNC_INTERVAL_IN_MINUTES = 60L;    public static final long SYNC_INTERVAL =            SYNC_INTERVAL_IN_MINUTES *            SECONDS_PER_MINUTE;    ContentResolver mResolver;    ...    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ...        mResolver = getContentResolver();        /返回定期同步        ContentResolver.addPeriodicSync(                ACCOUNT,                AUTHORITY,                Bundle.EMPTY,                SYNC_INTERVAL);        ...    }    ...}

在需要时运行Sync Adapter

设置一个刷新监听,手动同步适配器

public class MainActivity extends FragmentActivity {    ...    public static final String AUTHORITY =            "com.example.android.datasync.provider"    public static final String ACCOUNT_TYPE = "com.example.android.datasync";    public static final String ACCOUNT = "default_account";    Account mAccount;    ...    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ...        mAccount = CreateSyncAccount(this);        ...    }    public void onRefreshButtonClick(View v) {        ...        Bundle settingsBundle = new Bundle();        settingsBundle.putBoolean(                ContentResolver.SYNC_EXTRAS_MANUAL, true);        settingsBundle.putBoolean(                ContentResolver.SYNC_EXTRAS_EXPEDITED, true);        ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);    }

摘自android developer

0 0
原创粉丝点击