Android Aidl 的使用

Android Aidl 的使用

Binder框架 -- android AIDL 的使用

Binder框架 – 用户空间和驱动的交互

Binder框架 – Binder 驱动

Binder 框架 – binder 用户空间框架

Aidl 是android 跨进程通信的中一种,是一种RPC。底层基于binder 框架。通常用在C/S架构中。

Aidl 跨进程通信支持有限的数据类型

Aidl 可以进行跨进程通信,但是不是所有的数据类型都支持,支持的类型主要是:

  1. Java 的基本类型
  2. String 和CharSequence
  3. List 和 Map, 并且List和Map 对象的元素必须是AIDL支持的数据类型;以上三种类型都不需要导入(import)
  4. AIDL 自动生成的接口 需要导入(import)
  5. 实现android.os.Parcelable 接口的类. 需要导入(import)。

创建Aidl 文件

在android studio 中直接new 一个Aidl 文件 IHelloWorldInterface.aidl。注意文件命名规则,IXXX.adil。 只定义了一个printHelloWorld 接口。

interface IHelloWorldInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    String printHelloWorld();
}

编译后生成 IHelloWorldInterface.java 文件。由于文件是aidl 工具生成的,格式比较乱,代码用工具格式化后IHelloWorldInterface 是一个接口,里面主要是两个类静态虚类public Stub类和private Stub.Proxy 类,Stub为存根的意思,那就是服务端使用,Stub.Proxy 为代理类,客户端使用。

public interface IHelloWorldInterface extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements com.example.louiewh.aidlapplication.IHelloWorldInterface {
        private static final java.lang.String DESCRIPTOR = "com.example.louiewh.aidlapplication.IHelloWorldInterface";

        private static class Proxy implements com.example.louiewh.aidlapplication.IHelloWorldInterface {
            private android.os.IBinder mRemote;
            
            @Override
            public java.lang.String printHelloWorld() throws android.os.RemoteException {
              
            }
        }
    }
}

服务端的实现

服务端 HelloWorldService 继承 IHelloWorldInterface.Stub 接口

public class HelloWorldService  extends IHelloWorldInterface.Stub {
    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

    }

    @Override
    public String printHelloWorld() throws android.os.RemoteException {
        if(mListerner != null) {
            final int begin = mListerner.beginBroadcast();
            for (int i = 0; i < begin; i++) {
                mListerner.getBroadcastItem(i).onAidlListerner(
                        new StringBuilder().
                        append("Pid:").append(android.os.Process.myPid()).
                        append(" Threadtime:").append(SystemClock.uptimeMillis()).
                        toString()
                );
            }
            mListerner.finishBroadcast();
        }
        return "Hello AIDL!";
    }
}

创建Service

创建一个AidlService extends Service。AndroidManifext 中注册Service。 在Service 的
onBind 中返回HelloWorldService。

public class AidlService extends Service {
    public final static String TAG = "AidlService";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new HelloWorldService();
    }
}

代理端调用

service 启动

android Service 启动有两种方式,一种startService,一种binderService. 由于我们要获取Service 的代理端,使用binderService。在 MainActivity onCreate 中 binderService。

 Intent intent = new Intent(context, AidlService.class);
 context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

获取Service Proxy

然后 New 一个ServiceConnection, 在ServiceConnection中的onServiceConnected回调函数中会通过IHelloWorldInterface.Stub.asInterface返回Stub.proxy 对象。这样我们就拿到了服务的代理端。在MainActivity中设置text.setOnClickListener,当点击时显示printHelloWorld 的结果,就是进程PID和uptimeMillis时间。

ServiceConnection helloWorldConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            remoteService = IHelloWorldInterface.Stub.asInterface(iBinder);

         }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    
text.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    text.setText(remoteService.printHelloWorld());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
 });

service 退出

在Activity退出时,需要unbinder

protected void onDestroy() {
     helloWorldProxy.unbindService();
     super.onDestroy();
}

一个Aidl 通信的架构就基本完完成了.

需要注意的是ServiceConnection是异步通信

设置listener

现在客户端可以调用服务端了,如果服务端需要通知客户端呢,就需要listener 出场了,listener 同样基于Aidl。定义一个onAidlListerner 回调。

1. 定义IAidlListernerInterface

interface IAidlListernerInterface {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    void onAidlListerner(String str);
}

2. 定义一个抽象类 AidlListerner 继承IAidlListernerInterface.Stub。

  public  abstract class AidlListerner extends IAidlListernerInterface.Stub {
}

3. IHelloWorldInterface.aidl 中增加两个函数,注册listener 和 反注册listener。

 void registerListerner(IAidlListernerInterface listener);

 void unregisterListerner(IAidlListernerInterface listener);

