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">
<application
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">
<activity
android:name=".ThirdActivity"
android:process="com.demo.demo0.remote" />
<activity
android:name=".SecondActivity"
android:process=":remote" />
<activity
android: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 {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startActivity(new Intent(this, SecondActivity.class));
}
}
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
startActivity(new Intent(this, ThirdActivity.class));
}
}
public class ThirdActivity extends AppCompatActivity {
@Override
protected 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";
@Override
protected 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";
@Override
protected 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";
@Override
protected 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;
}
@Override
public int describeContents() {
return 0;
}
@Override
public 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>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public 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;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public 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 {
@Override
public java.util.List<com.demo.demo0.Book> getBookList() throws android.os.RemoteException {
return null;
}
@Override
public void addBook(com.demo.demo0.Book book) throws android.os.RemoteException {
}
@Override
public 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);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public 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;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public 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;
}
@Override
public 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() {
@Override
public 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 {
@Override
public 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() {
}
@Override
public 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 {
@Override
public void handleMessage(@NonNull Message msg) {
Log.d(TAG, "handleMessage: " + msg.getData().getString("service"));
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public 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();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected 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);
}
@Override
protected 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;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public 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() {
@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);
int N = mListenerList.beginBroadcast();
Log.d(TAG, "registerListener: mListenerList size = " + N);
mListenerList.finishBroadcast();
}
@Override
public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
int N = mListenerList.beginBroadcast();
Log.d(TAG, "unRegisterListener: mListenerList size = " + N);
mListenerList.finishBroadcast();
}
@Override
public 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);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "book1"));
mBookList.add(new Book(2, "book2"));
new Thread(new ServiceWorker()).start();
}
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.demo.demo0.permission.ACCESS_BOOK_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mBinder;
}
@Override
public 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 {
@Override
public 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() {
@Override
public void handleMessage(@NonNull Message msg) {
Log.d(TAG, "handleMessage: receive new book: " + msg.obj);
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public 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();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIBookManager = null;
Log.d(TAG, "onServiceDisconnected: ");
}
};
private IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
}
};
@Override
protected 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);
}
@Override
protected 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">
<permission
android:name="com.demo.demo0.permission.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal" />
<uses-permission android:name="com.demo.demo0.permission.ACCESS_BOOK_SERVICE" />
<application
android: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">
<service
android: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",
};
@Override
public void onCreate() {
super.onCreate();
new Thread(new TcpServer()).start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
mIsServiceDestroy = true;
}
private class TcpServer implements Runnable {
@Override
public 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() {
@Override
public 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() {
@Override
public 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;
}
}
};
@Override
protected 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() {
@Override
public void run() {
connectTCPServer();
}
}.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public 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() {
@Override
public 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">
<TextView
android:id="@+id/msg_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/msg_container"
android:orientation="horizontal">
<EditText
android:id="@+id/msg"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="8"
android:hint="输入你要发送的内容" />
<Button
android: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 = '^';
@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);
}
}
创建ICompute.aidl
interface ICompute {
int add(int a, int b);
}
创建ComputeImpl.java
public class ComputeImpl extends ICompute.Stub {
@Override
public 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() {
}
@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;
}
}
创建BinderPoolService.java,返回BinderSelectorImpl,但并不是给客户端使用,而是给BinderPool绑定
public class BinderPoolService extends Service {
private Binder mBinderPool = new BinderSelectorImpl();
@Nullable
@Override
public 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() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderSelector = IBinderSelector.Stub.asInterface(service);
try {
mBinderSelector.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mCountDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public 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;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public 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();
}
}
}