Android 进程间通信

书到用时方恨少,事非经过不知难。这篇文章主要讲述Android 进程间通信相关的知识,希望能为你提供帮助。

什么鬼!单例居然失效了,一个地方设置值,另个地方居然取不到,这怎么可能?没道理啊!排查半天,发现这两就不在一个进程里,才恍然大悟……
什么是进程按照操作系统中的描述:进程一般指一个执行单元,在 PC 和移动设备上指一个程序或者一个应用。
为什么要使用多进程我们都知道,系统为 APP 每个进程分配的内存是有限的,如果想获取更多内存分配,可以使用多进程,将一些看不见的服务、比较独立而又相当占用内存的功能运行在另外一个进程当中。
目录结构预览先放出最终实践后的目录结构,有个大概印象,后面一一介绍。
Android 进程间通信

文章图片

Android 进程间通信

文章图片

如何使用多进程androidManifest.xml 清单文件中注册 Activity、Service 等四大组件时,指定 android:process 属性即可开启多进程,如:
< activity android:name=" .Process1Activity" android:process=" :process1" /> < activity android:name=" .Process2Activity" android:process=" com.wuxiaolong.androidprocesssample.process2" />

Android 进程间通信

文章图片

说明:
1、com.wuxiaolong.androidprocesssample,主进程,默认的是应用包名;
2、android:process=" :process1" ,“:”开头,是简写,完整进程名包名 + :process1
3、android:process=" com.wuxiaolong.androidprocesssample.process2" ,以小写字母开头的,属于全局进程,其他应用可以通过 ShareUID 进行数据共享;
4、进程命名跟包名的命名规范一样。
进程弊端 Application 多次创建我们自定义一个 Application 类,onCreate 方法进行打印 Log.d(" wxl" , " AndroidApplication onCreate" ); ,然后启动 Process1Activity:
com.wuxiaolong.androidprocesssample D/wxl: AndroidApplication onCreate com.wuxiaolong.androidprocesssample:process1 D/wxl: AndroidApplication onCreate

