在进行设备账户管理的时候,我们会通过一个 AccountManager 类获取系统的账户管理类,获取的方法如下:
- AccountManager mAccountManager = (AccountManager) getSystemService(ACCOUNT_SERVICE);
或者
- AccountManager accountManager = AccountManager.get(context);
接下来我们需要通过 AccountManager 对象对账号系统进行操作。
1.获取账户信息
首先我们来查看一下如何获取用户已有的账户信息,如果你希望读取系统当前的账户信息,那么你首先需要在 manifest 文件中申明一个读取账户的权限,如下:
- <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
(1)获取所有账户信息
如果你希望获取到当前设备所有的账户信息,你可以使用:
- accountManager.getAccounts();
(2)获取特定的账户信息
如果你只希望获取自己或者特定的账户信息,你就应该使用:
- accountManager.getAccountsByType("com.kifile");
后面的参数是你自己定义的账户类型,怎么设置我会在接下来的文章中写出来。
然后,我们就可以通过上面获取到的工具类,读取到手机上的账户信息了,这里我写了一个 ListView 的 Adapter 用于展示当前系统中的账号信息:
- public class AccountAdapter extends BaseAdapter {
- private Account[] mAccounts;
- public AccountAdapter(Account[] accounts) {
- this.mAccounts = accounts;
- }
- @Override
- public int getCount() {
- return mAccounts != null ? mAccounts.length : 0;
- }
- @Override
- public Object getItem(int position) {
- return mAccounts[position];
- }
- @Override
- public long getItemId(int position) {
- return 0;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- TextView tv = new TextView(getBaseContext());
- tv.setText(mAccounts[position].name + " " + mAccounts[position].type);
- return tv;
- }
- }
获取当前所有系统账户信息以及设置 ListView 的 Adapter 的方法如下:
- mListView.setAdapter(new AccountAdapter(mAccountManager.getAccounts()));
显示效果如下:
同你当前设备进行对比,你就会发现我们已经将当前所有的账户信息显示了出来,同样地如果你只希望显示部分的账户信息,你就可以通过 getAccountByType 获取对应的账户列表。
2.建立自己的账号服务
通过上面的部分,我们已经知道了如何获取 Android 本身现有的账号信息,现在我们就开始着手建立属于自己的账号系统吧。
你需要知道的是,如果你希望建立自己的账号系统,那么你得在 manifest 文件中声明一个关于账号的Service,如下
- <service
- android:name="com.kifile.account.app.account.AccountService"
- android:enabled="true"
- android:exported="true">
- <intent-filter>
- <action android:name="android.accounts.AccountAuthenticator"/>
- </intent-filter>
- <meta-data
- android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/authenticator"/>
- </service>
在上面的代码中,我们通过设置 intent-filter 告知系统,我们当前应用中有一个账号服务,至于具体的账号信息则放在 meta-data 中的 android:resource 文件中提供, 该文件为authenticator.xml,放置路径为 res/xml,内容如下:
- <?xml version="1.0" encoding="utf-8"?>
- <account-authenticator
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:accountType="com.kifile"
- android:icon="@drawable/ic_launcher"
- android:smallIcon="@drawable/ic_launcher"
- android:label="@string/app_name"/>
在这里,我们就可以向系统提供相关的账户信息,用于在 Android Setting 目录下显示对应的账号信息。例如这里,我们就定义了当前的账户类型为"com.kifile",在账户系统中显示的标签为@string/app_name 对应的 String 对象,显示的 icon 为ic_launcher。
Ok,到了这里,我们已经向系统声明了一个账户相关的服务,现在让我们来具体实现他。
Android 为我们提供了一个叫做 AbstractAccountAuthenticator 的抽象类,也是通过它来实现:
- public static class Authenticator extends AbstractAccountAuthenticator {
- public Authenticator(Context context) {
- super(context);
- }
- @Override
- public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
- return null;
- }
- @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 {
- return null;
- }
- @Override
- public String getAuthTokenLabel(String authTokenType) {
- return null;
- }
- @Override
- public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
- return null;
- }
- @Override
- public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
- return null;
- }
- }
当我们创建好了一个AbstractAccountAuthenticator 类后,我们需要从 Service 中取得这个类的对象,代码如下:
- private Authenticator authenticator;
- @Override
- public void onCreate() {
- super.onCreate();
- authenticator = new Authenticator(this);
- }
- @Override
- public IBinder onBind(Intent intent) {
- return authenticator.getIBinder();
- }
当你完成上面的步骤之后,你就会发现,在你的设置页面点击添加账户时就会出现你自定义的账户了,如下:
3.添加账户
虽然通过上面的步骤,我们已经能够在添加账户的界面看到属于我们自己的账户类别了,但是你会发现当你点击它们的时候,没有任何作用,那么我们应该怎么在设备上完成添加账户的操作呢?
(1)加入添加账户的权限
添加账户也需要对应的权限,你应该在 manifest 文件中加入
- <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
(2)重写 Authenticator 的 addAccount 方法
当用户在添加账户页面选择账户进行添加或者调用accountManager.addAccount 的时候,系统会默认调用 AbstractAccountAuthenticator 中的 addAccount 方法,因此你需要重写 addAccount 方法,直接添加默认账户,或者跳转到某个页面,让用户填写用户信息,然后添加账户。
(3)使用 addAccountExplicitly 直接添加账户
如果你希望直接添加账户信息,你可以使用以下方法:
- Account account = new Account("Kifile,,,,12","com.kifile");
- accountManager.addAccountExplicitly(account,password,userdata);
使用之后,你就会发现在设置页面出现你所建立的账户,点击进去,会发现账户名什么的也已经成功设置:
(一)同步数据
通常而言,我们会在两种情况下使用我们的账号系统:
(1)登陆验证
登陆验证其实是一个很实用的功能,试想一下,加入你手机丢失了,而手机上又有很多应用,例如淘宝,QQ,拾到手机的人点击应用就可以登陆到你的账号上,这样一来我们就无法保证我们的信息和账户安全。为了防止这种情况的发生,你会修改你的密码,而当手机账户系统运行的时候,他会发现账户系统验证失败,就无法访问你相关账户了,以确保你的账户安全
(2)信息同步
如果你希望不同的设备上,同一账号的用户的某些信息要保持同步的话,那么你就可以通过账户系统自带的同步服务来实现
1.账户信息同步
账户信息同步其实主要来说有两种方式,一种是自动同步,一种是手动同步。从名字上大家就能够看出两者的区别,前者是我们设定一个固定时间间隔,让 android 系统帮我们自动同步数据,后者则是在应用中调用某个方法直接告诉设备,通知系统同步数据。
(1)自动同步:在这里需要留意的是,虽然前者有设置一个固定时间间隔,但是 android 会尽量将所有同步数据的时间都安排在一起,以减少唤醒设备的次数,因此你可能发现虽然你设置了一个固定的间隔时间,但是到了那个时间点,系统其实并没有按时同步数据。
设置自动同步的代码如下:
- ContentResolver.setSyncAutomatically(account1, "com.kifile.provider", true);
- ContentResolver.addPeriodicSync(account1, "com.kifile.provider", bundle, 10);
(2)手动同步:在我看来手动同步其实是很有作用的,因为当你对账户信息做出更改之后,你不能完全依赖系统的同步机制,他的同步时间可能会在很久之后,因此,你需要手动调用账户同步接口,令系统同步数据。
手动同步的代码如下:
- ContentResolver.requestSync(account1, "com.kifile.provider", bundle);
2.处理同步数据时的异常
同步数据时难免会出现问题,例如网络中断,或者是账号密码验证失败。对于这两种情况而言,前者不需要太过留意,只要之后能够同步正确数据即可,但是后者则需要你通知系统中断当前的账号服务,避免出现账号安全问题。
(二)设置账号页面
在账号信息页面,并不意味着只能暂时简单的账户信息,在这里,你可以根据你的需求自定义账号页面效果。
在自定义账号页面之前,你需要首先在authencator.xml(内容见之前的文章)中声明一下你需要使用的界面效果:
- android:accountPreferences="@xml/account_preferences"
当你设置好界面效果之后,能够发现账号页面除了基本的账号以外,还多了自己设置的相关界面,如下:
虽然界面效果肯定不如我们自己应用的华美,但是我们也能够向用户展示更多信息了。