上篇粗略的分析android添加账号的流程,本篇深入的解析下执行步骤。先来看图片,取自深入理解android卷2:

  上图详细的分析了addAccount的流程,下面我们结合源码来理解它

1、addAccount:其实这里省略了一步,应该是客户端addAccount——>AddAccountSettings.addAccount——>AccountManager.addAccount。我们看下setting是如何到AccountManager:

private void addAccount(String accountType) {
......
AccountManager.get(this).addAccount(
accountType,
null, /* authTokenType */
null, /* requiredFeatures */
addAccountOptions,
null,
mCallback,
null /* handler */);
mAddAccountCalled = true;
}
}

  代码直白的告诉我们就是去调用AccountManager.addAccount(好像是废话哎),但我们深入看get函数发现另有玄机

public static AccountManager get(Context context) {
if (context == null) throw new IllegalArgumentException("context is null");
return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);

  而对于getSystemService(xxx)来说就是ContextImpl对应的注册函数

registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
IAccountManager service = IAccountManager.Stub.asInterface(b);
return new AccountManager(ctx, service);
}})
public AccountManager(Context context, IAccountManager service, Handler handler) {
mContext = context;
mService = service;
mMainHandler = handler;
}

  ok,也就是说AccountManager.get(this)去创建了一个包含IAccountManager.Stub.asInterface(实质为AccountManagerService)的AccountManager.Proxy;这很重要下面会用到。

2—5、这几步都是在AccountManager.addAccount里,代码脉络也很清晰就一起解释吧。

public AccountManagerFuture<Bundle> addAccount(final String accountType,
......
return new AmsTask(activity, handler, callback) {
public void doWork() throws RemoteException {
mService.addAccount(mResponse, accountType, authTokenType,
requiredFeatures, activity != null, optionsIn);
}
}.start();
}

  创建AmsTask并start,而start函数实质是去执行doWork函数,故这里是去执行mService.addAccount。这里需要注意的是mResponse,它是在new AmsTask时被创建

mResponse = new Response();

  Response是AmsTask的内部类且继承自IAccountManagerResponse.Stub(重要)。

6—12、按流程是去执行mService.addAccount,上面分析到mService 为AccountManagerService即AccountManagerService.addAccount

  public void addAccount(final IAccountManagerResponse response, final String accountType,
......
new Session(accounts, response, accountType, expectActivityLaunch,
true /* stripAuthTokenFromResult */) {
@Override
public void run() throws RemoteException {
mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
options);
}
......
}
}.bind();
......

  可以看到此函数中包含很多操作,我们慢慢来剖析。

 private abstract class Session extends IAccountAuthenticatorResponse.Stub
implements IBinder.DeathRecipient, ServiceConnection {
......

   Session的构造函数中初始化mResponse,看参数可知mResponse =AmsTask.mResponse

public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
......
mResponse = response;
......
IAccountManagerResponse response = mResponse;
......

  Session是AccountManagerService内部抽象类,继承IAccountAuthenticatorResponse.Stub且实现ServiceConnection接口,这样才能调用下面的bind(第8步)函数。

void bind() {
......
if (!bindToAuthenticator(mAccountType)) {
Log.d(TAG, "bind attempt failed for " + toDebugString());
onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
}
}
private boolean bindToAuthenticator(StringauthenticatorType) {
//从mAuthenticatorCache中查询满足指定类型的服务信息
AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>
authenticatorInfo =
mAuthenticatorCache.getServiceInfo(
AuthenticatorDescription.newKey(authenticatorType));
......
Intentintent = new Intent();
intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
//设置目标服务的ComponentName
intent.setComponent(authenticatorInfo.componentName);
//通过bindService启动指定的服务,成功与否将通过第二个参数传递的
//ServiceConnection接口返回
if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
......
}

  这里分2步走:查询系统得到我们要添加账户类型(微信、微博、淘宝,这里用retme pocApp里的代码来解释,不熟悉概念的看资料2)的authenticatorInfo;bindServiceAsUser去bind微信账号的service(此service就是pocApp里的AuthenticationService)。而此时pocApp的AuthenticationService执行onBind(第9步)

package com.example.android.samplesync.authenticator;

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();
}

  注意这里返回的binder是mAuthenticator,而mAuthenticator是pocApp中的Authenticator(如果你不懂这些是什么玩意,建议结合pocApp看资料2)。

class Authenticator extends AbstractAccountAuthenticator {

  AbstractAccountAuthenticator类中有内部类继承

private class Transport extends IAccountAuthenticator.Stub {

  而在执行bindServiceAsUser后回去回调onServiceConnection函数(第10步),不清楚为什么要回调自行查资料。上面提到Session实现ServiceConnection接口,这里直接调用Session.onServiceConnection。这里的service就是onbind返回的Authenticator,故mAuthenticator = Authenticator

public void onServiceConnected(ComponentName name,IBinder service) {
//得到远端AAS返回的IAccountAuthenticator接口,这个接口用于
//AccountManagerService和该远端AAS交互
mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
try {
run();//调用匿名Session类实现的run函数,看第6步中的匿名类函数
} ......
}

  执行第11步 run

 public void run() throws RemoteException {
mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
options);
}

  第12步mAuthenticator.addAccount就是pocApp中Authenticator.addAccount

13、Authenticator.addAccount返回bundle。由于pocApp没有实现账户登陆,所以后面的流程走步下去了。但我们看

[-->AbstractAccountAuthenticator.java::Transport:addAccount]发现其内部会调用

response.onResult(bundle);

