Android进程间通信 AIDL使用详解

一.概述

Android跨进程通信的方式有Bundle,文件共享,Messenger,AIDL,socket.其中Messenger底层也是用到了AIDL.所以理解AIDL的使用以及原理,对于我们去理解进程间通信是有很大的帮助。今天,就AIDL的使用做一个详细的介绍。

二.支持的数据类型

  1. 八种基本数据类型:byte、char、short、int、long、float、double、boolean;
  2. String,CharSequence;
  3. 实现了Parcelable接口的数据类型;
  4. List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象;
  5. Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象;

三.服务端编码

这里使用一个例子来说明AIDL的使用,需要实现的功能是:客户端通过绑定服务端的Service的方式来调用服务端的方法,获取服务端的书籍列表并向其添加书籍,实现应用间的数据共享

服务端的代码如下:
新建一个项目,项目的包名为:com.demo.aidlservice
接着右键新建AIDL文件,命名为Book.创建完成后,系统会自动创建一个aidl文件夹,在里面找到了我们刚才创建的Book.aidl文件,Book.aidl有一个默认的方法,可以删除。
在这里插入图片描述
然后我们来定义Book实体类,Book类有bookId和bookName两个属性,并且让这个类实现Parcelable 接口

package com.demo.aidlservice;
import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

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

    @Override
    public int describeContents() {
        return 0;
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}

接着,修改Book.aidl文件,声明Book类为parcelable

// Book.aidl
package com.demo.aidlservice;
parcelable Book;
// Declare any non-default types here with import statements

然后,我们再创建一个IBookManager.aidl文件,里面定义了getBookList和addBook两个方法,这里需要注意,我们需要手动去导入包名。

// IBookManager.aidl
package com.demo.aidlservice;
import com.demo.aidlservice.Book;
// Declare any non-default types here with import statements

interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
   List<Book> getBookList();
   void addBook(in Book book);
}

然后,我们需要去clean下工程,使系统生成我们需要的文件,真正起到进程间通信的是系统帮我生成的这个文件。这个文件里面有内部静态抽象类 Stub,后面需要用到来生成Binder对象。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\maguifeng\\android-code\\AIDLService\\app\\src\\main\\aidl\\com\\demo\\aidlservice\\IBookManager.aidl
 */
package com.demo.aidlservice;
// Declare any non-default types here with import statements

public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.demo.aidlservice.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.demo.aidlservice.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.demo.aidlservice.IBookManager interface,
 * generating a proxy if needed.
 */
public static com.demo.aidlservice.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.demo.aidlservice.IBookManager))) {
return ((com.demo.aidlservice.IBookManager)iin);
}
return new com.demo.aidlservice.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.demo.aidlservice.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.demo.aidlservice.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.demo.aidlservice.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.demo.aidlservice.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;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public java.util.List<com.demo.aidlservice.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.demo.aidlservice.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.demo.aidlservice.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.demo.aidlservice.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);
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
public java.util.List<com.demo.aidlservice.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.demo.aidlservice.Book book) throws android.os.RemoteException;
}

介绍完了如何定义aidl接口。现在现在需要来创建一个 Service 供客户端远程绑定了,命名为BookManagerService

package com.demo.aidlservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.ArrayList;
import java.util.List;

/**
 * <pre>
 *
 * Created by MarvinMa
 * Time : 2019/5/8-9:50
 * Desc :
 * </pre>
 */
public class BookManagerService extends Service {
    private List<Book> bookList = new ArrayList<>();

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

    private void initData() {
        bookList.add(new Book(1,"语文"));
        bookList.add(new Book(2,"数学"));
        bookList.add(new Book(3,"英语"));
    }

    private IBookManager.Stub binder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return bookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            if(book != null){
                bookList.add(book);
            }
        }
    };
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}

客户端要找到服务端的Service,需要指定服务端的包名,还要设置action

 <service android:name=".BookManagerService">
        <intent-filter>
              <action android:name="com.demo.aidlservice.BookManagerService"></action>
         </intent-filter>
  </service>

至此,服务端的代码编写完毕

四.客户端编码

客户端需要再创建一个新的工程,包名命名为 com.demo.aidlclient
首先,我们需要把服务端的aidl接口和Book类完整复制过来,这里注意服务端包名和客户端的包名要保持一致。
在这里插入图片描述
然后,我们在客户端的MainActivity的布局新增两个按钮,获取和新增书籍按钮,分别向服务端获取数据以及新增数据。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.demo.aidlclient.MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_getBookList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="获取书籍" />

    <Button
        android:id="@+id/btn_addBook"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="添加书籍" />

</LinearLayout>
package com.demo.aidlclient;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.demo.aidlservice.Book;
import com.demo.aidlservice.IBookManager;
import com.demo.aidlservice.R;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    private List<Book> bookList;
    private IBookManager iBookManager;
    private boolean isConnect;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService();
        findViewById(R.id.btn_getBookList).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(isConnect){
                    try {
                        bookList = iBookManager.getBookList();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    logUtils();
                }
            }
        });
        findViewById(R.id.btn_addBook).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isConnect){
                    try {
                        iBookManager.addBook(new Book(4,"这是客户端向服务端添加的书籍化学"));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    logUtils();
                }
            }
        });
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iBookManager = IBookManager.Stub.asInterface(service);
            isConnect = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isConnect = false;
        }
    };

    private void bindService() {
        Intent intent = new Intent();
        intent.setPackage("com.demo.aidlservice");
        intent.setAction("com.demo.aidlservice.BookManagerService");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    private void logUtils() {
        for (Book book : bookList) {
            Log.e("MainActivity", book.bookId + "/" + book.bookName);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(isConnect){
            unbindService(serviceConnection);
        }
    }
}

最后通过日志来观察服务端和客户端的数据变化
在这里插入图片描述

五.总结

总结下AIDL使用:服务端首先要创建一个Service用来监听客户端的请求,然后创建一个AIDL文件,声明暴露给客户端的接口,最后在Service中实现这个aidl即可。客户端首先要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所需要的类型。接着调用AIDL中的方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值