深入理解Binder机制1-AIDL原理
Binder作为Android系统中重要的进程间通信方式,了解其基本的原理,对于分析问题具有重要的作用。由于Binder架构涉及的内容比较多,后面将会从应用层、框架层、Native层、内核层四个层次来说明Binder的原理。首先将从应用层的AIDL开始逐渐深入到内核层。整个系列的文章如下:
深入理解Binder机制1-AIDL原理
深入理解Binder机制2-注册服务addService
深入理解Binder机制3-获取服务getService
深入理解Binder机制4-bindService过程分析
深入理解Binder机制5-binder驱动分析
深入理解Binder机制6-总结篇
一、AIDL
在进行进程间通信时,需要将接口定义好,定义好之后创建aidl文件,将接口方法放在文件中。客户端和服务端,aidl文件要保持一致,包括包名。在build之后,会在客户端和服务端生成接口类。
1.aidl文件
[->IRemoteService.aidl]
// IRemoteService.aidl
package com.skytoby.server;
interface IRemoteService {
void addPhone(String name);
boolean getPhone(String name);
int getPid();
}
2.服务端
[->PhoneService.java]
package com.skytoby.server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class PhoneService extends Service {
List<String> phones = new ArrayList<>();
//实现binder接口
private IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public void addPhone(String name) throws RemoteException {
Log.d("phone binder","server add phone:"+name);
phones.add(name);
}
@Override
public boolean getPhone(String name) throws RemoteException {
Log.d("phone binder","server get phone:"+name);
if(phones.contains(name)){
return true;
}
return false;
}
@Override
public int getPid() throws RemoteException {
Log.d("phone binder","server getPid "+android.os.Process.myPid());
return android.os.Process.myPid();
}
@Override
public void linkToDeath(IBinder.DeathRecipient recipient, int flags) {
super.linkToDeath(recipient, flags);
Log.d("phone binder","server getPid linkToDeath");
}
};
@Override
public void onCreate() {
Log.d("phone binder","server onCreate");
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.d("phone binder","server onBind");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("phone binder","server onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.d("phone binder","server onDestroy");
super.onDestroy();
}
}
3.客户端
[->MainActivity.java]
package com.skytoby.client;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
import com.skytoby.server.IRemoteService;
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private IRemoteService mService;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
mTextMessage.setText(R.string.title_home);
bindService();
return true;
case R.id.navigation_dashboard:
mTextMessage.setText(R.string.title_dashboard);
unbindService();
return true;
case R.id.navigation_notifications:
mTextMessage.setText(R.string.title_notifications);
killService();
return true;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView navView = findViewById(R.id.nav_view);
mTextMessage = findViewById(R.id.message);
navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
Log.d("phone binder","onCreate");
}
void bindService(){
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.skytoby.server","com.skytoby.server.PhoneService"));
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
void unbindService(){
if(mService!=null){
unbindService(connection);
}
}
void killService(){
try {
if(mService!=null){
android.os.Process.killProcess(mService.getPid());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("phone binder","onServiceConnected");
try {
//获取代理
mService = IRemoteService.Stub.asInterface(service);
Log.d("phone binder","client getphone:"+mService.getPhone("apple"));
mService.addPhone("apple");
Log.d("phone binder","client getphone:"+mService.getPhone("apple"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("binder","onServiceDisconnected");
mService = null;
}
};
}
4.运行结果
点击客户端相应的控件,完成绑定服务,解绑服务,杀死service进程,日志如下:
16:09:10.394 27006-27006/com.skytoby.server D/phone binder: server onCreate
16:09:10.395 27006-27006/com.skytoby.server D/phone binder: server onBind
16:09:10.405 27006-27018/com.skytoby.server D/phone binder: server get phone:apple
16:09:10.407 27006-27018/com.skytoby.server D/phone binder: server add phone:apple
16:09:10.409 27006-27018/com.skytoby.server D/phone binder: server get phone:apple
16:16:55.497 27006-27006/com.skytoby.server D/phone binder: server onUnbind
16:16:55.502 27006-27006/com.skytoby.server D/phone binder: server onDestroy
16:16:57.955 27006-27020/com.skytoby.server D/phone binder: server getPid 27006
二、AIDL原理分析
1. IRemoteService类
aidl生成一个对应的IRemoteService.java,其原理还是利用了framework binder的架构,具体的内部原理后面介绍,先分析下这个生成类,其流程如下:
-
AIDL接口:继承IInterface。
-
Stub类:Binder的实现类,服务端通过这个类来提供服务。
-
Proxy类:服务器的本地代理,客户端通过这个类调用服务器的方法。
-
asInterface():客户端调用,将服务端的返回的Binder对象,转换成客户端所需要的AIDL接口类型对象。返回对象:
1.若客户端和服务端位于同一进程,则直接返回Stub对象本身;
2.否则,返回的是系统封装后的Stub.proxy对象。
-
asBinder():根据当前调用情况返回代理Proxy的Binder对象。
-
onTransact():运行服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。
-
transact():运行在客户端,当客户端发起远程请求的同时将当前线程挂起。之后调用服务端的onTransact()直到远程请求返回,当前线程才继续执行。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: G:\\Android studio\\Aidl\\client\\src\\main\\aidl\\com\\skytoby\\server\\IRemoteService.aidl
*/
package com.skytoby.server;
public interface IRemoteService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.skytoby.server.IRemoteService {
private static final java.lang.String DESCRIPTOR = "com.skytoby.server.IRemoteService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.skytoby.server.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.skytoby.server.IRemoteService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.skytoby.server.IRemoteService))) {
return ((com.skytoby.server.IRemoteService) iin);
}
return new com.skytoby.server.IRemoteService.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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_addPhone: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.addPhone(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getPhone: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
boolean _result = this.getPhone(_arg0);
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return true;
}
case TRANSACTION_getPid: {
data.enforceInterface(descriptor);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.skytoby.server.IRemoteService {
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 void addPhone(java.lang.String name) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
mRemote.transact(Stub.TRANSACTION_addPhone, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public boolean getPhone(java.lang.String name) 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(name);
mRemote.transact(Stub.TRANSACTION_getPhone, _data, _reply, 0);
_reply.readException();
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int getPid() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_addPhone = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPhone = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
public void addPhone(java.lang.String name) throws android.os.RemoteException;
public boolean getPhone(java.lang.String name) throws android.os.RemoteException;
public int getPid() throws android.os.RemoteException;
}
2. 原理
对于应用层来说bindService之后就可以和服务端进行交互了,可以不用里面具体的操作如何,这样的设计大大降低了使用了难度,对于binderService的具体的过程将在后面分析,下面是其分层次的调用图。