Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義賬戶類型和同步適配器模式 Custom Account Type & SyncAdapter

Android自定義賬戶類型和同步適配器模式 Custom Account Type & SyncAdapter

編輯:關於Android編程

自定義賬戶類型 Custom Account Type

當有多個APP共用一個賬號系統的時候,在用戶的Android設備上創建一個自定義賬戶用以處理登錄認證會方便很多,比如騰訊的QQ,浏覽器,應用寶系列,360安全衛士、手機助手系列等都是共用一個賬號的,這個賬戶在系統設置頁面的賬戶管理可以看到。
這裡寫圖片描述
創建自定義賬戶可以分三步:

1、創建認證Activity,這個Activity負責和用戶交互錄入用戶賬戶數據、驗證、保存憑證。
2、繼承實現AbstractAccountAuthenticator類。
3、創建賬戶認證的Serivice,。

下面具體介紹如何一步步創建自己的自定義賬戶:
使用到的權限


1. 創建LoginActivity類

因為這個Activity要和系統AccountManager交互所以他就有一些一般activity所沒有的特定需求。為了方便Android框架提供了一個基礎類, AccountAuthenticatorActivity ,通過繼承它你可以專注去創建你自己的自定義身份驗證。 這個類比較簡單,你也可以繼承自普通的Activity,把AccountAuthenticatorActivity裡面的代碼拷貝進去就行。然後就可以和普通Activity一樣處理,界面交互完全取決於你自己。

當如果AbstractAccountAuthenticator需要用一個Activity去處理請求,就可以通過傳遞相應Intent讓系統調用這個Activity來處理,關於AbstractAccountAuthenticator後面再講,先來說說AccountAuthenticatorActivity,查看源碼可知,他內部定義了兩個私有屬性。

private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
private Bundle mResultBundle = null;

mAccountAuthenticatorResponse是在onCreate中通過getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE) 獲取的。
mResultBundle是需要在finish前手動調用setAccountAuthenticatorResult()方法設置的,作為給啟動此activity請求的結果。示例:

Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mUsername);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK, intent);
finish();

如果沒有設置結果或設置為null,在response中將會被當當做error( ERROR_CODE_CANCELED)。

在LoginActivity中使用下面的方法在系統中創建自定義賬戶

Account account = new Account(name, AccountType);
   accountManager.addAccountExplicitly(account,passwd,null);

其中的AccountType是你自定義的常量,應該與你在manifest.xml中用於賬戶綁定service裡聲明的一致,service具體後面詳細介紹,如果與聲明的不一致會報權限錯誤。

需要特別注意的是實際使用中不應該把密碼明文保存,官方文檔是這麼說的:

AccountManager 不是一種加密服務。它僅僅按照你傳遞的內容用”’明文”’來存儲。在大部分設備上,這不是問題,因為設備把他們存儲在只有根用戶才能訪問的數據庫中。但是在已經被Root過的設備上,通過adb連接可以讓任何人讀取憑據信息。
因此,你不能直接傳遞用戶的真實密碼給AccountManager.addAccountExplicitly()。 相反,你應該存儲加密的安全令牌來保障限制攻擊者的使用

一般的做法是如果一個設備只能登陸一個賬戶的話,name用來標識應用程序,而真正的用戶名和臨時的訪問Token使用AccountManger.setUserData()保存在account的額外信息中。

2. 實現AbstractAccountAuthenticator

繼承AbstractAccountAuthenticator實現AccountAuthenticator,這個類是和系統通信的具體實現,比如在系統設置的賬戶管理中新建賬戶,系統調用的就是這個類中的addAccount方法,通過這個方法可以啟動上面創建的認證Activity。
AbstractAccountAuthenticator內部有幾個抽象方法,這幾個方法分別於AccountManager內部的幾個方法一一對應。

