android 添加帐户具体步骤

也可以转载http://blog.csdn.net/lyz_zyx/article/details/73571927
简介

我们在手机系统设置里会有“帐户”一项功能,点击进去会发现有一些我们认识的APP在里头,如QQ、微信、邮箱等。没错,这就是Android系统提供的帐户同步功能。任何第三方APP都可以通过此功能将数据在一定时间内同步到服务器中去。比如微信会将当前系统中登录的微信帐户的信息、聊天记录同步到微信服务器上去,又比如android原生系统中可以使用Google账号进行数据的同步。这里顺便提一下,系统在将APP帐户同步时,也会将深睡中的APP进程拉活,这点我们在文章后面也会说到。好了,下面我们将一步一步地来实现一个帐户同步的Demo。

创建帐户服务

第一步,定义一个action为android.accounts.AccountAuthenticator的Intent的Service,并在meta-data的resource属性指定一定说明该Account基本显示信息的xml文件,AndroidMainifest.xml添加代码如:

[html] view plain copy

<service  
    android:name="project.test.com.myapplication.AuthenticationService"  
   android:enabled="true"  
   android:exported="true">  
   <intent-filter>  
       <actionandroid:nameactionandroid:name="android.accounts.AccountAuthenticator" />  
   </intent-filter>  
   <meta-data  
        android:name="android.accounts.AccountAuthenticator"  
       android:resource="@xml/authenticator" />  
</service>  

authenticator.xml:
[html] view plain copy

<?xml version="1.0"encoding="utf-8"?>  
<account-authenticatorxmlns:androidaccount-authenticatorxmlns:android="http://schemas.android.com/apk/res/android"  
   android:accountType="project.test.com.myapplication.account.type"  
   android:icon="@mipmap/ic_launcher"  
   android:smallIcon="@mipmap/ic_launcher"  
   android:label="@string/app_name"  
    />  

注意:

android:accountType表示的是您的Account的类型,它必须是唯一的

第二步,创建帐户Service,并在Service的onBind中调AbstractAccountAuthenticator的getIBinder()返回其用于远程调用的IBinder:

[java] view plain copy

public class AuthenticationService extends Service {  
    privateAuthenticationService.AccountAuthenticator mAuthenticator;  

    privateAuthenticationService.AccountAuthenticator getAuthenticator() {  
        if(mAuthenticator == null)  
           mAuthenticator = new AuthenticationService.AccountAuthenticator(this);  
        returnmAuthenticator;  
    }  

    @Override  
    public voidonCreate() {  
       mAuthenticator = new AuthenticationService.AccountAuthenticator(this);  
    }  

    @Nullable  
    @Override  
    publicIBinder onBind(Intent intent) {  
        returngetAuthenticator().getIBinder();  
    }  

    classAccountAuthenticator extends AbstractAccountAuthenticator {  


        publicAccountAuthenticator(Context context) {  
           super(context);  
        }  

       @Override  
        publicBundle editProperties(AccountAuthenticatorResponse response, StringaccountType) {  
            returnnull;  
        }  

       @Override  
        publicBundle addAccount(AccountAuthenticatorResponse response, String accountType,String authTokenType, String[] requiredFeatures, Bundle options) throwsNetworkErrorException {  
           return null;  
        }  

       @Override  
        publicBundle confirmCredentials(AccountAuthenticatorResponse response, Accountaccount, Bundle options) throws NetworkErrorException {  
           return null;  
        }  

       @Override  
        publicBundle getAuthToken(AccountAuthenticatorResponse response, Account account,String authTokenType, Bundle options) throws NetworkErrorException {  
           return null;  
        }  

       @Override  
        publicString getAuthTokenLabel(String authTokenType) {  
           return null;  
        }  

       @Override  
        publicBundle updateCredentials(AccountAuthenticatorResponse response, Accountaccount, String authTokenType, Bundle options) throws NetworkErrorException {  
           return null;  
        }  

       @Override  
        publicBundle hasFeatures(AccountAuthenticatorResponse response, Account account,String[] features) throws NetworkErrorException {  
           return null;  
        }  
    }  
}  

说明:

我们在AuthenticationService类中定义了一个继承于AbstractAccountAuthenticator的内部类AccountAuthenticator,然后在AuthenticationService的onBind方法中返回了AccountAuthenticator类对象的getIBinder()。

