面试可能会问到的问题
- 从IPC的方式问到Binder的优势
- 为什么zygote跟其他服务进程的通讯不使用Binder
- Binder线程池和Binder机制
等等这些问题都是基于你对Binder的理解还有对其他IPC通讯的理解
IPC方式有多少种
- 传统的IPC方式有Socket、共享内存、管道、信号量等
- 安卓特有的是
Binder
,其他系统也有自己定义的比如window的wfc(Windows Communication Foundation)
Binder机制的优势
传统的ipc管道跟信号量都是比较轻的,基本不能用于复杂的IPC通讯,Socket有个缺点就是数据要复制两遍,第一遍是进程A复制到Socket通道,第二遍是Socket通道复制到进程B。共享内存的缺点就是不方便管理,如果频繁的对共享内存进行操作很可能会导致死锁、饥饿等问题。
为了解决这个痛点,安卓设计了Binder的通讯机制,提供更高效、安全、可靠的方式。下图是Binder机制图。只需要在内核里面复制一遍,这里可以简单的理解为单例模式,大家都只需要对Binder内存区域做操作,内存只有一份。
还有一些概念,比如我们应该都听说Binder是内核里面的,Binder是怎么用到安卓系统里面的,再看一个图 这里面有四个角色,其实很好理解,两个进程、一个Binder内核空间、一个ServiceManager服务。两个进程时采用的C/S结构一个客户端一个服务端。
Binder
内核空间就是一块存在于内核区的内存理解为一个通道ServiceManager
是安卓的核心服务之一,AMS、PMS、WMS这些是一样的。它里面提供了很多线程池。Binder线程池只是其中的一种。在这里可以理解为是管理Binder给外部用的提供了一个注册机制用于识别不同的进程。下面是其他的几个线程池- Activity Manager线程池:用于处理Activity启动、停止等操作,保证UI界面的流畅性。
- JobScheduler线程池:用于调度执行后台任务。
- MediaServer线程池:用于处理音视频等媒体数据。
- SurfaceFlinger线程池:用于处理UI界面绘制等操作。
有没有想过为什么要搞那么线程池,还要搞那么多个。其实也比较好理解,因为ServiceManager这个类的有很多地方用到,不可能是单线程处理的,这样就堵塞了。为什么搞那么多线程池是因为不同的功能不同比如有些调用比较频繁有些需要比较多的cpu。只有一个线程池的话很容易会导致占用时间过长等问题。
server进程
需要提供方法被别人调用,需要先在ServiceManager里面注册。- server注册完以后会提供接口给
client进程
调用
来到这里应该可以回答剩下的那两个问题了。
Binder线程池和Binder机制
- Binder线程池是ServiceManager提供的,利用的是Binder内核机制。
- Binder机制是安卓为了提供更高效、稳定、可靠的方式实现的一套基于内核的IPC机制。
为什么zygote进程跟其他进程通讯使用socket而不是binder
- Binder虽然在内核,但是提供服务的是ServiceManager,这个时候如果要给AMS提供Binder IPC就需要等ServiceManager先初始化好,这个是没办法保证的,如果要保证这个先后顺序又要搞多一套进程通讯就更麻烦了。
- 另外,由于Zygote进程只与少数几个进程进行通讯,使用Socket通讯的开销相对较小,因此选择Socket通讯更加合适。而且这里面是优化过的LocalSocket效率会更高。
上面一直说内核空间,那内核空间跟用户空间有什么区别呢?
- 内核程序运行在操作系统的内核空间,具有更高的权限和更快的执行速度,能够实现更底层的操作,如硬件驱动、文件系统等,因此通常用于操作系统的核心功能的实现。
- 用户程序运行在操作系统的用户空间,具有更多的自由度和可移植性,能够实现更丰富的功能,如应用程序、服务进程等,因此通常用于操作系统的外围功能和应用程序的实现。
简单的说就是内核空间有操作内存的方法,但是这一块对用户空间是封闭的,用户空间里面操作的都是内核提供的服务。比如操作文件用到的文件系统模块
和操作内存用到的内存管理模块
。拿内存管理模块里面用到的kmalloc
和kfree
来说。这两个方法在用户空间就调用不到,这是内核封装的方法。
`kmalloc()` 和 `kfree()`: 内核内存分配器,用于在内核空间中动态分配和释放内存。
// 这个还并不是c/c++的原生方法,是内核自己封装的
再看一下比如使用Binder的时候我们传的是一个序列化的文件,那他是怎么映射到内存中的?
在内核中有vm_map_ram
和vm_insert_page
这些方法可以把文件插入到内存地址中。而如果在用户空间需要跟这些打交道用到的还是内存管理模块的mmap
这些。 mmap
的实现在内核中使用了 vm_area_struct
。意思是内存管理模块提供给外面的一层封装。所以与其这么麻烦还不如直接放到内核里面更合适。
这些方法其实都不重要,只需要知道一点,这些方法在用户进程空间是拿不到的
基于Binder的IPC
这里是一些基础,可以不看了。因为这些用的少,方便自己以后看
AIDL
服务端
// IMyService.aidl
interface IMyService {
int add(int a, int b);
}
// MyService.java
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
private class MyBinder extends IMyService.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
}
客户端
// MainActivity.java
public class MainActivity extends AppCompatActivity {
private IMyService mService;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IMyService.Stub.asInterface(service);
try {
int result = mService.add(1, 2);
Toast.makeText(MainActivity.this, "Result: " + result, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
Messager
轻量级的AIDL只能单线程 服务端 MessengerService.java:
public class MessengerService extends Service {
private static class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
String clientMessage = (String) msg.obj;
Log.i("MessengerService", "Received message from client: " + clientMessage);
// Send a response back to the client
Messenger clientMessenger = msg.replyTo;
Message replyMessage = Message.obtain(null, 2);
Bundle bundle = new Bundle();
bundle.putString("serverResponse", "Hello from server!");
replyMessage.setData(bundle);
try {
clientMessenger.send(replyMessage);
} catch (RemoteException e) {
Log.e("MessengerService", "Failed to send message to client", e);
}
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger mMessenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
客户端 MessengerClient.java:
public class MessengerClient extends AppCompatActivity {
private Messenger mMessenger;
private static class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 2:
String serverMessage = msg.getData().getString("serverResponse");
Log.i("MessengerClient", "Received message from server: " + serverMessage);
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger mClientMessenger = new Messenger(new IncomingHandler());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
sendMessageToServer();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMessenger = null;
}
};
private void sendMessageToServer() {
if (mMessenger == null) {
return;
}
Message message = Message.obtain(null, 1);
message.obj = "Hello from client!";
message.replyTo = mClientMessenger;
try {
mMessenger.send(message);
} catch (RemoteException e) {
Log.e("MessengerClient", "Failed to send message to server", e);
}
}
}
广播、内容提供者
……
其实每次的面试你永远不知道,面试官下面会问到什么问题,所以在面试前我们需要全方位的做好提前预习的准备,面试造火箭,工作拧螺丝这个道理,想必大家都是懂的,所以我为完后的面试准备了一些面试习题进行复习,并整理成为了《Android 面试题集》的集合,如需要参考可以访问:https://qr18.cn/CKV8OZ