看到确实被创建两次,原因见:android:process 的坑,你懂吗?多数情况下,我们都会在工程中自定义一个 Application 类,做一些全局性的初始化工作,因为我们要区分出来,让其在主进程进行初始化,网上解决方案:
@Override public void onCreate() { super.onCreate(); String processName = AndroidUtil.getProcessName(); if (getPackageName().equals(processName)) { //初始化操作 Log.d(" wxl" , " AndroidApplication onCreate=" + processName); } }

AndroidUtil:
public static String getProcessName() { try { File file = new File(" /proc/" + android.os.Process.myPid() + " /" + " cmdline" ); BufferedReader mBufferedReader = new BufferedReader(new FileReader(file)); String processName = mBufferedReader.readLine().trim(); mBufferedReader.close(); return processName; } catch (Exception e) { e.printStackTrace(); return null; } }

静态成员和单例模式失效创建一个类 SingletonUtil:
public class SingletonUtil { private static SingletonUtil singletonUtil; private String userId = " 0" ; public static SingletonUtil getInstance() { if (singletonUtil == null) { singletonUtil = new SingletonUtil(); } return singletonUtil; }public String getUserId() { return userId; }public void setUserId(String userId) { this.userId = userId; } }

在 MainActivity 进行设置:
SingletonUtil.getInstance().setUserId(" 007" );

Process1Activity 取值,打印:
Log.d(" wxl" , " userId=" + SingletonUtil.getInstance().getUserId());

发现打印 userId=0,单例模式失效了,因为这两个进程不在同一内存了,自然无法共享。
进程间通信 文件共享既然内存不能共享,是不是可以找个共同地方,是的,可以把要共享的数据保存 SD 卡,实现共享。首先将 SingletonUtil 实现 Serializable 序列化,将对象存入 SD 卡,然后需要用的地方,反序列化,从 SD 卡取出对象,完整代码如下:
SingletonUtil
public class SingletonUtil implements Serializable{ public static String ROOT_FILE_DIR = Environment.getExternalStorageDirectory() + File.separator + " User" + File.separator; public static String USER_STATE_FILE_NAME_DIR = " UserState" ; private static SingletonUtil singletonUtil; private String userId = " 0" ; public static SingletonUtil getInstance() { if (singletonUtil == null) { singletonUtil = new SingletonUtil(); } return singletonUtil; }public String getUserId() { return userId; }public void setUserId(String userId) { this.userId = userId; } }

序列化和反序列化
public class AndroidUtil { public static boolean createOrExistsDir(final File file) { // 如果存在,是目录则返回true,是文件则返回false,不存在则返回是否创建成功 return file != null & & (file.exists() ? file.isDirectory() : file.mkdirs()); }/** * 删除目录 * * @param dir 目录 * @return {@code true}: 删除成功< br> {@code false}: 删除失败 */ public static boolean deleteDir(final File dir) { if (dir == null) return false; // 目录不存在返回true if (!dir.exists()) return true; // 不是目录返回false if (!dir.isDirectory()) return false; // 现在文件存在且是文件夹 File[] files = dir.listFiles(); if (files != null & & files.length != 0) { for (File file : files) { if (file.isFile()) { if (!file.delete()) return false; } else if (file.isDirectory()) { if (!deleteDir(file)) return false; } } } return dir.delete(); }/** * 序列化,对象存入SD卡 * * @param obj存储对象 * @param destFileDirSD卡目标路径 * @param destFileName SD卡文件名 */ public static void writeObjectToSDCard(Object obj, String destFileDir, String destFileName) {createOrExistsDir(new File(destFileDir)); deleteDir(new File(destFileDir + destFileName)); FileOutputStream fileOutputStream = null; ObjectOutputStream objectOutputStream = null; try { fileOutputStream = new FileOutputStream(new File(destFileDir, destFileName)); objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(obj); } catch (Exception e) { e.printStackTrace(); } finally { try { if (objectOutputStream != null) { objectOutputStream.close(); objectOutputStream = null; } if (fileOutputStream != null) { fileOutputStream.close(); fileOutputStream = null; } } catch (IOException e) { e.printStackTrace(); }}}/** * 反序列化,从SD卡取出对象 * * @param destFileDirSD卡目标路径 * @param destFileName SD卡文件名 */ public static Object readObjectFromSDCard(String destFileDir, String destFileName) { FileInputStream fileInputStream = null; Object object = null; ObjectInputStream objectInputStream = null; try { fileInputStream = new FileInputStream(new File(destFileDir, destFileName)); objectInputStream = new ObjectInputStream(fileInputStream); object = objectInputStream.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (objectInputStream != null) { objectInputStream.close(); objectInputStream = null; } if (fileInputStream != null) { fileInputStream.close(); fileInputStream = null; } } catch (IOException e) { e.printStackTrace(); }} return object; } }

需要权限:
< uses-permission android:name=" android.permission.WRITE_EXTERNAL_STORAGE" />

MainActivity 序列写入
SingletonUtil singletonUtil = SingletonUtil.getInstance(); singletonUtil.setUserId(" 007" ); AndroidUtil.writeObjectToSDCard(singletonUtil, SingletonUtil.ROOT_FILE_DIR, SingletonUtil.USER_STATE_FILE_NAME_DIR);

Process1Activity 反序列化取值
Object object = AndroidUtil.readObjectFromSDCard(SingletonUtil.ROOT_FILE_DIR, SingletonUtil.USER_STATE_FILE_NAME_DIR); if (object != null) { SingletonUtil singletonUtil = (SingletonUtil) object; Log.d(" wxl" , " userId=" + singletonUtil.getUserId()); //打印:userId=007 }

AIDLAIDL,Android 接口定义语言,定义客户端与服务端进程间通信,服务端有处理多线程时,才有必要使用 AIDL,不然可以使用 Messenger ,后文介绍。
单个应用,多个进程
服务端AIDL 传递数据有基本类型 int,long,boolean,float,double,也支持 String,CharSequence,List,Map,传递对象需要实现 Parcelable 接口,这时需要指定 in(客户端数据对象流向服务端)、out (数据对象由服务端流向客户端)。
1、Userbean.java
public class UserBean implements Parcelable { private int userId; private String userName; public int getUserId() { return userId; }public void setUserId(int userId) { this.userId = userId; }public String getUserName() { return userName; }public void setUserName(String userName) { this.userName = userName; }public UserBean() {}private UserBean(Parcel in) { userId = in.readInt(); userName = in.readString(); }/** * @return 0 或 1 ,1 含有文件描述符 */ @Override public int describeContents() { return 0; }/** * 系列化 * * @param dest当前对象 * @param flags 0 或 1,1 代表当前对象需要作为返回值,不能立即释放资源 */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(userId); dest.writeString(userName); }/** * 反序列化 */ public static final Creator< UserBean> CREATOR = new Creator< UserBean> () { @Override public UserBean createFromParcel(Parcel in) { return new UserBean(in); }@Override public UserBean[] newArray(int size) { return new UserBean[size]; } }; }

2、UserBean.aidl
Userbean.java 同包下创建对应的 UserBean.aidl 文件,与 aidl 调用和交互。
// UserBean.aidl package com.wuxiaolong.androidprocesssample; parcelable UserBean;

3、IUserManager.aidl
// IUserManager.aidl package com.wuxiaolong.androidprocesssample; // Declare any non-default types here with import statements //手动导入 import com.wuxiaolong.androidprocesssample.UserBean; interface IUserManager {//基本数据类型:int,long,boolean,float,double,String void hello(String aString); //非基本数据类型,传递对象 void getUser(in UserBean userBean); //in 客户端-> 服务端}

4、服务类
新建 AIDLService 继承 Service,并且实现 onBind() 方法返回一个你实现生成的 Stub 类,把它暴露给客户端。Stub 定义了一些辅助的方法,最显著的就是 asInterface(),它是用来接收一个 IBinder,并且返回一个 Stub 接口的实例 。
public class AIDLService extends Service {private Binder binder = new IUserManager.Stub() {@Override public void getUser(UserBean userBean) throws RemoteException { Log.d(" wxl" , userBean.getUserId() + " ," + userBean.getUserName() + " from AIDL Service" ); }@Override public void hello(String aString) throws RemoteException { Log.d(" wxl" , aString + " from AIDL Service" ); } }; @Nullable @Override public IBinder onBind(Intent intent) { return binder; }@Override public void onCreate() { super.onCreate(); } }

AndroidManifest 注册:
< service android:name=" .AIDLService" android:process=" :aidlRemote" />

以上创建完毕,build clean 下,会自动生成 aidl 对应的 java 类供客户端调用。
客户端1、app/build.gradle
需要指定 aidl 路径:
android { //…… sourceSets { main { java.srcDirs = [' src/main/java' , ' src/main/aidl' ] } } }

2、启动服务,建立联系
public class MainActivity extends AppCompatActivity {private ServiceConnection aidlServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IUserManager remoteService = IUserManager.Stub.asInterface(service); UserBean userBean = new UserBean(); userBean.setUserId(1); userBean.setUserName(" WuXiaolong" ); try { remoteService.getUser(userBean); remoteService.hello(" Hello" ); } 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, AIDLService.class); bindService(intent, aidlServiceConnection, Context.BIND_AUTO_CREATE); }@Override protected void onDestroy() { unbindService(aidlServiceConnection); super.onDestroy(); } }

打印:
com.wuxiaolong.androidprocesssample:aidlRemote D/wxl: 1,WuXiaolong from AIDL Service com.wuxiaolong.androidprocesssample:aidlRemote D/wxl: Hello from AIDL Service

多个应用,多进程
和上面基本差不多,把服务端和客户端分别创建的两个项目,可以互相通信,注意点:
1、服务端创建好的 aidl 文件,带包拷贝到客户端项目中;
2、客户端启动服务是隐式启动,Android 5.0 中对 service 隐式启动有限制,必须通过设置 action 和 package,代码如下:
AndroidManifest 注册:
< service android:name=" .AIDLService" > < intent-filter> < action android:name=" android.intent.action.AIDLService" /> < /intent-filter>

启动服务:
Intent intent = new Intent(); intent.setAction(" android.intent.action.AIDLService" ); intent.setPackage(" com.wuxiaolong.aidlservice" ); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

使用 MessengerMessenger 可以在不同的进程传递 Message 对象,而我们可以在 Message 对象中放入我们所需要的数据,这样就能实现进程间通信了。Messenger 底层实现是 AIDL,对 AIDL 做了封装, 不需要处理多线程,实现步骤也分为服务端和客户端,代码如下:
服务端
MessengerService:
public class MessengerService extends Service {private final Messenger messenger = new Messenger(new MessengerHandler()); private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MainActivity.MSG_FROM_CLIENT: //2、服务端接送消息 Log.d(" wxl" , " msg=" + msg.getData().getString(" msg" )); //4、服务端回复消息给客户端 Messenger serviceMessenger = msg.replyTo; Message replyMessage = Message.obtain(null, MSG_FROM_SERVICE); Bundle bundle = new Bundle(); bundle.putString(" msg" , " Hello from service." ); replyMessage.setData(bundle); try { serviceMessenger.send(replyMessage); } catch (RemoteException e) { e.printStackTrace(); } break; } super.handleMessage(msg); } }@Nullable @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } }

AndroidManafest.xml 注册:
< service android:name=" .MessengerService" android:process=" :messengerRemote" />

客户端
MainActivity
public class MainActivity extends AppCompatActivity { public static final int MSG_FROM_CLIENT = 1000; public static final int MSG_FROM_SERVICE = 1001; private Messenger clientMessenger; private ServiceConnection messengerServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //1、发送消息给服务端 clientMessenger = new Messenger(service); Message message = Message.obtain(null, MSG_FROM_CLIENT); Bundle bundle = new Bundle(); bundle.putString(" msg" , " Hello from client." ); message.setData(bundle); //3、这句是服务端回复客户端使用 message.replyTo = getReplyMessenger; try { clientMessenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } }@Override public void onServiceDisconnected(ComponentName name) {} }; private final Messenger getReplyMessenger = new Messenger(new MessengerHandler()); private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MainActivity.MSG_FROM_SERVICE: //5、服务端回复消息给客户端,客户端接送消息 Log.d(" wxl" , " msg=" + msg.getData().getString(" msg" )); break; } super.handleMessage(msg); } }@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Messenger 进行通信 Intent intent = new Intent(this, MessengerService.class); bindService(intent, messengerServiceConnection, Context.BIND_AUTO_CREATE); }@Override protected void onDestroy() { unbindService(messengerServiceConnection); super.onDestroy(); }}

打印信息:
com.wuxiaolong.androidprocesssample:remote D/wxl: msg=Hello from client. com.wuxiaolong.androidprocesssample D/wxl: msg=Hello from service.

最后《Android开发艺术探索》一书关于 Android 进程间通信这块,还有 ContentProvider、Socket 方式,由于篇幅所限,这里不一一介绍了,有兴趣可以自行查看。如果需要这次 Sample 的源码,可在我的公众号「吴小龙同学」回复:「AndroidProcessSample」获取。
参考《Android开发艺术探索》
Android 中的多进程,你值得了解的一些知识
Android使用AIDL实现跨进程通讯(IPC)
【Android 进程间通信】
Android 进程间通信

文章图片


    推荐阅读