Android进程间的通信 - IPC(机制)Binder的原理
1. 概述
当初决定分享内涵段子的这个项目我有些担心,担心很多东西心里虽然有了轮廓和细节。但是如果涉及到的东西比较多,那么就有可能没办法去讲太多的细节,况且某些哥们还不会C和C++,所以如果的确觉得IPC这方面比较难可以多去找找这方面的文章看看。这里我们就从三个方面去讲解:
1.1:进程间通信的一个小事例;
1.2:应用层的具体流程分析;
1.3:google源码层的具体走向;
所有分享大纲:2017Android进阶之路与你同行
视频讲解地址:http://pan.baidu.com/s/1pLrvFj9
2. 进程间通信的一个小事例
为什么会出现IPC这个概念,Android操作系统为了确保进程之间不会产生相互干扰,就是为了你挂了不会影响我,所以采用了进程隔离的机制,即为每个进程分配独立虚拟地址空间,进程之间感觉不到彼此的存在,感觉自己仿佛占用整个内存空间。这样保证了进程的数据安全,但是必然存在另外的问题,那就是进程间通信,进程不可能完全独立运行,有时候需要相互通信获取别的进程的运行结果等,因此需要想办法解决进程间通信的问题,所以就出现了IPC这个概念。其他就不说了,假设我A应用要去B里面应用获取的数据该怎么办,接下来我们就写这么一个实例,这里就涉及到两个单独的应用,我们就把A应用作为客户端,B应用作为服务端。
2.1 应用服务端:
// 编写aidl文件
interface UserCalcAIDL {
String getUserName();
String getUserPassword();
}
上面是编写aidl文件,类似于interface,这里我就不介绍aidl了,到后面再去介绍。然后我们在项目中新建一个服务Service,代码如下:
public class UserService extends Service{
private static final String TAG = "server";
// 应用间通信进行绑定
public IBinder onBind(Intent t)
{
Log.e(TAG, "onBind");
return mBinder;
}
// 应用间解绑
public boolean onUnbind(Intent intent)
{
Log.e(TAG, "onUnbind");
return super.onUnbind(intent);
}
// mBinder 的实例
private final UserCalcAIDL.Stub mBinder = new UserCalcAIDL.Stub()
{
@Override
public String getUserName() throws RemoteException {
return "Darren@163.com";
}
@Override
public String getUserPasword() throws RemoteException {
return "940223";
}
};
}
最后我们在AndroidManifest中注册配置Service,然后在主Activity中运行该服务,那么服务端代码就算告成了。
<service android:name="com.example.study.aidl.UserService" >
<intent-filter>
<action android:name="com.study.aidl.user" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
2.2 应用客户端:
另外再建一个工程,创建与服务端同样的aidl,然后再创建一个布局,里面包含4个按钮,分别为绑定服务,解除绑定,获取用户名,获取用户密码。布局代码我就不贴了,直接上Activity里面的代码:
public class MainActivity extends Activity{
private UserCalcAIDL mCalcAidl;
private ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("client", "onServiceDisconnected");
mCalcAidl = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("client", "onServiceConnected");
mCalcAidl = UserCalcAIDL.Stub.asInterface(service);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 点击BindService按钮时调用
* @param view
*/
public void bindService(View view) {
Intent intent = new Intent();
intent.setAction("com.study.aidl.user");
// 在Android 5.0之后google出于安全的角度禁止了隐式声明Intent来启动Service.也禁止使用Intent filter.否则就会抛个异常出来
intent.setPackage("com.study.aidl");
bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
}
/**
* 点击unBindService按钮时调用
* @param view
*/
public void unbindService(View view) {
if(mCalcAidl != null){
unbindService(mServiceConn);
}
}
/**
* 获取用户密码
* @param view
*/
public void getUserPassword(View view) throws Exception {
if (mCalcAidl != null) {
String userPassword = mCalcAidl.getUserPasword();
Toast.makeText(this, "用户密码:"+userPassword, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "服务器未绑定或被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT)
.show();
}
}
/**
* 获取用户名
* @param view
*/
public void getUserName(View view) throws Exception {
if (mCalcAidl != null) {
String userName = mCalcAidl.getUserName();
Toast.makeText(this, "用户名:"+userName, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "服务器未绑定或被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT)
.show();
}
}
}
2.3 运行的效果:
首先我们先把服务端B应用运行起来,然后我们把客户端A应用运行起来测试一下效果试试:
小事例运行效果3. 应用层的具体流程分析
接下来我们就来看一下,跨进程间的通信在应用层的具体走向,有请aidl出场,我们在客户端会通过
bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE)去请求连接,在onServiceConnected()通过IBinder获取UserCalcAIDL实例:
private UserCalcAIDL mCalcAidl;
private ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("client", "onServiceConnected");
// 请求连接后会调用该方法,通过IBinder service获取UserCalcAIDL实例mCalcAidl
mCalcAidl = UserCalcAIDL.Stub.asInterface(service);
}
};
/**
* 点击BindService按钮时调用
* @param view
*/
public void bindService(View view) {
Intent intent = new Intent();
intent.setAction("com.study.aidl.user");
// 在Android 5.0之后google出于安全的角度禁止了隐式声明Intent来启动Service.也禁止使用Intent filter.否则就会抛个异常出来
intent.setPackage("com.study.aidl");
// 请求绑定连接 服务端
bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
}
客户端在获取到aidl实例之后就可以调用里面的getUserName和getUserPassword方法,我们点击进去看一下,发现是一个抽象方法,这里我们就不得不说一下 UserCalcAIDL.java文件。这个文件我们自己并没有写我们只写了UserCalcAIDL.aidl文件是系统赠送给我们的,他里面有很多代码:
public interface UserCalcAIDL extends android.os.IInterface {
/**
* Stub 继承自Binder 实现了 UserCalcAIDL ,连接后服务端返回的mBinder 我们是这么new的
* new UserCalcAIDL.Stub() 也就是客服端连接后的 IBinder service
*/
public static abstract class Stub extends android.os.Binder implements com.hc.androidipc.UserCalcAIDL {
private static final java.lang.String DESCRIPTOR = "com.hc.androidipc.UserCalcAIDL";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 这个方法是在客户端 onServiceConnected 中调用的返回是一个 Stub.Proxy
*/
public static com.hc.androidipc.UserCalcAIDL asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.hc.androidipc.UserCalcAIDL))) {
return ((com.hc.androidipc.UserCalcAIDL) iin);
}
return new com.hc.androidipc.UserCalcAIDL.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_getUserName: {
// 服务端写数据 写入到reply里面
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getUserName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.hc.androidipc.UserCalcAIDL {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
// 这是传递进来的服务端给我们返回的 IBinder其实也是Stub
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
// 这个才是客户端获取用户名的实现方法
@Override
public java.lang.String getUserName() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 首先调用服务端返回的Ibinder实例调用transact方法写入到_reply中
mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0);
_reply.readException();
// 然后进行读取
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
// 返回读取到的用户名
return _result;
}
static final int TRANSACTION_getUserName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getUserPassword = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.lang.String getUserName() throws android.os.RemoteException;
public java.lang.String getUserPassword() throws android.os.RemoteException;
}
总结一下画一个流程图,客户端通过bindService连接服务端,会调用服务端Service的onBind方法返回一个UserCalcAIDL.Stub的mBinder实例,然后将该实例返回给客户端的onServiceConnected()方法里面,有两个参数有一个IBinder就是服务端返回的mBinder,然后客户端通过该实例建立一个新的AIDL.Stub.Proxy对象,我们在客户端调用获取信息方法的时候其实就是调用的AIDL.Stub.Proxy里面的getUserName()方法,通过mBinder的onTransact()方法写入数据,然后获取数据,就这么个流程了。
IPC Binder流程图.png4. google源码层的具体走向
你怎么知道调用bindService就会来到服务端的service的onBind()方法呢?当然是源码,记得前面有人说我很喜欢看源码,这其实是一种习惯不是喜不喜欢就像学习一样要渐渐成为一种习惯,有的时候我们宁愿反复的去百度搜索问题的解决方案,却忘记了有一个更好的办法就是源码:
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
try {
// 调用ActivityManagerNative.getDefault().bindService方法
int 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();
}
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
// binderService获取的也是一个远程服务的Binder对象,也是跨进程,而ServiceManager就是上面
// 流程图里用来管理这些服务和Binder驱动
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
// 获取到ActivityManager的管理类,最终调用ActivityManagerService是一个典型的跨进程通讯,
// 别问为什么千万别纠结
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
// 省略掉一些有关Activity的启动流程,我们再后面再说
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
if (app.thread == null) {
throw new RemoteException();
}
requestServiceBindingsLocked(r, execInFg);
}
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
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) {
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
// IApplicationThread
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
}
return true;
}
// 找了半天才找到这个方法,在ActivityThread的一个内部类
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
if (DEBUG_SERVICE)
Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
// 请求绑定
sendMessage(H.BIND_SERVICE, s);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
}
private 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);
// 然后把返回的binder实例公开回调出去
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
// 调用 onRebind 方法
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);
}
}
}
}
看到这里其实Android系统进程间通信机制Binder的总体架构,它由Client、Server、Service Manager和驱动程序Binder四个组件构成。那么Service Manager在这里起到了什么作用呢?其实Service Manager,它是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能。
Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要是由binder.h、binder.c和service_manager.c三个文件组成。Service Manager的入口位于service_manager.c文件中的main函数:
int main(int argc, char **argv) {
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
// 这个函数位于frameworks/base/cmds/servicemanager/binder.c文件中
// 通过文件操作函数open来打开/dev/binder设备文件。
// 设备文件/dev/binder是在Binder驱动程序模块初始化的时候创建的,
// 大家可以先看一下这个设备文件的创建过程。
// 进入到kernel/common/drivers/staging/android目录中,打开binder.c文件,
// 可以看到模块初始化入口binder_init:
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
LOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
// binder_loop函数进入循环,等待Client来请求了。
// binder_loop函数定义在frameworks/base/cmds/servicemanager/binder.c文件中
binder_loop(bs, svcmgr_handler);
return 0;
}
关于Native的代码我就不贴了,如果再贴下去很多人估计都要开始骂人了,上来就蒙B。
Native层具体的流程大致是:
1.先打开binder文件 -> 2.建立内存映射 -> 3.通知binder程序驱动 -> 4.进入循环等待请求的到来。
最近有人反应视频更新有点慢,我只能说还真是抱歉,因为我毕竟还是要生活所以还得去公司上班,目前还没有达到佛陀的心态,所以只能在周末跟大家分享了。看了这么多也不知道到底怎么用,这么麻烦干嘛?其实是为了后面的双进程守护做准备的,建立打不死的Service小强。
所有分享大纲:2017Android进阶之路与你同行