Android四大组件之bindService源码实现详解



前言

  通过前面系列博客Android Binder框架实现详细介绍了Binder系列文章,相信各位小伙们对Binder架构有了较深地理解。我们知道Binder对于Android的的地位是非常之重要,整个Java framework的提供ActivityManagerService、PackageManagerService等服务都是基于Binder架构来通信的,并且许多的Native层Service也是基于Binder的来实现的。关于Binder我想小伙们已经非常熟悉的掌握了,是时候来挑战更加综合性的东西了,这里我们以ActivityManagerService为例来展开一系列的讲解,主要分析其对Android四大组件的启动和管理。

   我们知道ActivityManagerService是Android的Java framework的服务框架最重要的服务之一。对于Andorid的Activity、Service、Broadcast、ContentProvider四大组件(江湖人称四剑客)的管理,包含其生命周期都是通过ActivityManagerService来完成的。对于这四剑客这里就不过多介绍了,因为这个对于Android入门的小伙来说是必须了解的东西了,本篇将要重点介绍的是AMS对Service通过bindService的全流程!

  • 注意:本篇的介绍是基于Android 7.xx平台为基础的,其中涉及的代码路径如下:
frameworks/base/services/core/java/com/android/server/am/--- ActivityManagerService.java--- ActiveServices.java--- ServiceRecord.java--- ProcessRecord.javaframeworks/base/core/java/android/app/--- IActivityManager.java--- ActivityManagerNative.java (内部包含AMP)--- ActivityManager.java--- IApplicationThread.java--- ApplicationThreadNative.java (内部包含ATP)--- ActivityThread.java (内含ApplicationThread)--- ContextImpl.javaframeworks/base/core/java/android/app/LoadedApk.java  
frameworks/base/core/java/android/app/IServiceConnection.aidl
frameworks/base/core/java/android/content/ServiceConnection.java
frameworks/base/core/java/android/util/Singleton.java
  • 在后续的源码分析过程中为了简述方便,会将ApplicationThreadProxy简称为ATPActivityManagerProxy简称为AMP,ActivityManagerService简称为AMS, ActivityManagerNative简称AMN, ApplicationThreadNative简称ATN,PackageManagerService简称为PKMS,ApplicationThread简称为AT,ActivityServices简称为AS


一. bindService开篇

  在正式开始着手bindService这一篇章的撰写之前,我一直在思考一个问题就是要以什么角度入手才能将本篇博客讲得更加的入味,对于我最简单而言就是平铺直述以源码的脉络为基础跟着代码分析直接开撸,转念一想这个虽然是最简单粗暴的套路,但是估计读者不会买账,因为跟读源码是最枯燥和乏味的。那么我们就跳出源码,站在上帝的视角出发,以进程交互的角度出发来分析bindService,我想这样反而小伙们更加容易接受! 在bindService的源码中将要涉及到三个主要的进程,分别是bindService发起端进程,system_server进程和目的端Service进程, 我们将会以这三个进程角度出来阐述bindService是怎么在这三个进程之间辗转腾挪达到远程绑定服务或者说是Binder传递功能的。对于绝大部分小伙们来说遇到一个新的知识点,一定是先从实例入手先使用,然后再深入原理及其实现,我们这里也不例外,先让我们看看bindService的实例怎么使用!

1.1 bindService使用实例

  bindService的实例涉及到目的端Service进程和client端发起端进程,让我们来开写,好久没有写过实例了让我们入手一番!

定义aidl文件

  bindService能实现的一个前提就是Android为我们提供了一套AIDL(Android Interface Definition Language)即Android接口定义语言,来帮助我们实现远程Binder的通信(当然我们也可以不使用AIDL语言,直接硬编码实现也行可以参见博客Android Binder实战开发指南之不借助AIDL创建Java层Binder服务)。好了前面说了这么多了,直接不如正题,编码一个aidl文件如下:

interface IAidlFun {void doAction();
}

此时编译器会对aidl文件自动编译生成,IAidlFun.java文件,我们看看其神秘面纱,我想如何小伙们对Binder有过一定的基础,对其中的代码就不会陌生了,这就是Android为了我们快速能使用Binder而做的一个模板啊,即生成通信层代码模板,而用户完全只用关心业务层的逻辑,降低开发难度。