4. HelloWorldService 中同样实现这两个函数。

同时有一个变量 ArrayList<IAidlListernerInterface> mListerner = new ArrayList<IAidlListernerInterface>() 保存,

在调用printHelloWorld 时,回调listener函数。

 public String printHelloWorld() throws RemoteException {
        if(mListerner != null) {
            for (int i = 0; i < mListerner.size(); i++) {
                mListerner.get(i).onAidlListerner(new StringBuilder().append("current time:").append(SystemClock.currentThreadTimeMillis()).toString());;
            }
        }
        return "Hello AIDL!";
    }

5. MainActivity 中 注册

这样在点击text 的时候 listenertext 会显示printHelloWorld 的结果。

remoteService.registerListener(new AidlListener() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }

            @Override
            public void onAidlListerner(String str) throws RemoteException {
                listenerText.setText(str);
            }
        });        

onDestory 中 unregisterListener。

6. 多进程模式下RemoteCallbackList

现在Service 和Activity 运行在一个进程中,Service 和 Client 通常运行在不同的进程中,现在我们配置Service 运行在另外一个进程。 这时候需要注意的是在HelloWorldService 中保存listener 的变量mListerner 类型为ArrayList 修改为: RemoteCallbackList<IAidlListernerInterface> mListerner = new RemoteCallbackList<IAidlListernerInterface>();
原因是因为垮进程的listener 的指针地址改变了。listener 的遍历方式也发生了变化。

 <service
     android:name=".AidlService"
     android:process=":AidlService">
 </service>  
if(mListerner != null) {
     final int begin = mListerner.beginBroadcast();
     for (int i = 0; i < begin; i++) {
         mListerner.getBroadcastItem(i).onAidlListerner(new StringBuilder().
                     append("Pid:").append(android.os.Process.myPid()).                      append("Current time:").append(SystemClock.currentThreadTimeMillis()).
                        toString()
        );
    }
    mListerner.finishBroadcast();
}

代理服务

在一个应用里可能有很多这样的Aidl 文件,通常的做法是每个Aidl 文件都都建一个Service 用来bind,这样一个APK 进程中有很多Service 存在。是不是我们用一个Service 就可以了,因为都是在后台运行,这样就大大减小了对内存的消耗。在讲到Aidl 传输数据类型的时候Aidl 本身也支持Aidl自动生成的Interface 类型的传输,而我们定义的业务Service,比如HelloWorldService 本身就是继承Aidl 自动生成的Interface 类型。所以可以有一个专业的Service 来传递业务Service。 通常在获取系统服务的时候是 getApplicationContext().getSystemService("XXX"), 同样也定义一个这样的API,来为应用内的调用提供APK 级别的Service 服务。

定义 IAidlBinderService.aidl

这个Aidl 文件之定义一个接口getService.

interface IAidlBinderService {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    IBinder getService(String service);
}

所有的业务Service 都通过getService 获取。

AidlBinderService 的服务端

onBind 的时候返回AidlBinderService 的Binder。
通过getService 传递进来的参数返回不同的Service 服务。这里一共两种业务Service,HelloWorldService HelloAidlService。这两种Service 单独一个java 文件,和Service 代码分离,当加入一个新的Service的时候,通常定义Aidl 文件,Service 代码继承 Stub 接口。然后定义getService 的字符串就可以加入一个新的Service。

public class AidlService extends Service {

    public final static String HELLOWORLDSERVICE = "HelloWorldService";
    public final static String HELLOAIDLSERVICE = "HelloAidlService";
    public final static String TAG = "AidlService";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new AidlBinderService();
    }

    class AidlBinderService extends IAidlBinderService.Stub{

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public IBinder getService(String service) throws RemoteException {
            switch(service) {
                case HELLOWORLDSERVICE:
                    return new HelloWorldService();
                case HELLOAIDLSERVICE:
                    return new HelloAidlService();
                default:
                    return null;
            }
        }
    }
}

AidlBinderService 的代理。

在处理AidlBinderService 的代理的时候,通常是在 ServiceConnection 回调中得到 iBinder。 这是一个异步的通信。我们希望代码能够耦合度更低,和业务能够分开,把AidlBinderService 单独的实现子啊一个java 文件中。一个做法是使用异步转同步的方法,直到ServiceConnection 的 onConnected 返回。但是这样毕竟有系统时间的消耗,而Service 的binder通常在APK 启动时,影响启动速度。这里继续沿用异步回到,两种方法,设置回调函数,使用Handler。AidlBinderServiceProxy 为单例模式,构造函数中去bindService,在onServiceConnected 使用传递进来的Handler 发送binderService 成功的消息。

获取AidlBinderService 的代理