通过这简单的两步,我们运行程序后再在“设置”-“帐户”-“添加帐户”中的列表中可以发现到我们的app了。

如图:

添加帐户

您会发现,像微信,如果当前手机微信没有登录过的话,在帐户列表中点击微信会自动添转到一个登录微信的Activity,但点击我们刚才app是跳转到一个空白没有任务操作,那是因为我们只是创建了帐户服务,还没有对其进行添加帐户的代码处理。

第一步,声明两个必要的权限:

[html] view plain copy

<uses-permissionandroid:nameuses-permissionandroid:name="android.permission.GET_ACCOUNTS" />                           // 查看帐户需要权限  
<uses-permissionandroid:nameuses-permissionandroid:name="android.permission.AUTHENTICATE_ACCOUNTS" />               // 添加帐户需要权限  

第二步,添加AuthenticatorActivity:

[java] view plain copy

public class AuthenticatorActivity extendsAppCompatActivity {  

    publicstatic final String ACCOUNT_TYPE ="project.test.com.myapplication.account.type";    // TYPE必须与account_preferences.xml中的TYPE保持一致  
    privateAccountManager mAccountManager;  

    @Override  
    protectedvoid onCreate(Bundle savedInstanceState) {  
       super.onCreate(savedInstanceState);  
       setContentView(R.layout.activity_authenticator);  

       mAccountManager = (AccountManager)getSystemService(ACCOUNT_SERVICE);  
       Account[] accounts =mAccountManager.getAccountsByType(ACCOUNT_TYPE);   // 获取系统帐户列表中已添加的帐户是否存在我们的帐户,用TYPE做为标识  
        if(accounts.length > 0) {  
           Toast.makeText(this, "已添加当前登录的帐户",Toast.LENGTH_SHORT).show();  
           finish();  
        }  

        ButtonbtnAddAccount = (Button)findViewById(R.id.btn_add_account);  
        btnAddAccount.setOnClickListener(new View.OnClickListener() {  
           @Override  
           public void onClick(View v) {  
               Account account = new Account(getString(R.string.app_name),ACCOUNT_TYPE);  
               mAccountManager.addAccountExplicitly(account, null, null);     // 帐户密码和信息这里用null演示  

               finish();  
            }  
        });  
    }  
}  

说明:

1、Activity中定义的成员变量ACCOUNT_TYPE用于我们当前APP获取系统帐户的唯一标识,这个在前面account_preferences.xml中也有提过,两处的声明必须是一致;

2、在onCreate中去获取系统帐户列表中已添加的帐户是否存在我们的帐户,用我们的TYPE做为标识,如果存在表示已经添加过,则退出当前页面;

3、如若从未添加过帐户,则模拟了一个登户操作,在操作成功后使用AccountManager的addAccountExplicitly方法往系统帐户中添加我们的帐户。

第三步,在AccountAuthenticator类中的addAccount方法里添加跳转登录的Activity:

[java] view plain copy

@Override  
public Bundle addAccount(AccountAuthenticatorResponseresponse, String accountType, String authTokenType, String[] requiredFeatures,Bundle options)  
        throwsNetworkErrorException {  
    final Bundle bundle = new Bundle();  
    final Intent intent = new Intent(mContext, AuthenticatorActivity.class);  
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,response);  
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);  
    return bundle;  
}  

我们再次运行程序,在“添加帐户”的点击和模拟登录后,便可在“设置”-“帐户”中看到我们的APP已添加了帐户了,如图:

同步帐户数据

当我们都完成了上面添加帐户的操作后,试着在帐户中点击会跳转到一个页面,如图:

这个页面只是列出了我们APP的图标和名称,并没有其它的操作和我们预期中的同步功能。所以接下来我们继续为APP加入同步的服务代码。

第一步,定义一个action为android.content.SyncAdapter的Intent的Service,并在meta-data的resource属性指定一定说明该同步基本显示信息的xml文件,AndroidMainifest.xml添加代码如:

[html] view plain copy

<service  
   android:name=".SyncService"  
   android:exported="true">  
   <intent-filter>  
       <action android:name="android.content.SyncAdapter" />  
   </intent-filter>  
   <meta-data  
       android:name="android.content.SyncAdapter"  
       android:resource="@xml/sync_adapter" />  
