理清Android中Binder机制的使用

---恢复内容开始---

昨天好不容易把Binder的使用方式搞清楚了,今天看代码的时候又被轰炸地体无完肤。最主要的问题是,在系统service和client的交互中,Binder使用的太频繁了,一次调用可能伴随着多个Binder的多次传递,不搞清楚这些Binder的来源和具体作用,往往就无法深刻理解交互是如何进行的。另外,对于Binder子类的命名方式又非常繁杂,这间接导致了代码阅读理解的困难。今天决定花些时间好好梳理下交互过程中Binder的具体使用,在此记录。

在开始前,先声明下个人对Binder的作用的理解,归纳起来就两句话:服务端创建的Binder用于给客户端调用服务端方法;客户端创建的Binder用于服务端执行异步回调。这里的服务端时指系统service,客户端是指调用系统service的其他类。

接下去以startActivity(intent)做例子,说明下在客户端执行startActivity(intent)后,Binder的传递和使用。

Intent intent=new Intent(ActivityA.this,ActivityB.class);
startActivity(intent);

很简单的一个例子,一步步跟踪下去,首先是startActivity

 @Override
    public void startActivity(Intent intent) {
        startActivity(intent, null);
    }

继续跟踪

 @Override
    public void startActivity(Intent intent, Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }

到startActivityForResult

 public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }

mInstrumentation对象是在创建activity时被赋值的,它实际来源于ActivityThread,每个程序只有一个。在此我们不关心它的其他功能,只需看它和服务端交互的部分。

我们注意到参数有很多个,其中包含两个Binder:mMainThread.getApplicationThread()和mToken。

mMainThread就是ActivityThread对象,每个程序只有一个,getApplicationThread获取的是一个ApplicationThread,该对象继承于ApplicationThreadNative类,而ApplicationThreadNative类继承于Binder。它是在Activity实例化时直接赋值的。注意,虽然ApplicationThread取名为Thread,但它和Thread没有关系,本质是一个Binder。

 final ApplicationThread mAppThread = new ApplicationThread();

记住,ActivityThread是作为客户端存在的,所以该Binder的作用是传递给服务端,让服务端做回调。

那mToken呢?继续跟踪

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config) {
        attachBaseContext(context);

       .....
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;

mToken是在attach方法里赋值,该方法在ActivityThread内调用

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
                ...
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config);

原来该token来源于ActivityClientRecord,向上查询performLaunchActiviity调用。

有两个方法调用了performLaunchActiviity

  private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity a = performLaunchActivity(r, customIntent);
  public final Activity startActivityNow(Activity parent, String id,
        Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
        Activity.NonConfigurationInstances lastNonConfigurationInstances) {
        ActivityClientRecord r = new ActivityClientRecord();
            r.token = token;
        ...
        return performLaunchActivity(r, null);
    }

startActivityNow里创建了ActivityClientRecord对象,但是它的toke也是参数传入的,而handleLaunchActivity,它实际上是通过一个handler被异步调用的,真正调用它的地方在scheduleLaunchActivity

 // we use token to identify this activity without having to send the
        // activity itself back to the activity manager. (matters more with ipc)
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
                int procState, Bundle state, List<ResultInfo> pendingResults,
                List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            ...

            queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
        }

在queueOrSendMessage方法中压入了handler的消息队列,最后执行到handleLaunchActivity

但是不管是在scheduleRelaunchActivity还是startActivityNow中,token都是作为参数传入的,事实上,scheduleRelaunchActivity和startActivityNow方法都是服务端通过传递过去的ApplicationThread回调的,也就是说token对象其实是在服务端创建的。这里的服务端便是指ActivityManagerService服务。

接下去看mInstrumentation的调用

  try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            int result = ActivityManagerNative.getDefault()
                .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, null, options, user.getIdentifier());
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
        }

ActivityManagerNative.getDefault()获取了一个服务端ActivityManagerService的引用并且转为了一个IActivityManager接口。如果熟悉aidl使用的话会知道Android习惯把Binder实现分为三部分:IService、IServiec.Stub、IService.Stub.Proxy,其中IService是个接口,定义了这个服务能提供哪些方法,IService.Stub是个继承了Binder与IService接口的抽象类,是具体服务的父类,服务类继承Stub类并且实现IService接口的方法,作为服务端存在。IService.Stub.Proxy则只继承了IService,当客户端获取了服务端的Binder引用后,会转为Proxy类,这样客户端就能直接调用服务端方法了。在这里,ActivityManagerNative实际上就等价于IActivityManager.Stub,不过由于IActivityManager方法太多,Android将其独立了出来。

