p, li { white-space: pre-wrap; }
IPC即Inter-Process Communication,含义为进程间的通信或者跨进程通信,是指两个进程之间进行数据交换的过程。
android 中使用多进程
android中使用多进程很“简单”,简单到只需要一行代码就可以开启一个进程。但是真正要用好多进程还是不简单的。android中使用进程只需要在manfiest中进行申明,形式如下:
<activity
android:name="com.reoger.cc.hello"
android:process = "com.reoger.cc.hello.word"/>
当然,也可以process的属性也可以设为以":"开始,例如
android:process =":remote"
表示运行在当前activity或者service的包名+.remote。
Binder基础
什么是binder?
Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,他的设备驱动是/dev/binder,该通信方式在Linux中没有。从Android Framework角度说,Binder是ManagerService的桥梁。从Android应用层角度来说,Binder是客户端和服务端调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
使用基础
本例将用aidl实现进程间的通信,同时服务端将会每隔5s给订阅图书信息的人发一条信息,同时客户端也可以其进行订阅和取消订阅操作。
首先先给出使用实例的整体框架视图,如图:
首先新建一个序列化的bean对象,命名为Book.java。
public class Book implements Parcelable {
private int id;
private String bookName;
public Book(int id, String bookName) {
this.id = id;
this.bookName = bookName;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", bookName='" + bookName + '\'' +
'}';
}
protected Book(Parcel in) {
id = 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(id);
dest.writeString(bookName);
}
}
然后新建aidl文件夹,并在里面添加Book.aidl文件,代码为:
// Book.aidl.aidl
package reoger.hut.test2;
parcelable Book;
记住一点,在aidl中,无论代码是否存在于同一个包中,都需要进行导入。
IBookManager.aidl的逻辑代码如下:
// IBookManager.aidl
package reoger.hut.test2;
import reoger.hut.test2.Book;
import reoger.hut.test2.IOnNewBookArrivedArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedArrivedListener listener);
void unRegisterListener(IOnNewBookArrivedArrivedListener listener);
}
然后是IOnNewBookArrivedArrivedListener.aidl的代码,如下:
// IOnNewBookArrivedArrivedListener.aidl
package reoger.hut.test2;
import reoger.hut.test2.Book;
interface IOnNewBookArrivedArrivedListener {
void onNewBookArrived(in Book newBook);
}
接下来是我们服务端的实现,这里我采用的是通过一个service来实现,
BookManagerService.java的代码如下。
public class BookManagerService extends Service {
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
private RemoteCallbackList<IOnNewBookArrivedArrivedListener> mListeners = new RemoteCallbackList<>();
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
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);
}
/**
* 注册监听
* @param listener 监听对象
* @throws RemoteException
*/
@Override
public void registerListener(IOnNewBookArrivedArrivedListener listener) throws RemoteException {
mListeners.register(listener);
Log.d("TAG", "增添监听成功");
}
@Override
public void unRegisterListener(IOnNewBookArrivedArrivedListener listener) throws RemoteException {
mListeners.unregister(listener);
Log.d("TAG", "增添监听成功");
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "android"));
mBookList.add(new Book(2, "ios"));
new Thread(new ServiceWorker()).start();
}
public BookManagerService() {
}
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("reoger.hut.test2.permission.ACCESS_BOOK_SERVICE");
if(check == PackageManager.PERMISSION_DENIED){
Log.e("TAG","没有对应的权限,无法启动service");
return null;
}
return mBinder;
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book book = new Book(bookId, "new Book# " + bookId);
onNewBookArrived(book);
}
}
}
private void onNewBookArrived(Book book) {
mBookList.add(book);
final int N = mListeners.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArrivedArrivedListener listener = mListeners.getBroadcastItem(i);
Log.d("TAG", "onNew Book Arrived ,notify listener :");
try {
if (listener != null) {
listener.onNewBookArrived(book);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
mListeners.finishBroadcast();
}
}
在manfiest文件中对service进行申明,并将其设置在一个单独的线程中运行,代码如下:
<service
android:name=".BookManagerService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
最后是我们的客户端代码,如下:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0x66:
Log.d("TAG","普天同庆 奔走相告"+msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
List<Book> list = null;
try {
list = bookManager.getBookList();
for (Book book : list) {
Log.d("TAG",book.toString());
}
Book book = new Book(3,"android 开发艺术探索");
bookManager.addBook(book);
List<Book> list2 = bookManager.getBookList();
for (Book book1 : list2) {
Log.d("TAG","--- "+book1.toString());
}
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBookManager bookManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this,BookManagerService.class);
bindService(intent,connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if(bookManager != null && bookManager.asBinder().isBinderAlive()){
try {
Log.d("TAG","正在停止服务...");
bookManager.unRegisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(connection);
super.onDestroy();
}
private IOnNewBookArrivedArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedArrivedListener.Stub(){
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(0x66,newBook).sendToTarget();
}
};
}
最后运行的结果主要通过打印的日志来显示。
通过日志信息,可以明确看到我们的服务端和客服端的确进行的数据交换,并且服务端每隔5s发送一个信息给客户端。
关于AIDL,有以下的几点需要特别的注意:
- AIDL支持基本的数据类型(除short以外的数据类型),也支持String和CharSequence和Parcelable。对于List只支持ArrayList,对于Map只支持HshMap.
- 如果AIDL文件中用到了自定义的Parcelable对象,必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。可以参考上面的示例 。
- AIDL中除了基本的数据类型,其他类型的参数必须标上方向:in,out或者inout,in表示输入型参数,out表示输出型参数,inout表示输入输出型参数。
- 在AIDL中,只支持方法,不支持声明静态常量。
- AIDL包结构在服务端和客户端必须保持一致,否运行会出错,这是因为客户端需要反序列化服务端中和AIDL接口相关的所有类。
- AIDL方法是执行在服务端的Binder线程池中的。