• 3

  • 3

  • Favorite

Android 进程间通信之binder - 实战

2 months ago

文章目录


我们从binder由来开始说起,说说Android在binder的规范写法和非规范的写法;应文章标题,通过实战代码讲述binder传输内容的组织形式。
相关的文章:
Android进程间通信之binder - 几个重要数字
Android进程间通信之binder - 可能导致的异常
Android进程间通信之binder - debug transaction
Android进程间通信之binder - 重要工具aidl
Android进程间通信之binder - 上层协议IPCThreadState
Android进程间通信之binder - 工具类Parcel

binder概述

openbinder是binder的祖先,实现了进程间通信,后来被Google应用在Android系统中;binder驱动在kernelv3.3版本上也加入了大家庭,驱动位于drivers/staging/android目录,在后面的kernel版本中binder成为kernel正真成员;驱动代码位于drivers/android/目录。
我们先来思考一个哲学问题,Android为什么选用binder作为主要的进程间通信方式?
在这里插入图片描述
既然binder是用来实现进程间通信,那我们先不多说,看个实例:

代码实战

第一步肯定是下载,打开Android studio IDE。。。。

完整实例

我们分别用java 和 c++实现binder通信

java实例

创建一个empty project,创建一个aidl文件;覆盖基本类型传输(包括数组),自定义object传输,binder对象传输(这儿的是匿名binder),文件fd传输;
aidl 文件关键字:parcelable ,in ,out, inout,oneway

// StudentScore.aidl
package com.exp.binder.model;

parcelable StudentScore;
复制代码
// IBinderTransaction.aidl
package com.exp.binder;

import com.exp.binder.model.StudentScore;

interface IBinderTransaction {
    int transactBasicType(int avgScore, in long[] courses);
    int transactBasicTypeKeywords(in int math, in int english, in int chinese, out int[] avgScore);
    int transactGetObject(out StudentScore stu);
    int transactObject(in StudentScore stu);
    int transactBinder(IBinder binder);
    int transactFd(in ParcelFileDescriptor fd);
    oneway void asyncWork();
}
复制代码
// IBinderToBinder.aidl
package com.exp.binder;

interface IBinderToBinder {
    void testbinder();
}
复制代码

自定义传输object

package com.exp.binder.model;
import android.os.Parcel;
import android.os.Parcelable;
public class StudentScore implements Parcelable {
    int chinese;
    int math;
    int english;
//此处省略get/set方法
。。。。。。
    public StudentScore(){}
    public StudentScore(Parcel in){
        readFromParcel(in);
    }
    @Override
    public int describeContents() {
        return 0;
    }
    public void readFromParcel(Parcel in) {
        chinese = in.readInt();
        math = in.readInt();
        english = in.readInt();
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //flags PARCELABLE_WRITE_RETURN_VALUE server返回时设置,如果有资源需要释放,再次判断此flags
        // PARCELABLE_ELIDE_DUPLICATES
        dest.writeInt(chinese);
        dest.writeInt(math);
        dest.writeInt(english);
    }
    public static final Creator<StudentScore> CREATOR = new Creator<StudentScore>() {
        public StudentScore createFromParcel(Parcel source) {
            return new StudentScore(source);
        }
        public StudentScore[] newArray(int size) {
            return new StudentScore[size];
        }
    };
}
复制代码

这里有一个关键点,自定义object在binder传输,需要定义一个在相同包名下的aidl文件,执行parcelable 此object,在需要使用到的aidl中import;

service代码:

package com.exp.binder.service;
//省略import类文件
。。。。。。
public class BinderTransactService extends Service {
    private final static String TAG = "BinderTransactService";
    public BinderTransactService() {
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return iBinder;
    }
    IBinder iBinder = new IBinderTransaction.Stub() {
        @Override
        public int transactBasicType(int avgScore, long[] courses) throws RemoteException {
            //简单传输不做处理
            return 0;
        }
        @Override
        public int transactBasicTypeKeywords(int math, int english, int chinese, int[] avgScore) throws RemoteException {
            avgScore[0] = (math+english+chinese) / 3;
            return avgScore[0];
        }
        @Override
        public int transactGetObject(StudentScore stu) throws RemoteException {
            stu.setChinese(10);
            stu.setEnglish(20);
            stu.setMath(30);
            return 0;
        }
        @Override
        public int transactObject(StudentScore stu) throws RemoteException {
            //简单传输不做处理
            return 0;
        }
        @Override
        public int transactBinder(IBinder binder) throws RemoteException {
            IBinderToBinder service = IBinderToBinder.Stub.asInterface(binder);
            service.testbinder();
            return 0;
        }
        @Override
        public int transactFd(ParcelFileDescriptor fd) throws RemoteException {
            int filefd = fd.getFd();
            Path transactFilePath = null;
            Path path = Paths.get("/proc/self/fd/"+filefd);
            try {
                transactFilePath = path.toRealPath();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (transactFilePath != null)
                Log.d(TAG, "transactFd: "+transactFilePath.toString());
            return 0;
        }
        @Override
        public void asyncWork() throws RemoteException {
            //因为是异步调用,client执行调用后,立马返回,虽然这儿sleep这么长时间,不会block调用者
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
}
复制代码

执行异步调用:
第一次运行碰到了anr,异步调用应该不会block主线程,那是怎么回事?(查看生成在/data/anr/目录下的trace文件)

"main" prio=5 tid=1 Sleeping
  | group="main" sCount=1 dsCount=0 obj=0x74a6f000 self=0xa568b400
  | sysTid=5595 nice=0 cgrp=default sched=0/0 handle=0xa99bd534
  | state=S schedstat=( 0 0 0 ) utm=8 stm=4 core=2 HZ=100
  | stack=0xbf3bb000-0xbf3bd000 stackSize=8MB
  | held mutexes=[1K
  at java.lang.Thread.sleep!(Native method)
  - sleeping on <0x0c1185ac> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:371)
  - locked <0x0c1185ac> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:313)
  at com.exp.binder.service.BinderTransactService$1.asyncWork(BinderTransactService.java:84)
  at com.exp.binder.MainActivity.onBtnClick(MainActivity.java:67)
  at com.exp.binder.MainActivity_ViewBinding$1.doClick(MainActivity_ViewBinding.java:33)
  at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:26)
  at android.view.View.performClick(View.java:5610)
  at android.view.View$PerformClick.run(View.java:22265)
  at android.os.Handler.handleCallback(Handler.java:751)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:154)
  at android.app.ActivityThread.main(ActivityThread.java:6077)
  at java.lang.reflect.Method.invoke!(Native method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
复制代码

奇怪异步调用怎么在主线程中执行了;这里引出两个概念,本地binder和远程binder。 注意后面是答案:BinderTransactService没有运行在单独进程中,虽然使用了binder调用方式,其本质还是直接对象调用;在onServiceConnected函数中,调用asInterface,如果是同一个进程直接返回对象,否则返回proxy,看如下代码逻辑:

public static com.exp.binder.IBinderTransaction asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.exp.binder.IBinderTransaction))) {
        return ((com.exp.binder.IBinderTransaction) iin);
    }
    return new com.exp.binder.IBinderTransaction.Stub.Proxy(obj);
}
复制代码

在AndroidManifest.xml给service添加process属性,android:process=":binder";apk安装过程中packagemanagerservice检查此属性,设置进程标记,在startservice时重新启动一个进程,加载此service。
接下来我们看看aidl给生成的java文件:
mRemote.transact(Stub.TRANSACTION_transactBasicTypeKeywords, _data, _reply, 0);
这个函数在proxy调用,将参数写到data中,最后一个flag 0表示同步传输,client调用端block在此函数上,当server端执行完成返回结果后,此函数会返回,将结果写在reply中;异步调用reply传入null;

此文件是aidl自动生成的,这是规范写法,上面说的in out 等关键字在这个文件生成代码逻辑不一样,下一篇文章aidl我们详细看看
/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.exp.binder;

public interface IBinderTransaction extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.exp.binder.IBinderTransaction {
        private static final java.lang.String DESCRIPTOR = "com.exp.binder.IBinderTransaction";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.exp.binder.IBinderTransaction interface,
         * generating a proxy if needed.
         */
        public static com.exp.binder.IBinderTransaction asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.exp.binder.IBinderTransaction))) {
                return ((com.exp.binder.IBinderTransaction) iin);
            }
            return new com.exp.binder.IBinderTransaction.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_transactBasicType: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    long[] _arg1;
                    _arg1 = data.createLongArray();
                    int _result = this.transactBasicType(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_transactBasicTypeKeywords: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _arg2;
                    _arg2 = data.readInt();
                    int[] _arg3;
                    int _arg3_length = data.readInt();
                    if ((_arg3_length < 0)) {
                        _arg3 = null;
                    } else {
                        _arg3 = new int[_arg3_length];
                    }
                    int _result = this.transactBasicTypeKeywords(_arg0, _arg1, _arg2, _arg3);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    reply.writeIntArray(_arg3);
                    return true;
                }