/** This file is auto-generated.  DO NOT MODIFY.* Original file: E:\\workspace\\PaxForAll\\src\\com\\example\\api\\aidl\\IAidlFun.aidl*/
package com.example.api.aidl;public interface IAidlFun extends android.os.IInterface {/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implementscom.example.api.aidl.IAidlFun {private static final java.lang.String DESCRIPTOR = "com.example.api.aidl.IAidlFun";/** Construct the stub at attach it to the interface. */public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.example.api.aidl.IAidlFun* interface, generating a proxy if needed.*/public static com.example.api.aidl.IAidlFun asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.example.api.aidl.IAidlFun))) {return ((com.example.api.aidl.IAidlFun) iin);}return new com.example.api.aidl.IAidlFun.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic boolean onTransact(int code, android.os.Parcel data,android.os.Parcel reply, int flags)throws android.os.RemoteException {switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_doAction: {data.enforceInterface(DESCRIPTOR);this.doAction();reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.example.api.aidl.IAidlFun {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic void doAction() throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_doAction, _data, _reply,0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_doAction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public void doAction() throws android.os.RemoteException;
}

目的端(Service进程)

...
public class AidlService extends Service {private BindService mBindService = null;private static final String TAG = AidlService.class.getSimpleName();@Overridepublic IBinder onBind(Intent intent) {Log.e(TAG,"onBind");return mBindService;}@Overridepublic void onCreate() {super.onCreate();Log.e(TAG,"onCreate");mBindService = new BindService();}//此处的IAidlFun.Stub是由编译器自动实现生成的,帮我们快速实现Binder的开发public class BindService extends IAidlFun.Stub {@Overridepublic void doAction() throws RemoteException {}}
...
}

发起端进程

private IAidlFun mRemoteServiceProxy;private void bindService() {// 发起端进程绑定远程服务端进程bindService(new Intent("com.example.aidl.aidlService"), mConnection,Context.BIND_AUTO_CREATE);}ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.d(TAG, "onServiceConnected");// 获取远程服务端Service代理端ProxymRemoteServiceProxy = IAidlFun.Stub.asInterface(service);try {// RPC远程服务端方法,实现跨进程交互mRemoteServiceProxy.doAction();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}};

bindService感觉用起来好爽,好简单啊!可是其中涉及的过程调用和源码逻辑各位小伙们都清楚吗,不清楚也没有关系,让我们来一起搞清楚!


1.2 bindService源码分析前期知识储备

  还是老套路,在正式开始分析之前我们还是磨刀不误砍柴工,我们还是对即将要分析的源码先来梳理梳理要准备的知识点,这样后续分析起来也会简单顺手一些。

1.2.1 AMS类图关系图

  AMS的代码非常多,几乎有好几万!所以在正式对其进行分析前,搞清楚其基本类图关系非常重要,其基本类图详见如下:
在这里插入图片描述

1.2.2 进程创建流程

  bindService调用过程中会牵涉到目的端进程的创建(即目的端服务进程没有创建的前提下),这个虽然不是本文的重点,但是还是大概看下其示意图,后面也会大概过下:
在这里插入图片描述

1.2.3 binderService时序图

  为了不耽误小伙们的时间,如果看了下面的时序图觉得有信心跟下去深入的可以接着往下分析,如果觉得没有毅力深入的可以就此打住了(因为分析源码真的是一件枯燥乏味的事情)!
在这里插入图片描述



二. 发起端进程发送bindService请求

  在正式开始bindService的分析之前,将要涉及到涉及到Context的继承关系类图,从下面的类图中可以看出,Context是一个接口(提供了很多的接口方法),ContextImp和ContextWrapper都是其实现类,我们常用的Activity、Service、Application都直接或间接继承自ContextWrapper。

在这里插入图片描述

2.1 ContextWrapper.bindService

在这里插入图片描述
  这里我们可以看到bindService会调用到ContextWrapper类的bindService中,源码如下:

    @Override//ContextWrapper.javapublic boolean bindService(Intent service, ServiceConnection conn,int flags) {return mBase.bindService(service, conn, flags);//这里的mBase指向ContextImpl(怎么来的这里就不分析了,给出结论),其方法bindService见章节2.2}

这里我们有如下几点需要注意:

  • bindService方法第二个入参ServiceConnection为一个接口类型对象
  • ServiceConnection内部方法onServiceConnected的一个入参参数为IBinder数据类型,它就是我们最终获取的远程Binder实体的服务代理端(同时它也是一个匿名Binder)。通过我们前面的博客知道IBinder类型是可以跨进程传递的,而这里也运用了IBinder的这个特性从而实现了bindService。
public interface ServiceConnection {//这里的入参service就是我们最终要获取的远程Binder Service的远程代理端public void onServiceConnected(ComponentName name, IBinder service);public void onServiceDisconnected(ComponentName name);
}

2.2 ContextImpl.bindService

	//ServiceConnection.javapublic boolean bindService(Intent service, ServiceConnection conn,int flags) {warnIfCallingFromSystemProcess();//权限检测//这里的入参conn是一个interface类型的接口类,供回调使用return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),Process.myUserHandle());}

  无需多言,直接调用方法bindServiceCommon进行下一步的处理。


2.3 ContextImpl.bindServiceCommon

	//ContextImpl.javafinal LoadedApk mPackageInfo;private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handlerhandler, UserHandle user) {IServiceConnection sd;//这是一个aidl生成的Binder接口类...if (mPackageInfo != null) {//获取的是内部静态类InnerConnectionsd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);//详见章节2.3.1} else {...}validateServiceIntent(service);//判断参数service的有效性try {...//通过Binder远程调用AMS的bindService方法,详见2.4int res = ActivityManagerNative.getDefault().bindService(mMainThread.getApplicationThread(), getActivityToken(), service,service.resolveTypeIfNeeded(getContentResolver()),sd, flags, getOpPackageName(), user.getIdentifier());...return res != 0;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

  这里的IServiceConnection是一个aidl生成的Binder通讯工具类文件,其定义如下所示(可以看到它被定义为oneway类型的,即是一个非阻塞的Binder调用):

//IServiceConnection.aidl
oneway interface IServiceConnection {//注意这里的入参IBinder service和ServiceConnection的方法中入参IBinder service,都是相同的入参那么有啥关联,这里先预留一个疑点,到了后续小伙伴们应该就清楚了void connected(in ComponentName name, IBinder service);
}

我们回过头来看看bindServiceCommon的主要功能,然后对其逐个出击,一一攻破:

  • 创建对象内部静态类LoadedApk.ServiceDispatcher.InnerConnection的对象
  • 调用AMS的代理端AMP,向AMS服务发起bindService请求

其中bindService的入参mMainThread.getApplicationThread()方法返回的是ApplicationThread对象, 该对象继承于ApplicationThreadNative(Binder服务端),这个ApplicationThread对象很重要,因为正是通过它串联其了AMS对发起端进程的ActivityThread的交互(如果把ApplicationThread当作服务端,那么此时AMS相关于ApplicationThread而言就是客户端)。其两者之间的关系建立详见下述的示意图,即AMS持有ApplicationThread的代理端,而应用端进程持有AMS的代理端AMP,二者相互持有各自Binder服务端的代理端进而完成了二者之间的RPC调用。
在这里插入图片描述

2.3.1 getServiceDispatcher

  在正式开始介绍getServiceDispatcher获取内部静态类LoadedApk.ServiceDispatcher.InnerConnection的对象之前,我们先看看LoadedApk的类图关系,如下(先熟悉熟悉,这样后续分析源码不会迷失):
在这里插入图片描述

	//LoadedApk.javaprivate final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices= new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();public final IServiceConnection getServiceDispatcher(ServiceConnection c,Context context, Handler handler, int flags) {synchronized (mServices) {LoadedApk.ServiceDispatcher sd = null;//ServiceDispatcher内部类ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);//查找mService中是否存在context的keyif (map != null) {//如果存在这样的map,则继续查找map中是否存在ServiceConnection类型的实例c的keysd = map.get(c);//}if (sd == null) {//没有则创建//创建服务分发对象,注意这里的参数c是ServiceConnection对象,详见2.3.2sd = new ServiceDispatcher(c, context, handler, flags);if (map == null) {map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();mServices.put(context, map);}//以ServiceConnection为key,ServiceDispatcher为value保存到map中map.put(c, sd);} else {sd.validate(context, handler);}//返回内部类对象InnerConnection实例return sd.getIServiceConnection();}}

在正式开始上述的源码分析前,我们先介绍介绍其中涉及的一个重要的数据结构对象mServices:

  • 可以看到它是一个Hashmap类型的数据,其key为Context类型对象
  • 然后mServices的value也是一个Hasmap,存放着Context对象所对应的ServiceConnection以及所对应的LoadedApk.ServiceDispatcher对象,即一个Context对象可以对应很多个ServiceConnection,但是同一个只会被创建一次

路障扫清了,让我们接着分析该方法,可以该方法返回LoadedApk.ServiceDispatcher.InnerConnection类型对象,该对象继承于IServiceConnection.Stub, 该类是由IServiceConnection.aidl类型文件由编译器怎懂自动生成的作为Binder服务端的实体端类。其中IServiceConnection.aidl在2.2章节已经有给出过了,就不放上了。

2.3.1 ServiceDispatcher

	//LoadedApk.javastatic final class ServiceDispatcher {//内部类private final ServiceDispatcher.InnerConnection mIServiceConnection;//用户传递的参数private final ServiceConnection mConnection;private final Context mContext;private final Handler mActivityThread;private final ServiceConnectionLeaked mLocation;//保存第三方进程bindService传递过来的参数private final int mFlags;private RuntimeException mUnbindLocation;private boolean mForgotten;private static class InnerConnection extends IServiceConnection.Stub {final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;InnerConnection(LoadedApk.ServiceDispatcher sd) {mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);}//注意这里的入参IBinder实例对象servicepublic void connected(ComponentName name, IBinder service) throws RemoteException {LoadedApk.ServiceDispatcher sd = mDispatcher.get();if (sd != null) {sd.connected(name, service);}}}ServiceDispatcher(ServiceConnection conn,Context context, Handler activityThread, int flags) {//创建InnerConnection对象mIServiceConnection = new InnerConnection(this);//用户定义的ServiceConnectionmConnection = conn;mContext = context;mActivityThread = activityThread;mLocation = new ServiceConnectionLeaked(null);mLocation.fillInStackTrace();//保存用户传递的参数,通常为Context.BIND_AUTO_CREATEmFlags = flags;}		
}//返回内部类对象InnerConnection实例IServiceConnection getIServiceConnection() {return mIServiceConnection;}

  通过源码我们发现ServiceDispatcher是LoadedApk的内部类。InnerConnection是ServiceDispatcher的静态内部类,是不是有点拗口啊,这就是为什么我在前面先其奉上了LoadedApk的类图的原因。其中ServiceDispatcher有一个重要的方法getIServiceConnection(),通过该方法返回的便是在其构造方法中创建的InnerConnection对象。而这个InnerConnection也是bindService中的关键类,为啥这么说呢,往后分析小伙们就知道了!


2.4 AMN.getDefault()

  继续回到章节2.3在获取到InnerConnection实例对象sd之后,调用ActivityManagerNative.getDefault().bindService,这里牵涉到一个重要的方法ActivityManagerNative.getDefault(),其实它在我们的博客中 Android Binder框架实现之Java层获取Binder服务源码分析已经有详细的介绍和分析了,但是为了博客的连贯性还是简单过下(主要是为了不太熟悉的小伙伴们)。

	//ActivityManagerNative.javastatic public IActivityManager getDefault() {return gDefault.get();}

这里的gDefault是Singleton对象实例,而Singleton我们可以看到是一个模板类对象,并且提供了一个单例方法,其定义如下:

//Singleton.java
public abstract class Singleton<T> {private T mInstance;protected abstract T create();public final T get() {synchronized (this) {if (mInstance == null) {//采用单例模式mInstance = create();}return mInstance;}}
}

我们将IActivityManager带入Singleton,得到如下的create方法过程:

	//ActivityManagerNative.javaprivate static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {protected IActivityManager create() {//此处等价于IBinder b = new BinderProxy(new BpBinder(handle));IBinder b = ServiceManager.getService("activity");if (false) {Log.v("ActivityManager", "default service binder = " + b);}//此处等价于IActivityManager am = new ActivityManagerProxy(new BinderProxy(new BpBinder(handle)))IActivityManager am = asInterface(b);if (false) {Log.v("ActivityManager", "default service = " + am);}return am;}};//注意此处我们的入参是BinderProxy类型,所以会走代理端static public IActivityManager asInterface(IBinder obj) {if (obj == null) {return null;}IActivityManager in =(IActivityManager)obj.queryLocalInterface(descriptor);if (in != null) {return in;}//即会走到此处return new ActivityManagerProxy(obj);}

这里即最终经过层层转换得到了AMS服务的代理端ActivityManagerProxy,进而借助它完成对AMS服务的RPC请求。

2.4.1 AMN.getDefault()小结

  AMN.getDefault()的调用流程基本分析结束了,我们对其小结一下:

  • AMN.getDefault()最终获取了AMS的远程Binder代理端AMP
  • AMS的Binder通信过程中提供了一个IActivityManager服务业务层接口,AMP类与AMS类都实现了IActivityManager接口方法,区别不同给的是AMS端显示了真正的具体服务,而AMP端是封装了相关的通信传输逻辑。AMP作为Binder通信的服务代理端,而AMS作为Binder通信的服务端实体,根据Android Binder框架实现之Java层Binder服务跨进程调用源码分析,AMP.bindService()最终调用AMS.bindService(),整个流程图如下:

在这里插入图片描述


2.5 AMP.getDefault()

	//ActivityManagerNative.javapublic int bindService(IApplicationThread caller, IBinder token,Intent service, String resolvedType, IServiceConnection connection,int flags,  String callingPackage, int userId) throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();//写入AMS Binder服务描述信息即android.app.IActivityManagerdata.writeInterfaceToken(IActivityManager.descriptor);//写入IApplicationThread 匿名Binder服务实体(这个在attachApplication时写入过)data.writeStrongBinder(caller != null ? caller.asBinder() : null);data.writeStrongBinder(token);service.writeToParcel(data, 0);data.writeString(resolvedType);//此处写入IServiceConnection 匿名Binder服务实体data.writeStrongBinder(connection.asBinder());data.writeInt(flags);data.writeString(callingPackage);data.writeInt(userId);//mRemote指向BinderProxy,而BinderProxy持有C++端的BpBinder,进而借助Binder驱动和AMS通信mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);reply.readException();int res = reply.readInt();data.recycle();reply.recycle();return res;}

  这里如果对Binder框架熟悉的小伙们应该对上述的调用过程是见怪不怪了,但是有几个点我们需要注意:

  • bindService中调用了三次Parcel类的方法writeStrongBinder(),这里我们需要注意writeStrongBinder()这三次写入的是Binder实体代理端还是代理端,是实名Binder还是匿名Binder。
  • 这里的mRemote指向BinderProxy,而BinderProxy持有C++端的BpBinder,而BpBinder作为远程Binder实体的通信代理端,最后借助Binder驱动和AMS通信,最后调用到ActivityManagerNative的onTransact()方法中


三. system_server进程处理bindService请求

  通过上面的层层冲关,打怪我们跳出了发起端进程,来到了system_server进程,让我们接着分析看看system_server是怎么处理bindService的RPC请求的。

3.1 AMN.onTransact

	//ActivityManagerNative.javapublic boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException {switch (code) {...case BIND_SERVICE_TRANSACTION: {data.enforceInterface(IActivityManager.descriptor);IBinder b = data.readStrongBinder();//此处b为ApplicationThread的代理端,转换后生成即ApplicationThreadProxy对象IApplicationThread app = ApplicationThreadNative.asInterface(b);IBinder token = data.readStrongBinder();Intent service = Intent.CREATOR.createFromParcel(data);String resolvedType = data.readString();b = data.readStrongBinder();//注意这里的b被重新赋值了int fl = data.readInt();String callingPackage = data.readString();int userId = data.readInt();//生成IServiceConnection的代理对象IServiceConnection.Stub.ProxyIServiceConnection conn = IServiceConnection.Stub.asInterface(b);int res = bindService(app, token, service, resolvedType, conn, fl,callingPackage, userId);//详见3.2reply.writeNoException();reply.writeInt(res);return true;}...}}

在正式开始上述的源码分析前,我们先来阐述一个重要的知识点,即在这个调用过程中涉及到两个进程,不妨令bindService的发起进程记为进程Process_A,AMS Service所属进程记为进程Process_B;那么进程Process_A通过Binder机制(采用IActivityManager接口)向进程Process_B发起请求服务,进程Process_B则通过Binder机制(采用IApplicationThread接口)向进程Process_A发起请求服务。也就是说进程Process_A与进程Process_B能相互间主动发起请求,进而完成进程通信,但是这里有一点需要注意IApplicationThread的Binder实体端并没有注册到servicemanager进程中,它是一个依赖于实名Binder的匿名Binder。

这里涉及IApplicationThread很重要,它串联起了AMS对App进程的生命周期及其其它的控制,那么下面直接把其相关的类图展示如下:

在这里插入图片描述

这里的IApplicationThread与IActivityManager的Binder通信原理一样,ATP作为Binder通信的客户端,ATN作为Binder通信的服务端,其中ApplicationThread继承ATN类,覆写其中的部分方法。

接着继续分析onTransact方法,其根据AMP传递过来的code值进入BIND_SERVICE_TRANSACTION分支,然后解读取通过Binder驱动传递过来的数据,解析完成之后调用AMN的方法bindService继续未完成之工作(这里的bindService在AMS服务中具体实现),这里从驱动中获取到数据然后解析这里就不重点关注了,我们这里重点关注一下AMN.bindService的几个入参:

  • 参数app: 发起端进程通过Binder IPC传递过来的ApplicationThread对象(匿名Binder服务端实体端)的Binder通信层代理端BinderProxy(BinderProxy(BpBinder())), 然后借助ATN.asInterface()方法生成新的代理对象ApplicationThreadProxy类型对象app(这个地方有点拗口,如果实在转不过来就算了,我们只需要知道此处获取了ApplicationThread的远程代理端ATP)
  • 参数conn: 根据发起端进程传递过来的InnerConnectiond对象(Binder服务端),同样通过转换后,生成IServiceConnection.Stub.Proxy类型对象conn
  • 参数service: 数据类型为Intent, 是指本次要启动的service的意图
  • 参数callingPackage: 发起方所属的包名
  • 参数fl: 是指flags, 此时等于Context.BIND_AUTO_CREATE, 即值为1

3.2 AMS.bindService

	//ActivityManagerService.java//final ActiveServices mServices;public int bindService(IApplicationThread caller, IBinder token, Intent service,String resolvedType, IServiceConnection connection, int flags, String callingPackage,int userId) throws TransactionTooLargeException {...synchronized(this) {return mServices.bindServiceLocked(caller, token, service,resolvedType, connection, flags, callingPackage, userId);}}

  AMS的担子很大,代码很多,为了的代码的条理性和代码的易阅读性AMS将事务委托给ActiveServices类实例对象进行处理。


3.3 ActiveServices.bindServiceLocked

  前方高能预警,小伙们可以趁着现在上个厕所,喝个水啥的,实在不行休息一下也行!因为后续的代码很长,很长,而且中途还不好休息最好是憋着一口气整完,不然又得找自己究竟在那里了!

	//ActiveServices.javaint bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,String resolvedType, final IServiceConnection connection, int flags,String callingPackage, final int userId) throws TransactionTooLargeException {...//查询发起端对应的进程记录结构final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);...ActivityRecord activity = null;//token不为空,代表着发起方具有activiy上下文if (token != null) {activity = ActivityRecord.isInStackLocked(token);if (activity == null) {Slog.w(TAG, "Binding with unknown activity: " + token);return 0;//存在token, 却找不到activity为空,则直接返回}}int clientLabel = 0;PendingIntent clientIntent = null;final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID;if (isCallerSystem) {//调用者是否是system_uid用户...}...//根据发送端所在进程的SchedGroup来决定是否为前台service.final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;//根据第三方进程传递进来的Intent信息来检索相对应的服务,这个retriev单词很契合ServiceLookupResult res =retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),Binder.getCallingUid(), userId, true, callerFg, isBindExternal);//详见章节3.3.1...//查询到相应的ServiceServiceRecord s = res.record;if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {//权限检测...}try {//取消服务的重启调度if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "BIND SERVICE WHILE RESTART PENDING: "+ s);}if ((flags&Context.BIND_AUTO_CREATE) != 0) {//根据前面的分析可知,此处传递过来的flags的值就是Context.BIND_AUTO_CREATE//更新当前service活动时间s.lastActivity = SystemClock.uptimeMillis();if (!s.hasAutoCreateConnections()) {// This is the first binding, let the tracker know.ServiceState stracker = s.getTracker();if (stracker != null) {stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(),s.lastActivity);}}}mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,s.appInfo.uid, s.name, s.processName);AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);//详见章节3.3.2//创建对象ConnectionRecord,此处connection来自发起方ConnectionRecord c = new ConnectionRecord(b, activity,connection, flags, clientLabel, clientIntent);IBinder binder = connection.asBinder();//这里表示IServiceConnection服务的远程代理端,看来Binder真的是很重要阿ArrayList<ConnectionRecord> clist = s.connections.get(binder);if (clist == null) {clist = new ArrayList<ConnectionRecord>();s.connections.put(binder, clist);}clist.add(c);// clist是ServiceRecord.connections的成员变量b.connections.add(c);//b是指AppBindRecordif (activity != null) {if (activity.connections == null) {activity.connections = new HashSet<ConnectionRecord>();}activity.connections.add(c);}b.client.connections.add(c);if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {b.client.hasAboveClient = true;}if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {s.whitelistManager = true;}if (s.app != null) {updateServiceClientActivitiesLocked(s.app, c, true);}clist = mServiceConnections.get(binder);if (clist == null) {clist = new ArrayList<ConnectionRecord>();mServiceConnections.put(binder, clist);}clist.add(c);if ((flags&Context.BIND_AUTO_CREATE) != 0) {s.lastActivity = SystemClock.uptimeMillis();//启动service,这个过程和startService过程一致if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,permissionsReviewRequired) != null) {//详见章节3.4return 0;}}if (s.app != null) {//此时表示service所属进程已经启动if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {s.app.treatLikeActivity = true;}if (s.whitelistManager) {s.app.whitelistManager = true;}// This could have made the service more important.//更新service所在进程优先级mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities|| s.app.treatLikeActivity, b.client);mAm.updateOomAdjLocked(s.app);}...if (s.app != null && b.intent.received) {// Service is already running, so we can immediately// publish the connection.try {//Service已经正在运行,则调用InnerConnection的代理对象c.conn.connected(s.name, b.intent.binder);} catch (Exception e) {Slog.w(TAG, "Failure sending service " + s.shortName+ " to connection " + c.conn.asBinder()+ " (in " + c.binding.client.processName + ")", e);}...//当第一个app连接到该binding, 且之前已被bind过, 则回调onRebind()方法if (b.intent.apps.size() == 1 && b.intent.doRebind) {requestServiceBindingLocked(s, b.intent, callerFg, true);}} else if (!b.intent.requested) {//最终回调onBind()方法requestServiceBindingLocked(s, b.intent, callerFg, false);}getServiceMap(s.userId).ensureNotStartingBackground(s);} finally {Binder.restoreCallingIdentity(origId);}return 1;}

是不是感觉有点懵,这一坨代码的,不要慌!我们理一理这一坨代码的主要功能有哪些:

  • 通常第三方进程调用system_server进程中的Java层Binder服务时候会做一些安全和功能的检测,这里也不例外,会判断发起端进程是否存在,是否存在activity上下文等信息
  • 调用retrieveServiceLocked查找根据第三方进程传递进来的Intent所对应的服务,即系统中是否存在符合intent携带消息的Service
  • 通过retrieveAppBindingLocked()方法创建AppBindRecord对象,该对象记录着当前ServiceRecord, intent以及发起方的进程ProcessRecord信息
  • 一切准备就绪之后,调用bringUpServiceLocked拉起目标服务(如果此时目标进程还没有创建的话,得先创建目标进程)

另外可以看到在AMS服务中会将发起端传递过来的connection存储起来,即将发起端进程传递过来的LoadedApk.ServiceDispatcher.InnerConnection的代理对象IServiceConnection.Stub.Proxy类型对象实例connection,保存到新创建的ConnectionRecord对象的成员变量. 再通过clist.add( c ), 将该ConnectionRecord对象添加到clist队列. 后面便可以通过clist来 查询发起方的信息。

3.3.1 ActiveServices.retrieveServiceLocked

	//ActiveServices.javafinal SparseArray<ServiceMap> mServiceMap = new SparseArray<>();private ServiceLookupResult retrieveServiceLocked(Intent service,String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal) {ServiceRecord r = null;...userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);ServiceMap smap = getServiceMap(userId);final ComponentName comp = service.getComponent();if (comp != null) {//根据服务名查找对应的ServiceRecordr = smap.mServicesByName.get(comp);}if (r == null && !isBindExternal) {Intent.FilterComparison filter = new Intent.FilterComparison(service);//根据Intent查找相应的相应的ServiceRecordr = smap.mServicesByIntent.get(filter);}if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0&& !callingPackage.equals(r.packageName)) {...r = null;}if (r == null) {//在smap中没有查找到对应的服务try {...//通过PKMS来查询相应的serviceResolveInfo rInfo = AppGlobals.getPackageManager().resolveService(service,resolvedType, ActivityManagerService.STOCK_PM_FLAGS| PackageManager.MATCH_DEBUG_TRIAGED_MISSING,userId);ServiceInfo sInfo =rInfo != null ? rInfo.serviceInfo : null;if (sInfo == null) {...}//组装组件名ComponentName name = new ComponentName(sInfo.applicationInfo.packageName, sInfo.name);if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {//声明externalService为true就是让该Service可以绑定并运行在调用方的App中,而不是在声明这个Service的App中if (isBindExternal) {if (!sInfo.exported) {throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +" is not exported");}if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +" is not an isolatedProcess");}// Run the service under the calling package's application.ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);if (aInfo == null) {throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +"could not resolve client package " + callingPackage);}//其实就是重新设置了一遍ServiceInfo,让此Service改名换姓sInfo = new ServiceInfo(sInfo);sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);sInfo.applicationInfo.packageName = aInfo.packageName;sInfo.applicationInfo.uid = aInfo.uid;name = new ComponentName(aInfo.packageName, name.getClassName());service.setComponent(name);} else {throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +name);}} else if (isBindExternal) {throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +" is not an externalService");}if (userId > 0) {//服务是否属于单例模式if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,sInfo.name, sInfo.flags)&& mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) {userId = 0;smap = getServiceMap(0);}sInfo = new ServiceInfo(sInfo);sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);}r = smap.mServicesByName.get(name);if (r == null && createIfNeeded) {Intent.FilterComparison filter= new Intent.FilterComparison(service.cloneFilter());ServiceRestarter res = new ServiceRestarter();BatteryStatsImpl.Uid.Pkg.Serv ss = null;BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();synchronized (stats) {ss = stats.getServiceStatsLocked(sInfo.applicationInfo.uid, sInfo.packageName,sInfo.name);}//创建ServiceRecordr = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);res.setService(r);//将创建的ServiceRecord信息放入smap列表中,因为之前该service并没有创建过,所以会加入smap哈希列表smap.mServicesByName.put(name, r);smap.mServicesByIntent.put(filter, r);for (int i=mPendingServices.size()-1; i>=0; i--) {ServiceRecord pr = mPendingServices.get(i);if (pr.serviceInfo.applicationInfo.uid == sInfo.applicationInfo.uid&& pr.name.equals(name)) {mPendingServices.remove(i);}}}} catch (RemoteException ex) {}}if (r != null) {//各种权限检查if (mAm.checkComponentPermission(r.permission,callingPid, callingUid, r.appInfo.uid, r.exported)!= PackageManager.PERMISSION_GRANTED) {//当exported=false则不允许启动if (!r.exported) {Slog.w(TAG, "Permission Denial: Accessing service " + r.name+ " from pid=" + callingPid+ ", uid=" + callingUid+ " that is not exported from uid " + r.appInfo.uid);return new ServiceLookupResult(null, "not exported from uid "+ r.appInfo.uid);}Slog.w(TAG, "Permission Denial: Accessing service " + r.name+ " from pid=" + callingPid+ ", uid=" + callingUid+ " requires " + r.permission);return new ServiceLookupResult(null, r.permission);} else if (r.permission != null && callingPackage != null) {final int opCode = AppOpsManager.permissionToOpCode(r.permission);if (opCode != AppOpsManager.OP_NONE && mAm.mAppOpsService.noteOperation(opCode, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {Slog.w(TAG, "Appop Denial: Accessing service " + r.name+ " from pid=" + callingPid+ ", uid=" + callingUid+ " requires appop " + AppOpsManager.opToName(opCode));return null;}}if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,resolvedType, r.appInfo)) {return null;}//创建Service查询结果,并返回return new ServiceLookupResult(r, null);}return null;}

  前方的代码又是大一坨,主要是牵扯到各种数据结构的转换和关联,得数据结构这得天下啊,看来学好数据结构真的很重要啊,好像跑题了啊!前面我们知道retrieveServiceLocked主要是查询我们的目的端服务ServiceLookupResult结构是否存在(如果前面没有调用过,当然不存在得创建),其查询按照如下步骤依次执行(这里不详细展开了,不然那真是没完没了的了,估计你要疯,我也要疯):

  • 通过userId查询mServiceMap中是否存在对应的ServiceMap实例,如果没有则创建一个并返回赋给ServiceMap实例smap
  • 根据服务名从实例对象smap.mServicesByName中查找相应的ServiceRecord,如果没有找到,则往下执行;
  • 根据Intent从实例对象smap.mServicesByIntent中查找相应的ServiceRecord,如果还是没有找到,则往下执行;
  • 通过PKMS来查询相应的ServiceInfo,如果仍然没有找到相关信息,则不再往下执行,如若找到则继续执行填充ServiceRecord数据结构

这里还有一个知识点,就是ServiceInfo.FLAG_EXTERNAL_SERVICE这个标志,是干啥的呢,说实话我也没有用过,我多方翻阅发现这是service在AndroidManifest.xml中新增加的属性android:externalService,当它置为true的时候可以让该Service可以绑定并运行在调用方的App中,而不是在声明这个Service的App中,意不意外惊不惊喜,详细的分析可以参见这个博客android:externalService的功能和原理,这个不是我们的重点不过多分析,我们接着继续往下分析!

接着调用AMS.isSingleton判断我们要查询的目的端Service是否是单例模式,其中符合单例模式需要满足下述三个条件之一:

  • 目的端组件uid>10000,且同时具有ServiceInfo.FLAG_SINGLE_USER flags和INTERACT_ACROSS_USERS权限,说实话这个地方我有个疑问就是目的端进程都没有创建uid怎么就分配了,特殊的进程除外啊
  • 目的端组件运行在system进程的情况
  • 目的端具有ServiceInfo.FLAG_SINGLE_USER flags,且uid=Process.PHONE_UID或者persistent app的情况

如果经过如上一顿猛如虎般的操作之后,依然没有找到合适的ServiceRecord对象,那么则会创建ServiceRecord实例,然后将前面解析,组装出来的相关参数填入ServiceRecord实例对象,然后分别以目的端服务的name和filter为key,存入到smap的mServicesByName和smap的mServicesByIntent。娘的这数据结构,左一坨右一坨的,难受!

3.3.2 ServiceRecord.retrieveAppBindingLocked

	//ServiceRecord.javafinal ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings= new ArrayMap<Intent.FilterComparison, IntentBindRecord>();//创建AppBindRecord对象记录着当前ServiceRecord, intent以及发起方的进程信息。public AppBindRecord retrieveAppBindingLocked(Intent intent,ProcessRecord app) {Intent.FilterComparison filter = new Intent.FilterComparison(intent);IntentBindRecord i = bindings.get(filter);if (i == null) {//创建连接ServiceRecord和fliter的记录i = new IntentBindRecord(this, filter);bindings.put(filter, i);}//此处app是指调用方所在进程AppBindRecord a = i.apps.get(app);if (a != null) {return a;}//创建ServiceRecord跟进程绑定的记录信息a = new AppBindRecord(this, i, app);i.apps.put(app, a);return a;}

  说实话看着这一大坨的数据结构对象,真的感觉干不动了。不详细分析了,这里我们需要知道AppBindRecord对象记录着当前ServiceRecord,intent以及发起方的进程信息,并且该记录会存在在ArrayMap对象bindings中存起来,供后续查询!


3.4 bringUpServiceLocked

  我们回到章节3.3继续未完成实名之bringUpServiceLocked,这也可以说是本篇的高潮部分了,因为该方法将会拉起服务端Service,倘若服务端进程还没有创建则会先创建进程进而再拉起服务。所以bringUpServiceLocked会分为两种情况处理!

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,boolean whileRestarting, boolean permissionsReviewRequired)throws TransactionTooLargeException {//此种情况前提是要启动的Service所属进程已经创建if (r.app != null && r.app.thread != null) {//兜兜转转最终调用到目的端Service.onStartCommand()过程,这个过程就不分析了,最后通过ApplicationThreadProxy调用到目的端ActivityThread进而控制目的端Service生命周期执行sendServiceArgsLocked(r, execInFg, false);return null;}if (!whileRestarting && r.restartDelay > 0) {return null;//等待延迟重启的过程,则直接返回}// 启动service前,把service从重启服务队列中移除if (mRestartingServices.remove(r)) {r.resetRestartCounter();clearRestartingIfNeededLocked(r);}//service正在启动,将delayed设置为falseif (r.delayed) {getServiceMap(r.userId).mDelayedStartList.remove(r);r.delayed = false;}//确保拥有该服务的user已经启动,否则停止,即多用户情况下if (mAm.mStartedUsers.get(r.userId) == null) {String msg = "";bringDownServiceLocked(r);return msg;}try {//服务正在启动,设置package停止状态为falseAppGlobals.getPackageManager().setPackageStoppedState(r.packageName, false, r.userId);} catch (RemoteException e) {} catch (IllegalArgumentException e) {...}//isolated为true表示Service独立运行到一个特定进程中final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;final String procName = r.processName;ProcessRecord app;if (!isolated) {//根据进程名和uid,查询ProcessRecordapp = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);if (app != null && app.thread != null) {//此时try {app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);//启动服务realStartServiceLocked(r, app, execInFg);return null;} catch (TransactionTooLargeException e) {throw e;} catch (RemoteException e) {...}}} else {app = r.isolatedProc;}//对于进程没有启动的情况下,先启动进程if (app == null && !permissionsReviewRequired) {//启动service所要运行的进程,这就牵涉到Android应用进程创建的流程了if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,"service", r.name, false, isolated, false)) == null) {//详见章节3.4.1bringDownServiceLocked(r);//进程启动失败处理逻辑return msg;}if (isolated) {r.isolatedProc = app;}}if (!mPendingServices.contains(r)) {mPendingServices.add(r);//注意此处,将ServiceRecord添加到mPendingServices中,后续会遍历此list}if (r.delayedStop) {r.delayedStop = false;if (r.startRequested) {stopServiceLocked(r);//停止服务}}return null;}

前面说过bringUpServiceLocked的启动分为两种情况:

  • 当目标进程已存在,则直接执行realStartServiceLocked();
  • 当目标进程不存在,则先执行startProcessLocked创建进程, 经过层层调用最后会调用到AMS.attachApplicationLocked, 然后再执行realStartServiceLocked(),这个逻辑我想也是小伙们最关心的, 我们后续从这个地方开撸

3.4.1 AMS.startProcessLocked

  startProcessLocked的路漫漫其修远兮吾将上下而求索,这个涉及的代码信息量太大,具体流程小伙们可以参见博客Android应用进程创建流程大揭秘,及最终会调用到目的端进程的ActivityThread.main方法里面,其中的过程大概可以使用如下的流程图表示:
在这里插入图片描述
我们就这ActivityThread.main接着分析(在这里我们只挑和我们这里相关的,其它的暂且不予关注),代码如下:

	//ActivityThread.javapublic static void main(String[] args) {...ActivityThread thread = new ActivityThread();thread.attach(false);//参数false是重点...}final ApplicationThread mAppThread = new ApplicationThread();private void attach(boolean system) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {final IActivityManager mgr = ActivityManagerNative.getDefault();try {mgr.attachApplication(mAppThread);//老套路了啊,远程调用AMS的attachApplication方法,注意这里的参数是IApplicationThread的Binder实体端,该调用流程详见3.4.2} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}} else {//不要问我谁会走此分支,我不会告诉你system_server的创建会走此分支的...}}

3.4.2 AMS.attachApplication

  到这里了我们就不详细分析目的端进程怎么通过Binder框架调用到AMS服务来了,大概的调用流程如下:

AMP.attachApplication() ---> 
BinderProxy.transact() --->
BpBinder.transact()--->
binder驱动传输--->
JavaBBinder.onTransact()--->
AMN.onTransact()--->
AMS.attachApplication()

这个前面章节有具体分析过了,举一反三即可。我们直捣黄龙直接分析AMS的attachApplication方法。

    //ActivityMangerService.java@Overridepublic final void attachApplication(IApplicationThread thread) {synchronized (this) {...attachApplicationLocked(thread, callingPid);...}}private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {...if (!badApp) {try {//寻找所有需要在该进程中运行的服务didSomething |= mServices.attachApplicationLocked(app, processName);//详见3.4.3checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");} catch (Exception e) {Slog.wtf(TAG, "Exception thrown starting services in " + app, e);badApp = true;}}...         }

好吗又调用到了ActiveServices对象实例中去了,我们接着分析其attachApplicationLocked方法。

3.4.2 ActiveServices.attachApplicationLocked

	//ActiveServices.javaboolean attachApplicationLocked(ProcessRecord proc, String processName)throws RemoteException {boolean didSomething = false;//遍历所有在mPendingServices列表中,等待在该进程中启动的service,此处mPendingServices的添加在3.4章节bringUpServiceLocked的最后,即mPendingServices.add阶段if (mPendingServices.size() > 0) {ServiceRecord sr = null;try {for (int i=0; i<mPendingServices.size(); i++) {sr = mPendingServices.get(i);if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid|| !processName.equals(sr.processName))) {continue;}mPendingServices.remove(i);i--;//将当前服务的包信息加入到procproc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,mAm.mProcessStats);//正常启动service,并开始其的生命周期realStartServiceLocked(sr, proc, sr.createdFromFg);didSomething = true;if (!isServiceNeeded(sr, false, false)) {bringDownServiceLocked(sr);}}} catch (RemoteException e) {Slog.w(TAG, "Exception in new application when starting service "+ sr.shortName, e);throw e;}}//对于正在等待重启并需要运行在该进程的服务,现在是启动它们的大好时机if (mRestartingServices.size() > 0) {ServiceRecord sr;for (int i=0; i<mRestartingServices.size(); i++) {sr = mRestartingServices.get(i);if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid|| !processName.equals(sr.processName))) {continue;}mAm.mHandler.removeCallbacks(sr.restarter);mAm.mHandler.post(sr.restarter);}}return didSomething;}

  兜兜转转,又来到了realStartServiceLocked方法,这个和前面章节总结的前后照应了:

  • 即当需要创建新的进程时候,会调用AMS.startProcessLocked启动进程,然后经过千辛万苦的各种辗转最后调用到attachApplicationLocked方法,进而调用realStartServiceLocked方法
  • 若不用创建进程,则直接调用realStartServiceLocked方法,进行剩余工作

3.5 ActiveServices.realStartServiceLocked

	//ActiveServices.javaprivate final void realStartServiceLocked(ServiceRecord r,ProcessRecord app, boolean execInFg) throws RemoteException {if (app.thread == null) {throw new RemoteException();}r.app = app;r.restartTime = r.lastActivity = SystemClock.uptimeMillis();final boolean newService = app.services.add(r);//发送delay消息,ANR预埋雷bumpServiceExecutingLocked(r, execInFg, "create");//详见3.5.1mAm.updateLruProcessLocked(app, false, null);mAm.updateOomAdjLocked();boolean created = false;try {...mAm.notifyPackageUse(r.serviceInfo.packageName,PackageManager.NOTIFY_PACKAGE_USE_SERVICE);app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);//这里的app.thread是IApplicationThread服务的远程代理客户端,服务进入onCreateapp.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),app.repProcState);//调用到远程服务端onCreate方法,详见章节3.6r.postNotification();created = true;} catch (DeadObjectException e) {//应用死亡通知Slog.w(TAG, "Application dead when creating service " + r);mAm.appDiedLocked(app);throw e;} finally {if (!created) {// Keep the executeNesting count accurate.final boolean inDestroying = mDestroyingServices.contains(r);serviceDoneExecutingLocked(r, inDestroying, inDestroying);if (newService) {app.services.remove(r);r.app = null;if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {Slog.w(TAG, " Failed to create Service !!!! ."+"This will introduce huge delay...  "+r.shortName + " in " + r.restartDelay + "ms");}}// Retry.//尝试重新启动服务if (!inDestroying) {scheduleServiceRestartLocked(r, false);}}}if (r.whitelistManager) {app.whitelistManager = true;}//通过bindService方式启动servicerequestServiceBindingsLocked(r, execInFg);//详见章节5.1updateServiceClientActivitiesLocked(app, null, true);if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),null, null));}//开启service的生命周期onStartCommand()sendServiceArgsLocked(r, execInFg, true);if (r.delayed) {getServiceMap(r.userId).mDelayedStartList.remove(r);r.delayed = false;}if (r.delayedStop) {r.delayedStop = false;if (r.startRequested) {stopServiceLocked(r);}}}

  记得我们经常说的Android四大组件中不能执行耗时操作吗,小伙们有想过为什么吗?这是因为有Android ANR的预埋雷机制,关于ANR的机制和原理可以详细参见博客理解Android ANR的触发原理,在bumpServiceExecutingLocked会发送一个延迟处理的消息SERVICE_TIMEOUT_MSG。在方法scheduleCreateService执行完成,也就是onCreate回调执行完成之后,便会remove掉该消息。但是如果没能在延时时间之内remove该消息,则会进入执行service timeout流程触发ANR,这就是为啥Android 四大组件不能执行耗时操作的原因。

3.5.1 ActiveServices.bumpServiceExecutingLocked

	//ActiveServices.javaprivate final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {long now = SystemClock.uptimeMillis();if (r.executeNesting == 0) {r.executeFg = fg;ServiceState stracker = r.getTracker();if (stracker != null) {stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);}if (r.app != null) {r.app.executingServices.add(r);r.app.execServicesFg |= fg;if (r.app.executingServices.size() == 1) {scheduleServiceTimeoutLocked(r.app);}}} else if (r.app != null && fg && !r.app.execServicesFg) {r.app.execServicesFg = true;scheduleServiceTimeoutLocked(r.app);//走入该分支,不要问我为啥,调试打印的结果}r.executeFg |= fg;r.executeNesting++;r.executingStart = now;}

  不管选择那个分支,最终都会走入scheduleServiceTimeoutLocked方法!

3.5.2 ActiveServices.scheduleServiceTimeoutLocked

	//ActiveServices.java//前台服务timeout时间为20static final int SERVICE_TIMEOUT = 20*1000;// 后台服务timeout时间为200sstatic final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;void scheduleServiceTimeoutLocked(ProcessRecord proc) {if (proc.executingServices.size() == 0 || proc.thread == null) {return;}long now = SystemClock.uptimeMillis();Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);msg.obj = proc;//在超时允许的时间内依然没有remove该SERVICE_TIMEOUT_MSG,则会执行SERVICE_TIMEOUT_MSG流程mAm.mHandler.sendMessageAtTime(msg,proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));}

  还记得我们老说的Service的ANR超时时间吗,什么前台服务是多少,后台服务是多少,一切的源头都在于此,这里会延时发送消息SERVICE_TIMEOUT_MSG,而这个延时的时间可以看到分为两种情况:

  • 对于前台服务,则超时时间为SERVICE_TIMEOUT,即timeout=20s
  • 对于后台服务,则超时间为SERVICE_BACKGROUND_TIMEOUT,即timeout=200s

注意这里的mAm.mHandler是在AMS中进行处理的,其处理逻辑如下所示:

//AMS.java
final class MainHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {...case SERVICE_TIMEOUT_MSG: {if (mDidDexOpt) {mDidDexOpt = false;Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);nmsg.obj = msg.obj;mHandler.sendMessageDelayed(nmsg, ActiveServices.SERVICE_TIMEOUT);return;}mServices.serviceTimeout((ProcessRecord)msg.obj);//又调用到ActiveServices中去了,老子不跟了} break;   ...         }}
}

3.6 ATP.scheduleCreateService

	//ApplicationThreadNative.javapublic final void scheduleCreateService(IBinder token, ServiceInfo info,CompatibilityInfo compatInfo, int processState) throws RemoteException {Parcel data = Parcel.obtain();data.writeInterfaceToken(IApplicationThread.descriptor);data.writeStrongBinder(token);info.writeToParcel(data, 0);compatInfo.writeToParcel(data, 0);data.writeInt(processState);try {mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null,IBinder.FLAG_ONEWAY);} catch (TransactionTooLargeException e) {Log.e("CREATE_SERVICE", "Binder failure starting service; service=" + info);throw e;}data.recycle();}

  历史和其的相似,看来在Android源码的学习过程中Binder是一个必须攻克的堡垒啊。这里的ATP是IApplicationThread服务的代理端,然后借助BInder框架会调用到其服务端,而该匿名Binder服务端注册在目的端进程中,其调用过程大概如下:

ATP.scheduleCreateService()---> 
BinderProxy.transact() --->
BpBinder.transact()--->
binder驱动传输--->
JavaBBinder.onTransact()--->
ATN.onTransact()--->
ATN.attachApplication()


四. 目的端进程处理bindService请求

  通过ATP的努力和我们的Binder框架的协助,我们跨越万水千山,完成了system_server所在进程到Service所在目的端进程调用过程,让我们接着分析看看目的端进程是怎么处理bindService的RPC请求的。我好难啊!

4.1 ATN.onTransact

@Override//ApplicationThreadNative.javapublic boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException {switch (code) {..case SCHEDULE_CREATE_SERVICE_TRANSACTION: {data.enforceInterface(IApplicationThread.descriptor);//常规操作,读取从binder驱动传递过来的业务数据IBinder token = data.readStrongBinder();ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data);CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);int processState = data.readInt();scheduleCreateService(token, info, compatInfo, processState);//由其子类ApplicationThread实现,详见章节4.2return true;}...}}

无需多言,直接进入下一关,打怪升级!


4.2 AT.scheduleCreateService

        public final void scheduleCreateService(IBinder token,ServiceInfo info, CompatibilityInfo compatInfo, int processState) {updateProcessState(processState, false);CreateServiceData s = new CreateServiceData();//创建CreateServiceData实例对象,为Service的创建准备数据s.token = token;s.info = info;s.compatInfo = compatInfo;sendMessage(H.CREATE_SERVICE, s);}

  巧用ActivityThread的主线程的Handler发送消息,这里我们可以总结一下规律,通常AMS通过ATP发送过来的消息,遵循如下的处理逻辑,如下:

scheduleXXX() ---> handleXXX()

4.3 H.handleMessage

//ActivityThread.java
private class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {...case CREATE_SERVICE:handleCreateService((CreateServiceData)msg.obj); //详见4.4break;case BIND_SERVICE:handleBindService((BindServiceData)msg.obj);//onBindbreak;case UNBIND_SERVICE:handleUnbindService((BindServiceData)msg.obj);break;case SERVICE_ARGS:handleServiceArgs((ServiceArgsData)msg.obj);  // serviceStartbreak;case STOP_SERVICE:handleStopService((IBinder)msg.obj);maybeSnapshot();break;...}}
}

无需多言,直接进入下一关,打怪升级!


4.4 AT.handleCreateService

	//AT.javaprivate void handleCreateService(CreateServiceData data) {//如果应用处于后台即将进行GC,此时我们将其调回到活动状态,并且跳过本次GCunscheduleGcIdler();LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);Service service = null;try {//通过反射创建目标服务对象java.lang.ClassLoader cl = packageInfo.getClassLoader();service = (Service) cl.loadClass(data.info.name).newInstance();} catch (Exception e) {if (!mInstrumentation.onException(service, e)) {throw new RuntimeException("Unable to instantiate service " + data.info.name+ ": " + e.toString(), e);}}try {if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);//创建ContextImpl对象ContextImpl context = ContextImpl.createAppContext(this, packageInfo);context.setOuterContext(service);//创建Application对象Application app = packageInfo.makeApplication(false, mInstrumentation);service.attach(context, this, data.info.name, data.token, app,ActivityManagerNative.getDefault());//调用service服务的onCreate()方法service.onCreate();//详见4.4.1mServices.put(data.token, service);try {//调用服务创建完成,将ANR预埋雷除去,看来和AMS的互动很频繁啊ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);//详见章节4.5} catch (RemoteException e) {throw e.rethrowFromSystemServer();}} catch (Exception e) {if (!mInstrumentation.onException(service, e)) {throw new RuntimeException("Unable to create service " + data.info.name+ ": " + e.toString(), e);}}}

该段代码,主要执行两个业务逻辑:

  • 执行目的端Service的onCreate方法
  • 调用AMS的serviceDoneExecuting方法,解除在前面3.5.2章节预埋的ANR雷,不然真的会爆炸的

4.4.1 Services.onCreate

	//Services.javapublic void onCreate() {}

  我曾跨过山河大海也穿过人山人海,终于来到了目标进程Service的生命周期onCreate方法中,通常Service的子类会重写该方法!不容易啊,终于看到了胜利曙光,但是还没有结束,依然还需要战斗!


4.5 AMS.serviceDoneExecuting

不要问我为啥来到此处,这都是套路,慢慢的套路!

	//AMS.javapublic void serviceDoneExecuting(IBinder token, int type, int startId, int res) {synchronized(this) {if (!(token instanceof ServiceRecord)) {Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);throw new IllegalArgumentException("Invalid service token");}//AMS很闲啊,啥也不干全部扔给了ActivityServices处理了,详见4.5.1mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);}}

无需多言,直接进入下一关,打怪升级!

4.5.1 ActivityServices.serviceDoneExecutingLocked

	//ActivityServices.javavoid serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {if (r != null) {...final long origId = Binder.clearCallingIdentity();serviceDoneExecutingLocked(r, inDestroying, inDestroying);//详见4.5.2Binder.restoreCallingIdentity(origId);...}...}

我感觉Android的开发工程师也很难受,一个方法里面代码太多了吗,小伙们说这个裹脚布,搞方法太多了吗,跳转的难受!

4.5.2 ActivityServices.serviceDoneExecutingLocked

 private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {r.executeNesting--;if (r.executeNesting <= 0) {if (r.app != null) {r.app.execServicesFg = false;r.app.executingServices.remove(r);if (r.app.executingServices.size() == 0) {//移除服务启动超时的消息,将ANR的雷排除mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);} else if (r.executeFg) {...}if (inDestroying) {mDestroyingServices.remove(r);r.bindings.clear();}mAm.updateOomAdjLocked(r.app);}r.executeFg = false;...if (finishing) {if (r.app != null && !r.app.persistent) {r.app.services.remove(r);}r.app = null;}}
}

  在此处终于将执行removeMessages将3.5.2章节预埋的ANR雷移除,即将消息息SERVICE_TIMEOUT_MSG从Handler的消息队列中移除。分析至此,小伙们应该了解了Service启动过程出现ANR,”executing service [发送超时serviceRecord信息]”,这往往是service的onCreate()回调方法执行时间过长的原因了。



五. 重返system_server进程

  此时的我在那里,该何处何从。此时让我们穿越时空重新回到system_server进程,继续章节3.5之未完成实名,革命仍未成功还需努力啊。

5.1 AS.requestServiceBindingsLocked

	//ActivityServices.javaprivate final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)throws TransactionTooLargeException {for (int i=r.bindings.size()-1; i>=0; i--) {//遍历有多少需要bindings的ServiceIntentBindRecord ibr = r.bindings.valueAt(i);if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {//调用另外一个重载的requestServiceBindingLocked方法break;}}}private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,boolean execInFg, boolean rebind) throws TransactionTooLargeException {if (r.app == null || r.app.thread == null) {return false;}if ((!i.requested || rebind) && i.apps.size() > 0) {try {//发送bind开始的消息,又开始埋雷了bumpServiceExecutingLocked(r, execInFg, "bind");r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);//服务进入onBind()r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,r.app.repProcState);if (!rebind) {i.requested = true;}i.hasBound = true;i.doRebind = false;} catch (TransactionTooLargeException e) {final boolean inDestroying = mDestroyingServices.contains(r);serviceDoneExecutingLocked(r, inDestroying, inDestroying);throw e;} catch (RemoteException e) {final boolean inDestroying = mDestroyingServices.contains(r);serviceDoneExecutingLocked(r, inDestroying, inDestroying);return false;}}return true;}

  通过bindService方式启动的服务, 那么该serviceRecord的bindings则一定不会空,不然后续的工作咋开展呢!而这里的入参ServiceRecord实例对象的创建可以回到章节3.5温故温故。接着继续调用重载的requestServiceBindingLocked方法继续处理,而在该重载的方法中主要执行了两项操作:

  • 调用bumpServiceExecutingLocked方法继续埋雷,倘若目标Service的onBind方法没有在指定的时间内执行完毕,则会触发ANR,这个已经在前面有过详细分析了
  • 接着调用目标服务端进程的AT代理端ATP的方法scheduleBindService,向AT发送Binder请求

5.2 ATP.scheduleBindService

	//ApplicationThreadNative.javapublic final void scheduleBindService(IBinder token, Intent intent, boolean rebind,int processState) throws RemoteException {Parcel data = Parcel.obtain();data.writeInterfaceToken(IApplicationThread.descriptor);data.writeStrongBinder(token);intent.writeToParcel(data, 0);data.writeInt(rebind ? 1 : 0);data.writeInt(processState);mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,IBinder.FLAG_ONEWAY);data.recycle();}

  AMS和AT之间的交互很频繁啊,目的端进程和system_server进程之间通过IApplicationThread和IActivityManager来回的折腾着。这里的ATP是IApplicationThread服务的代理端,然后借助BInder框架会调用到其服务端,而该匿名Binder服务端注册在目的端进程中,其调用过程大概如下:

ATP.scheduleBindService()---> 
BinderProxy.transact() --->
BpBinder.transact()--->
binder驱动传输--->
JavaBBinder.onTransact()--->
ATN.onTransact()--->
ATN.scheduleBindService()


六. 重返目的端服务进程

  这么来回的跳转,折腾着感觉心好累啊!但是木有办法,要想深入只能是操起锤子接着干了。

6.1 ATN.onTransact

@Override//ApplicationThreadNative.javapublic boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException {switch (code) {..case SCHEDULE_BIND_SERVICE_TRANSACTION: {data.enforceInterface(IApplicationThread.descriptor);IBinder token = data.readStrongBinder();Intent intent = Intent.CREATOR.createFromParcel(data);boolean rebind = data.readInt() != 0;int processState = data.readInt();scheduleBindService(token, intent, rebind, processState);//在其子类AT中实现该方法return true;}...}}

无需多言,直接进入下一关,打怪升级!


6.2 AT.scheduleBindService

        public final void scheduleBindService(IBinder token, Intent intent,boolean rebind, int processState) {updateProcessState(processState, false);BindServiceData s = new BindServiceData();//创建BindServiceData实例对象,接收AMS传递过来的Service信息s.token = token;s.intent = intent;s.rebind = rebind;sendMessage(H.BIND_SERVICE, s);}

  巧用ActivityThread的主线程的Handler发送消息,这里我们可以总结一下规律,通常AMS通过ATP发送过来的消息,遵循如下的处理逻辑,如下:

scheduleXXX() ---> handleXXX()

6.3 H.handleMessage

//ActivityThread.java
private class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {...case CREATE_SERVICE:handleCreateService((CreateServiceData)msg.obj); //onCreatebreak;case BIND_SERVICE:handleBindService((BindServiceData)msg.obj);//onBind,详见6.4break;case UNBIND_SERVICE:handleUnbindService((BindServiceData)msg.obj);break;case SERVICE_ARGS:handleServiceArgs((ServiceArgsData)msg.obj);  // serviceStartbreak;case STOP_SERVICE:handleStopService((IBinder)msg.obj);maybeSnapshot();break;...}}
}

无需多言,直接进入下一关,打怪升级!


6.4 AT.handleBindService

	//AT.javaprivate void handleBindService(BindServiceData data) {Service s = mServices.get(data.token);if (DEBUG_SERVICE)Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);if (s != null) {try {data.intent.setExtrasClassLoader(s.getClassLoader());data.intent.prepareToEnterProcess();try {if (!data.rebind) {//执行Service.onBind()回调方法,此时在远程服务进程的主线程中IBinder binder = s.onBind(data.intent);//将onBinde返回值传递回去,注意这里的binder还是Binder服务端,只有经过Binder传输以后才变成了代理端//这里的ActivityManagerNative.getDefault()获取的是AMS的代理端AMPActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);} else {s.onRebind(data.intent);ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);}ensureJitEnabled();} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}} catch (Exception e) {if (!mInstrumentation.onException(s, e)) {throw new RuntimeException("Unable to bind to service " + s+ " with " + data.intent + ": " + e.toString(), e);}}}}

该段代码,主要执行两个业务逻辑:

  • 执行目的端Service的onBind方法
  • 调用AMS的publishService方法,即将获取到Service中的匿名Binder实体通过Binder传递到AMS中

6.4.1 Services.onBind

	//Services.java@Nullablepublic abstract IBinder onBind(Intent intent);

  我曾跨过山河大海也穿过人山人海,终于来到了目标进程Service的生命周期onBind方法中,Service的onBind()是抽象方法, 所以大家创建Service子类时必须要覆写该方法, 返回IBinder对象, 也可以直接返回NULL.


6.5 AMP.publishService

	//ActivityManagerNative.javapublic void publishService(IBinder token,Intent intent, IBinder service) throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IActivityManager.descriptor);data.writeStrongBinder(token);intent.writeToParcel(data, 0);//将service.onbind的返回值传递给远端进程,这里写入的是bind目的服务端的Binder实体对象,经过Binder传输以后会转变成Binder代理端,并在AMS的内核进程中创建一个binder_ref引用	data.writeStrongBinder(service);mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);reply.readException();data.recycle();reply.recycle();}

  AMS和AT之间的交互很频繁啊,目的端进程和system_server进程之间通过IApplicationThread和IActivityManager来回的折腾着。这里的AMP是IActivityManager服务的代理端,然后借助BInder框架会调用到其服务端AMS,其调用过程大概如下:

AMP.publishService()---> 
BinderProxy.transact() --->
BpBinder.transact()--->
binder驱动传输--->
JavaBBinder.onTransact()--->
AMN.onTransact()--->
AMN.publishService()


七. 三进system_server进程

是时候展现真正的实力了,怎么从AMP到AMS的过程我们就不细说了,因为本篇博客里面已经有很多次这种操作了。我们直奔主题进入AMS。

7.1 AMS.publishService

	//AMS.javapublic void publishService(IBinder token, Intent intent, IBinder service) {if (intent != null && intent.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}synchronized(this) {if (!(token instanceof ServiceRecord)) {throw new IllegalArgumentException("Invalid service token");}//这里需要关注的是入参service为目的端IBinder的代理端mServices.publishServiceLocked((ServiceRecord)token, intent, service);}}

  远程服务的onBind()在目的服务端进程的返回值的IBinder类型是(Bn端), 在AMP.publishService()过程中经过data.writeStrongBinder(service)传递到底层驱动在system_server内核的binder_proc中创建上述IBinder实体的binder_ref引用, 再回到system_server进程中AMN.onTransact()中经过data.readStrongBinder()方法会获取该service所相对应的代理对象(Bp端).总而言之,此处的IBinder类型的service就是远程服务进程中的Bp端对象,并且此处的IBinder类型的service就是一个典型的匿名Binder,它并没有向servicemanager进程注册。


7.2 AS.publishServiceLocked

	//AS.java//注意此处的入参service为我们要启动服务端的代理端即(Bp)void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {final long origId = Binder.clearCallingIdentity();try {...if (r != null) {Intent.FilterComparison filter= new Intent.FilterComparison(intent);IntentBindRecord b = r.bindings.get(filter);if (b != null && !b.received) {b.binder = service;b.requested = true;b.received = true;for (int conni=r.connections.size()-1; conni>=0; conni--) {ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);for (int i=0; i<clist.size(); i++) {ConnectionRecord c = clist.get(i);if (!filter.equals(c.binding.intent.intent)) {...continue;}if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);try {//c.conn是指通往发起端进程的IServiceConnection.Stub.Proxy代理对象c.conn.connected(r.name, service);//} catch (Exception e) {...}}}}//解除章节5.1预先埋雷serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);}} finally {Binder.restoreCallingIdentity(origId);}}

  此处是关键,让我们先缓一缓。这里先取出ServiceRecord 中的ConnectionRecord列表,然后匹配对应的intent消息,如果匹配到则取出ConnectionRecord中的IServiceConnection实例对象conn,先换缓缓,这个ServiceRecord和ConnectionRecord又是啥时候创建的呢,这个就要回到章节3.3和章节3.3.1其中涉及到了ServiceRecord和ConnectionRecord创建。

还有一个核心就是这里的c.conn指代什么呢,关于此处就得回到2.3章节细品了,这里我们给出结论c.conn是指通往发起端进程的IServiceConnection.Stub.Proxy代理对象,其是经由Binder调用从发起端进程传递过来的,而我们此时通过该代理端通过Binder IPC调用到进入发起方进程的IServiceConnection.Stub实体中。关于该匿名Binder的传递路线如下所示:

发起端进程(IServiceConnection.Stub) ---> Binder驱动 ---->system_server进程(IServiceConnection.Stub.Proxy)

并且根据前面的分析可知,由于LoadedApk.ServiceDispatcher.InnerConnection 继承于IServiceConnection.Stub. 所以,接下来便由回到发起方进程中的InnerConnection对象,关于其调用过程这里简单阐述一下:

IServiceConnection.Stub.Proxy.connected()---> 
BinderProxy.transact() --->
BpBinder.transact()--->
binder驱动传输--->
JavaBBinder.onTransact()--->
IServiceConnection.Stub.onTransact()--->
InnerConnection.connected()


八. 重返发起端进程

  来返往复,曲折之路告一段落,我们终于返回了发起端进程。让我们接着来到发起端进程的LoadedApk.ServiceDispatcher.InnerConnection分析之,关于LoadedApk可以回过头到2.3.1章节看看其类图,再巩固巩固!

8.1 InnerConnection.connected

		//LoadedApk.java[InnerConnection]private static class InnerConnection extends IServiceConnection.Stub {final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;InnerConnection(LoadedApk.ServiceDispatcher sd) {mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);}public void connected(ComponentName name, IBinder service) throws RemoteException {LoadedApk.ServiceDispatcher sd = mDispatcher.get();if (sd != null) {//注意此处的sd为LoadedApk.ServiceDispatcher实例sd.connected(name, service);//详见章节8.2}}}

无需多言,直接进入下一关,打怪升级!


8.2 ServiceDispatcher.connected

		//LoadedApk.java[ServiceDispatcher]public void connected(ComponentName name, IBinder service) {if (mActivityThread != null) {//走此分支,mActivityThread不为null,并且这里的mActivityThread是一个Handler不是ActivtyThread,可以从章节2.2看到这个Handler是发起端主线程的Handler,接下来详见8.3mActivityThread.post(new RunConnection(name, service, 0));} else {doConnected(name, service);}}

无需多言,直接进入下一关,打怪升级!


8.3 RunConnection.run

		//LoadedApk.java[RunConnection]private final class RunConnection implements Runnable {RunConnection(ComponentName name, IBinder service, int command) {mName = name;mService = service;mCommand = command;}public void run() {if (mCommand == 0) {doConnected(mName, mService);} else if (mCommand == 1) {doDeath(mName, mService);}}final ComponentName mName;final IBinder mService;final int mCommand;}

  关于Handerl.post用法就不用多讲了,最后会触发RunConnection 的run方法执行,这里我们要注意doConnected方法的两个入参:

  • mName指代我们bind的目的端远程Service的组件名对象ComponentName 实例
  • mService指代我们bind的目的端远程端服务onBind()返回的返回的Binder实体的代理端对象

8.3 ServiceDispatcher.doConnected

		//LoadedApk.java[ServiceDispatcher]public void doConnected(ComponentName name, IBinder service) {ServiceDispatcher.ConnectionInfo old;ServiceDispatcher.ConnectionInfo info;synchronized (this) {if (mForgotten) {return;}old = mActiveConnections.get(name);if (old != null && old.binder == service) {return;}if (service != null) {info = new ConnectionInfo();info.binder = service;//创建死亡监听对象info.deathMonitor = new DeathMonitor(name, service);try {//建立死亡通知,绑定到目的端Service IBinder实体,详见8.4service.linkToDeath(info.deathMonitor, 0);mActiveConnections.put(name, info);} catch (RemoteException e) {mActiveConnections.remove(name);return;}} else {mActiveConnections.remove(name);}if (old != null) {old.binder.unlinkToDeath(old.deathMonitor, 0);}}if (old != null) {mConnection.onServiceDisconnected(name);}if (service != null) {//回调用户的onServiceConnected方法mConnection.onServiceConnected(name, service);}}

  到此处分析起来也比较简单了,主要执行了如下两个逻辑:

  • 先行创建死亡监听对象,也是内部类:LoadedApk.ServiceDispatcher.DeathMonitor,监听目的端服务Binder的情况
  • 调用用户传递过来的回调类ServiceConnection的onServiceConnected方法

8.5 DeathMonitor

		LoadedApk.java[DeathMonitor ]private final class DeathMonitor implements IBinder.DeathRecipient{DeathMonitor(ComponentName name, IBinder service) {mName = name;mService = service;}public void binderDied() {death(mName, mService);}final ComponentName mName;final IBinder mService;}public void doDeath(ComponentName name, IBinder service) {synchronized (this) {ConnectionInfo old = mActiveConnections.get(name);if (old == null || old.binder != service) {// Death for someone different than who we last// reported...  just ignore it.return;}mActiveConnections.remove(name);old.binder.unlinkToDeath(old.deathMonitor, 0);}mConnection.onServiceDisconnected(name);}

此处不在本文的分析的重点之内,总之这里会监听目的服务端的情况,如果其发生了异常则会通知到其bind的发起端!关于DeathMonitor强烈推荐参见博客Binder死亡通知机制之linkToDeath



总结

  至此,bindService已经基本分析完毕了,是时候重新拿去时序图来重新总结一把了,时序图如下:
在这里插入图片描述

  • 小伙伴门有没有发现在bindService中牵涉到了大量的Binder IPC,在Android的源码分析里真的是学好Android Binder很重要啊,并且在交互过程中牵涉到了好几处的匿名Binder,小伙们能找出来吗
  • 并且关于上述时序图中,红色代表Binder通信,绿色类代表发起端进程持有,黄色类代表system_server进程持有,蓝色代类代表目的端进程持有

从进程交互角度出发

  • 发起端进程: 首先通过bindService携带接口回调类ServiceConnection传递到LoadedApk中,接着在该类中通过getServiceDispatcher获取发起端进程的匿名Binder服务端(此为第一处匿名Binder),即LoadedApk.ServiceDispatcher.InnerConnection,该对象继承于IServiceConnection.Stub;再通过bindService调用到system_server进程
  • system_server进程: 依次通过scheduleCreateService和scheduleBindService方法, 远程调用到目的端进程进程(这里的前提是目的端进程已经创建,如果服务端进程还没有创建,则还牵涉到目的服务端进程的创建)
  • 目的端进程: 依次执行目的端Service的onCreate()和onBind()方法;,然后将onBind()方法的返回值IBinder(作为目的端进程匿名的Binder服务端,此处为第二处匿名Binder服务端),接着通过publishService将该匿名Binder服务端的代理端传递到system_server进程
  • system_server进程: 经过上述的一系列处理以后,利用IServiceConnection代理对象向发起端进程发起connected()调用, 并把target进程的onBind返回Binder对象的代理端传递到发起端进程
  • 发起端: 回调到onServiceConnection()方法, 该方法的第二个参数便是目的端进程的Binder代理端. 到此便成功地拿到了目的端进程的代理, 可以畅通无阻地进行RPC交互了

从匿名Binder传递角度出发

还记得我们一开篇就说的匿名/实名Binder吗,综管全篇不知小伙们发现没有其实bindService的实质就是匿名Binder跨进程的传递,当然也不全是毕竟还是目的端进程创建的一些操作呢!那么我们站在匿名Binder传递的角度来看看bindService的流程,其最最核心的一点就是目的端Service IBinder匿名BInder服务端怎么传递到发起端,其传递过程大概如下:

目的端Service(IBinder实体) ---->
Binder驱动传递 --->
system_server进程(IBinder实体对应的代理端) --->
发起端进程(IBinder实体对应的代理端)


写在最后

  经过此役我想小伙们一定意识到了Android Binder在Android的整个源码界的分量,想深入分析Android框架Binder是一道必须越过的坎。希望小伙们能越过Android Binder这道坎,在Android的道路上越走越远。好了,Android AMS框架之bindService源码实现详解到这里真的告一段落了,如果小伙们喜欢可以关注或者给我点个赞。最后特别鸣谢不在Android界的大神gityuan,因为是他给我了前进的力量!

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 思科hsrp

    ...

    2024/4/21 21:45:09
  2. 阿里云内部K8s、ECS、RDS、DevOps实战手册,了解阿里内部结构!

    有不少小伙伴&#xff0c;一直在后台问我要一些资料&#xff0c;同时&#xff0c;我也在想&#xff0c;其实大家谁都不缺资料&#xff0c;缺的是有实战价值&#xff0c;能够看了之后在实际的工作环境可以用起来的实战技术资料&#xff0c;而并非那些纸上谈兵的理论&#xff0c;…...

    2024/4/21 21:45:09
  3. 龟兔赛跑预测

    问题描述 话说这个世界上有各种各样的兔子和乌龟&#xff0c;但是研究发现&#xff0c;所有的兔子和乌龟都有一个共同的特点——喜欢赛跑。于是世界上各个角落都不断在发生着乌龟和兔子的比赛&#xff0c;小华对此很感兴趣&#xff0c;于是决定研究不同兔子和乌龟的赛跑。他发…...

    2024/4/21 21:45:07
  4. 吴恩达课后编程作业Course 1 - 神经网络和深度学习 - 第二周作业

    转载自&#xff1a;https://blog.csdn.net/u013733326/article/details/79639509 import numpy as np import h5py import matplotlib.pyplot as plt from lr_utils import load_dataset# sigmoid函数 def sigmoid(z):s 1 / (1 np.exp(-z))return s# 初始化参数w和b # 创建一…...

    2024/4/21 21:45:06
  5. promise对象的基本使用

    promise对象的基本使用 1.第一次使用 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document<…...

    2024/4/21 21:45:06
  6. 一文学会在Vue中使用Axios进行前后端通信

    这篇文章从昨天开始准备&#xff0c;到现在逐渐成型&#xff0c;主要是针对于在前后端分离项目&#xff0c;在Vue前端使用Axios进行通信&#xff0c;相当于Ajax一样&#xff0c;下面就写了一个登录的例子&#xff0c;来进行演示&#xff0c;同时在登陆到主页后会有一个列表数据…...

    2024/4/22 22:23:22
  7. JS的五种基本数据类型和一种引用数据类型

    JavaScript简介 JavaScript是一个编程语言&#xff0c;允许用户在浏览器页面上完成复杂的事情。 JS组成 ECMAScript DOM BOM 特点 执行环境&#xff1a;浏览器、NodeJS平台 不需要编译&#xff0c;解释性语言&#xff0c;脚本语言 弱类型语言 从上往下顺序解析执行 …...

    2024/4/21 21:45:03
  8. 【07】保留在字节码级别注解的应用场景

    &#xff08;1&#xff09;一个人只要自己不放弃自己&#xff0c;整个世界也不会放弃你. &#xff08;2&#xff09;天生我才必有大用 &#xff08;3&#xff09;不能忍受学习之苦就一定要忍受生活之苦&#xff0c;这是多么痛苦而深刻的领悟. &#xff08;4&#xff09;做难事必…...

    2024/4/21 21:45:02
  9. Java—压岁钱

    Java—压岁钱 Description SuShan过年要给孩子们发压岁钱喽&#xff0c;由于家里孩子很多&#xff0c;这可急坏了SuShan。你肯定以为她在担心钱不够&#xff0c;那你错了&#xff0c;她可是个有钱人儿&#xff0c;不差钱儿。她担心的是每个人分多少从而保证公平。 SuShan从瑞士…...

    2024/4/21 21:12:33
  10. 19.doGet()方法和doPost()方法有什么区别?

    doGet&#xff1a;GET方法会把键值对追加在请求的URL后面&#xff0c;因为URL对字符数目有限制&#xff0c;进而限制了用在客户端请求的参数值的数目&#xff0c;并且请求中的参数值是可见的&#xff0c;因此&#xff0c;敏感信息不能用这种方式传递。 doPost&#xff1a;POST…...

    2024/4/21 21:12:32
  11. 微信内置浏览器video标签自动全屏播放以及层级过高问题

    今天事用html5的<video>标签做微信内置浏览器视频播放页面时&#xff0c;需要在视频层级上显示类似弹幕的对话。发现部分机型&#xff08;华为P40 pro,oppo reno,iphone7p&#xff09;上点击播放视频后&#xff0c;视频自动全屏播放&#xff0c;页面其他元素无法显示。探…...

    2024/4/25 16:58:22
  12. 前端笔记00(前瞻)

    meta charset uft-8 前端工程师&#xff1a; 1.写页面 h5/css32.写人机交互 js/框架插件3.数据请求和绑定 ajax4.操作系统 linux5.HTTP和tcp网络知识 后端工程师&#xff1a; 1.业务逻辑处理2.数据处理&#xff08;数据库&#xff09;3.数据结构和算法&#xff08;服务器负…...

    2024/4/21 21:12:29
  13. Arrays中的asList注意的点以及解决方案

    前言&#xff1a; Arrays.asList&#xff08;&#xff09;返回ArrayList对象。但是这个ArrayList对象是Arrays内部类。 so方法返回的ArrayList对象的特性: 该方法不适用于基本数据类型&#xff08;byte,short,int,long,float,double,boolean&#xff09; 这个因为ArrayList本身…...

    2024/4/23 9:00:41
  14. 一文读懂Java线程池ThreadPoolExecutor的使用

    再实际项目中&#xff0c;我们可能会处理执行时间非常短但是数量非常大的请求&#xff0c;这个时候如果为每一个请求创建一个新的线程&#xff0c;可能会导致性能瓶颈。因为线程的创建和销毁的时间可能会大于任务执行的时间&#xff0c;系统性能就会大幅度降低。 JDK1.5提供了…...

    2024/4/21 21:45:02
  15. parallelStream的那些坑,不踩不知道,一踩吓一跳

    很多同学喜欢使用lambda表达式&#xff0c;它允许你定义短小精悍的函数&#xff0c;体现你高超的编码水平。当然&#xff0c;这个功能在某些以代码行数来衡量工作量的公司来说&#xff0c;就比较吃亏一些。 比如下面的代码片段&#xff0c;让人阅读的时候就像是读诗一样。但是…...

    2024/4/21 21:45:01
  16. Leetcode刷题之 【移除元素】

    1 题目 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新…...

    2024/4/24 11:41:29
  17. SeleniumLibrary+Robotframework项目实例(封装测试关键字)

    封装测试关键字1、创建Selenium关键字库文件Rescouce文件&#xff08;SeleniumResource.txt&#xff09;2、创建通用的业务逻辑Rescouce文件&#xff08;CommonResource.txt&#xff09;3、创建测试用例套件&#xff1a;学习笔记.txt如果大家在自己本地的环境上面也做到了这一步…...

    2024/4/21 18:19:24
  18. 完美解决github访问速度慢

    1.修改本地hosts文件&#xff1a; windows系统的hosts文件的位置如下&#xff1a;C:\Windows\System32\drivers\etc\hosts mac/linux系统的hosts文件的位置如下&#xff1a;/etc/hosts 2.增加http://github.global.ssl.fastly.net和http://github.com的映射&#xff1a; 获取…...

    2024/4/21 21:44:57
  19. Maven安装配置

    目录 一、Maven是什么 二、Maven安装及配置 1. Maven本地安装 2. Maven环境变量的配置 3. Maven 配置 repository 三、idea中创建meven webapp及配置 1. 配置 settings 2. 创建 meven webapp 一、Maven是什么 1. Maven是Apache下的一个纯java开发的开源项目&#xff0c;它是…...

    2024/4/21 21:44:55
  20. 写给想要做自动化测试的人

    接触了不少同行&#xff0c;由于他们之前一直做手工测试&#xff0c;现在很迫切希望做自动化测试&#xff0c;其中不乏工作5年以上的人。 本人从事软件自动化测试已经近5年&#xff0c;从server端到web端&#xff0c;从API到mobile&#xff0c;切身体会到自动化带来的好处与痛楚…...

    2024/4/21 21:44:54

最新文章

  1. 【Qt】.ui文件转.h文件

    1、打开qt命令行 2、转换 uic -o ui.h mainwindow.ui...

    2024/4/25 21:34:37
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. Oracle备份和还原的几种方式

    1、使用数据泵方式 exp demo/demoorcl buffer1024 filed&#xff1a;\back.dmp fully demo&#xff1a;用户名、密码 buffer: 缓存大小 file: 具体的备份文件地址 full: 是否导出全部文件 ignore: 忽略错误&#xff0c;如果表已经存在&#xff0c;则也是覆盖 exp demo/de…...

    2024/4/23 6:25:47
  4. npm常用命令技巧

    NPM (Node Package Manager) 是 JavaScript 的包管理工具&#xff0c;广泛用于管理项目中的依赖。无论是前端项目还是Node.js后端项目&#xff0c;NPM 都扮演着重要的角色。本文将介绍 NPM 中常用的几个命令&#xff0c;并提供相应的代码示例。 1. 初始化项目&#xff1a;npm …...

    2024/4/24 11:28:39
  5. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义&#xff1a;dp[i][j]表示当背包容量为j&#xff0c;用前i个物品是否正好可以将背包填满&#xff…...

    2024/4/25 1:03:45
  6. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/4/25 21:14:51
  7. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon&#xff0c;直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件&#xff0c;我们讨论Spring负载均衡以Spring Cloud2020之后版本为主&#xff0c;学习Spring Cloud LoadBalance&#xff0c;暂不讨论Ribbon…...

    2024/4/24 11:04:21
  8. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中&#xff0c;周界防范意义重大&#xff0c;对园区的安全起到重要的作用。常规的安防方式是采用人员巡查&#xff0c;人力投入成本大而且效率低。周界一旦被破坏或入侵&#xff0c;会影响园区人员和资产安全&#xff0c;…...

    2024/4/25 10:01:46
  9. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…...

    2024/4/25 16:50:01
  10. 【Objective-C】Objective-C汇总

    方法定义 参考&#xff1a;https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/4/25 13:02:58
  11. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    &#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格…...

    2024/4/24 9:58:43
  12. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/4/24 11:04:19
  13. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕&#xff0c;各大品牌纷纷晒出优异的成绩单&#xff0c;摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称&#xff0c;在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁&#xff0c;多个平台数据都表现出极度异常…...

    2024/4/25 17:43:17
  14. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令&#xff0c;这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/4/25 17:43:00
  15. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b&#xff0c;我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边&#xff0c; b b b 同理&#xff0c;则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/4/25 13:00:31
  16. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息&#xff0c;并安装一些有助于配置官方 NGINX 软件包仓库的软件包&#xff1a; apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/4/25 17:42:40
  17. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限&#xff08;ROW FORMAT&#xff09;配置标准HQL为&#xff1a; ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/4/25 3:28:56
  18. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…...

    2024/4/25 3:39:58
  19. --max-old-space-size=8192报错

    vue项目运行时&#xff0c;如果经常运行慢&#xff0c;崩溃停止服务&#xff0c;报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中&#xff0c;通过JavaScript使用内存时只能使用部分内存&#xff08;64位系统&…...

    2024/4/25 13:40:45
  20. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…...

    2024/4/25 13:01:30
  21. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧&#xff01; 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定&#xff0c;每一个构造函数都有一个 prototype 属性&#xff0c;指向另一个对象&#xff0c;所以我们也称为原型对象…...

    2024/4/25 15:31:26
  22. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错&#xff0c;但存在一个缺陷&#xff1a;无法禁止通过实例化多个对象来创建多名总统&#xff1a; President One, Two, Three; 由于复制构造函数是私有的&#xff0c;其中每个对象都是不可复制的&#xff0c;但您的目…...

    2024/4/25 17:31:15
  23. python django 小程序图书借阅源码

    开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…...

    2024/4/25 13:22:53
  24. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/4/25 1:03:22
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57