public class AidlBinderServiceProxy {
    static final String TAG = "AidlBinderServiceProxy";
    static final int AidlBinderService = 1;
    private Context mContext;
    private ServiceConnection mConnection;
    private IAidlBinderService mRemoteService;
    private Handler mHandler;
    private static AidlBinderServiceProxy instance;

    private AidlBinderServiceProxy(Context context, Handler handler) {
        mContext = context;
        mHandler = handler;
        initServiceConnection();
        Intent intent = new Intent(context, AidlService.class);
        context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    public static AidlBinderServiceProxy instance(Context context, Handler handler){

        synchronized (AidlBinderServiceProxy.class) {
            if(instance == null) {
                instance = new AidlBinderServiceProxy(context, handler);
            }
        }

        return instance;
    }

    public void unbindService() {

        Log.d(TAG, "unbindService");
        mContext.unbindService(mConnection);
        instance = null;
    }

    private void initServiceConnection() {
        mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                mRemoteService = IAidlBinderService.Stub.asInterface(iBinder);
                Message message = Message.obtain(mHandler, AidlBinderService);
                mHandler.sendMessage(message);
                Log.d(TAG, "onServiceConnected");
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {

            }
        };
    }

    public IBinder getService(String service) {
        if(mRemoteService == null) {
        }

        try {
            return mRemoteService.getService(service);
        } catch (RemoteException e) {
            Log.e(TAG, "getService " + "service");
            e.printStackTrace();
        }

        return null;
    }
}

实现业务Service 的Proxy

实现两个业务Service 的Proxy。HelloAidlProxy 如下,在构造函数中通过getService 获取到IBinder 对象,通过I HelloAidlInterface.Stub.asInterface 函数转为代理对象。

public class HelloAidlProxy {
    static final String TAG = "HelloAidlProxy";
    public AidlBinderServiceProxy  mAidlBinderService;
    private IHelloAidlInterface mRemoteService;


    public HelloAidlProxy(AidlBinderServiceProxy proxy) {

        mAidlBinderService = proxy;
        IBinder binder = mAidlBinderService.getService(AidlService.HELLOAIDLSERVICE);

        mRemoteService = IHelloAidlInterface.Stub.asInterface(binder);
    }

    public PidInfo getPidInfo() {
        if(mRemoteService != null){
            try {
                return mRemoteService.getPidInfo();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        return null;
    }
}

调用

修改MainActivity 的实现, 实现一个内部Handler 类:

class AidlHandler extends Handler{
    public void handleMessage(Message msg) {

        switch(msg.what) {
            case AidlBinderServiceProxy.AidlBinderService:
                Log.d("louie", "AidlBinderService");
                helloWorldProxy = new HelloWorldProxy(mAidlBinderService);
            default:
                break;
            }
        });            

使用代理服务前后App 的结构变化:

Aidl Service

https://www.processon.com/view/link/57a5991be4b02c28bf471316

Parcelable

上面已经实现了Aidl 生成的类型的数据跨进程传递,Aidl 生成的数据类型主要用于C/S 这样的架构。对于普通的对象如何处理呢,android 给我们准备了Parcelable 这种数据类型。和java 的Serializable 比较类似,Serializable 序列化基于文本,Parcelable 基于binder,效率更高。Parcelable 基于android提供的Parcel类型,将数据写入Parcel打包,需要的时候再从Parcel 读出完成序列化。

定义类继承 Parcelable

类 PidInfo implements Parcelable

  • 重写writeToParcel方法,将对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中
  • 重写describeContents方法,内容接口描述,默认返回0就可以
  • 实例化静态内部对象CREATOR实现接口Parcelable.Creator,在 createFromParcel new 了一个参数为 Parcel 构造函数,需要注意的是:
    这个构造函数实现的时候的读写顺序要和writeToParcel 方法一致
public class PidInfo implements Parcelable {
    private int mPid;
    
    PidInfo(int pid ){
        mPid = pid;
    }

    PidInfo(Parcel in ) {
        mPid = in.readInt();
    }

    public int getPid(){
        return mPid;
    }

    public void setPid(int pid ){
        mPid = pid;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mPid);
    }

    public static final Creator<PidInfo> CREATOR = new Creator<PidInfo>(){

        @Override
        public PidInfo createFromParcel(Parcel source) {
            return new PidInfo(source);
        }

        @Override
        public PidInfo[] newArray(int size) {
            return new PidInfo[0];
        }
    };
}

AIDL 声明

PidInfo 定义完后还不能直接使用,需要在Aidl 中声明:

  • 新建一个和类同名的Aidl 文件 PidInfo.aidl
  • 在PidInfo.aidl 中 声明PidInfo 为parcelable 类型
parcelable PidInfo;

code

Aild 的使用大概就是这些,code 地址:github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容