p, li { white-space: pre-wrap; }
IPC即Inter-Process Communication,含義為進(jìn)程間的通信或者跨進(jìn)程通信摹迷,是指兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過程涯呻。
android 中使用多進(jìn)程
android中使用多進(jìn)程很“簡單”搬味,簡單到只需要一行代碼就可以開啟一個(gè)進(jìn)程撑蚌。但是真正要用好多進(jìn)程還是不簡單的。android中使用進(jìn)程只需要在manfiest中進(jìn)行申明有巧,形式如下:
<activity
android:name="com.reoger.cc.hello"
android:process = "com.reoger.cc.hello.word"/>
當(dāng)然所宰,也可以process的屬性也可以設(shè)為以":"開始,例如
android:process =":remote"
表示運(yùn)行在當(dāng)前activity或者service的包名+.remote够傍。
Binder基礎(chǔ)
什么是binder甫菠?
Binder是Android中的一種跨進(jìn)程通信方式,Binder還可以理解為一種虛擬的物理設(shè)備冕屯,他的設(shè)備驅(qū)動(dòng)是/dev/binder寂诱,該通信方式在Linux中沒有。從Android Framework角度說安聘,Binder是ManagerService的橋梁痰洒。從Android應(yīng)用層角度來說,Binder是客戶端和服務(wù)端調(diào)用的Binder對(duì)象浴韭,通過這個(gè)Binder對(duì)象丘喻,客戶端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù),這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)念颈。
使用基礎(chǔ)
本例將用aidl實(shí)現(xiàn)進(jìn)程間的通信泉粉,同時(shí)服務(wù)端將會(huì)每隔5s給訂閱圖書信息的人發(fā)一條信息,同時(shí)客戶端也可以其進(jìn)行訂閱和取消訂閱操作。
首先先給出使用實(shí)例的整體框架視圖嗡靡,如圖:
首先新建一個(gè)序列化的bean對(duì)象跺撼,命名為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;
記住一點(diǎn),在aidl中点骑,無論代碼是否存在于同一個(gè)包中,都需要進(jìn)行導(dǎo)入谍夭。
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);
}
接下來是我們服務(wù)端的實(shí)現(xiàn),這里我采用的是通過一個(gè)service來實(shí)現(xiàn)紧索,
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);
}
/**
* 注冊(cè)監(jiān)聽
* @param listener 監(jiān)聽對(duì)象
* @throws RemoteException
*/
@Override
public void registerListener(IOnNewBookArrivedArrivedListener listener) throws RemoteException {
mListeners.register(listener);
Log.d("TAG", "增添監(jiān)聽成功");
}
@Override
public void unRegisterListener(IOnNewBookArrivedArrivedListener listener) throws RemoteException {
mListeners.unregister(listener);
Log.d("TAG", "增添監(jiān)聽成功");
}
};
@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","沒有對(duì)應(yīng)的權(quán)限,無法啟動(dòng)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文件中對(duì)service進(jìn)行申明珠漂,并將其設(shè)置在一個(gè)單獨(dú)的線程中運(yùn)行晚缩,代碼如下:
<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 開發(fā)藝術(shù)探索");
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","正在停止服務(wù)...");
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();
}
};
}
最后運(yùn)行的結(jié)果主要通過打印的日志來顯示媳危。
通過日志信息荞彼,可以明確看到我們的服務(wù)端和客服端的確進(jìn)行的數(shù)據(jù)交換,并且服務(wù)端每隔5s發(fā)送一個(gè)信息給客戶端待笑。
關(guān)于AIDL鸣皂,有以下的幾點(diǎn)需要特別的注意:
- AIDL支持基本的數(shù)據(jù)類型(除short以外的數(shù)據(jù)類型),也支持String和CharSequence和Parcelable暮蹂。對(duì)于List只支持ArrayList寞缝,對(duì)于Map只支持HshMap.
- 如果AIDL文件中用到了自定義的Parcelable對(duì)象,必須新建一個(gè)和它同名的AIDL文件仰泻,并在其中聲明它為Parcelable類型荆陆。可以參考上面的示例 集侯。
- AIDL中除了基本的數(shù)據(jù)類型被啼,其他類型的參數(shù)必須標(biāo)上方向:in,out或者inout,in表示輸入型參數(shù)浅悉,out表示輸出型參數(shù)趟据,inout表示輸入輸出型參數(shù)。
- 在AIDL中术健,只支持方法汹碱,不支持聲明靜態(tài)常量。
- AIDL包結(jié)構(gòu)在服務(wù)端和客戶端必須保持一致荞估,否運(yùn)行會(huì)出錯(cuò)咳促,這是因?yàn)榭蛻舳诵枰葱蛄谢?wù)端中和AIDL接口相關(guān)的所有類稚新。
- AIDL方法是執(zhí)行在服務(wù)端的Binder線程池中的。