AIDL学习分享

AIDL学习分享

AIDL文件分类:一类是parcelable对象,另一类是interface来定义接口。

服务器端

  1. parcelable对象,那序列化对象的方法有两种,为什么选择parcelable而不是serserializable,是因为设计内存操作时,在进程间通信serserializable的开销过大
// Book.aidl
//用于引入了一个序列化对象Book,供其他的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是一样的
package com.example.service;
//注意parcelable是小写
parcelable Book;
  1. 用来定义方法接口,以供项目使用来完成跨进程通信
// BookManager.aidl
package com.example.service;
//导入所需要使用的非默认支持数据类型的包
import com.example.service.Book;
 
interface BookManager {
    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    List<Book> getBooks();
    Book getBook();
    int getBookCount();
    //传参时除了Java基本类型以及String,CharSequence之外的类型
    //都需要在前面加上定向tag,具体加什么按需而定
    void setBookPrice(in Book book , int price)
    void setBookName(in Book book , String name)
    void addBookIn(in Book book);
    void addBookOut(out Book book);
    void addBookInout(inout Book book);
}
  1. 服务器端还需要编写自己的service,这个service需要在manifest中注册,就是安卓的四大组件之一。
    AIDLService.java类
package com.example.service;
public class AIDLService extends Service {
    //BookManager是aidl通信接口的名字
    private final IBinder mBookManager = new BookManager.Stub() {
        //实现aidl中声明的方法
        //mBookManager就是IBinder对象
    }
    public void onCreate() {
        //可以不用设置视图,可以做一些数据的初始化操作
    }
    public IBinder onBind(Intent intent) {
        //连接成功,返回获取到的aidl通信接口
        //将IBinder对象返回,提供给client端使用
        return mBookManager;
    }
    public void onDestroy() {
        //结束service
        super.onDestroy();
    }
  1. 此处还需要一个Book实体类,保证其包名与Book.aidl一致

客户端

客户端(client)的Activity类

 private BookManager mBookManager = null;
 private List<Book> mBooks;
    /**
     * 尝试与服务端建立连接
     */
    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setAction("com.example.aidl");
        intent.setPackage("com.example.service");
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

//创建ServiceConnection来实现IBinder对象调用aidl生成的类的操作方法
//asInterface是在跨进程时返回Stub.Proxy对象。这个Proxy对象可以直接调用aidl生成类的方法
//有了mServiceConnection ,才能使用attemptToBindService方法连接服务端Service
private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(getLocalClassName(), "service connected");
            //据实际情况返回 IBinder 的本地对象或其代理对象
            mBookManager = BookManager.Stub.asInterface(service);
            mBound = true;
			
			//连接后才可以使用mBookManager 的方法
            if (mBookManager != null) {
                try {
                    mBooks = mBookManager.getBooks();
                    Log.e(getLocalClassName(), mBooks.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

重写onServiceConnected,会传入一个IBinder对象,这个IBinder对象可以转换为mBookManager来操作我们在service中定义的方法。

项目结构图

引用自https://blog.csdn.net/qq_41739313/article/details/123322808
客户端发起请求,Service端返回请求,根据Service端的要求,处理请求。

AIDL工作原理

客户端AIDLActivity和服务器端AIDLService是通过BookManager这个aidl接口文件来实现通信的,从用户的角度看,是AIDLService.java和AIDLActivity.java在进行通信,实际上是客户端的本地代理Proxy在和服务器端的Stub类通过transact和onTransact进行通信;
下面来梳理一下Stub-Proxy的工作流程:
(1)AIDLService中的IBinder对象mBookManager是Stub的具体实现,在AIDLService和AIDLActivity绑定的时候被返回;
(2)被返回的mBookManager最终作为客户端onServiceConnnected中的参数;
(3)客户端AIDLActivity会通过本地代理Proxy调用Stub中的静态方法asInterface,mServiceBinder作为参数,最后拿到接口对象mBookManager;
在asInterface方法中,有如下判断流程:
(1)判断Binder是否处于当前进程,若不在,则构建Proxy并返回,构建Proxy时,把mBookManager赋值给mRemote;
(2)Proxy中实现的接口getInformation会调用mRemote的transact方法,而Binder的通信是靠transact和onTransact实现的;
(3)最后会走到Stub的onTransact,完成对mServiceBinder的调用;

生成java文件的源码分析

  1. IInterface:IRemoteService继承自IInterface,所有Binder通信的接口必须继承IInterface接口
  2. Stub继承了Binder类并实现了IRemoteService,而学过activity与service通信的都知道,在Service中的onBind方法会返回一个Binder对象,而Stub继承了Binder,又是一个抽象类,所以Stub可以作为我们跨进程通信时,服务端自定义Binder的父类。
    Stub 的构造方法中 , 调用了 Binder 的 attachInterface 方法 , 传入了 AIDL 文件的全类名 ; 作用是将该 AIDL 接口与 Binder 进行关联
  3. DESCRIPTOR:Binder的唯一标识,通常是包名+接口名组成的字符串
  4. asInterface:asInterface将服务端Binder转换为客户端所需的AIDL接口类型对象。在方法中首先会调用queryLocalInterface(DESCRIPTOR),参数是binder的唯一标识,用来查询本地接口。如果能查询到本地接口IRemoteService(客户端和服务端处于同一进程),就返回本地接口,不跨进程通信。如果查询不到本地接口,就构造Proxy代理对象并返回,通过代理对象可以跨进程通信。
  5. asBinder():用于返回当前Binder对象
  6. Proxy代理类:构造Proxy时,把服务端的Binder对象赋值给mRemote,Proxy中实现了自定义方法:setPause和setPlay ,通过调用mRemote的transact方法,最后走到Stub的onTransact,完成对服务端Binder的调用。
  7. transact 方法对应 Binder 底层发起 IPC 的请求函数 ;
  8. onTransact 方法用于处理不同的 IPC 请求 , 根据不同的值 , 处理不同的 IPC 请求 ;
    要处理的 IPC 请求对应的常量值定义 : 将要调用的方法变成 int 类型的 ID 常量值 , 根据传入的常量值执行相应的方法 , 方便 IPC 跨进程调用 ;

为什么有ADIL来定义接口,直接public interface不好吗?

通过继承Binder的方式,会获得Service的实例,然后调用Service里所有的方法。当有些方法不想被公开的时候,当然你可以将方法声明为private,但是倘若到后期,又需要公开这些方法,你是不是还要将声明改为public?当方法较多时,显然不符合松耦合的思想。
可以将MyBinder继承Binder的类私有,用MyBinder里的方法,调用Service中的私有方法(这些方法是不公开的,但是可以通过这种方法调用)。这时候,我们如果将MyBider里的方法私有,外部就访问部了Service的方法了。

参考文章:

https://www.jianshu.com/p/332c5a6e5608
https://blog.csdn.net/qq_41739313/article/details/123322808
https://blog.csdn.net/bless2015/article/details/45896427

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值