AccountManager getAuthToken问题

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;
    }
}
文章目录
  1. 1. 区别
  2. 2. 同步获取token
  3. 3. 异步获取token
  4. 4. 注意
|