  根据mAuthenticator.addAccount参数可知,response是IAccountAuthenticatorResponse类型就是上面的匿名Session类 。所以这里执行的是Session.onResult

  public void onResult(Bundle result) {
if (response != null) {
try {
......
response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
"null bundle returned");
} else {
......
response.onResult(result);
}
......
}

14、由第6步的Session构造函数可知 response就是AmsTask.mResponse,而AmsTask.mResponse =  new Response()这里调用AmsTask内部类Response.onResult

 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);
}
}

  这里调用startActivity去启动账号登陆activity(launchAnyWhere bug)。源码分析到此为止嘞,但是期间涉及到的很多类我们需要整理下,便于记忆和消化addAccount流程

  看到罗,addAccount就是上面这几个类之间的操作。其实addAccount总结起来也很简单,accountManager—>accountAuthenticator—>accountAuthenticatorRespone—>accountManagerRespone

参考资料:

1、[深入理解Android卷二 全文-第八章]深入理解ContentService和AccountManagerService

2、一步一步教你在 Android 里创建自己的账号系统(一)

3、https://github.com/retme7/launchAnyWhere_poc_by_retme_bug_7699048

android添加账户源码浅析的更多相关文章

  1. Android 手势识别类 ( 三 ) GestureDetector 源码浅析

    前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...

  2. Android开发之Theme、Style探索及源码浅析

    1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...

  3. Android源码浅析(六)——SecureCRT远程连接Linux,配置端点和字节码

    Android源码浅析(六)--SecureCRT远程连接Linux,配置端点和字节码 需要编译源码的同学,一般都是win+虚拟机吧,但是再虚拟机里体验并不是很好,所有市面上有很多的软件能够做到在wi ...

  4. Android源码浅析(三)——Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机

    Android源码浅析(三)--Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机 最近比较忙,而且又要维护自己的博客,视频和公众号,也就没 ...

  5. Android源码浅析(二)——Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境

    Android源码浅析(二)--Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境 接着上篇,上片主要是介绍了一些安装工具的小知识点Andr ...

  6. Android手势源码浅析-----手势绘制(GestureOverlayView)

    Android手势源码浅析-----手势绘制(GestureOverlayView)

  7. Android源码浅析(五)——关于定制系统,如何给你的Android应用系统签名

    Android源码浅析(五)--关于定制系统,如何给你的Android应用系统签名 今天来点简单的我相信很多定制系统的同学都会有一些特定功能的需求,比如 修改系统时间 静默安装 执行某shell命令 ...

  8. Android源码浅析(四)——我在Android开发中常用到的adb命令,Linux命令,源码编译命令

    Android源码浅析(四)--我在Android开发中常用到的adb命令,Linux命令,源码编译命令 我自己平时开发的时候积累的一些命令,希望对你有所帮助 adb是什么?: adb的全称为Andr ...

  9. Android源码浅析(一)——VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置

    Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 最近地方工作,就是接触源码的东西了,所以好东西还是要分享,系列开了这 ...

随机推荐

  1. 剑指 Offer 48. 最长不含重复字符的子字符串 + 动态规划 + 哈希表 + 双指针 + 滑动窗口

    剑指 Offer 48. 最长不含重复字符的子字符串 Offer_48 题目详情 解法分析 解法一:动态规划+哈希表 package com.walegarrett.offer; /** * @Aut ...

  2. HDOJ-4081(次小生成树+Prim算法)

    Qin Shi Huang's National Road System HDOJ-4081 本题考查的是次小生成树的问题,这里的解决方法就是先使用Prim算法求解最小生成树. 在求解最小生成树的时候 ...

  3. 通过序列号Sequence零代码实现订单流水号

    序列号管理 本文通过产品编码和订单流水号介绍一下序列号(Sequence)在crudapi中的应用. 概要 序列号 MySQL数据库没有单独的Sequence,只支持自增长(increment)主键, ...

  4. C++ 中的虚函数表及虚函数执行原理

    为了实现虚函数,C++ 使用了虚函数表来达到延迟绑定的目的.虚函数表在动态/延迟绑定行为中用于查询调用的函数. 尽管要描述清楚虚函数表的机制会多费点口舌,但其实其本身还是比较简单的. 首先,每个包含虚 ...

  5. 关于windows下服务一直处于启动ing的处理办法

    1,找到服务名称,xxxx 进入cmd 2,sc queryex xxxx,找到pid 3,taskkill /f /pid 123 就可以终止这个启动中的进程了

  6. springboot源码解析-管中窥豹系列之BeanFactoryPostProcessor(十一)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  7. C# 应用 - 使用 HttpClient 发起 Http 请求

    1. 需要的库类 \Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Net.Http.dll System.N ...

  8. 动态规划-最长公共上升子序列-n^2解法

    1. 题目描述 给定两个数列\(A, B\),如果他们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列.求\(A\)和\(B\)的最长公共上升子序列. 输入格 ...

  9. css整理之-----------技巧、黑魔法

    css 看起来比较简单,但是要想做的好也不是那么容易,我们在平时开发中,主要用css 来美化我们的html结构,所有我觉得css 还是挺重要的,这里记录整理一些关于css 的技巧以及容易忘记的知识点. ...

  10. PTA 将数组中的数逆序存放

    7-1 将数组中的数逆序存放 (20 分)   本题要求编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放,再按顺序输出数组中的元素. 输入格式: 输入在第一行中给出一个正整数n(1). ...