文章目錄
1 Android IPC簡介
任何一個操作系統都有相應的IPC機制,比如Windows上可以通過剪貼板、管道和郵槽等來進行進程間通信;Linux上可以通過命名管道、共享內容、信號量等來進行進程間通信。Android是一種基於Linux內核的移動操作系統,它的進程間通信並不能完全繼承自Linux,最有特色的進程間通信就是Binder。除了Binder,Android還支持Socket,通過Socket也可以實現任意兩個終端之間的通信,當然同一個設備上的兩個進程通過Socket通信也是可以的。
多進程使用場景:
-
一個應用某些模塊需要運行在單獨的進程中,或者加大一個應用可使用的內存所以需要通過多進程來獲取多份內存空間
-
當前應用需要向其他應用獲取數據
2 Android中的多進程模式
在Android中指定多進程只有一種方法,就是給四大組件在AndroidManifest.xml中指定 android:process
屬性。
假設應用包名爲com.example.ipc
// 全局進程,其他應用可通過ShareUID方式和它跑在同一個進程中
// 進程名稱爲com.example.ipc2.remote
android:process="com.example.ipc2.remote"
// :remote爲私有進程,其他應用組件不可以和它跑在同一個進程中
// :前面表示要加上包名,進程名稱爲com.example.ipc:remote
android:process=":remote"
Android系統會爲每個應用分配一個唯一的UID,具有相同UID的應用才能共享數據。需要說明的是,兩個應用通過ShareUID跑在同一個進程中是有要求的,需要這兩個應用有相同的ShareUID並且簽名相同纔可以。這種情況下,它們可以互相訪問對方的私有數據,比如data目錄、組件信息等。如果跑在同一個進程中,那麼除了共享data目錄、組件信息,還可以共享內存數據,或者說它們看起來就像一個應用的兩個部分。
3 多進程模式的運行機制
在四大組件聲明多進程後,會造成如下幾方面的問題:
-
靜態成員和單例模式完全失效(因爲每個獨立的進程是獨立的虛擬機,不在同一塊內存)
-
線程同步機制完全失效(與上面原因相同)
-
SharedPrefereces的可靠性下降(SharedPreferences不支持兩個進程同時去執行寫操作,否則會導致一定機率數據丟失,因爲SharedPreferences底層是通過讀/寫xml文件來實現的,併發讀寫都會出問題)
-
Application會多次創建(因爲是不同的進程即不同的獨立的虛擬機,啓動新進程相當於啓動一個新應用的過程)
4 IPC基礎概念介紹
4.1 Serializable接口
Serializable是java提供的一個序列化空接口,爲對象提供標準的序列化和反序列化操作。只需要在類中聲明標識即可實現默認序列化過程:
private static final long serialVersionUID = 1L;
serialVersionUID
是用來輔助序列化和反序列化的,序列化後的數據中的serialVersionUID和當前類的serialVersionUID相同才能正常被反序列化。
serialVersionUID
工作機制:
序列化時系統會把當前類的serialVersionUID寫入系列化文件中(也可能是其他中介),當反序列化的時候系統會去檢測文件中的serialVersionUID,看它是否和當前類serialVersionUID一致
-
如果一致說明序列化類的版本和當前版本相同,這個時候可以成功反序列化
-
如果不一致說明當前類和序列化的類相比發生了某些變換,比如成員刪減、類型發生變化,無法正常序列化
一般來說,應該手動指定serialVersionUID的值,不指定的話系統會自動計算當前類的hash值並把它賦值給serialVersionUID,手動指定能很大程度上避免反序列化過程的失敗。還要考慮一種情況,如果類結構發生了非常規性改變,比如修改了類名、修改了成員變量類型,這個時候儘管serialVersionUID驗證通過反序列化還是會失敗,因爲類結構有了毀滅性改變,無法從老版本的數據中還原出一個新的類結構對象。
靜態成員變量屬於類不屬於對象,所以不會參與序列化過程;其次是 transient
關鍵字標記的成員變量不參與序列化過程。
對象序列化和反序列化:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
// .... 其他成員變量
}
// 序列化
User user = new User(0, "jake", true);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();
// 反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User newUser = (User) in.readObject(); // 需要注意反序列化和序列化時不是同一個對象,只是內容相同
in.close();
4.2 Parcelable接口
public class User implements Parcelable {
public int userId;
public String userName;
public Book book;
public boolean isMale;
public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(userId);
out.writeString(userName);
out.writeInt(isMale ? 1 : 0);
out.writeParcelable(book, 0);
}
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
public User createFromParcel(Parcel in) {
return new User(in);
}
public User[] newArray(int size) {
return new User[size];
}
};
private User(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readInt() == 1;
book = in.readParcelable(Thread.currentThread(0.getContextClassLoader());
}
}
Parcel
內部包裝了可序列化的數據,可以在Binder中自由傳輸。序列化功能通過 writeToParcel()
完成,反序列化功能由 CREATOR
完成。
4.3 Parcelable和Serializable的選擇
Serializable是使用起來簡單但開銷很大,序列化和反序列化過程需要大量I/O操作。Parcelable是Android序列化方式,更適合用在Android上,它的缺點是使用起來稍微麻煩,但是效率很高,應首先Parcelable。
Parcelable主要用在內存序列化上,通過Parcelable將對象序列化到存儲設備中或者將對象序列化後通過網絡傳輸也是可以得,但過程會比較負責,這種情況建議使用Serializable。
4.4. Binder
4.4.1 Binder原理和代碼解析
Binder是Android中的一個類,它實現了IBinder接口。從IPC角度來說,Binder可以理解爲一種虛擬物理設備,它的設備驅動是/dev/binder,該通信方式在Linux中沒有;從Android Framework角度來說,Binder是ServiceManager連接各種Manager(AMS等)和相應ManagerService的橋樑;從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,當 bindService()
的時候,服務端會返回一個包含了服務端業務調用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務端提供的服務或數據。
Book.java
public clas Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
...省略
}
Book.aidl
parcelable Book;
IBookManager.aidl
import com.examle.ipc.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
系統自動生成的Binder類結構:
// 進行Binder通信的接口都要實現IInterface接口
public interface IBookManager extends IInterface {
public static abstract class Stub extends Binder implements IBookManager {
private static final String DESCRIPTOR = "com.example.ipc.aidl.IBookManager";
// 進程通信在Proxy中的mRemote.transact()調用後回調onTransact()
public boolean onTransact() { ... }
// 獲取本地或遠端訪問接口,對應Stub和Proxy,提供給客戶端訪問調用服務端接口的Binder
// 客戶端要調用binder接口時,就需要使用該方法獲取binder
public static IBookManager asInterface() { ... }
public IBinder asBinder() { return this; }
// 具體接口方法實現
public List<Book> getBookList() { ... }
public void addBook() { ... }
// 處理遠端進程通信代理類
private static class Proxy implements IBookManager {
private IBinder mRemote;
// 具體接口方法實現
public List<Book> getBookList() { ... }
public void addBook() { ... }
}
}
}
具體binder實現:
public interface IBookManager extends IInterface {
public static abstract class Stub extends Binder implements IBookManager {
private static final String DESCRIPTOR = "com.example.ipc.IBookManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if (obj == null) return null;
// 先從本地查找Binder,如果找到就使用,本地Binder即Stub
// 如果沒有找到,則使用代理,遠端Binder即Stub.Proxy
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof IBookManager) {
return ((IBookManager) inn);
}
return new IBookManager.Stub.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flag) throws RemoteException {
switch(code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_getBookList:
data.enforceInterface(DESCRIPTOR);
List<Book> _result = getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
case TRANSACTION_addBook:
data.enforceInterface(DESCRIPTOR);
Book _arg0;
if (data.readInt() != 0) {
_arg0 = Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
addBook(_arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IBookManager {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public List<Book> getBookList() throws RemoteException {
Parcel _data = Parcel.obtain();
Parce _reply = Parcel.obtain();
List<Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// transact發起binder,會回調到onTransact後寫入結果
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if (book != null) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
static final int TRANSACTION_getBookList = android.os.IBinder.FIRST_CALL_TRANSACTION + 0;
static final int TRANSACTION_addBook = android.os.IBinder.FIRST_CALL_TRANSACTION + 1;
}
public List<Book> getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
}
}
- DESCRIPTOR
Binder的唯一標識,一般用當前Binder的類名錶示
- asInterface(IBinder obj)
用於將服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象,這種轉換過程是區分進程的,如果客戶端和服務端位於同一個進程,那麼次方法返回的就是服務端的Stub對象本身,否則返回的是系統封裝後的Stub.Proxy對象
- asBinder()
用於返回當前Binder對象
- onTransact()
這個方法運行在服務端中的Binder線程池中,當客戶端發起跨進程請求時,遠程請求會通過系統底層封裝後交由此方法來處理。方法原型爲 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
。服務端通過code可以確定客戶端所請求的目標方法是什麼,接着從data中取出目標方法所需參數,然後執行目標方法。當目標執行完畢後,就向reply中寫入返回值。
需要注意的是,如果方法返回false,那麼客戶端的請求會失敗,因此我們可以利用這個特性來做權限驗證。
- Proxy#getBookList
這個方法運行在客戶端,當客戶端遠程調用此方法時,內部實現:創建該方法所需的Parcel對象_data,輸出型Parcel對象_reply和返回值對象List;寫入參數到_data中,調用 transact
方法發起RPC(遠程過程調用)請求,同時當前線程掛起;然後服務端的 onTransact
方法會被調用,直到RPC過程返回後,當前線程繼續執行,並從_reply中取出PRC過程的返回結果;最後返回_reply中的數據
- Proxy#addBook
和Proxy$getBookList一樣,只是沒有返回值,不需要從_reply中取出返回值
4.4.2 死亡代理
Binder運行在服務端進程,如果服務端進程由於某種原因異常終止,這時候到服務端的Binder死亡,會導致我們的遠程調用失敗。Binder提供了兩個配對方法 linkToDeath
和 unlinkToDeath
,通過 linkToDeath
給Binder設置死亡代理,當Binder死亡時,我們就會收到通知,可以重新發起連接請求恢復連接。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
// 服務端Binder斷開死亡時會回調
@Override
public void binderDied() {
if (mBookManager == null) return;
mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBookManager = null;
// 重新綁定遠程Service
}
};
// 客戶端綁定服務成功後,給Binder設置死亡代理
mService = IMessageBoxManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0);
5 Android中的IPC方式
5.1 使用Bundle
Bundle
由於實現了Parcelable接口,所以它可以方便地在不同的進程間傳輸。基於這一點,當我們在一個進程中啓動了另一個進程的Activity、Service和Receiver,我們就可以在Bundle中附加我們需要傳輸給遠程進程的信息並通過 Intent
發送出去。當前,傳輸的數據必須能夠被序列化。
5.2 使用文件共享
共享文件也是一種不錯的進程間通信方式,兩個進程通過讀/寫同一個文件來交換數據。由於Android系統基於Linux,使得其併發讀/寫文件可以沒有限制地進行,甚至兩個線程同時對同一個文件進行寫操作都是允許的,儘管這可能出問題。通過文件交換數據除了可以交換一些文本信息外,還可以序列化一個對象到文件系統中的同時從另一個進程中恢復這個對象。
// 在MainActivity中修改
private void persistToFile() {
new Thread(new Runnable() {
@Override
public void run() {
User user = new User(1, "hello world", false);
File dir = new File("path");
if (!dir.exists()) {
dir.mkdirs();
}
File cacheFile = new File("cachePath");
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(cacheFile));
oos.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
} finally {
MyUtils.close(oos);
}
}
}).start();
// SecondActivity中的修改
private void recoverFromFile() {
new Thread(new Runnable() {
@Override
public void run() {
User user = null;
File cacheFile = new File("cachePath");
if (cacheFile.exists()) {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(cacheFile));
user = (User) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
MyUtils.close(ois);
}
}
}
}).start();
}
}
通過文件共享的方式也是有侷限性的,比如併發讀/寫問題,如果併發讀/寫,讀出的內容有可能不是最新的,如果併發寫更嚴重了。因此我們要儘量避免併發寫的情況要考慮線程同步。文件共享方式適合在對數據同步要求不高的進程之間進行通信,並且要妥善處理併發讀/寫問題。
5.3 SharedPreference進程間通信的問題
SharedPreferences
是Android提供的輕量級存儲方案,它通過鍵值對的方式來存儲數據,在底層實現上它採用xml文件來存儲鍵值對,每個應用的SharedPreferences文件都可以在當前包所在的data目錄下查看到,/data/data/package name/shared_prefs
目錄。
SharedPreferences
也屬於文件的一種,但是由於系統對它的讀/寫有一定的緩存策略,即在內存中會有一份SharedPreferences文件的緩存,因此在多進程模式下,系統對它的讀/寫就變得不可靠,當面對高併發的讀/寫訪問,SharedPreferences有很大機率會丟失書,因此,不建議在進程間通信中使用SharedPreferences。
5.4 使用Messenger
Messenger
是一種輕量級的IPC方案,它的底層實現是AIDL,通過它可以在不同進程紅傳遞Message對象,在Message中放入我們需要傳遞的數據,就可以輕鬆地實現數據的進程間傳遞了。
// 服務端的實現
public class MessengerService extends Service {
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == Constants.MSG_FROM_CLIENT) {
// 在服務端通過客戶端傳遞過來的Messenger對象發送消息回覆客戶端
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null, Constants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply", "message is receive");
replyMessage.setData(bundle);
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
// 客戶端的實現
public class MessengerActivity extends Activity {
private Messenger mService;
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName, className, IBinder service) {
mService = new Messenger(service);
Message msg = Message.obtain(null, Constants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello, this is client");
msg.setData(data);
msg.replyTo = mGetReplyMessener; // 把客戶端的Messenger發送到服務端
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) { }
};
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == Constants.MSG_FROM_SERVICE) {
// 接收到服務端發來的消息
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
5.5 使用AIDL
Messenger
是以串行的方式處理客戶端發來的消息,如果大量的消息同時發送到服務端,服務端仍然只能一個個處理,如果有大量的併發請求,那麼用Messenger就不太合適了。同時,Messenger的作用主要是爲了傳遞消息,很多時候我們可能需要跨進程調用服務端的方法,這種情形用Messenger就無法做到了,可以使用AIDL來實現跨進程的方法調用。
// Book.aidl
package com.example.ipc;
parcelable Book;
// Book.java
public class Book implements Parcelable {
public int bookId;
public String bookName;
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
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 parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
}
// IBookManager.aidl
package com.vincent;
// Declare any non-default types here with import statements
import com.example.ipc.Book;
import com.example.ipc.IOnNewBookArrivedListener;
interface IBookManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
// IOnNewBookArrivedListener.aidl
package com.example.ipc;
// Declare any non-default types here with import statements
import com.vincent.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
// 服務端
public class BookManagerService extends Service {
private static final String TAG = "BMS";
private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
//RemoteCallbackList保存着所有跨進程的listener,在需要將listener註冊和解註冊時要用RemoteCallbackList,否則無法解註冊
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener 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() {
mIsServiceDestroyed.set(true);
super.onDestroy();
}
private void onNewBookArrived(Book book) {
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
if (l != null) {
try {
l.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestroyed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "new book#" + bookId);
try {
onNewBookArrived(newBook);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
// 客戶端
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private IBookManager mRemoteBookManager;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.d(TAG, "receive new book: " + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
try {
iBinder.linkToDeath(mDeathRecipient, 0);
mRemoteBookManager = bookManager;
mRemoteBookManager.registerListener(mOnNewBookArrivedListener);
new Thread(new Runnable() {
@Override
public void run() {
if (mRemoteBookManager != null) {
try {
List<Book> list = mRemoteBookManager.getBookList();
Log.i(TAG, "query book list: " + getBookName(list));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}).start();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mRemoteBookManager = null;
Log.e(TAG, "binder died.");
}
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mRemoteBookManager == null)
return;
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
// 重新綁定遠程服務
Intent intent = new Intent(MainActivity.this, BookManagerService.class);
bindService(intent, mConnection, BIND_AUTO_CREATE);
}
};
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
}
};
private String getBookName(List<Book> bookList) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bookList.size(); i++) {
Book book = bookList.get(i);
if (i == bookList.size() - 1) {
sb.append(book.bookName);
break;
}
sb.append(book.bookName).append(",");
}
return sb.toString();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (mRemoteBookManager != null) {
try {
mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
5.6 使用ContentProvider
ContentProvider
是Android中提供的專門用於不同應用間進行數據共享的方式,它天生就適合進程間通信。和Messenger一樣,ContentProvider的底層實現同樣也是Binder。
具體的ContentProvider用法可以看下面的一篇文章:
5.7 使用Socket
Socket
也成爲套接字,是網絡通信中的概念,它分爲流式套接字和用戶數據報套接字兩種,分別對英語網絡的傳輸控制層中的TCP和UDP協議。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
public class TCPServerService extends Service {
private boolean mIsServiceDestroyd = false;
private String[] mDefinedMessages = new String[] {
"你好啊,哈哈",
"請問你叫什麼名字呀?",
"今天北京天氣不錯啊",
};
@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
mIsServiceDestroy = true;
super.onDestroy();
}
private class TcpServer implements Runnable {
@SuppressWarnings("resource")
@Override
public void run() {
ServerSocket serverSocket = null;
try {
// 監聽8688端口
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
e.printStackTrace();
return;
}
while (!mIsServiceDestroy) {
try {
// 接收客戶端請求
final Socket client = serverSocket.accept();
new Thread() {
@Override
public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
}
private void reponseClient(Socket client) throws IOException {
// 用於接收客戶端消息
BufferReader in = new BufferReader(new InputStreamReader(client.getInputStream));
// 用於向客戶端發送消息
PrintWriter out = new PrintWriter(new BufferWriter(
new OutputStreamWriter(client.getOutputStream()), true);
while (!mIsServiceDestroy) {
String str = in.readLine();
if (str == null) break; // 客戶端斷開連接
int i = new Random().nextInt(mDefinedMessages.length);
String msg = mDefinedMessages[i];
out.println(msg);
MyUtils.close(out);
MyUtils.close(in);
client.close();
}
}
}
// 客戶端
public class TPCClientActivity extends Activity {
private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTED = 2;
private Button mSendButton;
private TextView mMessageTextView;
private EditText mMessageEditText;
private PrintWriter mPrintWriter;
private Socket mClientSocket;
private Handler mHandler = new Handler() {
@Override
public void handleMessge(Message msg) {
switch(msg.what) {
case MESSAGE_RECEIVE_NEW_MSG:
mMessageTextView.setText(mMessgeTextView.getText + (String) msg.obj);
break;
case MESSAGE_SOCKET_CONNECTED:
mSendButton.setEnabled(true);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcpclient);
// ... findViewById
Intent service = new Intent(this, TCPServerService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectTCPServer();
}
}.start();
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
mClientSocket.shutdownInput();
mClientSocket.close();
}
super.onDestroy();
}
@Override
public void onClick(View v) {
if (v == mSendButton) {
final String msg = mMessageEditTExt.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
mPrintWriter.println(msg);
mMessageEditText.setText("");
String time = formateDateTime(System.currentTimeMillis();
final String showedMsg = "self" + time + ":" +msg + "\n";
mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
}
}
}
@SuppressLint("SimpleDateFormat")
private String formateDateTime(long time) {
return new SimpleDateFormat("(HH:mm:ss)").formate(new Date(time));
}
private void connectTCPServer() {
Socket socket = null;
while (socket == null) {
try {
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
// 接收服務端消息
BufferReader br = new BufferReader(new InputStreamReader(socket.getInputStream()));
while (!TCPClientActivity.this.isFinish()) {
String msg = br.readLine();
if (msg != null) {
String time = formateDateTime(System.currentTimeMillis());
final String showedMsg = "server" + time + ":" + msg + "\n";
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg).sendToTarget();
}
MyUtils.close(mPrintWriter);
MyUtils.close(br);
socket.close();
} catch (IOException e) {
e.printStackTrance();
}
}
}
}
6 Binder連接池
這裏再次介紹一下AIDL,原因是AIDL是一種最常用的進程間通信方式,是日常開發中涉及進程間通信時的首選,所以我們需要額外強調一下。
AIDL大致流程:首先創建一個Service和一個AIDL接口,接着創建一個類繼承AIDL接口中的tub類並實現Stub中的抽象方法,在Service的onBind方法中返回這個類的對象,然後客戶端就可以綁定服務端Service,建立連接後就可以訪問遠程服務端的方法了。
上面的流程一般情況下沒有什麼問題,但在項目越來越龐大模塊越來越多的情況下,一個連接就創建一個Service,實際情況是不能無限制地增加Service,Service是四大組件之一本身就是一種系統資源。
在這種模式下,整個工作機制是這樣的:每個業務模塊創建自己的AIDL接口並實現此接口,這個時候不同業務模塊之間是不能有耦合的,所有實現細節我們要單獨開來,然後想服務端提供自己的唯一標識和其對應的Binder對象;對於服務端來說,只需要一個Service就可以了,服務端提供一個queryBinder接口,這個接口能夠根據業務模塊的特徵來返回相應的Binder對象給它們,不同的業務模塊拿到所需的Binder對象後就可以進行遠程方法調用了。
Binder連接池的主要作用就是將每個模塊的Binder請求統一轉發到遠程Service中去執行,從而避免了重複創建Service的過程。
// ISecurityCenter.aidl 提供加密功能
interface ISecurityCenter {
String encrypt(String content);
String decrypt(String password);
}
// ICompute.adil 提供計算加法功能
interface ICompute {
int add(int a, int b);
}
// IBinderPool.aidl
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
public class SecurityCenterImpl extends ISecurityCenter.Stub {
private static final char SECRET_CODE = '^';
@Override
public String encrypt(String content) throws RemoteException {
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] ^= SECRET_CODE;
}
return new String(chars);
}
@Override
public String decrypt(String password) throws RemoteException {
return encrypt(password);
}
}
public class ComputeImpl extends ICompute.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
public class BinderPoolService extends Service {
// BinderPoolImple extends IBinderPool.Stub
private Binder mBinderPool = new BinderPool.BinderPoolImpl();
@Override
public IBinder onBind(Intent intent) {
return mBinderPool;
}
}
public class BinderPool {
public static final int BINDER_NONE = -1;
public static final int BINDER_COMPUTE = 0;
public static final int BINDER_SECURITY_CENTER = 1;
// 上下文,爲了啓動BinderPoolService
private Context mContext;
private IBinderPool mBinderPool;
private static volatile BinderPool sInstance;
// 將bindService這一異步操作轉換成了同步操作
private CountDownLatch mConnectBinderPoolCountDownLatch;
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}
public static BinderPool getInstance(Context context) {
if (sInstance == null) {
synchronized(BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
private synchronized void connectBinderPoolService() {
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent(mContext, BinderPoolService.class);
mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// AIDL接口,根據客戶端傳遞過來的binderCode返回對應的Binder對象
public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return binder;
}
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace(0;
}
mConnectionBinderPoolCountDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
// 死亡代理
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService(); // 重新連接
}
};
public static class BinderPoolImpl extends IBinderPool.Stub {
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch(binderCode) {
case BINDER_SECURITY_CENTER:
binder = new SecurityCenterImpl();
break;
case BINDER_COMPUTE:
binder = new ComputeImpl();
break;
}
return binder;
}
}
}
// 客戶端
private void doWork() {
BinderPool binderPool = BinderPool.getInstance(this);
IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
mSecurityCenter = (ISecurityCenter) SecurityCenterImpl.asInterface(securityBinder);
String msg = "hello world";
try {
String password = mSecurityCenter.encrypt(msg);
System.out.println("password = " + password);
} catch (RemoteException e) {
e.printStackTrace();
}
IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
mCompute = ComputeImpl.asInterface(computeBinder);
try {
System.out.println("3+5 = " + mCompute.add(3, 5));
} catch (RemoteException e) {
e.printStackTrace();
}
}