3.Android中的IPC方式---使用AIDL(1)

Messenger是以串行的方式来处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端任然只能一个个处理,如果有大量的并发请求,那么用Messenger就太不合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。AIDL也是Messenger的底层实现,因此Messenger本质上也是AIDL,只不过系统为我们做了封装从而方便上层的调用,在Binder的基础上可以更加容易理解AIDL。
1.服务端
服务端首先要创建一个Service来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。
2.客户端
客户端所做的事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转换成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
3.AIDL接口的创建
如下所示,我们创建一个后缀为AIDL的文件,在里面声明了一个接口和两个接口方法

// IBookManager.aidl
package com.example.aidl;

// Declare any non-default types here with import statements
import com.example.aidl.Book;
interface IBookManager {
    List<Book> getBookList();
    void  addBook(in Book book);
}

AIDL文件中并不是所有的数据类型都是可以使用的,那么AIDL文件支持哪些数据类型呢

  • 基本数据类型(int , long , char , boolean , double等)
  • String 和 CharSequence
  • List:只支持ArrayList,里面的每个元素都必须能够被AIDL支持
  • Map:只支持HashMap,里面的每个元素都必须能够被AIDL支持,包括key和value
  • Parcelable: 所有实现了Parcelable接口的对象
  • AIDL:所有的AIDL接口本身也可以在AIDL中使用
    其中自定义的Parcelable对象和AIDL对象必须要显式import进来,不管它们是否和当前的AIDL文件位于同一个包内。如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。上面我们用到了Book这个类,就必须要创建Book.aidl,然后在里面添加如下内容
// Book.aidl
package com.example.aidl;
parcelable Book

AIDL中除了基本数据类型,其他类型的参数必须标上方向,in , out或者inout , in表示输入型参数,out表示输出型参数,inout表示输入输出型参数。AIDL接口中只支持方法,不支持声明静态常量,这一点区别于传统接口。
为了方便AIDL的开发,建议把所有和AIDL相关的类和文件全部放入同一个包中,这样的好处在于,当客户端是另一个应用时,我们可以直接把整个包复制到客户端工程中。AIDL包结构在服务端和客户端要保持一致,否则运行会出错,这是因为客户端需要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一样的话,就无法成功反序列化,程序也就无法正常运行。
4.远程服务端Service的实现
为了实现这个AIDL接口,我们先创建一个Service,称为BookManagerService,代码如下

package com.example.aidl
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class BookManagerService extends Service {
    private static final String TAG = "BMS";
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
             mBookList.add(book);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1,"Android"));
        mBookList.add(new Book(2,"IOS"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

上面是一个服务端Service的典型实现,首先在onCreate中初始化添加了两本书的信息,然后创建了一个Binder对象并在onBind中返回,这个对象继承自IBookManager.Stub并实现了它内部的AIDL方法,之前在Binder那一节已经介绍了。这里主要看getBookList和addBook这两个AIDL方法的实现,这里采用了CopyOnWriteArrayList,这个CopyOnWriteArrayList支持并发读写。前面提到,AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情况,所以要在AIDL方法中处理线程同步,这里使用CopyOnWriteArrayList来进行自动的线程同步。
前面说AIDL能使用的List只有ArrayList,但是这里却使用的CopyOnWriteArrayList(不是继承自ArrayList),为什么能够正常工作呢?这是因为AIDL支持的是抽象的List,而List只是一个接口,虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端。与此类似的还有ConcurrentHashMap。
然后我们需要在XML中注册这个Service
在这里插入图片描述
5.客户端的实现
客户端首先要绑定远程服务,绑定成功后将服务端返回的Binder对象转换成AIDL接口,然后就可以通过这个接口去调用服务端的远程方法了,代码如下:

package com.example.aidl;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

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.util.Log;

import java.util.List;

public class BookManagerActivity extends AppCompatActivity {
    public static final String TAG = "BookManagerActivity";
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> list = bookManager.getBookList();
                Log.i(TAG,"query book list,list type: " + list.getClass().getCanonicalName());
                Log.i(TAG,"query book list:" + list.toString());
            }catch (RemoteException e){
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this,BookManagerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

绑定成功以后,会通过bookManager去调用getBooklist方法,然后打印出所获取的图书信息。
接着在XML中注册此Activity,运行程序,log如下所示:
在这里插入图片描述
可以发现,虽然我们在服务端返回的是CopyOnWriteArrayList类型,但是客户端收到的仍然是ArrayList类型,证明之前的分析,第二行表示客户端成功得到了服务端的图书列表信息。
这就是一次完完整整的使用AIDL进行IPC的过程了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值