一、前言
最近在学习Android开发艺术探索一书,因为也是开发刚入门,刚学习到AIDL这部分的时候,即使对着书本敲还是遇到许多书上没有总结的问题,故写此篇防止后忘,同时,也希望可以帮助初学的小伙伴贡献自己的一份力量吧。
二、概述
那什么情况下才需要使用到AIDL呢?Android中IPC通信方式其实有很多啊,比如可以使用bundle、文件共享的方式、Messenger以及等等吧,但是再看看,使用Bundle,需要传递的数据可以被序列化,不能被序列化的数据就不能通过它在进程间传递数据了,对于文件共享,由于Android基于Linux,所以对于文件的并发读写没有限制,可能会导致多个线程,对于同一个文件同时进行写操作,这样就很容易导致问题的产生,至于Messenger,其实是对AIDL做了封装,底层实现还是AIDL。那什么情况必须要用AIDL呢,用官方文档中的一句话:“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL”。那毕竟学好数理化,走遍天下都不怕!
三、书中实例走起
实例简介:主要就是模仿实现服务端向客户端提供书籍,在服务端有新书的时候,将这个新书消息推送给客户端。
一.在main文件夹下新建一个aidl文件夹,建议将AIDL相关的所有东西都放在同一个文件夹下。这里依次创建Book类,IBookListener.aidl以及
IBookManager.aidl。
(1)Book类代码如下
package com.example.weixiao.myaidldemo;
import android.os.Parcel;
import android.os.Parcelable;
/**
*
* Created by weixiao on 2017/8/27.
*/
import android.os.Parcel;
import android.os.Parcelable;
/**
*
* Created by weixiao on 2017/8/27.
*/
public class Book implements Parcelable {
private int bookId;
private String bookName;//书名
private int bookId;
private String bookName;//书名
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Parcelable.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 parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
}
this.bookId = bookId;
this.bookName = bookName;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Parcelable.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 parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
}
主要就是实现序列化,用于进程间数据的传递。
(2)IBookListener.aidl(新建aidl和新建activity类似,右键新建AIDL new AIDL FILE,你会发现新建的aidl,接口里有一个basicTypes方法,其实没什么作用,可以直接删除)
// IOnBookListener.aidl
package com.example.weixiao.myaidldemo;
// Declare any non-default types here with import statements
import com.example.weixiao.myaidldemo.Book;
interface IOnBookListener {
void NewBookArrived(in Book book);//新书到达通知回调
}
package com.example.weixiao.myaidldemo;
// Declare any non-default types here with import statements
import com.example.weixiao.myaidldemo.Book;
interface IOnBookListener {
void NewBookArrived(in Book book);//新书到达通知回调
}
主要注意点:
1.手动显示导入
import com.example.weixiao.myaidldemo.Book;
2.AIDL中除了基本数据类型,其他类型的参数必须标上方向:in、out、inout,in表示输入型参数,out输出型参数,inout表示输入输出型参数,AIDL中只支持方法,不支持声明静态常量,是不同于一般接口的。
(3)IBookManager.aidl
// IBookManager.aidl
package com.example.weixiao.myaidldemo;
// Declare any non-default types here with import statements
import com.example.weixiao.myaidldemo.Book;
import com.example.weixiao.myaidldemo.IOnBookListener;
interface IBookManager {
List<Book> getBookList();//获取书籍列表
void addBook(in Book book);//添加新书
void registerListener(IOnBookListener listener);//注册新书通知监听
void unregisterListener(IOnBookListener listener);//取消新书通知
}
package com.example.weixiao.myaidldemo;
// Declare any non-default types here with import statements
import com.example.weixiao.myaidldemo.Book;
import com.example.weixiao.myaidldemo.IOnBookListener;
interface IBookManager {
List<Book> getBookList();//获取书籍列表
void addBook(in Book book);//添加新书
void registerListener(IOnBookListener listener);//注册新书通知监听
void unregisterListener(IOnBookListener listener);//取消新书通知
}
注意点嘛,和上面一样,需要把Book以及IOnBookListener显示导进来,切记。
以上工作完成以后,就是编译AIDL文件了,这里可能会出现一些问题,我就说说我碰到的一些问题吧。
1.aidl unknown type 这个原因还是因为没有显示导入相关类或者AIDL。
2.Error:(101, 58) 错误: 找不到符号
符号: 类 Book
符号: 类 Book
这个需要在build.gradle中添加
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "com.example.weixiao.myaidldemo"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "com.example.weixiao.myaidldemo"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
********************************************************************
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
********************************************************************************************
}
}
以上就是我遇到的主要问题。
二、服务端代码
package service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import com.example.weixiao.myaidldemo.Book;
import com.example.weixiao.myaidldemo.IBookManager;
import com.example.weixiao.myaidldemo.IOnBookListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Created by weixiao on 2017/8/27.
*/
public class BookManagerService extends Service {
private static final String TAG = "BMS";
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);//查了下,主要是保证原子性,就是多线程的情况下,只有一个线程操作
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(
);//CopyOnWriteArrayList支持并发读写,就是多个线程访问
private RemoteCallbackList<IOnBookListener> mListenerList = new RemoteCallbackList<>();//保证关闭时取消新书消息推送
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 registerListener(IOnBookListener listener) throws RemoteException {
mListenerList.register(listener);
}
@Override
public void unregisterListener(IOnBookListener listener) throws RemoteException {
mListenerList.unregister(listener);
}
};
@Override
public void onCreate() {
super.onCreate();
//服务端添加一些新书
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
new Thread(new ServiceWorker()).start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
mIsServiceDestoryed.set(true);
}
private void onNewBookArrived(Book book) throws RemoteException {
//新书到来了
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnBookListener listener = mListenerList.getBroadcastItem(i);
if (listener!=null){
listener.NewBookArrived(book);//将新书消息推给客户端
}
Log.d(TAG, "onNewBookArrived,notify listener:" + listener);
}
mListenerList.finishBroadcast();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
//do background processing here
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);//5秒推送一次,5秒创建一本新书
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "new Book #" + bookId);
onNewBookArrived(newBook);//
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import com.example.weixiao.myaidldemo.Book;
import com.example.weixiao.myaidldemo.IBookManager;
import com.example.weixiao.myaidldemo.IOnBookListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Created by weixiao on 2017/8/27.
*/
public class BookManagerService extends Service {
private static final String TAG = "BMS";
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);//查了下,主要是保证原子性,就是多线程的情况下,只有一个线程操作
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(
);//CopyOnWriteArrayList支持并发读写,就是多个线程访问
private RemoteCallbackList<IOnBookListener> mListenerList = new RemoteCallbackList<>();//保证关闭时取消新书消息推送
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 registerListener(IOnBookListener listener) throws RemoteException {
mListenerList.register(listener);
}
@Override
public void unregisterListener(IOnBookListener listener) throws RemoteException {
mListenerList.unregister(listener);
}
};
@Override
public void onCreate() {
super.onCreate();
//服务端添加一些新书
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
new Thread(new ServiceWorker()).start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
mIsServiceDestoryed.set(true);
}
private void onNewBookArrived(Book book) throws RemoteException {
//新书到来了
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnBookListener listener = mListenerList.getBroadcastItem(i);
if (listener!=null){
listener.NewBookArrived(book);//将新书消息推给客户端
}
Log.d(TAG, "onNewBookArrived,notify listener:" + listener);
}
mListenerList.finishBroadcast();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
//do background processing here
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);//5秒推送一次,5秒创建一本新书
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "new Book #" + bookId);
onNewBookArrived(newBook);//
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
当然记得在清单文件中注册哦,并设置为开启一个新的进程。
完成上面的两步,客户端就简单多了,如下
三、客户端
package com.example.weixiao.myaidldemo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.util.List;
import service.BookManagerService;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "BookManagerActivity";
private static final int MESSAGE = 1;
private IBookManager mIBookManager;
private int i = 0;
private IOnBookListener mBookListener = new IOnBookListener.Stub() {
@Override
public void NewBookArrived(Book book) throws RemoteException {
mHandler.obtainMessage(MESSAGE, book).sendToTarget();
}
};
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE:
//收到新书
Book book = (Book) msg.obj;
i++;
Log.e(TAG, "receive book" + i + "::" + book.getBookName());
break;
default:
super.handleMessage(msg);
break;
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager manager = IBookManager.Stub.asInterface(iBinder);
try {
mIBookManager = manager;
List<Book> books = manager.getBookList();//获取服务端图片列表并toast
for (Book book : books) {
Toast.makeText(MainActivity.this, book.getBookName(), Toast.LENGTH_SHORT).show();
}
manager.addBook(new Book(3, "Android开发艺术探索"));//添加新书
manager.addBook(new Book(4, "Android开发艺术探索2"));//添加新书
manager.addBook(new Book(5, "Android开发艺术探索3"));//添加新书
List<Book> newList = manager.getBookList();
manager.registerListener(mBookListener);//注册新书推送
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);//绑定服务
}
@Override
protected void onDestroy() {
if (mIBookManager != null && mIBookManager.asBinder().isBinderAlive()) {
try {
Log.e(TAG, "unregister listener" + mBookListener);
mIBookManager.unregisterListener(mBookListener);//取消新书推送消息
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.util.List;
import service.BookManagerService;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "BookManagerActivity";
private static final int MESSAGE = 1;
private IBookManager mIBookManager;
private int i = 0;
private IOnBookListener mBookListener = new IOnBookListener.Stub() {
@Override
public void NewBookArrived(Book book) throws RemoteException {
mHandler.obtainMessage(MESSAGE, book).sendToTarget();
}
};
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE:
//收到新书
Book book = (Book) msg.obj;
i++;
Log.e(TAG, "receive book" + i + "::" + book.getBookName());
break;
default:
super.handleMessage(msg);
break;
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager manager = IBookManager.Stub.asInterface(iBinder);
try {
mIBookManager = manager;
List<Book> books = manager.getBookList();//获取服务端图片列表并toast
for (Book book : books) {
Toast.makeText(MainActivity.this, book.getBookName(), Toast.LENGTH_SHORT).show();
}
manager.addBook(new Book(3, "Android开发艺术探索"));//添加新书
manager.addBook(new Book(4, "Android开发艺术探索2"));//添加新书
manager.addBook(new Book(5, "Android开发艺术探索3"));//添加新书
List<Book> newList = manager.getBookList();
manager.registerListener(mBookListener);//注册新书推送
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);//绑定服务
}
@Override
protected void onDestroy() {
if (mIBookManager != null && mIBookManager.asBinder().isBinderAlive()) {
try {
Log.e(TAG, "unregister listener" + mBookListener);
mIBookManager.unregisterListener(mBookListener);//取消新书推送消息
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
至此就大功告成了,下面是测试结果:
09-16 21:16:55.907 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book2::new Book #7
09-16 21:17:00.909 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book3::new Book #8
09-16 21:17:05.911 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book4::new Book #9
09-16 21:17:10.912 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book5::new Book #10
09-16 21:17:15.914 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book6::new Book #11
09-16 21:17:17.873 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: unregister listener com.example.weixiao.myaidldemo.MainActivity$1@e20bbf5
09-16 21:17:00.909 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book3::new Book #8
09-16 21:17:05.911 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book4::new Book #9
09-16 21:17:10.912 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book5::new Book #10
09-16 21:17:15.914 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: receive book6::new Book #11
09-16 21:17:17.873 27264-27264/com.example.weixiao.myaidldemo E/BookManagerActivity: unregister listener com.example.weixiao.myaidldemo.MainActivity$1@e20bbf5
四、总结
其实,因为是初学aidl,所以这点东西花费了我许多时间,但回过头,把之前的问题都梳理一遍,多少还是有些收获的,不要在乎每次进步多少,只要是再向前走,就别怕,就像蜗牛里面唱的那样不是嘛,我成为不了别人,我只能做好自己!加油。