标签:
在android提供的sdk中,samples目录下有一个叫SampleSyncAdapter的示例,它是一个账号与同步的实例,比如Google原始的android手机可以使用Google账号进行数据的同步。具体的比如你想实时同步你的通讯录到服务端,这时候你就可以通过这个实例来了解android提供的同步机制,从而实现自己的同步功能。先介绍一下账号的管理部分。至于账号管理的代码主要是在authenticator包下的三个类里面,还有就是一个叫authenticator 的xml文件。
AuthenticationService是一个继承Service的服务,这个服务其实是提供给其他的进程使用的,android系统会通过"android.accounts.AccountAuthenticator"这个Action找到它,并通过它来把我们自己的账号注册到“设置”中,其实这是一个AIDL的使用,它属于跨进程的调用。下面是manifest中的注册:
<service android:name=".authenticator.AuthenticationService" 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>
这个Service会在onBind方法里返回一个IBinder给客户端进程,如下:
@Override public IBinder onBind(Intent intent) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "getBinder()... returning the AccountAuthenticator binder for intent " + intent); } return mAuthenticator.getIBinder(); }
Authenticator是一个继承自AbstractAccountAuthenticator的类,AbstractAccountAuthenticator是一个虚类,它定义处理手机“设置”里“账号与同步”中Account的添加、删除和验证等功能的基本接口,并实现了一些基本功能。AbstractAccountAuthenticator里面有个继承于IAccountAuthenticator.Stub的内部类,以用来对AbstractAccountAuthenticator的远程接口调用进行包装。我们可以通过AbstractAccountAuthenticator的getIBinder()方法,返回内部类的IBinder形式,以便对此类进行远程调用,如上面代码onBind方法中的调用。AbstractAccountAuthenticator的源码位置在frameworks\base\core\java\android\accounts目录下。Authenticator只需要继承和实现AbstractAccountAuthenticator的几个方法就可以了,像我们所介绍的SampleSyncAdapter实例主要继承了两个方法,如下
</pre><p></p></div><div style="font-family:'Helvetica Neue'; font-size:14px"><p><pre name="code" class="java">//当在“设置”中添加账号时,会调用这个方法,跳转到添加账号页面 @Override public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) { Log.v(TAG, "addAccount()"); //指定AuthenticatorActivity为添加账号的页面,下面会介绍。 final Intent intent = new Intent(mContext, AuthenticatorActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; } //当执行mAccountManager.blockingGetAuthToken(account,Constants.AUTHTOKEN_TYPE, NOTIFY_AUTH_FAILURE);时调用该方法。 @Override public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle loginOptions) throws NetworkErrorException { Log.v(TAG, "getAuthToken()"); // 通过blockingGetAuthToken方法传来的Constants.AUTHTOKEN_TYPE if (!authTokenType.equals(Constants.AUTHTOKEN_TYPE)) { final Bundle result = new Bundle(); result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType"); return result; } final AccountManager am = AccountManager.get(mContext); final String password = am.getPassword(account); if (password != null) { final String authToken = NetworkUtilities.authenticate(account.name, password); if (!TextUtils.isEmpty(authToken)) { //如果已经到服务器验证过账号并保存到AccountManager中 final Bundle result = new Bundle(); result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); result.putString(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE); result.putString(AccountManager.KEY_AUTHTOKEN, authToken); return result; } } //如果没有到服务器验证过账号并保存到AccountManager中,则重新倒添加账号页面中验证。 final Intent intent = new Intent(mContext, AuthenticatorActivity.class); intent.putExtra(AuthenticatorActivity.PARAM_USERNAME, account.name); intent.putExtra(AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE, authTokenType); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; }
AuthenticatorActivity是一个继承自AccountAuthenticatorActivity的 activity,AccountAuthenticatorActivity的源码也是在frameworks\base\core\java \android\accounts目录下。AuthenticatorActivity主要的一个方法是handleLogin(View view),当点击Sign in按钮时会调用该方法,该方法会启动一个异步任务来请求服务器验证用户账号。验证成功后有
一个重要的方法:
/** * Called when response is received from the server for authentication * request. See onAuthenticationResult(). Sets the * AccountAuthenticatorResult which is sent back to the caller. We store the * authToken that's returned from the server as the 'password' for this * account - so we're never storing the user's actual password locally. * * @param result the confirmCredentials result. */ private void finishLogin(String authToken) { Log.i(TAG, "finishLogin()"); final Account account = new Account(mUsername, Constants.ACCOUNT_TYPE); if (mRequestNewAccount) { //直接向AccountManager添加一个帐户 mAccountManager.addAccountExplicitly(account, mPassword, null); //设置让这个账号能够自动同步 ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true); } else { mAccountManager.setPassword(account, mPassword); } final 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(); }
在上面的AuthenticationService注册中有个meta-data的名字为android.accounts.AccountAuthenticator,它所指向的xml文件是authenticator.xml,其内容如下:
<account-authenticator xmlns:android=" http://schemas.android.com/apk/res/android " android:accountType="com.example.android.samplesync" android:icon="@drawable/icon" android:smallIcon="@drawable/icon" android:label="@string/label" />
其中账户类型为com.example.android.samplesync,就是Constants.ACCOUNT_TYPE的值。这个有点像widget,需要一个xml提供你想要的信息。
标签:
原文地址:http://blog.csdn.net/robertcpp/article/details/51353296