在android跨进程通信中binder必不可少,从Activty的启动源码就可以看到Activity的周期控制就是ActivityThread和AMS之间的binder跨进程通信,谈到binder机制就绕不过AIDL;下面开始介绍;
1.AIDL代码实例
首先要了解binder机制,b必然绕不开AIDL;那么接着看一个AIDL的简单实例
- 1.service端:
public class IBookservice extends Service{
private ArrayList<Book> books;
@Nullable
@Override
public IBinder onBind(Intent intent) {
books = new ArrayList<>();
return binder;
}
//Stub
IBinder binder = new IBookManager.Stub(){
@Override
public List<Book> getBookList() throws RemoteException {
if (books.size() == 0){
return null;
}
return books;
}
@Override
public void addBook(Book book) throws RemoteException {
books.add(book);
}
};
}
- 2.client端
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private int index;
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获取的是一个代理对象 proxy
iBookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBookManager iBookManager;
private TextView txt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
bindService();
}
private void initView() {
Button btn = findViewById(R.id.btn);
txt = findViewById(R.id.txt);
btn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
txt.setText("");
Book book = new Book(String.valueOf(index), "java 基础");
index++;
try {
iBookManager.addBook(book);
List<Book> bookList = iBookManager.getBookList();
for (int i = 0;i < bookList.size();i++){
txt.append(bookList.get(i).toString() + "---");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void bindService() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.ipcclient",
"com.example.ipcclient.IBookservice"));
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
}
以上是一个简单的AIDL实例,只粘贴主要逻辑代码;如果是初学者,可以看完整代码:
项目地址
大体就是服务端进行图书的存储.addBook(book)和图书列表获取.getBookList();然后客户端通过binder跨进程向service传参实现图书存储和获取图书列表(即跨进程调用service端的方法)
2.AIDL源码
看完上面的代码,对于读者应该有了解到android的binder就是跨进程通信的一种载体,即通过binder(即AIDL)可以实现不同的client跨进程调用service中相应方法的目的 那么我们紧接这来看源码;
2.1.service端和client通过service的bind来实现链接
service:通过onbind方法 返回binder
@Nullable
@Override
public IBinder onBind(Intent intent) {
books = new ArrayList<>();
return binder;
}
//Stub
IBinder binder = new IBookManager.Stub(){
@Override
public List<Book> getBookList() throws RemoteException {
if (books.size() == 0){
return null;
}
return books;
}
@Override
public void addBook(Book book) throws RemoteException {
books.add(book);
}
};
client:通过binderService获取service传来的binder,通过操作binder实现跨进程
private void bindService() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.ipcclient",
"com.example.ipcclient.IBookservice"));
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获取的是一个代理对象 proxy
iBookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
这样通过andoroid–service的bind就实现了客户端和服务端的连接;
2.2AIDL源码
首先需要明确一点,service端和client端跨进程通讯的前提是共有的AIDL文件: 首先来看AIDL编译之后build/generated/source/aidl/debug下apt编译之后的binder实现源码:
package com.example.ipcclient;
public interface IBookManager extends android.os.IInterface
{
public static abstract class Stub extends android.os.Binder implements com.example.ipcclient.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.example.ipcclient.IBookManager";
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
public static com.example.ipcclient.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.ipcclient.IBookManager))) {
return ((com.example.ipcclient.IBookManager)iin);
}
return new com.example.ipcclient.IBookManager.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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.ipcclient.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.example.ipcclient.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.ipcclient.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.ipcclient.IBookManager
{
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 java.util.List<com.example.ipcclient.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.ipcclient.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.ipcclient.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.ipcclient.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.example.ipcclient.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.ipcclient.Book book) throws android.os.RemoteException;
}
IBookManager继承自IInterface,其中有两个重要的类 Stub 和 Proxy ,Stub是一个继承自IBookManager的抽象类,但是并没有实现其方法,可以被子类化,用于进程内使用;Proxy 是Stub的内部类,在跨进程通信中其中核心作用;
- 首先在service中实例化化Stub,并且初始化其方法:
@Override
public List<Book> getBookList() throws RemoteException {
if (books.size() == 0){
return null;
}
return books;
}
@Override
public void addBook(Book book) throws RemoteException {
books.add(book);
}
};
作为binder传递给Client,Client通过bindservice可获取:
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获取的是一个代理对象 proxy
iBookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
这里在ServiceConnection—>onServiceConnected(ComponentName name, IBinder service)获取service传递的binder,然后获取实例IBookManager.Stub.asInterface(service);
public static com.example.ipcclient.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.ipcclient.IBookManager))) {
return ((com.example.ipcclient.IBookManager)iin);
}
return new com.example.ipcclient.IBookManager.Stub.Proxy(obj);
}
需要注意的是如果是进程内的通信我们直接使用Stub即可,但是跨进程就返回 Proxy ,所以我们 client 端操作的其实是本地的一个代理,我们继续看上面代码的返回:
return new com.example.ipcclient.IBookManager.Stub.Proxy(obj);
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
这里的obj就是service的Binder即Stub,到这里你就会明白,进一步讲Client操作的就是一个传入Service端Stub的本地代理类; 紧接着,我们调用.asInterface返回实例的.addBook:
@Override public void addBook(com.example.ipcclient.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
这里逻辑就很明显了;首先
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
其中_data是参数,_reply是结果;就是说当我addBook(“java jvm”),_data就是”java jvm”这个参数,_reply就是添加”java jvm”之后的图书list的result;由于addBook没有返回值,那么我们来看 getBookList()
@Override public java.util.List<com.example.ipcclient.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.ipcclient.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.ipcclient.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这个方法就一目了然了; 上面代码的核心代码:
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
这里就是Service端的Stub,所以这里就是回调Service端Stub的transact方法,即Binder源码的transact方法:
/**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
*/
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
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 {
IoUtils.closeQuietly(fd);
}
}
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
return true;
} else if (code == SHELL_COMMAND_TRANSACTION) {
ParcelFileDescriptor in = data.readFileDescriptor();
ParcelFileDescriptor out = data.readFileDescriptor();
ParcelFileDescriptor err = data.readFileDescriptor();
String[] args = data.readStringArray();
ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
try {
if (out != null) {
shellCommand(in != null ? in.getFileDescriptor() : null,
out.getFileDescriptor(),
err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
args, shellCallback, resultReceiver);
}
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(err);
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
}
return true;
}
return false;
}
代码比较长,其实就是client调Service端的transact–>onTransact;由于Service端有重写nTransact,所以我们应该看最终的源码:
@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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.ipcclient.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.example.ipcclient.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.ipcclient.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
到这里的OnTransact中使用一个责任链,通过swich来区别code,进行不同的操作,这里的code用来区分调用方法,即将_data传入相应方法,然后将结果result写入_reply,最后由Client本地代理中对应方法return或者结束操作;
到这里一整个跨进程的binder操作就结束了,作为梳理我们给出图解,继续往下看;
3.AIDL图解
这个图就是对binder机制的一个概括;
android binder机制深究还会有很多,暂时浅谈到此;
共同进步 : )