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 = 1public 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.javapublic 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的工作机制图
相关文章推荐
- 4.IPC 机制(二) IPC基础概念介绍
- 6.IPC 机制(四) IPC基础概念介绍 Binder
- Android IPC机制之IPC概念、Android 多进程和相关基础知识
- IPC机制---03 IPC基础概念介绍
- 5.IPC 机制(三) IPC基础概念介绍 Parcelable接口
- IPC机制系列之二 IPC机制的基础概念Serializable、Parcelable以及Binder
- Android IPC基础概念介绍
- 面试题整理,英文简介、struts2功能,mybatis优点,jface概念,struts2组件,springmvc机制,配置事务,hiernate查询方法
- PHP基础之类和对象1——简介及基本概念
- 【 Makefile 编程基础之一】详细介绍Makefile概念和其机制用途;
- IPC机制 基础知识
- 【JAVA基础知识】Java 垃圾回收机制概念
- Android进程间通信(IPC)机制Binder简介和学习计划
- ASP.NET缓存机制基础概念
- IPC机制之一:简介、多进程模式
- IPC机制---01简介
- Java的多线程机制系列:(一)总述及基础概念
- struts2.1笔记03:AOP编程和拦截器概念的简介
- 矩阵论基础 2.1 矩阵的基本概念