IPC进程间通信(基础)

内容来自《Android开发艺术探索》一书。

1、Android 中开启多进程 和 多进程导致的问题

开启:

  • 一个应用对应至少一个进程,两个应用间通信就属于进程间通信。
  • 一个应用可以有多个进程,一个应用使用多进程的唯一方式:给四大组件指定android:process属性。
<activity android:name=".Main3Activity"
    android:process="com.lying.ms_ipc.remote3"/>

<activity android:name=".Main2Activity"
    android:process=":remote2"/>

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
  • Main2Activity 和 Main3Activity 使用了两种命名方式。Main2Activity使用的 : 表示前面加上包名,Main3Activity使用的是全路径名称。

  • 基于不同的命名方式,区别如下:以 : 开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。不是以 : 开头的,属于全局进程,其他应用可以通过ShareUID方式和它跑在同一个进程。
    注:系统会为每个应用分配一个唯一的UID,具有相同的UID才能共享数据。

  • 例:启动应用,首先会启动MainActivity,然后启动Main2Activity,然后启动Main3Activity。通过命令查看已启动的进程。

    查看命令:adb shell ps 或者 adb shell ps | findstr 包名

    注:Windows中使用 findstr,Linux中使用 grep。

E:\AndroidProject\MS_IPC>adb shell ps | findstr com.lying.ms_ipc
u0_a140      16567  1778 1870128 116828 0                   0 S com.lying.ms_ipc
u0_a140      16614  1778 1867788 109388 0                   0 S com.lying.ms_ipc:remote2
u0_a140      16656  1778 1893812 112740 0                   0 S com.lying.ms_ipc.remote3

问题:

由于系统会为每个应用或者说为每个进程 分配 一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,即:在不同的虚拟机中访问同一个类的对象会产生多个副本。导致以下问题:

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

    • 创建一个类,内有一个静态成员

      public class UserManager {
          public static int userId = 1;
      }
      
    • 在MainActivity的onCreate中修改 userId 的值为 666,然后启动 Main2Activity,在Main2Activity中打印userId。

      public class MainActivity extends AppCompatActivity {
      
          private static final String TAG = "MainActivity";
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              UserManager.userId = 666;
          }
      }
      
      public class Main2Activity extends AppCompatActivity {
      
          private static final String TAG = "Main2Activity";
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main2);
              Log.i(TAG, "onCreate: UserManager userId = " + UserManager.userId);
          }
      }
      
    • 查看打印值,发现Main2Activity中拿到的不是修改后的值,而是修改前的。

      I/Main2Activity: onCreate: UserManager userId = 1
      
  2. 线程同步机制完全失效。

    • 原因:不是一块内存了,无论锁对象还是全局类都不能保证线程同步,不同进程锁的不是同一个对象。
  3. SP的可靠性下降。

    • 原因:SP不支持两个进程同时写。
  4. Application会多次创建。

    • 原因:一个组件跑在新的进程中,系统要分配独立的虚拟机,这个过程其实就是启动一个应用过程。

    • 举例:自定义一个Application

      public class MyApplication extends Application {
      
          private static final String TAG = "MyApplication";
      
          @RequiresApi(api = Build.VERSION_CODES.P)
          @Override
          public void onCreate() {
              super.onCreate();
              String processName = Application.getProcessName();
              Log.i(TAG, "onCreate: MyApplication OnCreate processName = " + processName);
          }
      }
      
    • 在AndroidManifest文件中的 application 指定 android:name=".MyApplication"

    • 启动MainActivity,再启动Main2Activity

      I/MyApplication: onCreate: MyApplication OnCreate processName = com.lying.ms_ipc
      I/MyApplication: onCreate: MyApplication OnCreate processName = com.lying.ms_ipc:remote2
      
    • 看日志,Application的onCreate执行了两次。

2、序列化

序列化和反序列化概念:将实体转换为二进制流,然后在另一个地方将二进制流再反序列化成实体对象。序列化前 和 反序列化后的对象属于两个不同的对象,只是其中的内容相同,因为内容相同所以我们 IPC 可以使用序列化方式来进行数据的传递。

两种序列化方式的选择:Serializable开销大,Parcelable效率高。但Parcelable主要用在内存方面,序列化到存储设备或网络传输使用Serializable会好点。

2.1、通过 Serializable 接口进行序列化

  • serializable 是 java 提供的序列化接口。

  • (1)需要支持序列化的类 实现 Serializable 接口。

  • (2)在类中指明 serialVersionUID

    private static final long serialVersionUID = 8736492374937894L
    
  • (3)序列化过程

    User user = new User(0, "jake", ture);
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
    out.writeObject(user);
    out.close();
    
  • (4)反序列化过程

    ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
    User newUser = (User)in.readObject();
    in.close();
    

注意:serialVersionUID 是在序列化和反序列化中判断是不是正确的当前类。若不指明,默认为哈希值,当类结构有变化时,哈希值会改变,这时就会导致反序列化失败。所以最好指明 serialVersionUID;

2.2、通过 Parcelable 接口进行序列化

  • Parcelable 是Android 中的序列化方式

  • (1)需要序列化的类实现 Parcelable 接口

  • (2)实现 writeToParcel

  • (3)实现CREATOR

  • 举例如下:

    public class Book implements Parcelable {
    
        public int bookId;
        public String bookName;
    
        public Book(int bookId, String bookName){
            this.bookId = bookId;
            this.bookName = bookName;
        }
    
        /**
         * @return 当前对象的内容描述。含有文件描述符返回 1,否则返回 0.几乎所有情况都是返回0.
         */
        @Override
        public int describeContents() {
            return 0;
        }
        /**
         * 序列化
         */
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(bookId);
            dest.writeString(bookName);
        }
        /**
         * 反序列化
         */
        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];
            }
        };
    
        private Book(Parcel parcel){
            bookId = parcel.readInt();
            bookName = parcel.readString();
        }
    
        @NonNull
        @Override
        public String toString() {
            return "bookId = " + bookId + ", bookName = " + bookName;
        }
    }
    

注意:在举例中,Book中都是基本类型和String,如果其中还有个可序列化的对象 Library library;那么在 Book(Parcel parcel) 方法中需要增加这个

//是可序列化对象时,需要传入一个当前线程的上下文类加载器
library = parcel.readParcelable(Thread.currentThread().getContextClassLoader());

3、Binder

Binder是Android跨进程通信的一种方式。

工作原理如下图:

Binder工作机制.jpg
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容