。。。。。。
                case TRANSACTION_asyncWork: {
                    data.enforceInterface(descriptor);
                    this.asyncWork();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.exp.binder.IBinderTransaction {
            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 int transactBasicTypeKeywords(int math, int english, int chinese, int[] avgScore) 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);
                    _data.writeInt(math);
                    _data.writeInt(english);
                    _data.writeInt(chinese);
                    if ((avgScore == null)) {
                        _data.writeInt(-1);
                    } else {
                        _data.writeInt(avgScore.length);
                    }
                    mRemote.transact(Stub.TRANSACTION_transactBasicTypeKeywords, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                    _reply.readIntArray(avgScore);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
。。。。。。

            @Override
            public void asyncWork() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_asyncWork, _data, null, android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_transactBasicType = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_transactBasicTypeKeywords = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_transactGetObject = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_transactObject = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_transactBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
        static final int TRANSACTION_transactFd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
        static final int TRANSACTION_asyncWork = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
    }

    public int transactBasicType(int avgScore, long[] courses) throws android.os.RemoteException;

    public int transactBasicTypeKeywords(int math, int english, int chinese, int[] avgScore) throws android.os.RemoteException;

    public int transactGetObject(com.exp.binder.model.StudentScore stu) throws android.os.RemoteException;

    public int transactObject(com.exp.binder.model.StudentScore stu) throws android.os.RemoteException;

    public int transactBinder(android.os.IBinder binder) throws android.os.RemoteException;

    public int transactFd(android.os.ParcelFileDescriptor fd) throws android.os.RemoteException;

    public void asyncWork() throws android.os.RemoteException;
}
复制代码

native实例

android 源码网址:http://aospxref.com/,下面代码版本为android-11.0.0_r21
native实例我们看一个media service 源码:
http://aospxref.com/android-11.0.0_r21/xref/frameworks/av/services/medialog/MediaLogService.h

1.定义service,继承public BinderService, public BnMediaLogService 这两个类;
2.BinderService 实现了instantiate->push(),push函数往defaultServiceManager添加此MediaLogService重写getServiceName services。
3.初始化完成以后,joinThreadPool开始binder server 轮询;

在BinderService中有几个宏定义

IMPLEMENT_META_INTERFACE(MediaLogService, "android.media.IMediaLogService");
复制代码

实现了asInterface等binder必须接口。

不同app之间通信

看到最后,不知道你有没有产生这样一个疑问,上面的实例都是在同一个app项目中完成的,不同app之间怎么通信呢?

实名binder

实名binder怎么通信,Android提供了一个service命令,从servicemanager中找到实名binder,返回ibinder对象,在用transact方法call到远程进程。(具体逻辑留在下一篇文章中。。。)

binder相关的几个命令
adb shell service list #此命令查看Android中added的实名binder有那些
adb shell service call activity(service 名称) code i32 val1 s16 string1 #根据service onTransact方法读取的顺序写

contentprovider

写个provider,注册到system_server中,其它app从server中找到对应的provider,执行相应的增删改查接口,会调用到服务进程。
晚点时间将sample上传到github,在这儿贴出链接吧;

结束语

看完通篇,好像上面预留的问题没有答案;
其实这么一篇文档不能把binder相关都写完;先给出答案吧;其它几种进程间通信方式在用户空间几行代码就能实现一个简单通信的sample;无法做安全和有效性验证;所以Android选用binder,kernel驱动只是一个交易实现,binder协议都是在上层用户空间做的验证,libbinder.so,即IPCThreadState实现。

都看到这儿了,辛苦给点个赞呗!\

免责声明:文章版权归原作者所有,其内容与观点不代表Unitimes立场,亦不构成任何投资意见或建议。

java

3

Relevant articles

未登录头像

No more data