最近被公司调到了其他组,帮忙了一段时间Flutter,Android的东西落下了不少,还是打算继续从Framework方面继续学习。关于Android的IPC,之前也了解过,Binder的机制也可以说是研究过,但是由于不经常用,忘得也差不多了,现在连AIDL的基本使用都快忘了,更不用或Binder。所以这篇文章主要来介绍一下AIDL的基本使用。
一、服务端
服务端,也就是数据的提供方
第一步:创建一个实体类Book,要注意,Book要进行序列化的操作,也就是实现Parcelable接口
public class Book implements Parcelable {
private int bookId;
private String bookName;
private double bookPrice;
private List<String>bookShops;
public Book(int bookId,String bookName,double bookPrice,List<String>bookShops){
this.bookId = bookId;
this.bookName = bookName;
this.bookPrice = bookPrice;
this.bookShops = bookShops;
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
bookPrice = in.readDouble();
bookShops = in.createStringArrayList();
}
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);
dest.writeDouble(bookPrice);
dest.writeStringList(bookShops);
}
public void readFromParcel(Parcel in){
bookId = in.readInt();
bookName = in.readString();
bookPrice = in.readDouble();
bookShops = in.createStringArrayList();
}
}
第二步:创建两个AIDL文件:IBook和IBookManager。
IBook用于实体类Book的序列化声明,如下
// IBook.aidl
package com.example.bindertest;
parcelable Book;
IBookManager提供通信接口,用于通信代码的生成,注意:我们需要导入IBook这个类,如下:
// IBookManager.aidl
package com.example.bindertest;
import com.example.bindertest.IBook;
interface IBookManager {
List<Book>getBookList();
void addBook(in Book book);
}
第三步:创建服务Service
在创建Service之前,我们先进行项目的Build操作,Android Studio会根据aidl文件IBookManager.aidl生成对应的java类IBookManager.java,然后创建IBookManager.Stub对象,也就是Service中onBind方法需要的IBinder对象,Service的代码如下:
public class BookService extends Service {
private List<Book> bookList;
private final IBookManager.Stub stub = 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);
}else{
Toast.makeText(BookService.this.getApplicationContext(), "数据异常", Toast.LENGTH_SHORT).show();
}
}
};
@Override
public void onCreate() {
super.onCreate();
bookList = new ArrayList<>();
initData();
}
private void initData(){
List<String>bookShops = new ArrayList<>();
bookShops.add("新华书店");
bookShops.add("图灵书店");
Book book1 = new Book(0,"《活着》",26f,bookShops);
Book book2 = new Book(1,"《时间管理》",32f,bookShops);
Book book3 = new Book(2,"《狼道》",45f,bookShops);
bookList.add(book1);
bookList.add(book2);
bookList.add(book3);
}
@Override
public IBinder onBind(Intent intent) {
return stub;
}
}
当然不要忘了在清单文件中对Service进行注册,如下
<service
android:name=".BookService"
android:enabled="true"
android:exported="true">
</service>
第四步:启动Service
千万不要忘记启动Service,我使用了最简单的启动方法,在MainActivity中启动了服务:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this,BookService.class));
}
}
二、客户端
第一步:进行相关文件的拷贝
我们重新创建一个项目或者Module作为客户端,将服务端的IBook.aidl,IBookManager.aidl,Book.java这三类拷入到客户端,要注意的是:路径一定要和服务端的路径一样,如下图所示
红框所示为服务端包名路径,而蓝框为客服端的包名路径,不要弄错了
第二步:客户端进行连接,服务端数据的获取和修改,如下
public class MainActivity extends AppCompatActivity {
private IBookManager bookManager;
private boolean connected;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Toast.makeText(MainActivity.this, "服务链接成功", Toast.LENGTH_SHORT).show();
bookManager = IBookManager.Stub.asInterface(service);
connected = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Toast.makeText(MainActivity.this, "服务断开", Toast.LENGTH_SHORT).show();
connected = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//服务链接
findViewById(R.id.service_con_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
//第一种方法
intent.setClassName("com.example.bindertest","com.example.bindertest.BookService");
//第二种方法
// intent.setComponent(new ComponentName("com.example.bindertest","com.example.bindertest.BookService"));
getApplicationContext().bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
}
});
//解绑
findViewById(R.id.service_discon_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getApplicationContext().unbindService(serviceConnection);
}
});
//从服务端获取数据
findViewById(R.id.get_book_list_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
List<Book> bookList = bookManager.getBookList();
for (Book book : bookList) {
Log.i("TAG",book.toString());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
//修改服务端数据
findViewById(R.id.add_book_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book = new Book(4,"《红人经济》",45,new ArrayList<>());
try {
bookManager.addBook(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}
要注意:进行服务连接时,要指定包名和类名
三、其他
aidl文件中有三个关键字:in,out,inout,这三个关键字,建议大家看这篇文章,说的很清楚,而我要说的是自己踩得坑:
如果在AIDL文件中的参数为in,则重写实体类中Parcelable的writeToParcel
如果在AIDL文件中的参数为out,则重写实体类中的readFromParcel
如果在AIDL文件中的参数为inout,以上两个方法都要重写
当然,默认是in,所以实现Parcelable接口就行了;而out和inout要额外注意一下
以上就是AIDL的基本使用