Android AccountManager帳號管理(一)
AccountManager簡介
AccountManager帳號管理器,集中管理apps註冊的不同型別的帳號。
不同型別的帳號服務會使用不同的帳號登入和鑑權方式,所以AccountManager為不同型別的帳號提供一個外掛式authenticator模組,authenticators自己處理帳號登入/認證的具體細節,也可以自己儲存帳號資訊
簡言之,AccountManager是一個面向應用程式開發的元件,它提供了一套對應於IAccountManager協議的應用程式介面;這組介面通過Binder機制與系統服務AccountManagerService進行通訊,協作完成帳號相關的操作。同時,AccountManager接收authenticators提供的回撥,以便在帳號操作完成之後向呼叫此帳號服務的業務
- authenticators 即註冊帳號服務的app;
- 業務呼叫方 即使用authenticators提供的帳號服務的第三方,也可以是authenticator自己
使用AccountManager註冊帳號服務
如果應用想要註冊一個新的帳號服務,必須實現AbstractAccountAuthenticator類,這是建立一個account authenticator的抽象基礎類;然後新建一個authenticator service,註冊action必須為”android.accounts.AccountAuthenticator”,且該service要實現onBinder(android.content.Intent)方法,返回AbstractAccountAuthenticator實現類的例項
說下必須要註冊一個action為”android.accounts.AccountAuthenticator”的authenticator service:
首先,AbstractAccountAuthenticator是建立一個account authenticator必須實現的抽象基礎類,介面協議定義在IAccountAuthenticator中,是一個authenticator自定義自己登入/認證等的介面協議;
那如何將authenticator的實現回撥給AccountManagerService,供其調起authenticator的具體實現呢?
就是通過action註冊為”android.accounts.AccountAuthenticator”的authenticator service了:
這個action即為AccountManager#ACTION_AUTHENTICATOR_INTENT的常量值,系統服務AccountManagerService是通過bind到action為AccountManager#ACTION_AUTHENTICATOR_INTENT的intent service上來調起某個賬號型別的authenticator service,然後通過呼叫這個service的getBinder()方法來獲取AbstractAccountAuthenticator的實現例項,進而呼叫authenticator對帳號登入認證等服務的具體實現
至於每個帳號服務都定義一個action為”android.accounts.AccountAuthenticator”的service,那AccountManagerService是如何區分的呢?
當然是通過賬號型別了,每個accountType只能對應一個authenticator
那系統是如何知道每個authenticator service對應的賬號型別?
在AndroidManifest.xml中註冊authenticator service時宣告帳號屬性的meta-data配置,宣告的meta-data是一個name為 “android.accounts.AccountAuthenticator”的xml 資源(AccountManager#AUTHENTICATOR_META_DATA_NAME),該XML資原始檔定義了account-authenticator用到的一些屬性:如accountType;系統解析authenticator service info之後,loadXmlMetaData獲取authenticator 的xml屬性,然後利用 Xml.asAttributeSet即
final PackageManager pm = mContext.getPackageManager();
final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(new Intent("android.accounts.AccountAuthenticator", PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
for (ResolveInfo resolveInfo : resolveInfos) {
android.content.pm.ServiceInfo si = service.serviceInfo;
ComponentName componentName = new ComponentName(si.packageName, si.name);
PackageManager pm = mContext.getPackageManager();
XmlResourceParser parser = null;
try {
parser = si.loadXmlMetaData(pm, "android.accounts.AccountAuthenticator")
if (parser == null) {
throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
}
AttributeSet attrs = Xml.asAttributeSet(parser);
...//解析authenticator xml的帳號屬性
}
...
}
註冊一個測試帳號
建立一個繼承自AbstractAccountAuthenticator的類TestAccountAuthenticator
public class TestAccountAuthenticator extends AbstractAccountAuthenticator {
private Context mContext;
public TestAccountAuthenticator(Context context) {
super(context);
mContext = 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 {//登入介面的定製化實現
Intent addAccountIntent = new Intent(mContext, LoginActivity.class);
addAccountIntent.putExtra("authTokenType", authTokenType);
if (options != null) {
addAccountIntent.putExtras(options);
}
addAccountIntent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);//一定要把response傳入intent的extra中,便於將登入操作的結果回撥給AccountManager
Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, addAccountIntent);
return bundle;
}
@Override
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account) throws NetworkErrorException {//是否允許刪除你的賬號,這裡是不允許刪除,可自定義什麼時候可以被刪除,預設是true
Bundle bundle = new Bundle();
bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
return bundle;
}
@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 {//自己完成獲取鑑權token的流程
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;
}
}
建立一個authenticator service—TestAuthenticatiorService,實現onBinder()方法,在onBinder方法裡返回TestAccountAuthentcator的例項
public class TestAuthenticatiorService extends Service {
private static final String TAG = "XmAuthenticationService";
private TestAccountAuthenticator mAuthenticator;
@Override
public void onCreate() {
super.onCreate();
mAuthenticator = new TestAccountAuthenticator(this);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}
在AndroidManifest.xml檔案中註冊該TestAuthenticatorService
<service
android:name=".TestAuthenticatiorService"
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>
其中,authenticator是一個xml的資原始檔,定義了account的一些屬性
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.test"//賬號型別
android:icon="@drawable/icon"//設定-同步-新增 賬號型別的icon
android:smallIcon="@drawable/miniIcon"//小icon
android:label="@string/label"//設定-同步-新增 賬號型別的名稱
android:accountPreferences="@xml/account_preferences"//在設定中展示的一些偏好
android:customTokens="false"//authenticator是否要自己處理auth token的儲存和獲取許可權
/>
ps:說下customTokens屬性
如設定為true,就需要在TestAccountAuthenticator類的getAuthToken方法的實現中自己進行caller app的許可權檢查和token儲存問題
如不設定(預設為false)或設定為false,則是使用AccountManager的許可權檢查和儲存機制,預設只有簽名相同的app才可呼叫getAuthToken()方法,儲存在系統資料庫中,但要app判斷是否有效,失效要呼叫invalidate才可清除系統的儲存
到這裡,你就成功註冊了一個新的帳號型別了