Android IPC机制笔记

it2022-05-05  108

参考《Android开发艺术探索》

一、 IPC简介

IPC含义为跨进程通信,通常不同的APP运行在不同的进程中,当两个app需要交互时,就要跨进程进行通信;或者一个app开了多个进程,大部分情况下也是需要跨进程通信的。 IPC有多种方式实现,有Bundle、文件共享、Messenger、AIDL、ContentProvider、Socket等。

首先是IPC的基础部分

二、IPC基础

1.开启多进程

在AndroidMenifest中指定activity的process属性,即可将这个这个activity放在单独的进程中。

<activity android:name=".ThirdActivity" android:process=":remote"/>

这个activity将运行在包名.remote进程中。这个进程是当前应用的私有进程,其它应用不可以和它运行在同一个进程中。也可以直接指定一个字符串作为进程名。

多进程模式下进程运行在独立的虚拟机中,不同虚拟机分配有不同的内存地址,因此不同进程访问同一类会有多个副本,当一个进程改变了类的变量时对另一个进程并没有影响。因此

静态成员和单例会失效线程同步机制失效Application存在多次创建现象

2.序列化和反序列化

Serialable Serialable是Java中提供的序列化接口,一个类若要序列化只需要实现Serialable接口即可。如: public class User implements Serializable { private String name; private int id; private boolean isMale; public User(String name, int id, boolean isMale) { this.name = name; this.id = id; this.isMale = isMale; } public String getName() { return name; } public int getId() { return id; } public boolean isMale() { return isMale; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", id=" + id + ", isMale=" + isMale + '}'; } }

序列化时可以通过ObjectOutputStream#writeObject(Object)方法将类实例写入通过流写入文件或者其它的地方,反序列化时通过ObjectInputStream#readObject()方法从输入流中读取即可。 2. Parcelable Parcelable是Android提供的序列化和反序列化方法,使用相较于Serialable稍显复杂但是效率较高。需要序列化的类要实现Parcelable接口并实现序列化和反序列化的过程:

