android添加账户源码浅析

  上篇粗略的分析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;这很重要下面会用到。

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流程

参考资料:

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

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

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

时间: 2024-11-07 06:27:55

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

Android应用Preference相关及源码浅析(Preference组件家族篇)

1 前言 前一篇(点我阅读前一篇<Android应用Preference相关及源码浅析(SharePreferences篇)>)我们讨论分析使用了Android的SharePreferences,相信看过的朋友都有了自己的感悟与理解,这一篇我们继续乘热打铁来说说SharePreferences的衍生品--Preference组件. 其实Preference组件大家一定不陌生,因为Android系统的Setting应用及我们市面上一些符合Android设计思想的应用的设置界面一般都会用它来实现,

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

1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解整个Android中Theme.Style的关系及结构,这样我们就能游刃有余的面对实际开发中遇到的很多问题了,也就免得在自定义时遇到各种坑,譬如不清楚该继承哪个parent.不清楚为何背景会有一个黑边等. 本文主要分两部分来进行简单粗略的浅析,首先会围绕Theme与Style的定义及在App开发中的

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

Android源码浅析(三)--Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机 最近比较忙,而且又要维护自己的博客,视频和公众号,也就没仔细的梳理源码的入门逻辑,今天也就来讲一个源码的玩法,各位看官,一起学习学习! 看本篇博客之前,先看下我的前面两篇 Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 Android源码浅析(二)--Ubuntu Roo

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

Android源码浅析(二)--Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境 接着上篇,上片主要是介绍了一些安装工具的小知识点Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置,其实Ubuntu Kylin 16.04 LTS也只是为了体验,我们为了追求稳定,还是使用了Ubuntu14.04 这里提供一个国内镜像的下载链接,可以用迅雷,下载下来之后后缀

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

Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 最近地方工作,就是接触源码的东西了,所以好东西还是要分享,系列开了这么多,完结 的也没几个,主要还是自己覆盖的太广了,却又不精通,嘿嘿,工作需要,所以写下了本篇博客 一.VMware 12 我选择的虚拟机试VMware,挺好用的感觉,下载VMware就不说了,善用搜索键嘛,这里我提供一个我现在在用的 下载地址:链接:http://pan.baidu.com/s/1k

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

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

Android应用ViewDragHelper详解及部分源码浅析

[工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 很久没有更新博客了,忙里偷闲产出一篇.写这片文章主要是去年项目中的一个需求,当时三下五除二的将其实现了,但是源码的阅读却一直扔在那迟迟没有时间理会,现在拣起来看看吧,否则心里一直不踏实. 关于啥是ViewDragHelper,这里不再解释,官方下面这个解释已经很牛逼了,如下: /** * ViewDragHelper is a utility class for

Android应用进程间通信之Messenger信使使用及源码浅析

1 背景 这个知识点是个low货,刚开始其实想在之前一篇文章<Android异步消息处理机制详解及源码分析>一文中作为一个知识点分析的,但是想了又想又觉得该放在后面进程间通信分析时再分析.然并卵,还是单独拿出来写一篇分析一下吧. 提到Message和Handler其实大家都很熟悉,但是说到Messenger估计有些人还是不太常用的,更有甚者都能把Messenger拼写错误为Messager,以为是Message加了个r,当然,网络上对于Messenger的文章现在也很多了,但是个人分析总结总归

Android应用Loaders全面详解及源码浅析

1 背景 在Android中任何耗时的操作都不能放在UI主线程中,所以耗时的操作都需要使用异步实现.同样的,在ContentProvider中也可能存在耗时操作,这时也该使用异步操作,而3.0之后最推荐的异步操作就是Loader.它可以方便我们在Activity和Fragment中异步加载数据,而不是用线程或AsyncTask,他的优点如下: 提供异步加载数据机制: 对数据源变化进行监听,实时更新数据: 在Activity配置发生变化(如横竖屏切换)时不用重复加载数据: 适用于任何Activit