文章目录
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();
}
}