学习笔记之IPC - Android进程通信
Posted 我叫白小飞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记之IPC - Android进程通信相关的知识,希望对你有一定的参考价值。
IPC
IPC就是 inter-process communication的缩写,含义是 跨进程通信,在学习这之前,我们需要聊什么是进程。进程是系统运行程序的最小单位,是程序的一个运行实例。一个进程的创建是通过Process.start()方法来完成的,其中的参数可以定制启动后最先执行的线程,通常是指定一个ActivityThread(主线程),process会通过socket把创建进程的请求发送给zygote,最终由zygote来fork一个新的进程,也就是我们创建的APP的实例。具体的进程分析可以参考以下两篇文章:Android process 的启动流程 ;Android进程系列第二篇—Zygote进程的启动流程
安卓中的多进程
为什么要使用多进程
- 我们开发应用时也遇到过需要常驻后台的应用,例如:跑步、音乐、手机管家之类的软件,核心服务不会因为主应用被后台清理后功能停止;
- 我们如果要做一个大而全的应用,例如里边有地图、大图浏览、webview、监控服务、下载服务等吃内存的大户,这个时候内存分配给单一进程的空间可能会不够,因为安卓分配给进程的阈值可能是48M、24M、16m等,超过阈值可能会导致主进程OOM。
合理使用多进程,我们可以很大程度上避免应用的奔溃以及提升用户体验;
使用的风险
多进程固然效果不错,但是如果使用不当,也会造成很多麻烦,例如:
- 多虚拟机潜在问题:多进程创建之后,会创建独立的运行空间,并且有独立的虚拟机,很多Java的特性会在多进程中失效:
- 静态变量和单利模式完全失效:因为不同进程之间的内存空间是不同的,所以虚拟机的方法区内的静态变量也是相互独立的,因为单例模式是基于静态变量的,所以单利也会失效,所以在访问多进程的相同变量时值可能不同;
- 线程同步可能完全失效:多进程是不同的虚拟机的,而线程同步是通过虚拟机调度完成的,所以会失效;
- Application会多次创建
进程的创建等于创建一个新的应用实例,所以会再创建一个新的application,onCreate方法会被调用多次,所以不要在application中创建过多的静态变量,这样回导致内存增加;
- 文件读写问题
文件读写是泛指,其中包括:数据库、文件、sp等,由于Java中文件锁和队列机制都是虚拟机级别的,所有不同进程访问同一个文件时,锁是不管用的。
实例
安卓中常用的创建对进程的方式为在androidManifest文件中,给activity、service、brodcastreceiver、contentprovider等四大组件设置process属性,其实设置属性也有两种写法,分别是":remote" 和直接写进程名:process=“com.xxxxx.xxx:remote”; process=“com.xxxx.xxx”,如下:
<activity android:name=".SecondActivity"
android:process=":remote">
</activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
项目运行之后,从mainactivity跳转到secondactivity,然后通过命令:adb shell ps -ef|grep com.xxxx.xxxx 查看当进程:
- 这两种命名方式的区别:最明显的应该就是名字上了,直接 :remote 这样的命名方式最简单,其次就是用冒号这种形式创建的进程是属于私有进程的,其他应用的组件不可以运行在该进程;
IPC概念介绍
接下来看一下IPC,前面已经大概说了什么是IPC,也就是概念,现在我们聊聊ipc中涉及到的几个概念:Serializable、Parcelable以及Binder。为什么要了解Serializable和Parcelable接口呢?因为我们知道Binder是用来通信的,通信就涉及到信息传递(数据传递),我们传递的信息不是无规则的,相传什么就传什么,而Binder的数据传输就用到上述两个接口,咱们一个个来看:
- Serializable接口
它是java提供的一个序列化接口,什么又是序列化?请看文章:对Java Serializable(序列化)的理解和总结,其实大概意思就是将一个对象转换为字节序列的过程就是序列化。序列化和反序列化过程就是将对象I/O流的读写。我们再实现Serializable接口的时候一般系统会提示生成一个serialVersionUID的变量,这个变量的作用就是防止对象结构变化之后反序列化失败。 - Parcelable接口
实现Parcelable接口是另一种序列化方式,安卓独有的,它的序列化过程比较繁琐,具体请看代码:
public class User implements Parcelable
public int userId;
public String userName;
public boolean isMale;
public Book book;
protected User(Parcel in)
userId = in.readInt();
userName = in.readString();
isMale = in.readByte() != 0;
@Override
public void writeToParcel(Parcel dest, int flags)
dest.writeInt(userId);
dest.writeString(userName);
dest.writeByte((byte) (isMale ? 1 : 0));
@Override
public int describeContents()
return 0;
public static final Creator<User> CREATOR = new Creator<User>()
@Override
public User createFromParcel(Parcel in)
return new User(in);
@Override
public User[] newArray(int size)
return new User[size];
;
我们注意到继承完之后自动重写了writeToParcel方法,这个方法就是序列化的主要方法,Parcel 这个对象内部包装了可序列化的数据,可以在Binder中任意传输。
区别
实现Serializable接口的方式进行了I/O操作,而Parcelable则是在内存中序列化,直接降对象序列化到磁盘或者序列化之后通过网络传输,这是其一,其二,Serializable使用简单,只需要继承继承该接口即可,而Parcelable使用起来比较麻烦。
- Binder
Binder是一个继承了IBinder接口的一个类,它是Android中一种跨进程通信的方式。Android系统的虚拟地址内存分为用户空间和内核空间,用户空间为私有空间,内核空间为共享空间,Binder的机制就是通过共享内核空间实现进程通信;Binder把进程A生成的IPC数据(在用户空间生成),传递给BinderDriver,Binder Driver在内核空间运行,之后Binder Driver再把IPC数据传递给进程B。IPC数据由4部分组成,Handle、RPC数据、RPC代码、Binder协议,Handle是服务号,用来区分不同的服务,RPC代码和RPC数据分别是B应用待调用的函数和函数的参数,Binder协议表示IPC数据的处理方法,包括两种,从IPC层传递到Binder Driver和从Binder Driver传递到IPC层。
安卓中的IPC方式
Android中实现IPC的方式有很多种,实质上其实多进程之间的数据传递,只要实现了这个目的就等于实现了IPC,一下就介绍几种方式:
- Bundle 这是最简单的方式,因为前边我们介绍了如何在安卓开启多进程,那我们启动多进程时,可以直接带上数据,这样就可以简单实现多进程的通信。
startActivity(new Intent(...).putExtra("data",bundle));
- 文件共享: 我们可以让不同的进程访问同一个文件(xml、文本、序列化对象等)实现进程通信,但是我们也知道不同进程之间是不能保证文件锁的一致的,这样会导致数据不一致的问题,所以不推荐使用。
- 使用消息机制 Massager:其实这种实现的方式底层也是AIDL:
客户端消息处理:
public class ClientHandler extends Handler
@Override
public void handleMessage(@NonNull Message msg)
super.handleMessage(msg);
if (msg.what == 2)
// 获取到服务端返回的信息并打印出来
String str = msg.getData().getString("reply");
if (BuildConfig.DEBUG) Log.e("ClientMHandler", str);
服务端消息处理
public class ServiceHandler extends Handler
@Override
public void handleMessage(@NonNull Message msg)
super.handleMessage(msg);
if (msg.what == 1)
Log.e("ServiceHandler",msg.getData().getString("msg"));
// 获取客户端的信使实例
Messenger client = msg.replyTo;
// 新建返回消息
Message message = Message.obtain();
message.what = 2;
Bundle bundle = new Bundle();
bundle.putString("reply", "收到客户端的消息了!!");
message.setData(bundle);
try
// 给客户端发送消息
client.send(message);
catch (RemoteException e)
e.printStackTrace();
服务端
public class MessengerService extends Service
// service启动时就创建一个信使
Messenger messenger = new Messenger(new ServiceHandler());
@Nullable
@Override
public IBinder onBind(Intent intent)
// service和client的绑定
return messenger.getBinder();
客户端
public class MainActivity extends AppCompatActivity
private ServiceConnection conn = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
// 通过与服务端绑定,获取Binder,也就是service,然后通过service创建一个和service通信的信使
Messenger messenger = new Messenger(service);
//创建消息
Message message = Message.obtain();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putString("msg", "你好服务端!");
message.setData(bundle);
// 创建一个客户端的信使,传过去,然后服务端才能根据这个信使给客户端发消息
message.replyTo = new Messenger(new ClientHandler());
try
messenger.send(message);
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);
findViewById(R.id.btn_start_service).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent intent = new Intent();
intent.setClass(MainActivity.this, MessengerService.class);
bindService(intent,conn, Service.BIND_AUTO_CREATE);
);
以上是binder的简单使用,我们可以再结合AIDL的只是对其进行优化,稍后专门做一下AIDL的用法;
5. ContentProvider :其实ContentProvider 也是IPC机制,底层也是通过AIDL实现的多进程通信,不过由于ContentProvider 的特殊机制,它的职责计较专一,就是为其他程序提供数的。
6. socket: 学名“套接字”,是网络通信中的概念,它分为流式套接字和数据报套接字相中,分别对应网络的传输控制层中的TCP和UDP协议,TCP是基于“三次握手”建立的连接,可提供稳定的双向传输,而UDP是无连接的单向传输没有像TCP一样的超时重传的机制,所以他的连接可能会出现数据丢失的问题,并且连接也不够稳定,但是因为没有握手机制,所以它的效率是要高于TCP的。因为是基于网络层的,所以不受限于应用的用户空间,所以它也是跨进程通信的手段之一,后续会详细对socket做介绍;
参考链接
以上是关于学习笔记之IPC - Android进程通信的主要内容,如果未能解决你的问题,请参考以下文章
Android:安卓学习笔记之进程间通信方式(IPC)的简单理解和使用
Android进程间通信(IPC)机制Binder简要介绍和学习计划
转Android进程间通信(IPC)机制Binder简要介绍和学习计划
Android:安卓学习笔记之Binder 机制的简单理解和使用