背景
目前Android的跨进程通讯主要就是采用Binder机制进行IPC通讯的,在实际的开发中,在跨进程的访问中我们也优先选用AIDL进行通讯,因为它相比传统IPC基于C/S 架构易用性高,只需要拷贝一次,所以我们有必要对AIDL进行详细的了解。
1.Binder优势
1.从性能的角度看,binder是只拷贝一次的 Socket 需要拷贝两次,内存共享需要拷贝0次 仅次内存拷贝
2.从安全角度上来看,Android可以通过binde分配自己的Uid是鉴别的重要标志手段,服务端可以根据uid进行控制安全性
3.从语言角度来说Binder比较符合面向对象的思想,也模糊了内部具体实现
1、简单使用 举个栗子
比如我们要通过跨进程实现一个录屏功能,我们不需要知道另外一个服务是怎么实现录屏的功能的,我们只需要定义三个接口,就可以实现调用另外一个服务的方法,也就是跟另外一个服务通讯,命令它去开始录屏或者停止录屏,录屏完保存在一个路径中,这样客户端只需要实现用户界面变化即可。
定义AIDL接口,三个接口 分别在客户端和服务端各自放一份,然后构建一下,在build 下的source就会生成一些模板代码 ,带下看模板代码
interface IScreenRecord {
boolean isRecording();
boolean startRecord(String path, boolean isRecordAudio, boolean isRecordMicrophone);
boolean stopRecord();
}
Client 客户端关键代码
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mScreenRecord = IScreenRecord.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mScreenRecord = null;
}
};
mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)
第一个 是创建了mConnection 它能让我们拿到binder 对象, 然后我们开启bindService 服务,同时会把mConnection传入进去。
Service服务端关键代码
private IScreenRecord.Stub mStub = new IScreenRecord.Stub() {
@Override
public boolean isRecording() throws RemoteException {
return mRecorder.isRecording();
}
@Override
public boolean startRecord(String path, boolean isRecordAudio,
boolean isRecordMicrophone) throws RemoteException {
return mRecorder.startRecord(path, true, isRecordAudio, isRecordMicrophone,
getVideoWidth(RESOLUTION_DEFAULT), getVideoHeight(RESOLUTION_DEFAULT), mRecordCallback);
}
@Override
public boolean stopRecord() throws RemoteException {
return mRecorder.stopRecord();
}
};
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
服务端 这边继承Service 重写 onBind 将 Stub返回 回去, 服务端在上边构造了一个Stub ,里面实现了AIDL接口里面定义的方法,也就是录屏的真正实现。
IScreenRecord.Stub是从ADIL接口文件中 在build的时候自动构建生产的文件,它默认在build/Source/AIDL下面,我把我的粘贴出来在下面。
这样我们就完成了AIDL跨进程通讯
package com.ifpdos.screenrecorder;
public interface IScreenRecord extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.ifpdos.screenrecorder.IScreenRecord {
private static final java.lang.String DESCRIPTOR = "com.ifpdos.screenrecorder.IScreenRecord"
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ifpdos.screenrecorder.IScreenRecord interface,
* generating a proxy if needed.
*/
public static com.ifpdos.screenrecorder.IScreenRecord asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.ifpdos.screenrecorder.IScreenRecord))) {
return ((com.ifpdos.screenrecorder.IScreenRecord) iin);
}
return new com.ifpdos.screenrecorder.IScreenRecord.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public 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_isRecording: {
data.enforceInterface(DESCRIPTOR);
boolean _result = this.isRecording();
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return true;
}
case TRANSACTION_startRecord: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
boolean _arg1;
_arg1 = (0 != data.readInt());
boolean _arg2;
_arg2 = (0 != data.readInt());
boolean _result = this.startRecord(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return true;
}
case TRANSACTION_stopRecord: {
data.enforceInterface(DESCRIPTOR);
boolean _result = this.stopRecord();
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.ifpdos.screenrecorder.IScreenRecord {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public boolean isRecording() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_isRecording, _data, _reply, 0);
_reply.readException();
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public boolean startRecord(java.lang.String path, boolean isRecordAudio, boolean isRecordMicrophone) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(path);
_data.writeInt(((isRecordAudio) ? (1) : (0)));
_data.writeInt(((isRecordMicrophone) ? (1) : (0)));
mRemote.transact(Stub.TRANSACTION_startRecord, _data, _reply, 0);
_reply.readException();
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public boolean stopRecord() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_stopRecord, _data, _reply, 0);
_reply.readException();
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_isRecording = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_startRecord = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_stopRecord = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
public boolean isRecording() throws android.os.RemoteException;
public boolean startRecord(java.lang.String path, boolean isRecordAudio, boolean isRecordMicrophone) throws android.os.RemoteException;
public boolean stopRecord() throws android.os.RemoteException;
}
2.什么是Binder
其实无数人一直在纠结什么是Binder,网上也是一堆书再介绍各种什么是binder 它又有多牛逼,能跨进程通讯 等等,我觉得这样子觉得binder永远学不会。其实Binder就是一个普通的类,而且才500行左右的代码,它之所以重要,是因为他是数据输出和数据输入的中心,就像高铁的站台一样,有些人需要通过站台下车,有些人需要靠站台上车。
Binder中有个核心的方法 onTransact
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (code == INTERFACE_TRANSACTION) {
reply.writeString(getInterfaceDescriptor());
return true;
} else if (code == DUMP_TRANSACTION) {
ParcelFileDescriptor fd = data.readFileDescriptor();
String[] args = data.readStringArray();
if (fd != null) {
try {
dump(fd.getFileDescriptor(), args);
} finally {
try {
fd.close();
} catch (IOException e) {
// swallowed, not propagated back to the caller
}
}
}
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
return true;
}
return false;
}
我们追看一下 关键的两个方法 writeString和 readStringArray
public final void writeString(String val) {
nativeWriteString(mNativePtr, val);
}
public final String readString() {
return nativeReadString(mNativePtr);
}
其实我们发现这两个方法都是native方法,整个传输逻辑都是在C层面进行的,看到这里,我们只需要明白Binder就是一个收发的机器即可。
3.一切是怎么开始的?
我们先把目光放在客户端中,客户端是主动发起连接的人,首先我们分析客户端是怎么拿到Binder的,客户端其实没有直接拿到Binder 他是拿到一个代理对象proxy, 也叫代理,我们在成成的模板文件中可以看到他持有binder对象。
在这里可能已经有点绕了。
我首先介绍一个Stub 和 Proxy的关系, Stub就类似一个下车的车站,Proxy就类似一个上车的车站,我们收信息通过 Stub 发送信息通过 Proxy
然后我们看下 客户端做了什么准备工作
IScreenRecord.Stub.asInterface(service);
客户端就做了一个这个准备工作然后就开始开启服务将ConnService 传递进去了,我们看下一这个准备工作作用
public static com.ifpdos.screenrecorder.IScreenRecord asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.ifpdos.screenrecorder.IScreenRecord))) {
return ((com.ifpdos.screenrecorder.IScreenRecord) iin);
}
return new com.ifpdos.screenrecorder.IScreenRecord.Stub.Proxy(obj);
}
这个准备工作的作用就是将服务端传过来的binder 构造成了一个Proxy对象,让Proxy 有发送跨进程通讯的能力
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.ifpdos.screenrecorder.IScreenRecord))) {
return ((com.ifpdos.screenrecorder.IScreenRecord) iin);
}
上边这一段的意思是如果要连接的进程其实是在本地进程也就是同个进程的话 压根就不用跨进程通讯了,直接调用就好了
我们关注点主要是最后一句话,将服务端的binder传给了Proxy 让proxy客户发送数据另外一个进程,那些发送的过程就是将发送的数据组装 然后通过binder的native方法发送我们就不细看了。
好了客户端的准备工作做完,小伙伴们最想知道的其实是服务端为什么可以把binder传给客户端?是怎么传给客户端的。
好的 那我们就看一下客户端在启动服务的时候,是会把ServiceConnection传给startService开启服务的,我们追一下这部分的代码。
Context.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)
我们都知道 Context的实现具体是在ContextImpl中的 我们就直接去ContextImpl看到
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}
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) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
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();
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
return res != 0;
} catch (RemoteException e) {
return false;
}
}
我们看到 ServiceConnection 其实是被封装成了 IServiceConnection
然后执行了一行比较关键的
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, user.getIdentifier());
这个是比较关键难懂的一句话,其实这句话的意思是我要开启这个进程并且开启这个服务,而且会把ServiceConnection 传递过去。
我们好好分析这句话
ActivityManagerNative 其实也是个binder 也就是说系统他在开启服务的时候,也是玩AIDL进程间的通讯的 他调用对象就是AMS 它也是个服务。
ActivityManagerNative.getDefault() 他其实是拿到一个对AMS 的proxy 我们看代码
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
他从缓存中拿到IBinder 然后也是熟悉的操作asInterface(b);拿到了一个ActivityManagerProxy对象
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);
}
我们看下ActivityManagerProxy的部分代码
class ActivityManagerProxy implements IActivityManager
{
public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType, IServiceConnection connection,
int flags, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeStrongBinder(token);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(connection.asBinder());
data.writeInt(flags);
data.writeInt(userId);
mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
data.recycle();
reply.recycle();
return res;
}
}
我们在看一下AMS中 bindService的实现,最后调到bindServiceLocked
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
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) {
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
}
int clientLabel = 0;
PendingIntent clientIntent = null;
final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID;
if (isCallerSystem) {
// Hacky kind of thing -- allow system stuff to tell us
// what they are, so we can report this elsewhere for
// others to know why certain services are running.
service.setDefusable(true);
clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT);
if (clientIntent != null) {
clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
if (clientLabel != 0) {
// There are no useful extras in the intent, trash them.
// System code calling with this stuff just needs to know
// this will happen.
service = service.cloneFilter();
}
}
}
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
mAm.enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"BIND_TREAT_LIKE_ACTIVITY");
}
if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) {
throw new SecurityException(
"Non-system caller " + caller + " (pid=" + Binder.getCallingPid()
+ ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service);
}
final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
if (res == null) {
return 0;
}
if (res.record == null) {
return -1;
}
ServiceRecord s = res.record;
boolean permissionsReviewRequired = false;
// If permissions need a review before any of the app components can run,
// we schedule binding to the service but do not start its process, then
// we launch a review activity to which is passed a callback to invoke
// when done to start the bound service's process to completing the binding.
if (Build.PERMISSIONS_REVIEW_REQUIRED) {
if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
s.packageName, s.userId)) {
permissionsReviewRequired = true;
// Show a permission review UI only for binding from a foreground app
if (!callerFg) {
Slog.w(TAG, "u" + s.userId + " Binding to a service in package"
+ s.packageName + " requires a permissions review");
return 0;
}
final ServiceRecord serviceRecord = s;
final Intent serviceIntent = service;
RemoteCallback callback = new RemoteCallback(
new RemoteCallback.OnResultListener() {
@Override
public void onResult(Bundle result) {
synchronized(mAm) {
final long identity = Binder.clearCallingIdentity();
try {
if (!mPendingServices.contains(serviceRecord)) {
return;
}
// If there is still a pending record, then the service
// binding request is still valid, so hook them up. We
// proceed only if the caller cleared the review requirement
// otherwise we unbind because the user didn't approve.
if (!mAm.getPackageManagerInternalLocked()
.isPermissionsReviewRequired(
serviceRecord.packageName,
serviceRecord.userId)) {
try {
bringUpServiceLocked(serviceRecord,
serviceIntent.getFlags(),
callerFg, false, false);
} catch (RemoteException e) {
/* ignore - local call */
}
} else {
unbindServiceLocked(connection);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
});
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName);
intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
if (DEBUG_PERMISSIONS_REVIEW) {
Slog.i(TAG, "u" + s.userId + " Launching permission review for package "
+ s.packageName);
}
mAm.mHandler.post(new Runnable() {
@Override
public void run() {
mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
}
});
}
}
final long origId = Binder.clearCallingIdentity();
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) {
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);
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>();
}
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();
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
}
if (s.app != null) {
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.
mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
|| s.app.treatLikeActivity, b.client);
mAm.updateOomAdjLocked(s.app);
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
+ ": received=" + b.intent.received
+ " apps=" + b.intent.apps.size()
+ " doRebind=" + b.intent.doRebind);
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
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);
}
// If this is the first app connected back to this binding,
// and the service had previously asked to be told when
// rebound, then do so.
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
getServiceMap(s.userId).ensureNotStartingBackground(s);
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
我们看到AMS对开启服务的实现还是比较复杂的,我们先不看前面那么多逻辑看到最后
c.conn.connected(s.name, b.intent.binder);
其实最后一句话这句话就是解答小伙伴们的问题的,服务端是怎么把binder转给客户端的,是AMS持有SeriviceConnect 对象和服务端的binder对象,然后回调了SeriviceConnect 的connected方法,把binder对象传给客户端。把整个流程连接起来了。
事情还没有完,刨根问底的小伙伴们肯定是要问题这个对象是怎么组装的。
c.conn.connected是从之前封装过的 ConnectionRecord对象的
final IServiceConnection conn; 字段,调用connected方法没有问题 小伙伴都没有问题
s.name 是指定的服务名也问题不大,不再深入。
主要是想知道ams是怎么拿到服务的binder的对象
b.intent.binder 中的 b是指 AppBindRecord
intent 指的是IntentBindRecord
binder 是IntentBindRecord 里面的 binder
在bindServiceLocked方法中 当进程已经起来服务已经起来,前面会判断各种情况,比如进程没有起来,服务没开启等情况都做了处理,我们就不再详细看,我们只看最后一种情况
requestServiceBindingLocked
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
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);
}
// If this is the first app connected back to this binding,
// and the service had previously asked to be told when
// rebound, then do so.
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
}
我们看一下 requestServiceBindingLocked
private final boolean requestServiceBindingLocked(ServiceRecord r,
IntentBindRecord i, boolean execInFg, boolean rebind) {
if (r.app == null || r.app.thread == null) {
// If service is not currently running, can't yet bind.
return false;
}
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (RemoteException e) {
if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
return false;
}
}
return true;
}
我们看下 r.app.thread.scheduleBindService这个方法 其实thread 就是ActivityThread 我们直接去到
public final void scheduleBindService(IBinder token, Intent intent, boolean rebind, int processState) {
this.updateProcessState(processState, false);
ActivityThread.BindServiceData s = new ActivityThread.BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
ActivityThread.this.sendMessage(121, s);
}
发现用的是handler的消息发送机制 直接定位到处理信息的地方
case 121:
Trace.traceBegin(64L, "serviceBind");
ActivityThread.this.handleBindService((ActivityThread.BindServiceData)msg.obj);
Trace.traceEnd(64L);
break;
private void handleBindService(ActivityThread.BindServiceData data) {
Service s = (Service)this.mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 0, 0, 0);
}
this.ensureJitEnabled();
} catch (RemoteException var4) {
}
} catch (Exception var5) {
if (!this.mInstrumentation.onException(s, var5)) {
throw new RuntimeException("Unable to bind to service " + s + " with " + data.intent + ": " + var5.toString(), var5);
}
}
}
}
发现它是从之前创建的对象中获取了服务端的对象,然后直接调用onBind方法,最后通过ActivityManagerNative 又走了一次AIDL 将binder返回给了 AMS
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);
AMS拿到binder方法如下
public void publishService(IBinder token, Intent intent, IBinder service) {
// Refuse possible leaked file descriptors
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");
}
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}
最后调用了publishServiceLocked方法
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
+ " " + intent + ": " + service);
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)) {
if (DEBUG_SERVICE) Slog.v(
TAG, "Not publishing to: " + c);
if (DEBUG_SERVICE) Slog.v(
TAG, "Bound intent: " + c.binding.intent.intent);
if (DEBUG_SERVICE) Slog.v(
TAG, "Published intent: " + intent);
continue;
}
if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
try {
c.conn.connected(r.name, service);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + r.name +
" to connection " + c.conn.asBinder() +
" (in " + c.binding.client.processName + ")", e);
}
}
}
}
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
我们看到了关键代码 c.conn.connected(r.name, service); 这个客户端就可以拿到binder对象,进行跟服务端的通讯了。
哈哈哈 分析了一大波源码之后,我们终于知道几个点,一个是系统之间其实也是很多个服务组成的,他们也是频繁的进行AIDL通讯,来进行工作,Binder其实没有什么神秘之处,它就是个搭载了Native方法的类。在bindService的时候系统通过一系列判断将服务唤起,然后将服务的binder传递给客户端,客户端就可以跟服务端通讯了。