一文搞懂Android賬戶系統(tǒng)的使用方式簡(jiǎn)介概述實(shí)戰(zhàn)環(huán)境布置基本使用(注冊(cè)、登錄和退出)注冊(cè)登錄退出附錄
一文搞懂Android賬戶系統(tǒng)的使用方式
簡(jiǎn)介
Android賬戶系統(tǒng)是利用Android系統(tǒng)為應(yīng)用對(duì)其賬戶信息進(jìn)行管理,同時(shí)避免每次進(jìn)入應(yīng)用都需用戶手動(dòng)輸入用戶名密碼進(jìn)行驗(yàn)證的繁瑣操作。
概述
Android賬戶系統(tǒng)是利用AccountManager家族的系統(tǒng)接口,將應(yīng)用的賬戶信息交給系統(tǒng)管理,并與后臺(tái)服務(wù)端自認(rèn)證處理。
我們可以在設(shè)置界面中看到Android系統(tǒng)管理的賬戶相關(guān)信息:

Account家族均在系統(tǒng)的android/accounts文件目錄中,主要有如下幾個(gè)類成員:
AbstractAccountAuthenticator
Account
AccountAuthenticatorActivity
AccountAutheticatorResponse
AccountManager
AuthenticatorDescription
當(dāng)然應(yīng)用是通過(guò)AccountManager去和系統(tǒng)服務(wù)AccountManagerService進(jìn)行交互,具體的功能實(shí)現(xiàn)還是在service中。
實(shí)戰(zhàn)
應(yīng)用想實(shí)現(xiàn)通過(guò)Android系統(tǒng)管理自己的賬戶信息,大致需要進(jìn)行如下幾個(gè)步驟:
環(huán)境布置;
實(shí)現(xiàn)添加賬戶、登錄及退出登錄操作;
環(huán)境布置
首先是環(huán)境布置,這里的環(huán)境布置是應(yīng)用實(shí)現(xiàn)該功能的基本配置。
1、首先是權(quán)限配置,若要使用系統(tǒng)的該功能,需要獲取一些相關(guān)的權(quán)限:
<!--AccountManager所需權(quán)限-->
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
2、在AndroidManifest.xml文件中添加系統(tǒng)服務(wù)AccountService,為了告訴系統(tǒng)當(dāng)前應(yīng)用需要使用Account服務(wù)
<service android:name=".acounts.AccountService"
android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator"/> //1
</service>
3、在2中,我們注意到注釋1處配置了一份資源文件,該文件是定義當(dāng)前應(yīng)用的賬戶類型(通常就是包名)
在res目錄下,和values同級(jí)的目錄xml(若沒(méi)有則新建)下定義authenticator.xml文件(名字自定義)
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.welthy.demo" //1
android:icon="@mipmap/ic_launcher_round"
android:smallIcon="@mipmap/ic_launcher"
android:label="@string/app_name"/>
主要的就是注釋1的accountType,我們?cè)O(shè)為包名。
4、新建2中定義的AccountService
public class AccountService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
MyAuthenticator myAuthenticator = new MyAuthenticator(this); //1
return myAuthenticator.getIBinder();
}
}
在這個(gè)服務(wù)中,我們只實(shí)現(xiàn)其onBind方法,目的就是為了返回告訴系統(tǒng)當(dāng)前應(yīng)用的Authenticator的binder引用。以便系統(tǒng)可以調(diào)用我們自定義的Authenticator中的相關(guān)操作,以便實(shí)現(xiàn)賬戶功能。
5、新建4中提到的MyAuthenticator,該類是繼承自AbstractAccountAuthenticator
public class MyAuthenticator extends AbstractAccountAuthenticator {
?
private Context mContext;
public MyAuthenticator(Context context) {
super(context);
this.mContext = context;
}
......
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
Intent intent = new Intent(mContext, AccountActivity.class);
intent.putExtra(AccountActivity.ARG_ACCOUNT_TYPE, accountType);
intent.putExtra(AccountActivity.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent); //1
return bundle; //2
}
String authToken;
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
final AccountManager am = AccountManager.get(mContext); //3
authToken = am.peekAuthToken(account, authTokenType); //4
if (TextUtils.isEmpty(authToken)) { //5
final String password = am.getPassword(account);
if (password != null) {
//向服務(wù)器再次請(qǐng)求
}
// authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType); //調(diào)用服務(wù)端認(rèn)證登錄,獲取其authToken
//認(rèn)證成功
if (!TextUtils.isEmpty(authToken)) { //6
Log.d("wx","authentication success");
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
return result;
}
?
//未注冊(cè)過(guò)
Log.d("wx","never registered."); //7
final Intent intent = new Intent(mContext, AccountActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONS E, response);
intent.putExtra(AccountActivity.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(AccountActivity.ARG_AUTH_TYPE, authTokenType);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
......
}
新建的Authenticator繼承系統(tǒng)的AbstractAccountAuthenticator后,我們需要實(shí)現(xiàn)其中的抽象方法,我們主要需要實(shí)現(xiàn)其中的addAccount()和getAuthenToken()方法,其他的默認(rèn)實(shí)現(xiàn)即可。
首先看到addAccount()方法,我們就是新建Intent意圖,并將該Intent封裝到Bundle中。目的是告訴系統(tǒng)我們的登錄界面,以及當(dāng)前應(yīng)用的賬戶類型(包名),系統(tǒng)會(huì)去提取該Bundle中意圖,然后去啟動(dòng)我們指定的登錄界面進(jìn)行登錄操作。通過(guò)指定AccountManager.KEY_INTENT告訴系統(tǒng)我們的intent信息。
然后是getAuthToken()方法。首先在注釋1,獲得其AccountManager引用。然后通過(guò)它調(diào)用peekAuthToken獲取當(dāng)前賬戶信息的authToken。若該authToken為空的話,則會(huì)進(jìn)入注釋5,將該賬戶信息向服務(wù)端發(fā)起認(rèn)證請(qǐng)求。若認(rèn)證成功的話,則會(huì)進(jìn)入注釋6,我們直接返回該bundle即可。最后若沒(méi)認(rèn)證則進(jìn)入注釋7,代表該賬戶從未進(jìn)行過(guò)注冊(cè),需要跳轉(zhuǎn)到登錄界面進(jìn)行注冊(cè)登錄。
賬戶系統(tǒng)的基本布置就是如此,當(dāng)然還有Authenticator中其他的抽象方法進(jìn)行相關(guān)的操作。
基本使用(注冊(cè)、登錄和退出)
注冊(cè)
當(dāng)環(huán)境配置完后,我們只需通過(guò)AccountManager,調(diào)用其addAccount()方法就可完成一個(gè)賬戶信息的注冊(cè)
Account account = new Account(username,ARG_ACCOUNT_TYPE); //1
AccountManager am = AccountManager.get(AccountActivity.this); //2
am.addAccountExplicitly(account,pwd,null); //3
注釋1,創(chuàng)建當(dāng)前用戶對(duì)應(yīng)的Account封裝;注釋2,獲取AccountManager引用;注釋3,調(diào)用addAccountExplicitly()方法進(jìn)行賬戶添加(內(nèi)部有幾個(gè)addAccountXXX方法,將在源碼分析中再進(jìn)行說(shuō)明)。
登錄
登錄時(shí)我們通過(guò)調(diào)用getAuthToken()方法進(jìn)行登錄操作,關(guān)于驗(yàn)證及驗(yàn)證結(jié)果的操作在getAuthToken的講解中已進(jìn)行過(guò)說(shuō)明。
Account account = new Account(username,ARG_ACCOUNT_TYPE); //1
AccountManager am = AccountManager.get(AccountActivity.this); //2
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() { //3
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Log.d("wx","ok run");
Intent it = new Intent(AccountActivity.this, MainActivity.class);
startActivity(it);
}catch (Exception e){}
}
};
am.getAuthToken(account,ARG_ACCOUNT_TYPE,null,AccountActivity.this,callback,null); //4
注釋1,獲取當(dāng)前需登錄的賬戶封裝,注釋2,獲取AccountManager引用,注釋3,我們定義了一個(gè)AccountManagerCallback,為了接收驗(yàn)證成功的結(jié)果回調(diào)。注釋4,調(diào)用AccountManager的getAuthToken()進(jìn)行登錄操作。
退出
對(duì)于Android賬戶系統(tǒng)來(lái)說(shuō),要退出代表移除當(dāng)前賬戶,及時(shí)再輸入其用戶名和密碼也沒(méi)有,就像從未注冊(cè)過(guò)一樣,因此該操作若需要的話建議在系統(tǒng)設(shè)置中操作。我們通過(guò)AccountManager.invalidateAuthToken ()方法將該賬戶的authToken置為無(wú)效。
以上是賬戶相關(guān)的基本操作,更詳細(xì)可參考附錄1中的信息。
附錄
1、http://blog.udinic.com/2013/04/24/write-your-own-android-authenticator/