大家好,又见面了,我是你们的朋友全栈君。
接着上一篇,本文就解决《篇Binder详解》末尾抛出的问题,也就是如下的问题:
我们客户端(即MainActivity)接受远程对象是在自己重写的ServiceConnection的onServiceConnected()方法中接收的,那么系统是何时对ServiceConnection的onServiceConnect()方法进行回调的呢?这一切要从我们调用bindService()说起。
bindService()的发源地
我们知道,在Activity要启动Service有两种方式,一种就是startService(),还有一种就是bindService(),两者的使用场景不一样,启动的参数也不同,前者是当Service和当前组件在同一个进程时使用,后者是当Service和当前组件不在同一个进程时使用,而且bindService()还需要传递一个ServiceConnection实例。虽然我们在Activity中可以直接使用bindService(),但是bindService()并不是Activity的方法,而是Context的方法,我们都知道,四大组件的超类都是Context,只不过各组件的继承链不一样,比如Activity的继承链是:Activity→ContextThemeWrapper→ContextWrapper→Context。此外Context中的bindService()只是一个abstract方法,真正的实现是在ContextImpl中,而且ContextWrap也把bindService()给包装了一下,包装的目的就是去调用ContextImpl中真正实现了的bindService(),所以我们在Activity中调用bindService()的时候最后实际上是调用了ContextImpl的bindService()。但是在Activity的继承链中我们似乎并没有发现ContextImpl的影子?确实,Activity并没有直接继承ContextImpl,ContextImpl是一个隐藏的类。那么Activity是如何去使用到ContextImpl的bindService的()?我们可以看到在ContextWrap中有一个变量叫做mBase,此mBase就是一个Context实例,确切说是一个ContextImpl实例,而且ContextWrapper的bindService()就是一行代码:mBase.bindService()来实现调用ContextImpl的bindService()的。那么mBase他是在什么时候被赋值的?那就是在Activity被创建时调用其attach()的时候传递过来的,当ActivityThread在创建Activity的时候就会调用ContextImpl的createActivityContext()方法new一个ContextImpl实例,并调用Activity的atatch()方法为mBase赋值。说了这么多,其实就是想说两点,第一bindService()是ContextImpl中的方法,第二既然Activity没有直接实现ContextImpl,那为什么我们可以直接调用bindService()。
bindService()所做的一切
现在我们开始对bindService()进行分析,通过上面的介绍,那我们的分析自然是从ContextImpl的bindService()开始了,上代码:
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
//这个方法仅仅是打印日志用于警告,忽略
warnIfCallingFromSystemProcess();
//调用了bindServiceCommon()看下面
return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}
/**
* 启动远程Service
* @param service Intent指定要启动的Service
* @param conn ServiceConnection,用于Service连接后就通过这个对象来回调其方法返回远程的Binder对象
* @param flags
* @param user
* @return
*/
private boolean bindServiceCommon(Intent service, ServiceConnection conn,
int flags, UserHandle user) {
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
//mPackageInfo是一个LoadApk实例,此方法用于返回一个IServiceConnection实例也就是InnerConnection类型的对象
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
//获取令牌,表示要bindService的身份,这里就是用来表明Activity的身份
IBinder token = getActivityToken();
if (token == null
&& (flags & BIND_AUTO_CREATE) == 0
&& mPackageInfo != null
&& mPackageInfo.getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
service.prepareToLeaveProcess();
//AMS调用bindService()
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
if (res < 0) {
throw new SecurityException("Not allowed to bind to service "
+ service);
}
return res != 0;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
可以看到在bindServiceCommon()方法中,会首先通过mPackageInfo.getServiceDispatcher()获取一个IServiceConnection实例,这个mPackageInfo是一个LoadApk实例,我们去看看getServiceDispatcher()是个什么方法:
/**
* 获取一个IServiceConnection实例,
* @param c
* @param context
* @param handler
* @param flags
* @return
*/
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
//ServiceDispatcher是LoadedApk的内部类,在此方法中就会创建一个InnerConnection实例
sd = new ServiceDispatcher(c, context, handler, flags);
//如果本地还没有此链接就创建新的连接并保存到数组
if (map == null) {
map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
//返回InnerConnection实例mIServiceConnection
return sd.getIServiceConnection();
}
}
首先会从本地map中读取是否已经存在ServiceDispatcher实例,如果没有就会去创建一个,注意ServiceDispatcher是LoadedApk的内部类,而ServiceDispatcher里还有一个内部类InnerConnection,这个
InnerConnection是很重要的,它就是继承了IServiceConnection.Stub的类。在ServiceDispatcher的构造方法中就会根据我们bindService()时传入的ServiceConnection实例创建InnerConnection,最后在方法的结尾,调用ServiceDispatcher实例返回一个IServiceConnection实例。这里再强调一下,
IServiceConnection就相当于上一篇文章的CustomBinder,而InnerConnection相当于TestService的MyBinder!我们可以看到IServiceConnection的结构和CustomBinder的结构一模样的。而且在IServiceConnection.Stub中有一个抽象的connected()方法,此方法就是在InnerConnection中实现的,而InnerConnection中的connected()方法最终会调用到我们在调用bindService()时传入的ServiceConnection的onServiceConnected()方法,从而告知客户端服务已连接,并且返回一个远程对象。那么InnerConnection的connected()方法何时被调用的呢?
上面讲了一大堆获取了IServiceConnection的过程,我们回到ContextImpl的bindServiceCommon()方法中去,在获取IServiceConnection实例后,就又通过跨进程通信让ActivityManagerService调用bindService()方法,也就是通过下面一段代码:
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
关于他是如何跨进程通信到ActivityManagerService的,请看另一篇文章
http://blog.csdn.net/u012481172/article/details/49658633; 好,接下来进入到AMS的bindService(),看下AMS的bindService()方法如下:
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
synchronized(this) {
//这个mServices是一个ActivityService实例
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
}
忽略掉不重要的信息,直接看方法的最后一行,发现是调用了ActivityService的bindServiceLocked()方法,好我们去看看这个方法:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
//根据调用者来获取正在运行的当前app的所有进程信息
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
//如果没有找到进程记录就说明进程跪了,抛出异常
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when binding service " + service);
}
ActivityRecord activity = null;
if (token != null) {
//根据token获取ActivityRecord,也就是获取调用bindService()的那个Activity
//这一步就是从任务栈中去查找Activity
activity = ActivityRecord.isInStackLocked(token);
//如果没有找到就会绑定失败
if (activity == null) {
return 0;
}
}
int clientLabel = 0;
PendingIntent clientIntent = null;
final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
//这里根据Intent获取Service
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
ServiceRecord s = res.record;
final long origId = Binder.clearCallingIdentity();
try {
if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
}
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
}
mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
s.appInfo.uid, s.name, s.processName);
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
//创建一个Connection记录,Service的连接
//ConnectionRecord的解释是:Description of a single binding to a service
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);
b.connections.add(c);
if (activity != null) {
if (activity.connections == null) {
activity.connections = new HashSet<ConnectionRecord>();
}
//把ConnectionRecord保存到ActivityRecord中也就是云行的Activity,
activity.connections.add(c);
}
if (s.app != null && b.intent.received) {
try {
//这里开始就是调用了InnerConnection的connected()方法,这种调用是跨进程的,即它调用时会首先调用IServiceConnection.Stub.Proxy的connected()方法
//这个流程在上一篇文章已经讲过了,不再赘述,总之,它最后就调用了我们的MainActivity中定义的MyServiceConnection的onServiceConnected()方法,这个b.intent.binder就是远程的对象即IServiceConnection对象。
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
其实这个方法很长,已经省了不少代码了,可以看到它里面调用了c.conn.connected(s.name,b.intent.binder),这就是回调了MainActivity中我们定义的MyServiceConnection的onServiceConnected()方法了。至此,整个binderService()的过程已经讲完了,虽然分析得还有点粗糙,今后会慢慢研究慢慢体会了~~
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132819.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...