AIDL用法

本文依旧参考别人文章,只是用自己的语言重复一遍,只做加深印象和方便查阅之用
原文地址

服务端代码编写

在创建aidl文件之前,先了解一下

  • AIDL文件可以分为两类。一类用来声明实现了Parcelable接口的数据类型,以供其他AIDL文件使用那些非默认支持的数据类型。还有一类是用来定义接口方法,声明要暴露哪些接口给客户端调用,定向Tag就是用来标注这些方法的参数值。

那上面的“非默认支持的数据类型”是什么呢?
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文件中用到了自定义的Parcelable对象 必须创建同名的AIDL文件 并声明为Parcelable类型本例中,我们需要传递是Book类(将其序列化这不用说)

public class Book implements Parcelable {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Book(String name) {

        this.name = name;
    }

    @Override
    public String toString() {
        return "book name:" + name;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
    }

    protected Book(Parcel in) {
        this.name = in.readString();
    }

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

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
    public void readFromParcel(Parcel dest) {
        name = dest.readString();
    }
}

提一下,如果使用AS的Parcelable插件序列化是没有最后一个readFromParcel()方法(反正我是没有),需要手动填上。

这时我们new aidl文件,名字为Book,并修改(new出来的aidl文件中有个方法,不用可以删除)
如果出现重名,,则先创建aidl,再创建实体类

修改前

package com.kt.ktservice;
interface Book{
   
}

修改后

package com.kt.ktservice;

parcelable Book;//注意小写parecelable

以上就是第一种aidl,用来声明实现了Parcelable接口的数据类型。

现在来创建定义接口方法的aidl文件

package com.kt.ktservice;
import com.kt.ktservice.Book;
//即使是同一个包下也要导入,要注意

interface BookController {

     List getBookList();       

      void addBookInOut(inout Book book);
}

然后make project来生成aidl java代码供我们使用

现在需要来创建一个 Service 供客户端远程绑定了

public class AIDLService extends Service {


    private final String TAG = "YJM";

    private List bookList;

    public AIDLService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        bookList=new ArrayList<>();
        initDate();
    }

    private void initDate() {
        Book book1 = new Book("1");
        Book book2 = new Book("2");
        Book book3 = new Book("123");
        Book book4 = new Book("456");
        bookList.add(book1);
        bookList.add(book2);
        bookList.add(book3);
        bookList.add(book4);
    }

    private final BookController.Stub stub = new BookController.Stub() {

        @Override
        public List getBookList() throws RemoteException {
            return bookList;
        }

        @Override
        public void addBookInOut(Book book) throws RemoteException {
            if (book != null) {
                book.setName("服务器收到了提交的新书");
                bookList.add(book);
            } else {
                Log.e(TAG, "接收到了一个空对象 InOut");
            }
        }

    };

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

@Override
    protected void onStop() {
//  完了解绑一下好习惯
        unbindService(serviceConnection);
        super.onStop();
    }

}

最后,服务端还有一个地方需要注意,因为服务端的Service需要被客户端来远程绑定,所以客户端要能够找到这个Service,可以通过先指定包名,之后再配置Action值或者直接指定Service类名的方式来绑定Service
如果是通过指定Action值的方式来绑定Service,那还需要将Service的声明改为如下所示:

AIDL用法_第1张图片

客户端代码编写

客户端需要再创建一个新的工程,包名命名为 com.kt.ktclient

首先,需要把服务端的AIDL文件以及Book类复制过来,将 aidl 文件夹整个复制到和Java文件夹同个层级下,不需要改动任何代码(甚至包名)

AIDL用法_第2张图片

之后,需要创建和服务端Book类所在的相同包名来存放 Book类
AIDL用法_第3张图片

界面代码 xml两个按钮

public class MainActivity extends AppCompatActivity {

    public BookController bookController;
    public boolean connect;
    public List bookList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent();
        intent.setPackage("com.kt.ktservice");
        intent.setAction("com.kt.abc");**
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
    public void getshu(View view) {
        if(connect) {
            try {
                bookList = bookController.getBookList();
                log();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
    public void change(View view) {
        if(connect) {
            try {
                bookController.addBookInOut(new Book("这是新书"));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookController = BookController.Stub.asInterface(service);
            connect=true;

        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            connect=false;
        }
    };
    private void  log(){
        for (Book b: bookList) {
            Log.e("YJM",b.toString());
        }
    }
}

到此为止,客户端获取到了服务端的数据,也向服务端传送了数据,那么服务端怎么回复客服端呢,而不是客户端一直被动查询呢,请看下篇。

最后

定向tag

在服务端的接口aidl文件中void addBookInOut(inout Book book)方法中的参数有个前缀。这个叫做定向Tag有三种。in 、out、inout。
区别在于in是数据只能由客户端传向服务端,服务端对数据的修改不会影响到客户端

Out类型的表现形式是:数据只能由服务端传向客户端,即使客户端向方法接口传入了一个对象,该对象中的属性值也是为空的,即不包含任何数据,服务端获取到该对象后,对该对象的任何操作,就会同步到客户端这边

inout:自然就是两边刷新数据(提一点:两边的数据虽然一样,但是不是同一个对象)

tag也不能都用inout,在底层是有开销的

你可能感兴趣的:(AIDL用法)