直接查看ActivityManagerService的startActivityAsUser方法

@Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags,
            String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
        enforceNotIsolatedCaller("startActivity");
        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
                false, true, "startActivity", null);
        // TODO: Switch to user app stacks here.
        return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
                null, null, options, userId);
    }

留意caller和resultTo两个Binder,caller是客户端ActivityThread创建的Binder,用于服务端回调,接下去就能看到,但是resultTo是服务端自己创建的,这里要拿来干什么呢?而且服务端究竟是何时创建每个Activity对应的Binder的?继续看下去。

mStackSupervisor对象是一个ActivityStackSupervisor类,我们不关心该类的作用,只看它的startActivityMayWait方法

final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, String profileFile,
            ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
            Bundle options, int userId) {
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors()) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        boolean componentSpecified = intent.getComponent() != null;

        // Don‘t modify the client‘s object!
        intent = new Intent(intent);

        // Collect information about the target of the Intent.
        ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
                profileFile, profileFd, userId);

        synchronized (mService) {
            int callingPid;
            if (callingUid >= 0) {
                callingPid = -1;
            } else if (caller == null) {
                callingPid = Binder.getCallingPid();
                callingUid = Binder.getCallingUid();
            } else {
                callingPid = callingUid = -1;
            }

            final ActivityStack stack = getFocusedStack();
            stack.mConfigWillChange = config != null
                    && mService.mConfiguration.diff(config) != 0;
            if (DEBUG_CONFIGURATION) Slog.v(TAG,
                    "Starting activity when config will change = " + stack.mConfigWillChange);

            final long origId = Binder.clearCallingIdentity();

            if (aInfo != null &&
                    (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
                // This may be a heavy-weight process!  Check to see if we already
                // have another, different heavy-weight process running.
                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
                    if (mService.mHeavyWeightProcess != null &&
                            (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
                            !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
                        int realCallingUid = callingUid;
                        if (caller != null) {
                            ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
                            if (callerApp != null) {
                                realCallingUid = callerApp.info.uid;
                            } else {
                                Slog.w(TAG, "Unable to find app for caller " + caller
                                      + " (pid=" + callingPid + ") when starting: "
                                      + intent.toString());
                                ActivityOptions.abort(options);
                                return ActivityManager.START_PERMISSION_DENIED;
                            }
                        }

                        IIntentSender target = mService.getIntentSenderLocked(
                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
                                realCallingUid, userId, null, null, 0, new Intent[] { intent },
                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
                                | PendingIntent.FLAG_ONE_SHOT, null);

                        Intent newIntent = new Intent();
                        if (requestCode >= 0) {
                            // Caller is requesting a result.
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
                        }
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
                                new IntentSender(target));
                        if (mService.mHeavyWeightProcess.activities.size() > 0) {
                            ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
                                    hist.packageName);
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
                                    hist.task.taskId);
                        }
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
                                aInfo.packageName);
                        newIntent.setFlags(intent.getFlags());
                        newIntent.setClassName("android",
                                HeavyWeightSwitcherActivity.class.getName());
                        intent = newIntent;
                        resolvedType = null;
                        caller = null;
                        callingUid = Binder.getCallingUid();
                        callingPid = Binder.getCallingPid();
                        componentSpecified = true;
                        try {
                            ResolveInfo rInfo =
                                AppGlobals.getPackageManager().resolveIntent(
                                        intent, null,
                                        PackageManager.MATCH_DEFAULT_ONLY
                                        | ActivityManagerService.STOCK_PM_FLAGS, userId);
                            aInfo = rInfo != null ? rInfo.activityInfo : null;
                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
                        } catch (RemoteException e) {
                            aInfo = null;
                        }
                    }
                }
            }

            int res = startActivityLocked(caller, intent, resolvedType,
                    aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
                    callingPackage, startFlags, options, componentSpecified, null);

            if (stack.mConfigWillChange) {
                // If the caller also wants to switch to a new configuration,
                // do so now.  This allows a clean switch, as we are waiting
                // for the current activity to pause (so we will not destroy
                // it), and have not yet started the next activity.
                mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                        "updateConfiguration()");
                stack.mConfigWillChange = false;
                if (DEBUG_CONFIGURATION) Slog.v(TAG,
                        "Updating to new configuration after starting activity.");
                mService.updateConfigurationLocked(config, null, false, false);
            }

            Binder.restoreCallingIdentity(origId);

            if (outResult != null) {
                outResult.result = res;
                if (res == ActivityManager.START_SUCCESS) {
                    mWaitingActivityLaunched.add(outResult);
                    do {
                        try {
                            mService.wait();
                        } catch (InterruptedException e) {
                        }
                    } while (!outResult.timeout && outResult.who == null);
                } else if (res == ActivityManager.START_TASK_TO_FRONT) {
                    ActivityRecord r = stack.topRunningActivityLocked(null);
                    if (r.nowVisible) {
                        outResult.timeout = false;
                        outResult.who = new ComponentName(r.info.packageName, r.info.name);
                        outResult.totalTime = 0;
                        outResult.thisTime = 0;
                    } else {
                        outResult.thisTime = SystemClock.uptimeMillis();
                        mWaitingActivityVisible.add(outResult);
                        do {
                            try {
                                mService.wait();
                            } catch (InterruptedException e) {
                            }
                        } while (!outResult.timeout && outResult.who == null);
                    }
                }
            }

            return res;
        }
    }