//返回一個bundle,這個bundle可以包含一個用於啟動編輯賬戶Properties的Activity的Intent,對應於accountManager.editProperties()
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType)
//返回一個bundle, 包含添加賬戶Activity的Intent,對應於 accountManager.addAccountExplicitly()
Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)
//檢查用戶傳遞過來的憑證 對應於 AccountManager.confirmCredentials()
Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options)
//獲取Token,對應於AccountManager.getAuthToken() 其他進程一般調用這個進行認證
Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
//更新用戶憑證 對應於AccountManager.updateCredentials()
Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
//獲取authToken的本地化標簽,具體用處我也沒搞明白
String getAuthTokenLabel(String authTokenType)
//檢查驗證account支持的是否支持請求驗證的功能
Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features)

以上各個方法返回的bundled內容的 Key可以包含下面幾種情況:

AccountManager.KEY_INTENT :聲明後面要啟動的intent
AccountManager.KEY_ERROR_CODE:請求出錯的錯誤碼
AccountManager.KEY_ERROR_MESSAGE:請求出錯的錯誤信息
AccountManager.KEY_BOOLEAN_RESULT:請求是否成功
AccountManager.KEY_ACCOUNT_NAME
AccountManager.KEY_ACCOUNT_TYPE
AccountManager.KEY_AUTHTOKEN

等內容或者其他自定義信息。

上面的方法選擇必要的方法實現,不需要的功能返回null即可,下面是兩個方法實現的例子

@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {

    final Intent intent = new Intent(mContext, LoginActivity.class);
    intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, authTokenType);
                intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
    return bundle;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {

    final Bundle result = new Bundle();

    if(authTokenType.equals(AuthTokenType)){
        String authToken = "1111111111";//這裡應該從服務器獲取驗證
        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,true);
        result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
        result.putString(AccountManager.KEY_ACCOUNT_TYPE, AccountType);
        result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
    }else{
        result.putInt(AccountManager.KEY_ERROR_CODE,-1);
        result.putString(AccountManager.KEY_ERROR_MESSAGE,"Auth Failed!");
    }
    return result;
}

3. 創建AuthService

這個服務其實是提供給其他的進程使用的,它的Action為android.accounts.AccountAuthenticator,android系統會通過這個Action找到它,並通過它來把我們自己的賬號注冊到“設置”中,其實這是一個AIDL的使用,它屬於跨進程的調用。
它的實現非常簡單,在onBind()中返回authenticator.getIBinder()即可。
示例代碼:

public class AuthService extends Service {
    private AccountAuthenticator authenticator;
    public AuthService() {
        authenticator = new AccountAuthenticator(this);
    }
    @Override
    public IBinder onBind(Intent intent) {
        return authenticator.getIBinder();
    }
}

然後需要在manifest.xml中注冊,注冊需要遵循一些規則:
1、 action必須注冊為android.accounts.AccountAuthenticator,
2、 需要在meta-data中提供具體的的配置信息,這些信息包含在一個xml文件中,
示例如下:


   
      
   
   

xml/authenticator.xml的內容如下


其中android:accountPreferences=”@xml/account_preferences”並不是必須的,account_preferences是一個PreferenceScreen的布局文件,可以用來寫個跳轉到同步設置頁面的功能。

至此自定義賬戶的創建就完成了,在應用中調用AccountManger的相關方法就可以使用了。

同步適配器 SyncAdapter

同步適配器框架(SyncAdapter Framework)是Android提供的一套移動端與服務端數據同步的解決方案,它有以下優點

插件化結構 自動執行 自動檢測網絡 省電 有賬戶認證機制

最常見的是用於備份,聯系人同步等各種雲同步功能。

SyncAdapter依賴於自定義賬戶、和ContentProvider,即時你的同步功能沒有使用賬戶認證和ContentProvider,也要提供一個虛擬的實現,這是SyncAdapter的必要組件。

下面就來一步步完成一個同步適配器的創建,實現一個SyncAdapter分以下幾步:
需要用到的權限




1.創建自定義賬戶

這個前面已經講過了,按照前面的完成即可。

2.創建SyncContentProvider

由於ContentProvider不是本文的重點,所以就不詳細介紹了,如果不熟的話關於他的詳細知識請自行搜索,這裡為了演示同步適配器的功能只是把繼承自ContentProvider的增刪改查方法簡單的返回而已。

public class SyncContentProvider extends ContentProvider {
    public SyncContentProvider() {
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }
    @Override
    public String getType(Uri uri) {
        return null;
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public boolean onCreate() {
        return true;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        return null;
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        return 0;
    }
}

然後在manifest.xml文件中注冊,比普通的provider多了一個android:syncable=”true”的屬性


其中android:authorities的值後面注冊同步服務的時候會用到。

3.創建SyncAdapter類

繼承自AbstractThreadedSyncAdapter,並實現其方法。

在構造方法中初始化需要用到的組件,比如初始化一個ContentResolver
除了構造方法外只有一個onPerformSync方法需要實現,這是真正要運行的同步方法,這個方法運行在獨立的線程中,其中可以進行聯網耗時操作。
這個方法是系統負責調用的,需要注意的是:

如果沒有網絡,點擊手動同步這個方法也不會立即被調用,而是會加入到等待同步的任務中,等到有網絡的時候才會運行。 不會有同一個認證賬戶下的兩個相同的同步任務同時存在於系統任務隊列的,比如已經有了一個定時的同步任務存在,那麼手動調用同步方法也不會運行的。

4.創建用於啟動同步任務的服務SyncService

和自定義賬戶類型一樣,它的實現也非常簡單,在onBind中返回syncAdapter.getSyncAdapterBinder()即可。
代碼示例:

public class SyncService extends Service {
    private static SyncAdapter syncAdapter;
     //用於保證SyncAdapter的單例
    private static final Object sSyncAdapterLock = new Object();
    @Override
    public void onCreate() {
        super.onCreate();
        synchronized (sSyncAdapterLock){
            if(syncAdapter==null) {
                syncAdapter = new SyncAdapter(this, true);
            }
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        return syncAdapter.getSyncAdapterBinder();
    }
}

在manifest.xml中注冊服務:


    
        
    
    

其中action和meta-data的name屬性都是固定的,meta-data中resource對應的xml文件的寫法配置如下:


android:accountType的值要和自定義賬戶的一致 android:contentAuthority的值要和上面contentProvider注冊的android:authorities的值相一致。 android:allowParallelSyncs當值為true的時候,如果你的賬戶類型支持同時多個不同賬戶存在的話,就可以有多個SyncAdapter的實例,同時運行同步任務,如果不支持多個賬戶的話設置為false就可以。 android:userVisible的作用是指示在在設置的賬戶管理中,同步開關是否可見。 android:isAlwaysSyncable=”true” 系統可以自動同步。

至此同步適配器的配置就完成了,下面講講如何調用SyncAdapter的同步功能:
需要調用運行同步適配器的情況一般有下面這四種:

服務端數據改變
服務端數據改變,推送消息到本地,接收到推送的廣播後可以調用
ContentResolver.requestSync(ACCOUNT, AUTHORITY,bundle);
其中account是要同步的賬戶,authority是ContentProvider注冊的android:authorities, bundle用來傳遞一些其他的數據給SyncAdapter 本地數據改變
本地數據庫有改變可以給ContentProvider設置觀察者ContentObserver,
mResolver.registerContentObserver(mUri, true, observer);
在ContentObserver的監聽方法中調用ContentResolver.requestSync(ACCOUNT, AUTHORITY, bundle); 定時同步
ContentResolver.addPeriodicSync(ACCOUNT,AUTHORITY,Bundle,SYNC_INTERVAL); 用戶有同步需要
原則上不應該由用戶進行動同步,但是如果有需求也可以提供交互調用ContentResolver.requestSync(…)進行同步。
    Bundle settingsBundle = new Bundle();
    settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
    ettingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved