您的位置:首页 > 其它

2.1 IPC机制简介与基础概念

2016-12-13 17:29 686 查看

1. 简介

IPC:进程间通信(Inter Process Communication),指两个进程之间进行数据交换

进程与线程

进程:一个执行单元,移动设备上的一个应用或程序。一个进程可以包含多个线程。Android中最有特色的进程间通信方式是Binder

线程:CPU调度的最小单元

多进程是进程间通信的前提,IPC应用场景:

一个应用采取多进程模式实现,应用自身需要传递数据

两个应用需要传递数据。ContentProvider

2.一个应用多个进程

一个应用开启多进程的方式:

四大组件(Activity、Service、BroadcastReceiver、ContentProvider)在Manifest中添加 android:process属性

JNI在native层去fork一个新进程

2.1 android:process实现多进程

MainActivity,SecondActivity、ThirdActivity在Manifest中配置如下:

<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:label="@string/app_name"
android:process=":remote">
</activity>
<activity
android:name=".ThirdActivity"
android:process="com.jieqiong.ipc.remote">
</activity>


点击MainActivity 的按钮,跳转至SecondActivity

点击SecondActivity的按钮,跳转至ThirdActivity

通过Android Device Monitor查看进程号



或者通过 adb shell ps | grep jieqiong.ipc 查看

u0_a201   12288 532   1726436 46032 SyS_epoll_ 0000000000 S com.jieqiong.ipc
u0_a201   12788 532   1727484 48580 SyS_epoll_ 0000000000 S com.jieqiong.ipc:remote
u0_a201   13030 532   1737260 43528 SyS_epoll_ 0000000000 S com.jieqiong.ipc.remote


启动了3个进程,进程号分别为12288,12788,13030

SecondActivity进程名为:com.jieqiong.ipc:remote

ThirdActivity进程名为:com.jieqiong.ipc.remote

android:process

以 : 开头,如 android:process=”:remote”,是当前应用的私有进程,其他应用的组件 不可以跟它跑在同一个进程。

不以 : 开头,如 android:process=”com.jieqiong.ipc.remote”,属于全局进程,其他应用通过shareUID方式和它跑在同一个进程中。

Android中两个应用通过ShareUID方式跑在同一个进程,要求签名相同,可以访问对方的私有数据,如data目录,组件信息,内存数据等。

Android每个应用都会有一个唯一的UID,具有相同UID的应用才能共享数据。

2.2 android:process存在的问题

定义UserManager,添加静态变量 userId = 1

public class UserManager {
public static int userId = 1;
}


MainActivity中将 userId 设为2

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UserManager.userId = 2;

String processName = Utils.getProcessName(getApplicationContext(), Process.myPid());
Log.d("MainActivity", "### processName = " + processName + ", UserManager.userId = " + UserManager.userId);

Button btnMain = (Button) findViewById(R.id.btn_Main);
btnMain.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
}


Utils

public class Utils {
public static String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt
.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
}


SecondActivity中打印userId

public class SecondActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
String processName = Utils.getProcessName(getApplicationContext(), Process.myPid());
Log.d("SecondActivity", "### processName = " + processName + ", UserManager.userId = " + UserManager.userId);
}
}


log如下:

12-13 17:03:43.710 26201-26201/? D/MainActivity: ### processName = com.jieqiong.ipc, UserManager.userId = 2
12-13 17:03:49.049 26792-26792/? D/SecondActivity: ### processName = com.jieqiong.ipc:remote, UserManager.userId = 1


MainActivity和SecondActivity运行在单独的进程中,两个进程中都存在UserManager类,这两个类互不干扰,在一个进程中修改userId 的值,只影响当前进程,不影响其他进程。

多进程存在的问题:

静态成员 和 单例模式 完全失效

线程同步机制完全失效(不是一块内存,线程锁的不是一个对象)

SharedPreferences的可靠性下降(不支持两个进程同时执行写操作)

Application会多次创建(每个进程创建一个Application)

3. IPC基础概念介绍

跨进程通信的方式

intent

共享文件,SharedPreference

基于Binder的Messenger、AIDL

Socket

当通过Intent和Binder传输数据时,就需要用Parcelable和Serializable接口来完成对象的序列化,Serializable还可以完成对象的持久化,比如将对象持久化到存储设备或通过网络传输给其他客户端

3.1 Serializable接口

Manifest添加读写权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>


User.java

public class User implements Serializable {
//辅助 序列化和 反序列化 过程
private static final long serialVersionUID = 287483306032875107L;

public int userId;
public String userName;
public boolean isMale;

public User(int userId, String userName, boolean isMale){
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}

}


MainActivity中添加2个按钮:序列化,反序列化

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

final String path = Environment.getExternalStorageDirectory() + "/cache.txt";
Button btnXuLieHua = (Button) findViewById(R.id.btn_xu_lie_hua);
btnXuLieHua.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//序列化
User user = new User(1, "Jack", true);
try {
ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream(path));
outputStream.writeObject(user);
outputStream.close();

} catch (IOException e) {
e.printStackTrace();
}
}
});

Button btnFanXuLieHua = (Button) findViewById(R.id.btn_fan_xu_lie_hua);
btnFanXuLieHua.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//反序列化
try {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(path));
User user = (User) inputStream.readObject();
inputStream.close();
Log.d("MainActivity", "### user.name = " + user.userName);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

}
});
}

}


serialVersionUID 的作用是 辅助 序列化和反序列化。

原则上序列化后的数据中的serialVersionUID 只有和当前类的serialVersionUID 相同才能正常地 被反序列化。

serialVersionUID 的工作机制:序列化时 系统把当前累的serialVersionUID 写入序列化文件(或 其他中介),反序列化时 系统 会检测文件中的serialVersionUID ,判断跟当前类的serialVersionUID 是否一致:

一致的话,说明 序列化的类的版本 和 当前累的版本是相同的,可以成功反序列化;

不一致的话,说明 当前类 和 序列化的类 相比 发生了变化,不能正常反序列化

可以不指定serialVersionUID,编译工具会自动生成一个hash值,这样序列化和反序列化的serialVersionUID 是相同的

手动设置 serialVersionUID 会避免反序列化失败。比如升级版本时,修改了成员变量,反序列化仍然可以成功,避免crash

如果修改了类名 或者 成员变量的类型,即使 serialVersionUID 一致,反序列化会失败,因为 老版本的数据 还原出一个新的类结构的对象。

静态成员变量 不参与序列化,因为静态成员变量属于类,不属于对象

transient标记的成员变量不参与 序列化

3.2 Parcelable接口

User2.java

public class User2 implements Parcelable {

public int userId;
public String userName;
public boolean isMale;

public Book book;//book是一个可序列化对象

public User2(int userId, String userName, boolean isMale){
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}

@Override
public int describeContents() {
//describeContents完成内容描述功能
//如果当前对象中存在文件描述符,返回1,其他情况均返回0
return 0;
}

@Override
public void writeToParcel(Parcel parcel, int i) {
//序列化
parcel.writeInt(userId);
parcel.writeString(userName);
parcel.writeInt(isMale ? 1 : 0);
parcel.writeParcelable(book, 0);
}

//反序列化
public static final Creator<User2> CREATOR = new Creator<User2>() {
@Override
public User2 createFromParcel(Parcel in) {
return new User2(in);
}

@Override
public User2[] newArray(int size) {
return new User2[size];
}
};

private User2(Parcel in){
userId = in.readInt();
userName = in.readString();
isMale = in.readInt() == 1;
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
}


Book.java

public class Book implements Parcelable {

public int bookId;
public String bookName;

public Book() {

}

public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}

public int describeContents() {
return 0;
}

public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}

public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
}

public Book[] newArray(int size) {
return new Book[size];
}
};

private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}

}


MainActivity添加按钮,点击按钮,携带user2对象的intent传递给SecondActivity

MainActivity.java

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Button btnOpenSecondActivity = (Button) findViewById(R.id.btn_open_second_activity);
btnOpenSecondActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
User2 user2 = new User2(2, "Jarry", false);
intent.putExtra("User2", user2);
startActivity(intent);
}
});
}

}


SecondActivity.java

public class SecondActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
User2 user2 = getIntent().getParcelableExtra("User2");
//02-15 13:18:11.835 17253-17253/? D/SecondActivity: ### user id = 2, name = Jarry
Log.d("SecondActivity", "### user id = " + user2.userId + ", name = " + user2.userName);

}
}


Parcelable的方法说明:



Android中很多类已经实现了Parcelable接口,比如 Intent,Bundle,Bitmap等

List和Map也可以序列化,前提是每个元素是可序列化的

Parcelable 和 Serializable 对比:

Serializable 是Java 中的序列化接口,使用简单但是需要 大量I/O操作,开销很大;

Parcelable 是Android的序列化方式,更适合于 Android平台,写起来麻烦,但是效率高,是Android推荐的序列化方式。

将对象 序列化到存储设备 或者 将对象序列化后 通过网络传输,Parcelable 比较复杂,建议使用 Serializable 。其他情况建议使用 Parcelable

3.3 Binder

从Android framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等)和相应ManagerService的桥梁;

从Android应用层来说,Binder是客户端和服务端进行通信的媒介

Book.java

package com.jieqiong.ipc;

import android.os.Parcel;
import android.os.Parcelable;

/**
* Created by jieqiong on 2016/12/14.
*/

public class Book implements Parcelable {

public int bookId;
public String bookName;

public Book() {

}

public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}

public int describeContents() {
return 0;
}

public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}

public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
}

public Book[] newArray(int size) {
return new Book[size];
}
};

private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}

@Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
}

}


Book.aidl

// Book.aidl
package com.jieqiong.ipc;

parcelable Book;


IBookManager.aidl

// IBookManager.aidl
package com.jieqiong.ipc;

//即使跟Book在一个包中,也需要导入Book类
import com.jieqiong.ipc.Book;

interface IBookManager {
//用于从远程服务端 获取图书列表
List<Book> getBookList();
//用于往图书列表中添加一本书
void addBook(in Book book);
}


自动生成IBookManager.java

(app\build\generated\source\aidl\debug\com\jieqiong\ipc\IBookManager.java)

IBookManager.java

继承了IInterface接口,同时自己也是个接口

声明了getBookList 和 addBook两个方法,同时声明了 TRANSACTION_getBookList 和 TRANSACTION_addBook 两个id用于标识在transact过程中 客户端请求的是哪个方法

声明了内部类Stub,继承自Binder

Stub的内部代理类的作用:

当客户端和服务端位于同一个进程时,方法调用不会走跨进程的transact过程

当客户端和服务端位于不同进程时,方法调用走transact过程

/*
* This file is auto-generated.  DO NOT MODIFY.
* Original file: D:\\demo\\ipc\\app\\src\\main\\aidl\\com\\jieqiong\\ipc\\IBookManager.aidl
*/
package com.jieqiong.ipc;
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jieqiong.ipc.IBookManager
{
//DESCRIPTOR Binder的唯一标识,一般用当前Binder的类名表示
private static final java.lang.String DESCRIPTOR = "com.jieqiong.ipc.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.jieqiong.ipc.IBookManager interface,
* generating a proxy if needed.
* 将服务端的Binder对象转换成客户端所需的aidl接口类型的对象,这种转换过程是区分进程的
* 如果客户端和服务端 位于同一进程,该方法返回的是服务端的Stub对象本身
* 如果客户端和服务端 不在同一进程,该方法返回的是系统封装后都Stub.proxy对象
*/
public static com.jieqiong.ipc.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.jieqiong.ipc.IBookManager))) {
return ((com.jieqiong.ipc.IBookManager)iin);
}
return new com.jieqiong.ipc.IBookManager.Stub.Proxy(obj);
}
/**
* 返回当前Binder对象
*/
@Override public android.os.IBinder asBinder()
{
return this;
}

/**
* onTransact运行在服务端中的Binder线程池中
* 当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
* code 通过code确定客户端请求的目标方法
* data 从data中取出目标方法所需的参数,然后执行目标方法
* reply执行完目标方法,将返回值写入reply中
* return 如果返回false,客户端去请求失败。可以用这个特性来做权限验证,过滤掉 不希望访问的进程
*/
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.jieqiong.ipc.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.jieqiong.ipc.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.jieqiong.ipc.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.jieqiong.ipc.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;
}
/**
* Proxy#getBookList 运行在客户端
* 首先创建该方法所需要的输入型Parcel 对象_data,输入型Parcel对象_reply和返回值对象_result
* 然后把该方法的参数信息写入 _data 中
* 接着调用transact方法发起RPC(远程过程调用)请求,同时挂起当前线程
* 然后 服务端的 onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,
* 并从_reply中取出RPC过程的返回结果
* 最后返回_reply中的数据
*/
//用于从远程服务端 获取图书列表

@Override public java.util.List<com.jieqiong.ipc.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.jieqiong.ipc.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.jieqiong.ipc.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Proxy#addBook 运行在客户端,运行过程和getBookList一样,
* addBook没有返回值,不需要从_reply中取出返回值
*/
//用于往图书列表中添加一本书

@Override public void addBook(com.jieqiong.ipc.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);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
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 java.util.List<com.jieqiong.ipc.Book> getBookList() throws android.os.RemoteException;
//用于往图书列表中添加一本书

public void addBook(com.jieqiong.ipc.Book book) throws android.os.RemoteException;
}


客户端发起远程请求时,当前线程会被挂起 直到 服务端进程返回数据,所以 如果远程方法耗时,不能在UI线程中发起远程请求

服务端的Binder方法运行在Binder线程池中,所以Binder方法需要采用同步发方式实现

Binder的工作机制图

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: