IPC
- IPC是什么?
- 多进程带来的问题
- IPC前提
- Serializable
- Parcelable
- Binder
- Android中的IPC
- Bundle
- 文件共享
- Messenger
- AIDL
- ContentProvider
- Socket
- 不同IPC优缺点
- Binder连接池
IPC是什么?
Inter-Process Communcation,含义为进程间通信或者跨进程通信
Android中的多进程是指一个应用中存在多个进程的情况,默认进程名为包名,使用多进程需要给四大组件设置android:process属性
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.demo.demo0"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activityandroid:name=".ThirdActivity"android:process="com.demo.demo0.remote" /><activityandroid:name=".SecondActivity"android:process=":remote" /><activityandroid:name=".MainActivity"android:process=""><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
- 以 : 开头的进程会附加包名,属于当前应用的私有进程,其他应用的组件不可以和它跑在同一进程
- 不以 : 开头的进程为全局进程,其他应用可通过相同的ShareUID及签名和它跑在同一进程
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startActivity(new Intent(this, SecondActivity.class));}
}
public class SecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);startActivity(new Intent(this, ThirdActivity.class));}
}
public class ThirdActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_third);}
}
对于如上的配置,启动3个Activity后,在AS中可看到有三个进程
多进程带来的问题
新建一个类UserManger
public class UserManager {public static int sUserId = 1;
}
在MainActivity中修改sUserId为2并打印
public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);UserManager.sUserId = 2;Log.d(TAG, "onCreate: sUserId = " + UserManager.sUserId);startActivity(new Intent(this, SecondActivity.class));}
}
在SecondActivity再次打印
public class SecondActivity extends AppCompatActivity {private static final String TAG = "SecondActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);Log.d(TAG, "onCreate: sUserId = " + UserManager.sUserId);startActivity(new Intent(this, ThirdActivity.class));}
}
结果却不同
不同进程的组件会拥有独立的虚拟机、Application和内存空间,会造成
- 静态成员和单例模式完全失效
- 线程同步机制完全失效
- SharedPreferences的可靠性下降
- Application会多次创建
IPC前提
Serializable
对于实现Serializable的类
public class User implements Serializable {private static final long serialVersionUID = 1L;private int userId;private String userName;private boolean isMale;public User(int userId, String userName, boolean isMale) {this.userId = userId;this.userName = userName;this.isMale = isMale;}
}
可通过ObjectOutputStream和ObjectInputStream实现序列化和反序列化,恢复后的对象内容完全一样但不是同一对象
- 只有序列化后的数据中的serialVersionUID和当前类的serialVersionUID相同才能正常被反序列化
- static和tranisient修饰的域不会参与序列化过程
public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);User user = new User(0, "tom", true);try (ObjectOutputStream out = new ObjectOutputStream(openFileOutput("data", Context.MODE_PRIVATE))) {out.writeObject(user);} catch (IOException e) {e.printStackTrace();}try (ObjectInputStream in = new ObjectInputStream(openFileInput("data"))) {User newUser = (User) in.readObject();Log.d(TAG, "onCreate: user == newUser " + (user == newUser));} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}
Parcelable
对于实现Parcelable的类可通过Intent和Binder传输
public class User implements Parcelable {private int userId;private String userName;private boolean isMale;public User(int userId, String userName, boolean isMale) {this.userId = userId;this.userName = userName;this.isMale = isMale;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel out, int flags) {out.writeInt(userId);out.writeString(userName);out.writeInt(isMale ? 1 : 0);}public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {@Overridepublic User createFromParcel(Parcel in) {return new User(in);}@Overridepublic User[] newArray(int size) {return new User[size];}};private User(Parcel in) {userId = in.readInt();userName = in.readString();isMale = in.readInt() == 1;}
}
各个方法功能如下
Binder
以下通过AIDL分析Binder,创建Book.java
public class Book implements Parcelable {private int bookId;private String bookName;public Book(int bookId, String bookName) {this.bookId = bookId;this.bookName = bookName;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(bookId);dest.writeString(bookName);}public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {@Overridepublic Book createFromParcel(Parcel source) {return new Book(source);}@Overridepublic Book[] newArray(int size) {return new Book[size];}};private Book(Parcel source) {bookId = source.readInt();bookName = source.readString();}
}
创建Book.aidl,声明Book
parcelable Book;
创建IBookManager.aidl,需要手动import
import com.demo.demo0.Book;interface IBookManager {List<Book> getBookList();void addBook(in Book book);
}
在AS菜单栏的Build,点击Clean Project,再点击Rebuild Project,会生成IBookManager.java,位置如下图
public interface IBookManager extends android.os.IInterface {/*** Default implementation for IBookManager.*/public static class Default implements com.demo.demo0.IBookManager {@Overridepublic java.util.List<com.demo.demo0.Book> getBookList() throws android.os.RemoteException {return null;}@Overridepublic void addBook(com.demo.demo0.Book book) throws android.os.RemoteException {}@Overridepublic android.os.IBinder asBinder() {return null;}}/*** Local-side IPC implementation stub class.*/public static abstract class Stub extends android.os.Binder implements com.demo.demo0.IBookManager {private static final java.lang.String DESCRIPTOR = "com.demo.demo0.IBookManager";/*** Construct the stub at attach it to the interface.*/public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.demo.demo0.IBookManager interface,* generating a proxy if needed.*/public static com.demo.demo0.IBookManager asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.demo.demo0.IBookManager))) {return ((com.demo.demo0.IBookManager) iin);}return new com.demo.demo0.IBookManager.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {java.lang.String descriptor = DESCRIPTOR;switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(descriptor);return true;}case TRANSACTION_getBookList: {data.enforceInterface(descriptor);java.util.List<com.demo.demo0.Book> _result = this.getBookList();reply.writeNoException();reply.writeTypedList(_result);return true;}case TRANSACTION_addBook: {data.enforceInterface(descriptor);com.demo.demo0.Book _arg0;if ((0 != data.readInt())) {_arg0 = com.demo.demo0.Book.CREATOR.createFromParcel(data);} else {_arg0 = null;}this.addBook(_arg0);reply.writeNoException();return true;}default: {return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.demo.demo0.IBookManager {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.util.List<com.demo.demo0.Book> getBookList() throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List<com.demo.demo0.Book> _result;try {_data.writeInterfaceToken(DESCRIPTOR);boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().getBookList();}_reply.readException();_result = _reply.createTypedArrayList(com.demo.demo0.Book.CREATOR);} finally {_reply.recycle();_data.recycle();}return _result;}@Overridepublic void addBook(com.demo.demo0.Book book) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);if ((book != null)) {_data.writeInt(1);book.writeToParcel(_data, 0);} else {_data.writeInt(0);}boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {getDefaultImpl().addBook(book);return;}_reply.readException();} finally {_reply.recycle();_data.recycle();}}public static com.demo.demo0.IBookManager sDefaultImpl;}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 static boolean setDefaultImpl(com.demo.demo0.IBookManager impl) {// Only one user of this interface can use this function// at a time. This is a heuristic to detect if two different// users in the same process use this function.if (Stub.Proxy.sDefaultImpl != null) {throw new IllegalStateException("setDefaultImpl() called twice");}if (impl != null) {Stub.Proxy.sDefaultImpl = impl;return true;}return false;}public static com.demo.demo0.IBookManager getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}public java.util.List<com.demo.demo0.Book> getBookList() throws android.os.RemoteException;public void addBook(com.demo.demo0.Book book) throws android.os.RemoteException;
}
- 所有在Binder中传输的接口都需要继承IInterface
- DESCRIPTOR:唯一标识,用类名表示
- asInterface():将服务端的Binder对象转换成客户端所需的AIDL接口对象,同一进程返回Stub,不同进程返回Stub.proxy
- asBinder():返回当前Binder
- onTransact():运行在服务端的Binder线程池中,处理跨进程请求,通过code判断目标方法,从data获取方法参数,向reply写入返回值
- getBookList():客户端调用时,创建输入型Parcel对象_data(并写入参数)、输出型Parcel对象_reply和返回值对象List,接着调用transact()发起Remote Process Call并挂起线程。随后服务端的onTransact()被调用,直到结束后线程继续执行,并从reply获取返回结果
- addBook():同上
当Binder所在服务端进程异常终止时,会导致远程调用失败,通过linkToDeath()可为Binder设置一个死亡代理,当Binder死亡时回调binderDied()可再次重新发起请求
IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {}
};
Binder binder = new Binder();
binder.linkToDeath(deathRecipient, 0);
或者在onServiceDisconnected()中重连远程服务,区别在于
- onServiceDisconnected()在客户端的UI线程中被回调
- binderDied()在客户端的Binder线程池中被回调,不能访问UI
Android中的IPC
Bundle
Activity、Service、Reciver都支持在Intent中传递Bundle数据,传输的数据必须能够被序列化
文件共享
Android中的文件可以支持并发读写,两个进程可通过读 / 写同一个文件来交换数据,但只适合对数据同步要求不高的进程之间进行通信
SharedPreference通过XML文件来存储键值对,但系统对其读写具有缓存策略,内存中只有一份SharedPreference文件的缓存,在多进程模式下会变得不可靠
Messenger
Messenger底层实现基于AIDL,一次处理一个请求,在服务端不用考虑线程同步
服务端需指定android:process,代码如下
- 新建ServiceMessengerHandler处理客户端请求,并从replyTo获取clientMessenger返回响应数据
- 新建ServiceMessenger在onBind()返回其Binder
public class MessengerService extends Service {public static final String TAG = "MessengerService";private final Messenger mServiceMessenger = new Messenger(new ServiceMessengerHandler());private static class ServiceMessengerHandler extends Handler {@Overridepublic void handleMessage(@NonNull Message msg) {Log.d(TAG, "handleMessage: " + msg.getData().getString("client"));Messenger clientMessenger = msg.replyTo;Message serviceMsg = new Message();Bundle bundle = new Bundle();bundle.putString("service", "msg from service");serviceMsg.setData(bundle);try {clientMessenger.send(serviceMsg);} catch (RemoteException e) {e.printStackTrace();}}}public MessengerService() {}@Overridepublic IBinder onBind(Intent intent) {return mServiceMessenger.getBinder();}
}
如下为客户端的代码
- 新建ClientMessengerHandler处理服务端返回的数据
- 新建ClientMessenger传给服务端,用于服务端传递数据
- 在成功绑定服务时获取serviceMessenger发送数据
public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private Messenger mClientMessenger = new Messenger(new ClientMessengerHandler());private static class ClientMessengerHandler extends Handler {@Overridepublic void handleMessage(@NonNull Message msg) {Log.d(TAG, "handleMessage: " + msg.getData().getString("service"));}}private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Messenger serviceMessenger = new Messenger(service);Message clientMsg = new Message();Bundle data = new Bundle();data.putString("client", "msg from client");clientMsg.setData(data);clientMsg.replyTo = mClientMessenger;try {serviceMessenger.send(clientMsg);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent = new Intent(this, MessengerService.class);bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}@Overrideprotected void onDestroy() {super.onDestroy();}
}
运行程序,客户端和服务端都成功接收到数据
AIDL
Messenger只能串行处理客户端的请求,当需要并行处理客户端请求或需要调用服务端的方法时可以使用AIDL,AIDL支持的数据类型有
- 基本数据类型,除此之外的参数需标上方向:in、out或inout
- String和CharSequence
- ArrayList,其元素都必须被AIDL所支持
- HashMap,其元素都必须被AIDL所支持
- 实现了Parcelable的对象,需新建同名aidl文件并进行声明,使用时需import
- AIDL接口本身可以在AIDL文件中使用,使用时需import
需要注意,AIDL相关文件最好放在同一个包中,且包结构在服务端和客户端要保持一致,否则无法反序列化
创建Book.java,实现Parcelable
public class Book implements Parcelable {private int bookId;private String bookName;public Book(int bookId, String bookName) {this.bookId = bookId;this.bookName = bookName;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(bookId);dest.writeString(bookName);}public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {@Overridepublic Book createFromParcel(Parcel source) {return new Book(source);}@Overridepublic Book[] newArray(int size) {return new Book[size];}};private Book(Parcel source) {bookId = source.readInt();bookName = source.readString();}
}
创建Book.aidl,声明Book
parcelable Book;
创建IOnNewBookArrivedListener.aidl,用于回调客户端
import com.demo.demo0.Book;interface IOnNewBookArrivedListener {void onNewBookArrived(in Book newBook);
}
创建IBookManager.aidl
import com.demo.demo0.Book;
import com.demo.demo0.IOnNewBookArrivedListener;interface IBookManager {List<Book> getBookList();void addBook(in Book book);void registerListener(IOnNewBookArrivedListener listener);void unRegisterListener(IOnNewBookArrivedListener listener);
}
创建服务端的BookManagerService
public class BookManagerService extends Service {private static final String TAG = "BookManagerService";private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();private Binder mBinder = new IBookManager.Stub() {@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}@Overridepublic void addBook(Book book) throws RemoteException {mBookList.add(book);}@Overridepublic void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {mListenerList.register(listener);int N = mListenerList.beginBroadcast();Log.d(TAG, "registerListener: mListenerList size = " + N);mListenerList.finishBroadcast();}@Overridepublic void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {mListenerList.unregister(listener);int N = mListenerList.beginBroadcast();Log.d(TAG, "unRegisterListener: mListenerList size = " + N);mListenerList.finishBroadcast();}@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {int check = checkCallingOrSelfPermission("com.demo.demo0.permission.ACCESS_BOOK_SERVICE");if (check == PackageManager.PERMISSION_DENIED) {return false;}String packageName = null;String[] packages = getPackageManager().getPackagesForUid(getCallingUid());if (packages != null && packages.length > 0) {packageName = packages[0];}if (packageName != null && !packageName.startsWith("com.demo")) {return false;}return super.onTransact(code, data, reply, flags);}};@Overridepublic void onCreate() {super.onCreate();mBookList.add(new Book(1, "book1"));mBookList.add(new Book(2, "book2"));new Thread(new ServiceWorker()).start();}@Overridepublic IBinder onBind(Intent intent) {int check = checkCallingOrSelfPermission("com.demo.demo0.permission.ACCESS_BOOK_SERVICE");if (check == PackageManager.PERMISSION_DENIED) {return null;}return mBinder;}@Overridepublic void onDestroy() {super.onDestroy();mIsServiceDestroyed.set(true);}private void onNewBookArrived(Book book) throws RemoteException {mBookList.add(book);int N = mListenerList.beginBroadcast();for (int i = 0; i < N; i++) {IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);if (listener != null) {listener.onNewBookArrived(book);}}mListenerList.finishBroadcast();}private class ServiceWorker implements Runnable {@Overridepublic void run() {while (!mIsServiceDestroyed.get()) {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}int booId = mBookList.size() + 1;Book newBook = new Book(booId, "book" + booId);try {onNewBookArrived(newBook);} catch (RemoteException e) {e.printStackTrace();}}}}
}
- 使用CopyOnWriteArrayList支持并发读/写,Binder会按照List的规范去访问数据并返回一个ArrayList给客户端
- 使用RemoteCallbackList存储客户端listener,内部实现了线程同步,客户端终止后还能自动移除,否则Binder会将客户端过来的对象反序化成新的对象,对象不一样无法unregister
- 可以在onBiner()或onTransact()中校验权限
- 内部创建ServiceWorker每5秒添加数据并回调客户端
创建客户端MainActivity
public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;private IBookManager mIBookManager;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(@NonNull Message msg) {Log.d(TAG, "handleMessage: receive new book: " + msg.obj);}};private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mIBookManager = IBookManager.Stub.asInterface(service);try {List<Book> bookList = mIBookManager.getBookList();Log.d(TAG, "onServiceConnected: bookList type = " + bookList.getClass().getCanonicalName());Log.d(TAG, "onServiceConnected: bookList = " + bookList.toString());mIBookManager.registerListener(mIOnNewBookArrivedListener);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {mIBookManager = null;Log.d(TAG, "onServiceDisconnected: ");}};private IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {@Overridepublic void onNewBookArrived(Book newBook) throws RemoteException {mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();}};@Overrideprotected 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);}@Overrideprotected void onDestroy() {super.onDestroy();if (mIBookManager != null && mIBookManager.asBinder().isBinderAlive()) {try {mIBookManager.unRegisterListener(mIOnNewBookArrivedListener);} catch (RemoteException e) {e.printStackTrace();}}unbindService(mConnection);}
}
manifest文件如下,为BookManagerService设置不同进程,申明权限,和使用权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.demo.demo0"><permissionandroid:name="com.demo.demo0.permission.ACCESS_BOOK_SERVICE"android:protectionLevel="normal" /><uses-permission android:name="com.demo.demo0.permission.ACCESS_BOOK_SERVICE" /><applicationandroid:name=".Config"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><serviceandroid:name=".BookManagerService"android:process=":remote" /><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
ContentProvider
可以看Android基础——ContentProvider和contentResolver,不再赘述
Socket
分为流式套接字和用户数据报套接字,分别对应网络传输控制层中的TCP和UDP协议,使用需声明网络权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
服务端需指定android:process,代码如下
- 监听8688端口,每当有客户端连接时,就生成一个Socket
- 客户端断开连接时,获取的输入流为null,服务端也断开连接
- 当收到客户端信息后随机回复
public class TCPServerService extends Service {private boolean mIsServiceDestroy = false;private String[] mDefinedMessages = new String[]{"随机回复1","随机回复2","随机回复3","随机回复4","随机回复5",};@Overridepublic void onCreate() {super.onCreate();new Thread(new TcpServer()).start();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onDestroy() {super.onDestroy();mIsServiceDestroy = true;}private class TcpServer implements Runnable {@Overridepublic void run() {ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(8688);} catch (IOException e) {System.err.println("fail to connect 8688");e.printStackTrace();return;}while (!mIsServiceDestroy) {try {final Socket client = serverSocket.accept();System.out.println("accept");new Thread() {@Overridepublic void run() {try {responseClient(client);} catch (IOException e) {e.printStackTrace();}}}.start();} catch (IOException e) {e.printStackTrace();}}}}private void responseClient(Socket client) throws IOException {try (BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true)) {out.println("welcome to chatBox");while (!mIsServiceDestroy) {String str = in.readLine();System.out.println("client send: " + str);if (str == null) {System.out.println("client quit");break;}int i = new Random().nextInt(mDefinedMessages.length);String msg = mDefinedMessages[i];out.println(msg);}}}
}
客户端,代码如下
- 创建线程连接服务端Socket,因为不能在主线程中访问网络,若连接失败会再1s后重连
- 服务端断开连接时,获取的输入流为null,客户端也断开连接
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private static final int MESSAGE_RECEIVE_NEW_MSG = 1;private static final int MESSAGE_SOCKET_CONNECTED = 2;private static final String TAG = "MainActivity";private Button mSendButton;private TextView mMessageTextView;private EditText mMessageEditText;private PrintWriter mPrintWriter;private Socket mClientSocket;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(@NonNull Message msg) {switch (msg.what) {case MESSAGE_RECEIVE_NEW_MSG:mMessageTextView.setText(mMessageTextView.getText() + (String) msg.obj);break;case MESSAGE_SOCKET_CONNECTED:mSendButton.setEnabled(true);break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mMessageTextView = findViewById(R.id.msg_container);mMessageEditText = findViewById(R.id.msg);mSendButton = findViewById(R.id.send);mSendButton.setOnClickListener(this);Intent service = new Intent(this, TCPServerService.class);startService(service);new Thread() {@Overridepublic void run() {connectTCPServer();}}.start();}@Overrideprotected void onDestroy() {super.onDestroy();if (mClientSocket != null) {try {mClientSocket.shutdownInput();mClientSocket.close();} catch (IOException e) {e.printStackTrace();}}}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.send:String msg = mMessageEditText.getText().toString();if (TextUtils.isEmpty(msg)) {return;}new Thread(new Runnable() {@Overridepublic void run() {if (mPrintWriter != null) {mPrintWriter.println(msg);}}}).start();mMessageEditText.setText("");String time = formatDateTime(System.currentTimeMillis());final String showMsg = "client " + time + ": " + msg + "\n";mMessageTextView.setText(mMessageTextView.getText() + showMsg);break;}}private String formatDateTime(long time) {return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));}private void connectTCPServer() {while (mClientSocket == null) {try {mClientSocket = new Socket("localhost", 8688);mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mClientSocket.getOutputStream())), true);mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);System.out.println("connect server success");} catch (IOException e) {SystemClock.sleep(1000);System.out.println("connect tcp server failed, retry...");}}try (BufferedReader br = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()))) {while (!isFinishing()) {String msg = br.readLine();System.out.println("server send: " + msg);if (msg == null) {System.out.println("server quit...");break;}String time = formatDateTime(System.currentTimeMillis());String showMsg = "server " + time + ": " + msg + "\n";mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showMsg).sendToTarget();}} catch (IOException e) {e.printStackTrace();}}
}
activity_main.xml代码如下,用于显示信息
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/msg_container"android:layout_width="match_parent"android:layout_height="match_parent" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignBottom="@id/msg_container"android:orientation="horizontal"><EditTextandroid:id="@+id/msg"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="8"android:hint="输入你要发送的内容" /><Buttonandroid:id="@+id/send"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="2"android:text="发送" /></LinearLayout>
</RelativeLayout>
通信过程如下
不同IPC优缺点
Binder连接池
AIDL使用过程:
- 新建一个类继承Stub并实现抽象方法
- 新建一个Service在onBind()中返回该类的对象
- 在客户端绑定Service
Service会在后台运行占据内存,不可能为每一个AIDL都新建一个Service,这时候就需要使用Binder连接池将Binder请求统一转发到Service
创建ISecurityCenter.aidl
interface ISecurityCenter {String encrypt(String content);String decrypt(String password);
}
创建SecurityCenterImpl.java
public class SecurityCenterImpl extends ISecurityCenter.Stub {private static final char SECRET_CODE = '^';@Overridepublic 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);}@Overridepublic String decrypt(String password) throws RemoteException {return encrypt(password);}
}
创建ICompute.aidl
interface ICompute {int add(int a, int b);
}
创建ComputeImpl.java
public class ComputeImpl extends ICompute.Stub {@Overridepublic int add(int a, int b) throws RemoteException {return a + b;}
}
创建IBinderSelector.aidl
interface IBinderSelector {IBinder queryBinder(int binderCode);
}
创建BinderSelectorImpl.java
- 利用不同的binderCode返回不同的Binder
- 当要添加新业务时,只需要新增.aidl文件和修改BinderSelectorImpl
public class BinderSelectorImpl extends IBinderSelector.Stub {public static final int BINDER_COMPUTE = 1;public static final int BINDER_SECURITY_CENTER = 2;public BinderSelectorImpl() {}@Overridepublic 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;}
}
创建BinderPoolService.java,返回BinderSelectorImpl,但并不是给客户端使用,而是给BinderPool绑定
public class BinderPoolService extends Service {private Binder mBinderPool = new BinderSelectorImpl();@Nullable@Overridepublic IBinder onBind(Intent intent) {return mBinderPool;}
}
创建BinderPool.java
- 使用单例,初始化时绑定BinderPoolService,并实现断线重连
- 通过CountDownLatch将异步的bindService转为同步,确保初始化时成功连上Service,才能调用queryBinder()
public class BinderPool {private static final String TAG = "BinderPool";private Context mContext;private IBinderSelector mBinderSelector;private static volatile BinderPool sInstance;private CountDownLatch mCountDownLatch;private BinderPool(Context context) {mContext = context;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() {mCountDownLatch = new CountDownLatch(1);Intent service = new Intent(mContext, BinderPoolService.class);mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);try {mCountDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}public IBinder queryBinder(int binderCode) {IBinder binder = null;if (mBinderSelector != null) {try {binder = mBinderSelector.queryBinder(binderCode);} catch (RemoteException e) {e.printStackTrace();}}return binder;}private ServiceConnection mBinderPoolConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mBinderSelector = IBinderSelector.Stub.asInterface(service);try {mBinderSelector.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);} catch (RemoteException e) {e.printStackTrace();}mCountDownLatch.countDown();}@Overridepublic void onServiceDisconnected(ComponentName name) {}private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {Log.d(TAG, "binderDied: ");mBinderSelector.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);mBinderSelector = null;connectBinderPoolService();}};};
}
MainActivity代码如下
- 使用时需利用线程,因为连接Service或调用Binder方式可能都是耗时的
- 根据code获取Binder,转为对应的实现即可调用
public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private ISecurityCenter mSecurityCenter;private ICompute mCompute;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(new Runnable() {@Overridepublic void run() {work();}}).start();}public void work() {BinderPool binderPool = BinderPool.getInstance(this);IBinder securityBinder = binderPool.queryBinder(BinderSelectorImpl.BINDER_SECURITY_CENTER);mSecurityCenter = SecurityCenterImpl.asInterface(securityBinder);String msg = "hello";try {String encryptMsg = mSecurityCenter.encrypt(msg);Log.d(TAG, "onCreate: encrypt = " + encryptMsg);Log.d(TAG, "onCreate: decrypt = " + mSecurityCenter.decrypt(encryptMsg));} catch (RemoteException e) {e.printStackTrace();}IBinder computeBinder = binderPool.queryBinder(BinderSelectorImpl.BINDER_COMPUTE);mCompute = ComputeImpl.asInterface(computeBinder);try {Log.d(TAG, "onCreate: add = " + mCompute.add(1, 2));} catch (RemoteException e) {e.printStackTrace();}}
}