当有多个APP共用一个账号系统的时候,在用户的Android设备上创建一个自定义账户用以处理登录认证会方便很多,比如腾讯的QQ,浏览器,应用宝系列,360安全卫士、手机助手系列等都是共用一个账号的,这个账户在系统设置页面的账户管理可以看到。
创建自定义账户可以分三步:
1、创建认证Activity,这个Activity负责和用户交互录入用户账户数据、验证、保存凭证。
2、继承实现AbstractAccountAuthenticator类。
3、创建账户认证的Serivice,。
下面具体介绍如何一步步创建自己的自定义账户:
使用到的权限
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
因为这个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的额外信息中。
继承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;
}
这个服务其实是提供给其他的进程使用的,它的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文件中,
示例如下:
...>
"android.accounts.AccountAuthenticator" />
"android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
xml/authenticator.xml的内容如下
"http://schemas.android.com/apk/res/android"
android:accountType="你的AccountType "
android:icon="@drawable/icon"
android:smallIcon="@drawable/miniIcon"
android:label="@string/label"
android:accountPreferences="@xml/account_preferences"
/>
其中android:accountPreferences=”@xml/account_preferences”并不是必须的,account_preferences是一个PreferenceScreen的布局文件,可以用来写个跳转到同步设置页面的功能。
至此自定义账户的创建就完成了,在应用中调用AccountManger的相关方法就可以使用了。
同步适配器框架(SyncAdapter Framework)是Android提供的一套移动端与服务端数据同步的解决方案,它有以下优点
最常见的是用于备份,联系人同步等各种云同步功能。
SyncAdapter依赖于自定义账户、和ContentProvider,即时你的同步功能没有使用账户认证和ContentProvider,也要提供一个虚拟的实现,这是SyncAdapter的必要组件。
下面就来一步步完成一个同步适配器的创建,实现一个SyncAdapter分以下几步:
需要用到的权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
这个前面已经讲过了,按照前面的完成即可。
由于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”的属性
<provider
android:name=".syncadapter.SyncContentProvider"
android:authorities="cn.sleepycoder.customaccount.provider"
android:enabled="true"
android:exported="true"
android:syncable="true">
provider>
其中android:authorities的值后面注册同步服务的时候会用到。
继承自AbstractThreadedSyncAdapter,并实现其方法。
在构造方法中初始化需要用到的组件,比如初始化一个ContentResolver
除了构造方法外只有一个onPerformSync方法需要实现,这是真正要运行的同步方法,这个方法运行在独立的线程中,其中可以进行联网耗时操作。
这个方法是系统负责调用的,需要注意的是:
和自定义账户类型一样,它的实现也非常简单,在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中注册服务:
<service
android:name=".service.SyncService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_adapter" />
service>
其中action和meta-data的name属性都是固定的,meta-data中resource对应的xml文件的写法配置如下:
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="cn.sleepycoder.customaccount"
android:contentAuthority="cn.sleepycoder.customaccount.provider"
android:allowParallelSyncs="false"
android:supportsUploading="false"
android:isAlwaysSyncable="true"
android:userVisible="true">
sync-adapter>
至此同步适配器的配置就完成了,下面讲讲如何调用SyncAdapter的同步功能:
需要调用运行同步适配器的情况一般有下面这四种:
Bundle settingsBundle = new Bundle();
settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
ettingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
Demo下载地址:
http://download.csdn.net/detail/w804518214/9654425