</service>  

sync_adapter.xml:

[html] view plain copy

<?xml version="1.0"encoding="utf-8"?>  
<sync-adapterxmlns:androidsync-adapterxmlns:android="http://schemas.android.com/apk/res/android"  
   android:accountType="project.test.com.myapplication.account.type"  
   android:allowParallelSyncs="false"  
   android:contentAuthority="project.test.com.myapplication.account.provide"  
   android:isAlwaysSyncable="true"  
   android:supportsUploading="false"  
android:userVisible="true"/>  

说明:

android:accountType表示的是您的Account的类型,一定要跟前面保持一致

android:allowParallelSyncs 是否支持多账号同时同步

android:contentAuthority 指定要同步的ContentProvider

android:isAlwaysSyncable 设置所有账号的isSyncable为1

android:supportsUploading 设置是否必须notifyChange通知才能同步

android:userVisible 设置是否在“设置”中显示

第二步,创建同步Service,并在Service的onBind中调AbstractThreadedSyncAdapter的getIBinder()返回其用于远程调用的IBinder:

[java] view plain copy

public class SyncService extends Service {  

    privatestatic final Object sSyncAdapterLock = new Object();  
    privatestatic SyncAdapter sSyncAdapter = null;  

    @Override  
    public voidonCreate() {  
       synchronized (sSyncAdapterLock) {  
            if(sSyncAdapter == null) {  
               sSyncAdapter = new SyncAdapter(getApplicationContext(), true);  
            }  
        }  
    }  

    @Nullable  
    @Override  
    public IBinderonBind(Intent intent) {  
        returnsSyncAdapter.getSyncAdapterBinder();  
    }  

    classSyncAdapter extends AbstractThreadedSyncAdapter {  
        publicSyncAdapter(Context context, boolean autoInitialize) {  
           super(context, autoInitialize);  
        }  

       @Override  
        publicvoid onPerformSync(Account account, Bundle extras, String authority,ContentProviderClient provider, SyncResult syncResult) {  
            //TODO 实现数据同步  
            // getContext().getContentResolver().notifyChange(AccountProvider.CONTENT_URI,null, false);        }  
    }  
}  

说明:

1、我们在SyncService类中定义了一个继承于AbstractThreadedSyncAdapter的内部类SyncAdapter,然后在SyncService的onBind方法中返回了SyncAdapter类对象的getIBinder()。

2、SyncAdapter类中onPerformSync方法就是实现同步数据到服务器的代码,这里省略

第三步,上面我们在sync_adapter.xml中指定了一个ContentProvider,所以此刻当然是创建用于上传的ContentProvider:

[html] view plain copy

<provider  
   android:name=".AccountProvider"  
   android:authorities="project.test.com.myapplication.account.provide"  
   android:exported="false"  
   android:syncable="true"/>  

[java] view plain copy

public class AccountProvider extends ContentProvider{  

    publicstatic final String AUTHORITY ="project.test.com.myapplication.account.provide";  
    publicstatic final String CONTENT_URI_BASE = "content://" + AUTHORITY;  
    publicstatic final String TABLE_NAME = "data";  
    publicstatic final Uri CONTENT_URI = Uri.parse(CONTENT_URI_BASE + "/" +TABLE_NAME);  

    @Override  
    publicboolean onCreate() {  
        returnfalse;  
    }  

    @Nullable  
    @Override  
    publicCursor query(Uri uri, String[] projection, String selection, String[]selectionArgs, String sortOrder) {  
        returnnull;  
    }  

    @Nullable  
    @Override  
    publicString getType(Uri uri) {  
        returnnull;  
    }  

    @Nullable  
    @Override  
    public Uriinsert(Uri uri, ContentValues values) {  
        returnnull;  
    }  

    @Override  
    public intdelete(Uri uri, String selection, String[] selectionArgs) {  
        return0;  
    }  

