这一次,binder真正理解了(一) -----跨进程通信以及AIDL的使用
前言
网上有很多有关于binder文章的讲述,读了很多文章,有些直接讲源码,对初学的来说比较抽象,这系列文章先从使用背景,从运用上逐步深入去介绍知识点,希望能有一个更好的理解。这系列文章先从ipc通信讲起,通过AIDL的使用去探binder原理,这一次和我一起理清binder吧,有哪里讲的不好的点,欢迎指正补充,希望这一系列能让大家在以后的面试中能稳稳的回答出binder的相关问题。
概述
在日常的app开发当中,大家有没有跨进程的使用呢。还是一个进程走到底呢 。
在android里,一个应用启动对应着一个进程,那一个应用多创建进程又有什么好处呢,又有什么应用场景呢。
- 分担主进程的内存压力
当应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。手机分配给每个进程的内存都是有限的,在一些webiview的优化文章里,让webview独自使用一个进程。
- 使应用常驻后台,防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。
Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。坏处:消耗用户的电量,多占用了系统的空间,若所有应用都这样占用,系统内存很容易占满而导致卡顿,应用程序架构会变得复杂,因为要处理多进程之间的通信。
看来一个应用多进程还是有一些好处的,在创建多进程的同时,想必需要了解一些进程间通信,就比如上文提到的webview优化,那我们应用让webview打开指定文章,肯定需要传递url吧。还有一种进程通信的业务场景,大家应该经常遇到,就比如第三方登录,我们使用微信登陆和qq登陆,两个应用都是不同的进程,他们也需要使用进程间通信吧。看来,进程间通信所应用的业务场景比较多的。当然Android底层的实现用到进程间通信更多。
进程间通信方式(IPC通信方式)
每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同的进程互相访问资源并进行协调工作,才有了进程间通信。
每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间,当然内核空间的大小是可以通过参数配置调整的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间却是可共享的。(换句话说,你A进程要和B进程通信,在用户空间两者无法通信,可以将两者转向内核通信)那么进程间通信都有哪几种方式呢,android是基于linux系统的,这边继承了linux系统的ipc通信方式,也有自己独有的通信方式。
-
一 使用Bundle
我们直到,四大组件中的三大组件(Activity、Service、Receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,由于Bundle实现了Parcelable接口,所以它可以方便地在不同的进程间传输。
传输的数据必须能够序列化,比如基本类型、实现Parcelable接口的对象,实现了Serializable接口的对象以及一些Android支持的特殊对象。
除了直接传递数据这种典型的使用场景,它还有一种特殊的使用场景。这种方式的核心思想在于将原本需要在A进程的计算任务转移到B进程的后台Service中去执行,避免了A进程在计算,不能把计算结果传递给要启动的B进程里。
- 二 使用文件共享
共享文件也是一种不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。由于Android系统基于Linux,使得其并发读/写文件可以没有限制地进行,甚至两个线程同时对同一个文件进行写操作都是允许的。
通过文件共享这种方式来共享数据对文件格式是没有具体要求的,比如可以是文本文件,也可以是XML文件,只要读/写双方约定数据格式即可。通过文件共享的方式也是有局限性的,比如并发读/写的问题。
- 三 使用Socket
Socket也称为“套接字”,是网络通信的概念,它分为流失套接字和用户数据包套接字两种,分别对应于网络的传输控制层的TCP和UDP协议。
一般用于设计我们的聊天室程序,首先在远程Service建立一个TCP服务,然后在主界面中连接TCP服务,连接上了以后,就可以给服务端发消息了。使用Socket的工作机制。
- 四 使用Messenger
通过Messenger可以在不同进程中传递Messenger对象,在Messenger中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。
- 五 使用AIDL
AIDL也是Messenger的底层实现,可以用来跨进称调用服务端,底层实现是binder。
- 六 ContentProvider
ContentProvider的底层实现同样也是Binder。系统已封装好使用起来十分简单,(毕竟ContentProvider是用于和其他应用进行交互的)
系统预置了许多ContentProvider,比如通讯录信息、日程表信息等,要跨进程访问这些消息,只需要通过ContentResolver的query、update、insert和delet方法即可。
我们可以看到Messenger,AIDL,ContentProvider底层实际都是用Binder。
走错片场了,binder牛逼。
那Binder是什么? Binder作为Android系统提供的一种IPC机制。确实这句话就是他的定义,他就是Android系统来解决跨进程通信的一个机制,之后的系列文章会讲解他是如何运作的,从java层逐步往native层深入下去。
AIDL 实现进程间通信
首先我们先从基于binder机制的AIDL讲起,如何实现进程间通信 ,现在我想实现这样一个功能,进程A的Activity传递数据给进程B的Service,进程B处理完数据再将数据返回给A (整个过程可以类比第三方登录)。可以参考下方流程图,首先进程A的clientActivity将MyData对象传给进程B的Service操作一通,等进程B的Service操作完在将处理后的MyData对象返回给进程A,大家可以试着想想该如何实现这种场景,再继续阅读下面的代码。
RemoteService
注册Service
<service
android:name=".binder.RemoteService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
编写AIDL文件
- 要操作的MyData对象
MyData.aidl
package com.hugh.byteadvance.binder;
parcelable MyData;
- IRemoteService.aidl 实际操作的函数,之后的Activity进程要调用这边的opration函数
IRemoteService.aidl
package com.hugh.byteadvance.binder;
import com.hugh.byteadvance.binder.MyData;
import com.hugh.byteadvance.binder.ICompletedListener;
interface IRemoteService {
int getPid();
MyData getMyData();
void operation(in MyData parameter1 );
void registerListener(in ICompletedListener listener);
void unregisterListener(in ICompletedListener listener);
}
- ICompletedListener 模仿耗时操作通过回调来通知
ICompletedListener.aidl
package com.hugh.byteadvance.binder;
import com.hugh.byteadvance.binder.MyData;
interface ICompletedListener {
void onOperationCompleted(in MyData result);
}
Service部分代码编写
operation实现了具体操作,将数据相加,相乘最后通过回调返回给进程A Activity。
这里实现了同步和异步的操作,同步可以参考getPid(),getMyData()。
异步这边可以参考下operation(),注意这边的回调使用到了RemoteCallbackList。
public class RemoteService extends Service {
private static final String TAG = "aaa";
MyData mMyData;
//声明
private RemoteCallbackList<ICompletedListener> callbackList;
@Override
public void onCreate() {
super.onCreate();
initMyData();
callbackList = new RemoteCallbackList<>();
}
/**
* 初始化MyData数据
**/
private void initMyData() {
mMyData = new MyData();
mMyData.setData1(10);
mMyData.setData2(20);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public int getPid() throws RemoteException {
Log.e(TAG, "android.os.Process.myPid()" + android.os.Process.myPid());
return android.os.Process.myPid();
}
@Override
public MyData getMyData() throws RemoteException {
Log.i(TAG, "[RemoteService] getMyData() " + mMyData.toString());
return mMyData;
}
@Override
public void operation(MyData parameter1 ) throws RemoteException {
try {
Log.e(TAG, "operation 被调用,延时3秒,模拟耗时计算");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int param1 = parameter1.getData1();
int param2 = parameter1.getData2();
MyData result = new MyData(param1 + param2,param1 *param2);
//在操作 RemoteCallbackList 前,必须先调用其 beginBroadcast 方法
//此外,beginBroadcast 必须和 finishBroadcast配套使用
int count = callbackList.beginBroadcast();
for (int i = 0; i < count; i++) {
ICompletedListener listener = callbackList.getBroadcastItem(i);
if (listener != null) {
listener.onOperationCompleted(result);
}
}
}
@Override
public void registerListener(ICompletedListener listener) throws RemoteException {
callbackList.register(listener);
Log.e(TAG, "registerListener 注册回调成功");
}
@Override
public void unregisterListener(ICompletedListener listener) throws RemoteException {
callbackList.unregister(listener);
Log.e(TAG, "unregisterListener 解除注册回调成功");
}
//此处可以用于权限拦截
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
};
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "[RemoteService] onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.i(TAG, "[RemoteService] onDestroy");
super.onDestroy();
}
}
Activity代码编写
讲一下Activity的流程
-
首先新建ServiceConnection对象,在ServiceConnected和onServiceDisconnected写下相关的操作
-
启动Service,并绑定Service,在onServiceConnected的回调中,获取IRemoteService的代理对象
-
mRemoteService.registerListener(completedListener);
注册监听后 ,mRemoteService.operation(myData);
通过代理对象执行相应的方法。 -
最后
onOperationCompleted
再回调中获取相关结果。
重点:我们的操作都需要获取到IRemoteService的代理对象,就能进行进程间的通信。
public class ClientActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "aaa";
private IRemoteService mRemoteService;
private boolean mIsBound;
private TextView mCallBackTv;
private Button mBtnRegister;
private Button mBtnOperate;
private TextView mTvResult;
private TextView mBtnUnRegister;
private ICompletedListener completedListener = new ICompletedListener.Stub() {
@Override
public void onOperationCompleted(final MyData result) throws RemoteException {
runOnUiThread(new Runnable() {
@Override
public void run() {
mTvResult.setText("运算结果: 加法:" + result.getData1() + "------运算结果: 乘法:" + result.getData2());
}
});
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder);
mCallBackTv = (TextView) findViewById(R.id.tv_callback);
mTvResult = findViewById(R.id.tv_operate_result);
mBtnOperate = findViewById(R.id.btn_operate);
mBtnRegister = findViewById(R.id.btn_register);
mBtnUnRegister = findViewById(R.id.btn_un_register);
mBtnRegister.setOnClickListener(this);
mBtnOperate.setOnClickListener(this);
mBtnUnRegister.setOnClickListener(this);
mCallBackTv.setText("unattached");
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteService = IRemoteService.Stub.asInterface(service);
String pidInfo = null;
MyData myData = null;
try {
myData = mRemoteService.getMyData();
pidInfo = "pid=" + mRemoteService.getPid() +
", data1 = " + myData.getData1() +
", data2=" + myData.getData2();
} catch (RemoteException e) {
e.printStackTrace();
}
Log.i(TAG, "[ClientActivity] onServiceConnected " + pidInfo);
mCallBackTv.setText(pidInfo);
Toast.makeText(ClientActivity.this, "connected", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "[ClientActivity] onServiceDisconnected");
mCallBackTv.setText("disconnected");
mRemoteService = null;
Toast.makeText(ClientActivity.this, "disconnected", Toast.LENGTH_SHORT).show();
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_register:
/**
* 注册监听
*/
if (mRemoteService == null) {
Toast.makeText(ClientActivity.this, "请先点击bind,建立链接", Toast.LENGTH_SHORT).show();
} else {
try {
Toast.makeText(ClientActivity.this, "注册监听成功", Toast.LENGTH_SHORT).show();
mRemoteService.registerListener(completedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case R.id.btn_operate:
/**
* 调用Service异步方法
*/
if (mRemoteService == null) {
Toast.makeText(ClientActivity.this, "请先点击bind,建立链接", Toast.LENGTH_SHORT).show();
} else {
try {
Log.e("aaa", "送入数据");
MyData myData = new MyData(2, 88);
mRemoteService.operation(myData);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case R.id.btn_un_register:
/**
* 取消监听
*/
if (mRemoteService == null) {
Toast.makeText(ClientActivity.this, "请先点击bind,建立链接", Toast.LENGTH_SHORT).show();
} else {
try {
mRemoteService.unregisterListener(completedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
}
public void clickHandler(View view) {
switch (view.getId()) {
case R.id.btn_bind:
bindRemoteService();
break;
case R.id.btn_unbind:
unbindRemoteService();
break;
case R.id.btn_kill:
killRemoteService();
break;
}
}
/**
* 绑定远程服务
*/
private void bindRemoteService() {
Log.i(TAG, "[ClientActivity] bindRemoteService");
Intent intent = new Intent(ClientActivity.this, RemoteService.class);
intent.setAction(IRemoteService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallBackTv.setText("binding中");
}
/**
* 解除绑定远程服务
*/
private void unbindRemoteService() {
if (!mIsBound) {
return;
}
Log.i(TAG, "[ClientActivity] unbindRemoteService ==>");
unbindService(mConnection);
mIsBound = false;
mCallBackTv.setText("unbinding...");
}
/**
* 杀死远程服务
*/
private void killRemoteService() {
Log.i(TAG, "[ClientActivity] killRemoteService");
try {
android.os.Process.killProcess(mRemoteService.getPid());
mCallBackTv.setText("kill success");
} catch (RemoteException e) {
e.printStackTrace();
Toast.makeText(ClientActivity.this, "kill failure", Toast.LENGTH_SHORT).show();
}
}
}
小结
大家可以参考demo和上面的流程图来理清下两进程间通信的流程。这样有助于后面的源码理解,这边为了方便阅读,只贴出了部分功能代码。在下一章为大家带来binder原理相关的解析。
工欲善其事,必先利其器。既然要读源码,那我们必须要先准备好相应的源码吧。
首先看下Android系统的架构图
我们可以看到binder驱动在kernel层,接下来的系列文章里,会讲解binder如何从应用层到framework层再到kernel层的整个链路。话不多说。给大家推荐下源码下载的地址。
注:Binder系列文章 framework 源码使用 android10 release 分支,kernel 部分使用 common 的 android-4.9-q-release 分支。
源码涉及目录
/framework/base/core/java/ (Java)
/framework/base/core/jni/ (JNI)
/framework/native/libs/binder (Native)
/framework/native/cmds/servicemanager/ (Native)
/kernel/drivers/staging/android (Driver)
Java framework
/framework/base/core/java/android/os/
- IInterface.java
- IBinder.java
- Parcel.java
- IServiceManager.java
- ServiceManager.java
- ServiceManagerNative.java
- Binder.java
/framework/base/core/jni/
- android_os_Parcel.cpp
- AndroidRuntime.cpp
- android_util_Binder.cpp (核心类)
Native framework
/framework/native/libs/binder
- IServiceManager.cpp
- BpBinder.cpp
- Binder.cpp
- IPCThreadState.cpp (核心类)
- ProcessState.cpp (核心类)
/framework/native/include/binder/
- IServiceManager.h
- IInterface.h
/framework/native/cmds/servicemanager/
- service_manager.c
- binder.c
Kernel
kernel/drivers/android
- binder.c
源码下载
https://github.com/hughcoder/ByteAdvance