时间: 2024-10-06 00:30:53

理清Android中Binder机制的使用的相关文章

转 理解Android系统Binder机制

一.Binder机制概述 在Android开发中,很多时候我们需要用到进程间通信,所谓进程间通信,实现进程间通信的机制有很多种,比如说socket.pipe等,Android中进程间通信的方式主要有三种: 1.标准Linux Kernel IPC 接口: 2.标准D-BUS接口: 3.Binder接口. 其中,Binder机制是使用最且最被认可的,因为Binder机制有以下优点: 1.相对于其它IPC机制,Binder机制更加简洁和快速: 2.消耗的内存相对更少: 3.传统的IPC机制可能会增加

android当中Binder机制,Looper与MessageQueue机制

上周四讨论内容:android 当中Binder机制,Looper与MessageQueue相关知识,相关链接: http://blog.csdn.net/innost/article/details/6124685 http://blog.csdn.net/innost/article/details/6055793 android当中Binder机制,Looper与MessageQueue机制,布布扣,bubuko.com

Android系统Binder机制学习总结

一.Binder机制概述 在Android开发中,很多时候我们需要用到进程间通信,所谓进程间通信,实现进程间通信的机制有很多种,比如说socket.pipe等,Android中进程间通信的方式主要有三种: 1.标准Linux Kernel IPC 接口: 2.标准D-BUS接口: 3.Binder接口. 其中,Binder机制是使用最且最被认可的,因为Binder机制有以下优点: 1.相对于其它IPC机制,Binder机制更加简洁和快速: 2.消耗的内存相对更少: 3.传统的IPC机制可能会增加

Android中Binder的基础知识点

Android Binder基础知识点 一 传统IPC和Binder机制的比较 传统IPC: 1)收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份. 2)接入点开放,无法建立私有通道. 3)socket, 管道和消息队列需要两次数据拷贝,传输效率差. 4)共享内存的方式控制复杂,难以使用. Binder机制: 1)为发送方添加UID/PID身份. 2)既支持实名Binder也支持匿名Binder. 3)传输过程只需要一次拷贝. 二 Binder中的面向对象思想 Binder对象是一

Java简单模拟Android中Handler-Message机制

在Android中主线程与子线程的通信十分重要,Google工程师为我们提供了Handler-Message机制来解决他们之间的交互问题.今天,我们就来简单理解Handler-Message机制的原理,在Java中简单模拟该机制.代码示例Github地址HandlerDemo 首先,看一下简单流程图(不太专业) 由上图可知,流程中主要相关类有Handler.Message.MessageQueue.Looper:下面,我们就围绕它们来简单分析该流程图: 1.我们一般在主线程创建Handler,接

Android中资源管理机制详细分析

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/42386549 在Android中,所有的资源都在res目录下存放,包括drawable,layout,strings,anim等等,当我们向工程中加入任何一个资源时,会在R类中相应会为该 资源分配一个id,我们在应用中就是通过这个id来访问资源的,相信做过Andorid开发的朋友对于这些肯定不会陌生,所以这个也不是我今天想要说的,我今天想和大家一起学习的是Android是如何管理资源的,在

android中反射机制

本文介绍Android反射机制实现与原理,在介绍之前,要和Java进行比较,所以先看下Java中的反射相关知识: 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某种机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 在Java中的反射机制,被称为Reflection(大家看到这个单词,第一个想法应该就是去开发文档中

Android中synchronized机制详解(转)

JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块.假如再细的分类,synchronized可作用于instance变量.object reference(对象引用).static函数和class literals(类名称字面常量)身上.在进一步阐述之前,我们需要明确几点: A.无论synchronized关键字加在方法上还是对象上,他取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问.

深入探索Android IPC/Binder机制

https://thenewcircle.com/s/post/1340/Deep_Dive_Into_Binder_Presentation.htm 注意用键盘上的上下键翻页