Android中级——IPC

news/2024/11/13 4:28:20/

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();}}
}

http://www.ppmy.cn/news/748916.html

相关文章

MySQL库表操作

目录 1、SQL语句分类 2、书写规范 &#xff08;二&#xff09;关键字不区分大小写 &#xff08;三&#xff09;字符串和日期时间常数要用单引号 &#xff08;四&#xff09;单词之间需要使用半角空格或者换行符进行分隔 &#xff08;五&#xff09;注释 3、库操作 创建…

p20华为云电脑白屏_华为P20云电脑方案曝光:运行Windows系统

【手机中国新闻】5月8日消息&#xff0c;华为手机产品线副总裁李小龙在微博上展示了华为最新的黑科技。只要拥有一台华为P20、Mate10系列或Mate RS保时捷设计手机&#xff0c;就可以随时随地使用Windows系统来进行办公。这个方案被华为称之为“云电脑”&#xff0c;首发于华为M…

android 华为p20点击按钮没效果,华为P20这6个功能,很实用但很少人知道!

原标题&#xff1a;华为P20这6个功能&#xff0c;很实用但很少人知道&#xff01; 华为P20徕卡三摄的跑分曾让多少拍照手机都自愧不如&#xff0c;很多用户上手亲测效果也确实强悍&#xff0c;但除了徕卡三摄&#xff0c;P20是不是在体验上就和其他手机没区别了&#xff1f; 当…

京东华为P20手机评论数据抓取

用selenium 和chromedriver抓取京东手机P20的评论信息 import time from selenium import webdriver import csv from lxml import etreeclass JDSpider:def __init__(self):self.base_url https://www.jd.comself.opt webdriver.ChromeOptions()self.opt.set_headless()sel…

华为P20 如时升Android 9.0,冬日如何保持好身材?升级EMUI9.0的华为P20系列有妙招...

立冬是冬季的第一个节气,随着步入深秋初冬之际寒意更深。立冬节气,也有秋收冬藏的含义,因此很多地方会有立冬日就是滋补日的习俗。但如今人们生活水平不断提高,一旦滋补过量的确是件麻烦的事情,尤其是一些女性更担心身材走样这个问题。那这些问题有什么好的办法解决呢?其实很简…

华为p20nfc怎么复制门禁卡_华为P20手机来刷门禁_如何给P20手机添加绑定门禁卡的方法...

现在大家感觉出入小区大门带张卡比较麻烦&#xff0c;今天就来说说如何设置咱们的这个华为P20手机来刷门禁&#xff0c;如何来给咱们的华为P20手机添加门禁卡&#xff0c;使用该功能结合手机NFC&#xff0c;可以将华为手机秒变门钥匙&#xff0c;轻松开门禁。免去忘记带门禁卡&…

华为p20能更新鸿蒙么,华为Mate20+P20系列不再进行安全更新

原标题&#xff1a;华为Mate20P20系列不再进行安全更新 对于华为Mate 20系列用户、华为P20系列用户来讲有一个不太好的消息传出。 1月18日华为更新了最新的华为EMUI、Magic UI安全更新机型列表&#xff0c;从列表中我们可以看到华为Mate 20、华为Mate 20 Pro、华为Mate 20 X、华…

华为p20云空间无法清理_华为P20 的安全和隐私功能原来这么好用!

P20是一款主打拍摄的手机&#xff0c;新一代徕卡双摄配合AI智能芯片&#xff0c;让你和摄影师的距离不再遥远&#xff0c;尤其是其手持夜景功能&#xff0c;但是今天呢&#xff0c;咋们不聊摄像&#xff0c;聊聊安全和隐私&#xff01; 华为一向都很重视用户的安全和隐私保护&a…