AccountManager提供了一个获取账户token的方法getAuthToken(),此方法有一个参数,是传一个Ativity的,这个activity是用来打开自定的登录页面或授权页面的,方法介绍上面写着这个参数不能为空must not be null,其实是可以传空值的。空与非空有什么区别呢?
区别
从定义来看,它是用来used only to call startActivity(),是系统帮我们打开Authencator里面的getAuthToken返回的intent。我们可以通过AccountManager的源码看到:
它的内部类class Response
/** Handles the responses from the AccountManager */
private class Response extends IAccountManagerResponse.Stub {
public void onResult(Bundle bundle) {
Intent intent = bundle.getParcelable(KEY_INTENT);
if (intent != null && mActivity != null) {
// since the user provided an Activity we will silently start intents
// that we see
mActivity.startActivity(intent);
// leave the Future running to wait for the real response to this request
} else if (bundle.getBoolean("retry")) {
try {
doWork();
} catch (RemoteException e) {
// this will only happen if the system process is dead, which means
// we will be dying ourselves
}
} else {
set(bundle);
}
}
当判断到传入的activity不为空时,则会用它来startActivity,跳转到我们自己定义的登录或授权页面。
如果activity传入的为空,则AccountManager内部将不会帮处理getAuthToken回传的intent,它会将这个intent抛给调用者,需要调用者去处理此intent。这个比较适合没有Activity的地方,比如service;其实它里面就提供了一个参数没有activity的同名方法。
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options,
final boolean notifyAuthFailure,
AccountManagerCallback<Bundle> callback, Handler handler)
同步获取token
public String getAuthToken() {
AccountManager am = AccountManager.get(context);
Account[] accounts = am.getAccountsByType("yourtype");
if(accounts != null && accounts.length != 0) {
mAccountManagerFuture = accountManager.getAuthToken(accounts[0], scope, bundle, activity, (AccountManagerCallback)null, (Handler)null);
Bundle result;
try {
result = (Bundle)this.mAccountManagerFuture.getResult(20, TimeUnit.SECONDS);
} catch (OperationCanceledException e) {
//超时或用户取消操作都会抛此异常
} catch (Exception e) {
}
String authToken = null;
if(mAccountManagerFuture.isDone()) {
if(!result.containsKey(AccountManager.KEY_AUTHTOKEN)) {
if(result.containsKey(AccountManager.KEY_INTENT)) {
Intent intent = (Intent)result.getParcelable(AccountManager.KEY_INTENT);
//自己处理intent
}
if(bundle.containsKey(AccountManager.KEY_ERROR_MESSAGE)) {
String msg = bundle.getString(AccountManager.KEY_ERROR_MESSAGE);
//自己处理错误信息
}
}
authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
}
return authToken;
} else {
//不存在账户,则可以调起登录页面
}
}
异步获取token
AccountManager内部会通过回调来将结果返回给调用方。
public void getAuthToken() {
AccountManager am = AccountManager.get(context);
Account[] accounts = am.getAccountsByType("yourtype");
if(accounts != null && accounts.length != 0) {
mAccountManagerFuture = accountManager.getAuthToken(account[0], scope, bundle, activity, new AccountManagerCallback() {
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle result = (Bundle)future.getResult();
if(result != null) {
if(result.containsKey(AccountManager.KEY_AUTHTOKEN)) {
String intent = result.getString(AccountManager.KEY_AUTHTOKEN);
} else if(result.containsKey(AccountManager.KEY_INTENT)) {
Intent intent1 = (Intent)result.getParcelable(AccountManager.KEY_INTENT);
//自己处理Intent,如果activity不为空,则不会回传intent,无需自己处理
} else if(result.containsKey(AccountManager.KEY_ERROR_MESSAGE)) {
String msg = result.getString(AccountManager.KEY_ERROR_MESSAGE);
//自己处理错误信息
}
} catch (OperationCanceledException e) {
//超时或用户取消操作都会抛此异常
} catch (Exception e) {
}
}
}, (Handler)null);
}
}
注意
需要注意的是,在Android SDK >= 23,即Android6.0上,在原有的AndroidManifest.xml声明权限的基础上,新增了运行时权限动态检测。如果提供account的app与调用的app签名不一样,获取账户的权限android.permission.GET_ACCOUNTS也需要添加动态检测,如果只是在manifest里面声明,AndroidStudio不会提示有问题,但是执行下面代码的时候accounts.length一直是0。
AccountManager am = AccountManager.get(context);
Account[] accounts = am.getAccountsByType("yourtype");
if(accounts.length == 0){
//will always true
}
那我们如何添加动态检测权限?
private static final int REQUEST_CODE_GET_ACCOUNTS = 101;
@TargetApi(23)
public void checkAccountsPermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
//做自己的动作
return;
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.GET_ACCOUNTS)
!= PackageManager.PERMISSION_GRANTED) {
//申请 GET_ACCOUNTS 权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS},
REQUEST_CODE_GET_ACCOUNTS);
} else {
//继续动作
}
}
如果发现没有权限,ContextCompat.checkSelfPermission(this, Manifest.permission.GET_ACCOUNTS) 将返回-1,这个时候不等于PackageManager.PERMISSION_GRANTED即0,将要去申请权限,这个时候系统会弹框提示,让用户确认。
我们还可以通过activity里面实现onRequestPermissionsResult方法来处理用户对权限的确认或取消。
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[]
grantResults) {
switch (requestCode) {
case REQUEST_CODE_GET_ACCOUNTS:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用户已授权,可以继续获取token了
} else {
Toast.makeText(this, getString(R.string.accounts_permision_denied)).show();
}
break;
}
}