public class Book implements Parcelable { private String bookName; private int bookId; public Book(String bookName, int bookId) { this.bookName = bookName; this.bookId = bookId; } private Book(Parcel in) { bookName = in.readString (); bookId = in.readInt (); } public static final Creator<Book> CREATOR = new Creator<Book> () { @Override public Book createFromParcel(Parcel in) { return new Book (in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; public String getBookName() { return bookName; } public int getBookId() { return bookId; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString (bookName); dest.writeInt (bookId); } @Override public String toString() { return "Book{" + "bookName='" + bookName + '\'' + ", bookId=" + bookId + '}'; } }

通过writeToParcel(Parcel dest, int flags)方法实现序列化过程,通过createFromParcel(Parcel in)实现反序列化,通过describeContents()返回内容描述,一般返回0。 Parcelabe可作为Intent携带的数据在组件和进程间传递。 3. aidl aidl是常用的跨进程通信方式,底层基于Binder,类似于客户端和服务端的架构,客户端根据自身需求规定接口,服务端则实现接口中的方法,并将结果通过Binder返回个客户端。其特殊之处在于接口定义需要定义为.aidl文件,使用到的自定义类需要实现Parcelabe接口,即可序列化。下面是一个例子: 假设客户端是读者,服务端是图书馆,读者需要知道图书馆的所有藏书,并且也可以向其中添加书,因此客户端要定义一个接口IBookManager,这个接口是一个aidl接口,新建IBookManager.aidl文件如下:

// IBookManager.aidl package com.github.xiaogegechen.ipctest; import com.github.xiaogegechen.ipctest.Book; import com.github.xiaogegechen.ipctest.IOnNewBookArrivedListener; // Declare any non-default types here with import statements interface IBookManager { void addBook(in Book book); List<Book> getBookList(); }

Book类实现了Parcelabe接口,并且新建Book.aidl文件并声明,如下:

package com.github.xiaogegechen.ipctest; parcelable Book;

接着需要服务端实现客户端的需求,将新建的Book.aidl、Book.java、IBookManager.aidl文件拷贝到服务端app工程中,build工程,可以发现生成了一个新的类IBookManager.java,如下:

/* * This file is auto-generated. DO NOT MODIFY. * Original file: F:\\program\\Android\\IPCTestClient\\app\\src\\main\\aidl\\com\\github\\xiaogegechen\\ipctest\\IBookManager.aidl */ package com.github.xiaogegechen.ipctest; // Declare any non-default types here with import statements // android.os.IInterface接口中定义了asBinder()方法, public interface IBookManager extends android.os.IInterface { /** * Local-side IPC implementation stub class. * 服务端的binder */ public static abstract class Stub extends android.os.Binder implements com.github.xiaogegechen.ipctest.IBookManager { private static final java.lang.String DESCRIPTOR = "com.github.xiaogegechen.ipctest.IBookManager"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface (this, DESCRIPTOR); } /** * Cast an IBinder object into an com.github.xiaogegechen.ipctest.IBookManager interface, * generating a proxy if needed. * 将服务端的binder对象封装成客户端需要的binder对象 */ public static com.github.xiaogegechen.ipctest.IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface (DESCRIPTOR); if (((iin != null) && (iin instanceof com.github.xiaogegechen.ipctest.IBookManager))) { return ((com.github.xiaogegechen.ipctest.IBookManager) iin); } return new com.github.xiaogegechen.ipctest.IBookManager.Stub.Proxy (obj); } @Override public android.os.IBinder asBinder() { return this; } /** * 从data中取出参数,从code中取出方法标志,从而调用服务端相应的方法,并将结果写进reply中 */ @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_addBook: { data.enforceInterface (descriptor); com.github.xiaogegechen.ipctest.Book _arg0; if ((0 != data.readInt ())) { _arg0 = com.github.xiaogegechen.ipctest.Book.CREATOR.createFromParcel (data); } else { _arg0 = null; } this.addBook (_arg0); reply.writeNoException (); return true; } case TRANSACTION_getBookList: { data.enforceInterface (descriptor); java.util.List<com.github.xiaogegechen.ipctest.Book> _result = this.getBookList (); reply.writeNoException (); reply.writeTypedList (_result); return true; } default: { return super.onTransact (code, data, reply, flags); } } } /** * 客户端的binder */ private static class Proxy implements com.github.xiaogegechen.ipctest.IBookManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } // 将客户端的binder对象封装成服务端的binder对象 @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } // 客户端会调用这个方法,此方法将调用服务端的transact()方法,transact()方法调用onTransact()方法,onTransact()方法 // 内部会调用服务端的addBook()方法,从而完成从客户端到服务端的调用 @Override public void addBook(com.github.xiaogegechen.ipctest.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); } // 调用服务端的addBook方法,参数在_data中,结果在_reply中,只是这里没有返回值 mRemote.transact (Stub.TRANSACTION_addBook, _data, _reply, 0); _reply.readException (); } finally { _reply.recycle (); _data.recycle (); } } // 和addBook()方法类似 @Override public java.util.List<com.github.xiaogegechen.ipctest.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.github.xiaogegechen.ipctest.Book> _result; try { _data.writeInterfaceToken (DESCRIPTOR); // 调用服务端的addBook方法,参数在_data中,结果在_reply中 mRemote.transact (Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException (); _result = _reply.createTypedArrayList (com.github.xiaogegechen.ipctest.Book.CREATOR); } finally { _reply.recycle (); _data.recycle (); } return _result; } } // 方法标记符号,用于区分接口方法 static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void addBook(com.github.xiaogegechen.ipctest.Book book) throws android.os.RemoteException; public java.util.List<com.github.xiaogegechen.ipctest.Book> getBookList() throws android.os.RemoteException; }

服务端需要继承IBookManager.Stub类并实现客户端规定的接口中的方法,然后将这个对象binder对象放回个客户端,而客户端需要调用IBookManager.Stub.asInterface()方法将这个binder封装成客户端需要的接口,从而实现在客户端调用服务端的服务。服务端:

public class MyService extends Service { private static final String TAG = "MyService"; private CopyOnWriteArrayList<Book> mBooks; private IBookManager.Stub mStub = new IBookManager.Stub () { @Override public void addBook(Book book) throws RemoteException { mBooks.add (book); } @Override public List<Book> getBookList() throws RemoteException { return mBooks; } }; @Override public void onCreate() { super.onCreate (); mBooks = new CopyOnWriteArrayList<> (); mBooks.add (new Book ("Book1", 1)); mBooks.add (new Book ("Book2", 2)); } @Override public IBinder onBind(Intent intent) { return mStub; } @Override public void onDestroy() { super.onDestroy (); } }

客户端:

public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private TextView mTextView; private IBookManager mBookManager; private ServiceConnection mConnection = new ServiceConnection () { @Override public void onServiceConnected(ComponentName name, IBinder service) { mBookManager = IBookManager.Stub.asInterface (service); try { mBookManager.registerListener (mOnNewBookArrivedListener); List<Book> bookList = mBookManager.getBookList (); Log.d (TAG, "onServiceConnected: " + bookList); mBookManager.addBook (new Book ("Book3", 3)); List<Book> newBookList = mBookManager.getBookList (); Log.d (TAG, "onServiceConnected: " + newBookList); } 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); mTextView = findViewById (R.id.text_view); Intent intent = new Intent (); intent.setAction ("com.github.xiaogegechen.ipctest.MyService"); intent.setPackage ("com.github.xiaogegechen.ipctest"); bindService (intent, mConnection, BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy (); unbindService (mConnection); } }

三、IPC方式

1. Bundle

Bundle实现了Parcelable接口,所以支持在四大组件中通过Intent携带Bundle数据从而实现进程间通信,使用比较简单。

2. 文件共享

通过共享文件实现进程通信,也比较简单,但是要注意并发写的问题

3. 使用aidl

上述已经介绍过。

4. 使用ContentProvider

Android四大组件之一,专门为进程间共享数据服务。

5. 使用Socket

socket工作原理(摘自大神博客) 很显然,socket也是可以完成进程间通信的。 服务端:

public class SocketService extends Service { private static final String TAG = "SocketService"; private boolean mIsServiceDestroyed = false; private static final String[] CHAT_CONTENT = new String[]{ "你好啊!哈哈", "请问你叫什么名字啊", "今天沈阳天气不错啊,shy", "你知道吗?我只和你聊天的哦!", "给你讲个笑话吧,你比我帅,哈哈!" }; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate (); new Thread (new Runnable (){ @Override public void run() { ServerSocket serverSocket = null; try { // 监听5000端口 serverSocket = new ServerSocket (5000); } catch (IOException e) { Log.e (TAG, "open socket failed"); e.printStackTrace (); return; } Log.d (TAG, "open socket successfully"); while(!mIsServiceDestroyed){ try { // 等待有客户端连接进来,当没有客户端连接时会阻塞 final Socket socket = serverSocket.accept (); Log.d (TAG, "a client connect, it is: " + socket); replyClient (socket); } catch (IOException e) { e.printStackTrace (); } } } }).start (); } private void replyClient(Socket socket) throws IOException{ BufferedReader reader = new BufferedReader (new InputStreamReader (socket.getInputStream ())); PrintWriter writer = new PrintWriter (new BufferedWriter (new OutputStreamWriter (socket.getOutputStream ()))); while(!mIsServiceDestroyed){ // 读取客户端发来的信息,以"\n"为参考,当客户端没有发送信息时会阻塞 // 读到输入流的尾部时返回null String line = reader.readLine (); Log.d (TAG, "receive message from client, the message is: " + line); if(line == null){ // 客户端断开链接,结束 break; } // 随机进行回复 int idx = new Random ().nextInt (CHAT_CONTENT.length); String reply = CHAT_CONTENT[idx]; writer.println (reply); writer.flush (); } Log.d (TAG, "client quit!"); // 断开读取客户端的连接 reader.close (); // 断开向客户端发消息的连接 writer.close (); // 关闭socket socket.close (); } @Override public void onDestroy() { mIsServiceDestroyed = true; super.onDestroy (); } }

客户端:

public class SocketActivity extends AppCompatActivity { private static final String TAG = "SocketActivity"; private static final int CONNECT_OK = 0; private static final int NEW_MESSAGE_ARRIVED = 1; private static final int SEND_MESSAGE = 2; private Button mButton; private TextView mTextView; private EditText mEditText; private PrintWriter mPrintWriter; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler (){ @Override public void handleMessage(Message msg) { super.handleMessage (msg); switch (msg.what){ case CONNECT_OK: mButton.setEnabled (true); break; case NEW_MESSAGE_ARRIVED: mTextView.append ("from server:\n"); String reply = (String) msg.obj; mTextView.append (reply); mTextView.append ("\n"); break; case SEND_MESSAGE: String str = (String) msg.obj; mTextView.append (str); break; default: break; } } }; private Socket mSocket; @Override protected void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView (R.layout.activity_socket); mButton = findViewById (R.id.send); mTextView = findViewById (R.id.text_chat); mEditText = findViewById (R.id.input); mButton.setEnabled (false); mButton.setOnClickListener (new View.OnClickListener () { @Override public void onClick(View v) { final String str = mEditText.getText ().toString (); mTextView.append ("from client:\n" + str + "\n"); new Thread (new Runnable (){ @Override public void run() { mPrintWriter.println (str); mPrintWriter.flush (); } }).start (); } }); // 开启远程服务 Intent intent = new Intent (); intent.setAction ("com.github.xiaogegechen.ipctest.SocketService"); intent.setPackage ("com.github.xiaogegechen.ipctest"); ComponentName componentName = startService (intent); Log.d (TAG, "startService, componentName is: " + componentName); new Thread (new Runnable (){ @Override public void run() { Socket socket = null; boolean success = false; while(!success){ try { // 建立链接,三次握手会在这里完成 socket = new Socket ("localhost", 5000); mSocket = socket; // 建立向服务端发送数据的连接 mPrintWriter = new PrintWriter (new BufferedWriter (new OutputStreamWriter (mSocket.getOutputStream ()))); mPrintWriter.println ("hello"); mPrintWriter.flush (); success = true; mHandler.sendEmptyMessage (CONNECT_OK); } catch (IOException e) { e.printStackTrace (); Log.e (TAG, "connect tcp server failed! retry ..."); // 暂停一秒继续重试 SystemClock.sleep (1000); } } Log.d (TAG, "connection ok!"); try { // 链接成功,开始通信 BufferedReader reader = new BufferedReader (new InputStreamReader (socket.getInputStream ())); while(!SocketActivity.this.isFinishing ()){ // 读取服务端传回的信息 String reply = reader.readLine (); Log.d (TAG, "receive: " + reply); if(!TextUtils.isEmpty (reply)){ Message message = mHandler.obtainMessage (); message.what = NEW_MESSAGE_ARRIVED; message.obj = reply; message.sendToTarget (); } } Log.d (TAG, "quit..."); // 关闭向服务端发送数据的连接 mPrintWriter.close (); // 关闭从服务端读数据的链接 reader.close (); // 关闭socket socket.close (); } catch (IOException e) { e.printStackTrace (); } } }).start (); } @Override protected void onDestroy() { try { if (mSocket != null) { mSocket.shutdownInput (); mSocket.close (); } } catch (IOException e) { e.printStackTrace (); } super.onDestroy (); } }

四、Binder连接池

当客户端有大量的服务需要服务端完成时,如果是普通的aidl方法就需要创建镀铬服务,非常不方便管理,这时可以使用binder连接池。这时服务端仅仅需要开启一个服务即可,当客户端需要服务的时候,通过一个queryCode向该服务发送请求,拿到自己需要的Binder,利用这个binder拿到结果。

需要在客户端添加一个aidl接口,接收一个queryCode并返回binder,服务端开启服务并实现这个接口,根据不同的queryCode返回不同的binder,如下:

// IBinderPool.aidl package com.github.xiaogegechen.ipctest; import android.os.IBinder; interface IBinderPool { // 根据queryCode返回相应的binder IBinder queryBinder(int queryCode); }

服务端:

// BinderPoolService.java public class BinderPoolService extends Service { @Override public IBinder onBind(Intent intent) { return new BinderPoolImpl (); } } // BinderPoolImpl.java public class BinderPoolImpl extends IBinderPool.Stub{ @Override public IBinder queryBinder(int queryCode) throws RemoteException { IBinder binder = null; switch(queryCode){ case QueryCode.CODE_ICOMPUTE: binder = new CopmuteImpl (); break; case QueryCode.CODE_ISECURIT_CENTER: binder = new SecurityCenterImpl (); break; default: break; } return binder; } }

当客户端有IPC需求的时候,就需要开启这个服务,将返回的binder封装成客户端定义的IBinderPool,通过向IBinderPool提供queryCode拿到对应的binder,在将此binder封装成对应的客户端接口即可使用:

public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private ServiceConnection mConnection = new ServiceConnection () { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 封装成客户端需要的binderPool IBinderPool binderPool = IBinderPool.Stub.asInterface (service); try { // 拿到服务端ICompute的IBinder IBinder computeBinder = binderPool.queryBinder (QueryCode.CODE_ICOMPUTE); // 封装成客户端需要的ICompute ICompute compute = ICompute.Stub.asInterface (computeBinder); // 计算 int result = compute.add (10, 20); Log.d (TAG, "result is: " + result); IBinder securityBinder = binderPool.queryBinder (QueryCode.CODE_ISECURIT_CENTER); ISecurityCenter securityCenter = ISecurityCenter.Stub.asInterface (securityBinder); String decode = securityCenter.decode ("hello world"); Log.d (TAG, decode); } 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 (); intent.setPackage ("com.github.xiaogegechen.ipctest"); intent.setAction ("com.github.xiaogegechen.ipctest.binder_pool.BinderPoolService"); bindService (intent, mConnection, BIND_AUTO_CREATE); } }

binder连接池避免了service的重复创建,使用非常方便,因此应该尽量在项目中使用binder连接池管理IPC请求。


最新回复(0)