    @Override  
    public intupdate(Uri uri, ContentValues values, String selection, String[] selectionArgs){  
        return0;  
    }  
}  

说明:

这里我们只是用于演示同步数据,并没有对ContentProvider进行实际操作,大家在实际开发中可以自行去完善。

最后一步,执行同步。我们来修改AuthenticatorActivity的模拟登录代码,使在在登录成功后将其设置为自动同步:

[java] view plain copy

……  
Button btnAddAccount =(Button)findViewById(R.id.btn_add_account);  
btnAddAccount.setOnClickListener(newView.OnClickListener() {  
    @Override  
    public voidonClick(View v) {  
        Accountaccount = new Account(getString(R.string.app_name), ACCOUNT_TYPE);  
       mAccountManager.addAccountExplicitly(account, null, null);                          // 帐户密码和信息这里用null演示  
        // 自动同步  
        Bundle bundle= new Bundle();  
       ContentResolver.setIsSyncable(account, AccountProvider.AUTHORITY, 1);  
       ContentResolver.setSyncAutomatically(account, AccountProvider.AUTHORITY,true);  
       ContentResolver.addPeriodicSync(account, AccountProvider.AUTHORITY,bundle, 30);    // 间隔时间为30秒  
        // 手动同步  
        //ContentResolver.requestSync(account, AccountProvider.AUTHORITY, bundle);  
       finish();  
    }  
});  

说明:

账户信息同步其实有两种方式,一种是自动同步,就是在预设定的时间间隔,让Android系统帮我们完成自动同步数据。这里要注意的是,因为Android本身为了考虑同步所带来的消耗和减少唤醒设备的次数,所以这里预设的时间并一定准确,系统其实在内部做了一些同步的算法处理,例如尽量将所有需要同步数据都安排在某个时间点,所以我们预设的那个时间值仅仅是作为一个参考罢了。另一种方式是手动同步,手动同步则是在应用中调用某个方法直接告诉设备,通知系统同步数据。

当我们再次在已添加的帐户列表中点击我们的APP后会变成这样,如图:

更改同步页面

如果您希望在已添加的帐户列表中点击我们的APP后出现一个自定义的页面,那么就得

修改authenticator.xml文件,加入accountPreferences属性:

[html] view plain copy

<?xml version="1.0"encoding="utf-8"?>  
<account-authenticatorxmlns:androidaccount-authenticatorxmlns:android="http://schemas.android.com/apk/res/android"  
   android:accountType="project.test.com.myapplication.account.type"  
   android:icon="@mipmap/ic_launcher"  
    android:smallIcon="@mipmap/ic_launcher"  
   android:label="@string/app_name"  
   android:accountPreferences="@xml/account_preferences"  
/>  

account_preferences.xml文件:

[html] view plain copy

<?xml version="1.0"encoding="utf-8"?>  
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">  
   <PreferenceCategoryandroid:titlePreferenceCategoryandroid:title="PreferenceCategory_title" />  
   <PreferenceScreen  
       android:key="key1"  
       android:title="PreferenceScreen_title"  
       android:summary="PreferenceScreen_summary">  
       <intent  
           android:action="key1.ACTION"  
           android:targetPackage="key1.package"  
           android:targetClass="key1.class" />  
   </PreferenceScreen>  
</PreferenceScreen>  

我们再次运行APP,然后往已添加的帐户列表中点击我们的APP后便会出现一个中间页面,如图:

拉活机制

对于某些APP来说,当然都希望自己的进程尽量的不被杀死。于是乎,就出现的各种进程保活的方法,其中帐户同步也算为其中的一种。因为系统帐户自动同步会在特定时间时同步APP数据到服务器上去。上面实现也看到,登录帐户和执行同步帐户数据都是通过我们自己的代码来实现,所以在同步数据时会把已退出或完全结束的APP再次拉活起来。笔者在部分手机中验证过我们今天介绍的Demo,发现效果还是行得通的。当然由于国内手机厂商的各种订制和阉割,也会出现某部分手机是行不通的,大家有时间可以自行去尝试。这点也算是Android系统的一个小漏洞。希望大家能善用帐户同步功能,养成良好的开发信仰,尊重用户意愿。因为并不是所有用户都是愿意让不相关的APP任其后台常驻的!

总结

本文只为简单介绍和演示Android帐户同步的实现步骤,大家如若需要更完整的帐户同步实现,有兴趣可以自行去研究一下在Anroid SDK中samples目录下也有一个叫SampleSyncAdapter的帐号与同步的实例。另外上面介绍的Demo源码可以点击这里下载

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值