Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android應用開發筆記(7):構建自己的Android賬戶與內容同步機制,例程SampleSyncAdapter的分析

Android應用開發筆記(7):構建自己的Android賬戶與內容同步機制,例程SampleSyncAdapter的分析

編輯:Android開發實例

裝過Android版的Facebook、lastfm的同學是否對於這些應用的功能感到驚喜,它們可以定期更新朋友的最新信息,將最新近況和心情短語集成入聯系人中。這些應用全部是以Android2.0後的賬戶和同步機制為基礎的。Google的例程中給出了名為SampleSyncAdpater的例子,通過分析該例子可以學會Android中的Account驗證、同步Adapter的使用。

 

詳細例子代碼可以看sdk samples中提供的源碼,現在拿2.2中的版本來簡要說明。

 

 

首先是 class Authenticator extends AbstractAccountAuthenticator ,該類是賬戶認證類,打開手機的Setting裡,有Account&Sync 一項,Authenticator就是實現其中的賬號功能的類。

 

  1. // in Authenticator.java  
  2. public Bundle addAccount(AccountAuthenticatorResponse response,  
  3. String accountType, String authTokenType, String[] requiredFeatures,  
  4. Bundle options) {  
  5. final Intent intent = new Intent(mContext, AuthenticatorActivity.class);  
  6. intent.putExtra(AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE,  
  7. authTokenType);  
  8. intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,  
  9. response);  
  10. final Bundle bundle = new Bundle();  
  11. bundle.putParcelable(AccountManager.KEY_INTENT, intent);  
  12. return bundle;  

其中addAccount方法用來定義需要增加賬號時的操作,如調用AuthenticatorActivity來進行賬號的添加認證。

在AuthenticatorActivity.java中定義了handleLogin(),此方法由login_activity.xml中的android:onClick="handleLogin"定義與ui中的okbutton的關聯。

 

  1. // in layout/login_activity.xml  
  2. <Button 
  3. android:id="@+id/ok_button" 
  4. android:layout_width="wrap_content" 
  5. android:layout_height="wrap_content" 
  6. android:layout_gravity="center_horizontal" 
  7. android:minWidth="100dip" 
  8. android:text="@string/login_activity_ok_button" 
  9. android:onClick="handleLogin" /> 

handleLogin()將ui中的用戶名和密碼取得,並創建一個試圖認證的線程,通過網絡去服務端驗證。

