1. 程式人生 > >Android AccountManager帳號管理(一)

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才可清除系統的儲存

到這裡,你就成功註冊了一個新的帳號型別了