NetworkUtilities.java中的 public static boolean authenticate(String username, String password, Handler handler, final Context context)方法展示了通過網絡驗證的具體流程。得到服務端驗證結果後,在sendResult()中通過handler.post調用來實現onAuthenticationResult()在AuthenticatorActivity中的運行。onAuthenticationResult()判斷驗證通過則結束AuthenticatorActivity,否則報出用戶名密碼錯,讓用戶在AuthenticatorActivity中再次嘗試驗證。

 

  1. // AuthenticatorActivity.java中的handleLogin()方法  
  2. /**  
  3. * Handles onClick event on the Submit button. Sends username/password to  
  4. * the server for authentication.  
  5. *  
  6. * @param view The Submit button for which this method is invoked  
  7. */ 
  8. public void handleLogin(View view) {  
  9. if (mRequestNewAccount) {  
  10. mUsername = mUsernameEdit.getText().toString();  
  11. }  
  12. mPassword = mPasswordEdit.getText().toString();  
  13. if (TextUtils.isEmpty(mUsername) || TextUtils.isEmpty(mPassword)) {  
  14. mMessage.setText(getMessage());  
  15. } else {  
  16. showProgress();  
  17. // Start authenticating...  
  18. mAuthThread =  
  19. NetworkUtilities.attemptAuth(mUsername, mPassword, mHandler,  
  20. AuthenticatorActivity.this);  
  21. }  

 

 

  1. // NetworkUtilities中的authenticate()方法通過網絡訪問具體來實現服務端的驗證,sendResult()來使調用結果被AuthenticatorActivity的onAuthenticationResult()調用。  
  2. /**  
  3. * Connects to the Voiper server, authenticates the provided username and  
  4. * password.  
  5. *  
  6. * @param username The user's username  
  7. * @param password The user's password  
  8. * @param handler The hander instance from the calling UI thread.  
  9. * @param context The context of the calling Activity.  
  10. * @return boolean The boolean result indicating whether the user was  
  11. *         successfully authenticated.  
  12. */ 
  13. public static boolean authenticate(String username, String password,  
  14. Handler handler, final Context context) {  
  15. final HttpResponse resp;  
  16. final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();  
  17. params.add(new BasicNameValuePair(PARAM_USERNAME, username));  
  18. params.add(new BasicNameValuePair(PARAM_PASSWORD, password));  
  19. HttpEntity entity = null;  
  20. try {  
  21. entity = new UrlEncodedFormEntity(params);  
  22. } catch (final UnsupportedEncodingException e) {  
  23. // this should never happen.  
  24. throw new AssertionError(e);  
  25. }  
  26. final HttpPost post = new HttpPost(AUTH_URI);  
  27. post.addHeader(entity.getContentType());  
  28. post.setEntity(entity);  
  29. maybeCreateHttpClient();  
  30. try {  
  31. resp = mHttpClient.execute(post);  
  32. if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {  
  33. if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  34. Log.v(TAG, "Successful authentication");  
  35. }  
  36. sendResult(true, handler, context);  
  37. return true;  
  38. } else {  
  39. if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  40. Log.v(TAG, "Error authenticating" + resp.getStatusLine());  
  41. }  
  42. sendResult(false, handler, context);  
  43. return false;  
  44. }  
  45. } catch (final IOException e) {  
  46. if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  47. Log.v(TAG, "IOException when getting authtoken", e);  
  48. }  
  49. sendResult(false, handler, context);  
  50. return false;  
  51. } finally {  
  52. if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  53. Log.v(TAG, "getAuthtoken completing");  
  54. }  
  55. }  
  56. }  
  57. /**  
  58. * Sends the authentication response from server back to the caller main UI  
  59. * thread through its handler.  
  60. *  
  61. * @param result The boolean holding authentication result  
  62. * @param handler The main UI thread's handler instance.  
  63. * @param context The caller Activity's context.  
  64. */ 
  65. private static void sendResult(final Boolean result, final Handler handler,  
  66. final Context context) {  
  67. if (handler == null || context == null) {  
  68. return;  
  69. }  
  70. handler.post(new Runnable() {  
  71. public void run() {  
  72. ((AuthenticatorActivity) context).onAuthenticationResult(result);  
  73. }  
  74. });  

 

 

  1. // AuthenticatorActivity.java中的onAuthenticationResult,來根據驗證結果來選擇結束認證或重新嘗試。  
  2. /**  
  3. * Called when the authentication process completes (see attemptLogin()).  
  4. */ 
  5. public void onAuthenticationResult(boolean result) {  
  6. Log.i(TAG, "onAuthenticationResult(" + result + ")");  
  7. // Hide the progress dialog  
  8. hideProgress();  
  9. if (result) {  
  10. if (!mConfirmCredentials) {  
  11. finishLogin();  
  12. } else {  
  13. finishConfirmCredentials(true);  
  14. }  
  15. } else {  
  16. Log.e(TAG, "onAuthenticationResult: failed to authenticate");  
  17. if (mRequestNewAccount) {  
  18. // "Please enter a valid username/password.  
  19. mMessage  
  20. .setText(getText(R.string.login_activity_loginfail_text_both));  
  21. } else {  
  22. // "Please enter a valid password." (Used when the  
  23. // account is already in the database but the password  
  24. // doesn't work.)  
  25. mMessage  
  26. .setText(getText(R.string.login_activity_loginfail_text_pwonly));  
  27. }  
  28. }  

 

Account的驗證完畢後,就生成了賬號,可以開始使用同步功能了。同步的主要邏輯在public class SyncAdapter extends AbstractThreadedSyncAdapter中實現。

 

  1. // SyncAdapter.java中的OnPerformSync方法,主要的同步邏輯  
  2. @Override 
  3. public void onPerformSync(Account account, Bundle extras, String authority,  
  4. ContentProviderClient provider, SyncResult syncResult) {  
  5. List<User> users;  
  6. List<Status> statuses;  
  7. String authtoken = null;  
  8. try {  
  9. // use the account manager to request the credentials  
  10. authtoken =  
  11. mAccountManager.blockingGetAuthToken(account,  
  12. Constants.AUTHTOKEN_TYPE, true /* notifyAuthFailure */);  
  13. // fetch updates from the sample service over the cloud  
  14. users =  
  15. NetworkUtilities.fetchFriendUpdates(account, authtoken,  
  16. mLastUpdated);  
  17. // update the last synced date.  
  18. mLastUpdated = new Date();  
  19. // update platform contacts.  
  20. Log.d(TAG, "Calling contactManager's sync contacts");  
  21. ContactManager.syncContacts(mContext, account.name, users);  
  22. // fetch and update status messages for all the synced users.  
  23. statuses = NetworkUtilities.fetchFriendStatuses(account, authtoken);  
  24. ContactManager.insertStatuses(mContext, account.name, statuses);  
  25. } catch (final AuthenticatorException e) {  
  26. syncResult.stats.numParseExceptions++;  
  27. Log.e(TAG, "AuthenticatorException", e);  
  28. } catch (final OperationCanceledException e) {  
  29. Log.e(TAG, "OperationCanceledExcetpion", e);  
  30. } catch (final IOException e) {  
  31. Log.e(TAG, "IOException", e);  
  32. syncResult.stats.numIoExceptions++;  
  33. } catch (final AuthenticationException e) {  
  34. mAccountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE,  
  35. authtoken);  
  36. syncResult.stats.numAuthExceptions++;  
  37. Log.e(TAG, "AuthenticationException", e);  
  38. } catch (final ParseException e) {  
  39. syncResult.stats.numParseExceptions++;  
  40. Log.e(TAG, "ParseException", e);  
  41. } catch (final JSONException e) {  
  42. syncResult.stats.numParseExceptions++;  
  43. Log.e(TAG, "JSONException", e);  
  44. }  

onPerformSync中的執行流程中,使用NetworkUtilities中的fetchFriendUpdates和fetchFriendStatuses來訪問服務端的聯系人更新,並使用了例程中自己封裝的ContactManager來讀取、更新聯系人信息。

 

那Account和SyncAdapter及其Service和xml定義之間是如何關聯的呢? AndroidManifest.xml中定義了AccountAuthenticator,SyncAdapter及對應的Service和xml定義的關聯。

 

  1.     <application 
  2. android:icon="@drawable/icon" 
  3. android:label="@string/label"> 
  4. <!-- The authenticator service --> 
  5. <service 
  6. android:name=".authenticator.AuthenticationService" 
  7. android:exported="true"> 
  8. <intent-filter> 
  9. <action 
  10. android:name="android.accounts.AccountAuthenticator" /> 
  11. </intent-filter> 
  12. <meta-data 
  13. android:name="android.accounts.AccountAuthenticator" 
  14. android:resource="@xml/authenticator" /> 
  15. </service> 
  16. <service 
  17. android:name=".syncadapter.SyncService" 
  18. android:exported="true"> 
  19. <intent-filter> 
  20. <action 
  21. android:name="android.content.SyncAdapter" /> 
  22. </intent-filter> 
  23. <meta-data 
  24. android:name="android.content.SyncAdapter" 
  25. android:resource="@xml/syncadapter" /> 
  26. <meta-data 
  27. android:name="android.provider.CONTACTS_STRUCTURE" 
  28. android:resource="@xml/contacts" /> 
  29. </service> 
  30. <activity 
  31. android:name=".authenticator.AuthenticatorActivity" 
  32. android:label="@string/ui_activity_title" 
  33. android:theme="@android:style/Theme.Dialog" 
  34. android:excludeFromRecents="true" 
  35. <!--  
  36. No intent-filter here! This activity is only ever launched by  
  37. someone who explicitly knows the class name  
  38. --> 
  39. </activity> 
  40. </application> 

 

更詳細的代碼細節和執行流程,可以去把SDK中的SampleSyncAdapter代碼運行起來體會一下,不過要實現整個流程,必須搭建聯系人的服務器端,例程中在目錄samplesyncadapter_server中也提供了簡單的server端python代碼,需要搭建在google app engine上。搭建過程遇到一些問題,由於對python不熟我弄了幾天才解決好搭建成功,其中遇到的一個model moudle找不到的問題需要你在model中新建一個__init__.py的空文件,來說明是一個python模塊,如果你也遇到此問題,希望對你有幫助。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved