IPC进程间通信

IPC机制

1. 为何要开启多进程

为何开启android应用要开启多进程,主要有以下几点:
(1)单进程所分配的内存不够,需要更多的内存。在早期android系统只为一个单进程的应用分配了16M的可用内存,随着手机的硬件的提升和android系统的改进,虽然可分配内存越来越多,但仍旧可以通过开启多进程来获取更多的内存来处理自己App的业务
(2)独立运行的组件,比如个推,它的服务会另开一个进程。
(3)运行一些”不可见人”的操作,比如获取用户的隐私数据,比如双守护进程来防止被用户杀掉

2. 开启多进程

android:process=":remote" //进程名
用adb命令可以查看进程信息 adb shell ps

用Messenger进行进程间通信 :

Messenger可以在不同进程中传递Message对象,我们在Message中加入我们想要传的数据就可以在进程间的进行数据传递了。
服务端:

public class MyService extends Service {
   public MyService() {
   }

   @Override
   public IBinder onBind(Intent intent) {
       Log.d("TAG", "onBind: ");
       // TODO: Return the communication channel to the service.
       //Messenger创建一个IBinder,服务端通过onBind()方法使其返回客户端
       return new Messenger(handler).getBinder();
   }

   @Override
   public void onCreate() {
       super.onCreate();
       Log.d("TAG", "onCreate: ");
   }

   private Handler handler = new Handler(){
       @Override
       public void handleMessage(@NonNull Message msg) {
           super.handleMessage(msg);
           switch (msg.what){
               //1 客户端传来的消息
               case 1:
                   //接收到客户端的消息
                   Log.d("TAG", "我是服务端,收到你的消息: "+msg.getData().get("lala"));
                   //返回给客户端的消息
                   Message msgToClient = Message.obtain(null,2);
                   //得到客户端传来的Messenger对象
                   Messenger clientMessenger = msg.replyTo;
                   try {
                       clientMessenger.send(msgToClient);
                   } catch (RemoteException e) {
                       e.printStackTrace();
                   }
                   break;
           }
       }
   };
}

AndroidMainfest.xml

 <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="haha"/>
            </intent-filter>
</service>

客户端

public class MainActivity extends AppCompatActivity {
   private Messenger mMessenger;
   
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       //绑定服务
       Intent intent = new Intent("haha");
       intent.setPackage("com.thundersoft.ipcdemo");
       bindService(intent,conn,BIND_AUTO_CREATE);
   }
   private ServiceConnection conn = new ServiceConnection() {
       @Override
       public void onServiceConnected(ComponentName name, IBinder service) {
           Log.d("TAG", "已经绑定服务: ");
           //客户端使用IBinder将Messenger(引用服务的Handler)实例化,
           mMessenger = new Messenger(service);
           Message message = Message.obtain(null,1);
           Bundle bundle = new Bundle();
           bundle.putString("lala","你好,我是客户端,请问你收到信息了吗?");
           message.setData(bundle);
           //将Messenger传递给服务端
           message.replyTo=new Messenger(mHandler);
           try {
               mMessenger.send(message);
           } catch (RemoteException e) {
               e.printStackTrace();
           }
       }

       @Override
       public void onServiceDisconnected(ComponentName name) {

       }
   };
   private Handler mHandler = new Handler(){
       @Override
       public void handleMessage(@NonNull Message msg) {
           super.handleMessage(msg);
           switch (msg.what){
               case 2:
                   Log.d("TAG", "服务端返回的消息: ");
                   break;
           }

       }
   };
}

注意:在客户端的AndroidMainfest.xml中导入服务的包 :

 <queries>
        <package android:name="com.thundersoft.ipcdemo"/>
    </queries>

总结:
(1) <服务端>Messenger创建一个IBinder,服务端通过onBind()方法使其返回客户端
(2)<客户端>客户端使用IBinder将Messenger(引用服务的Handler)实例化,然后使用后者将Message对象发送给服务端。

(3)服务端收到了客户端的消息,服务端回应客户端。
首先在handleMessage回调中收到客户端信息时,我们调用Message.replyTo得到客户端传过来的Messenger对象,创建消息并通过Messenger发送给客户端。

: 是通过各自的messenger来进行通信的,所以思想是想法获得对方的messenger

通过Binder的onTransact完成跨进程通信

我们 android studio 新建两个工程(两个 moudle 也可以,这里目的为创建两个运行在不同进程的app),一个Server,一个Client,而后,在Server中,创建类继承android.os.Binder,重写方法onTransact(int code, Parcel data, Parcel reply, int flags),注意这四个参数;
(1) code:方法标识符,因为Client端对Server端的所有调用都会走到Server端的这个方法,所以理所应当Client端应该传递一个参数过来用以表示要调用哪个方法,注意这个int类型的标识必须介于 FIRST_CALL_TRANSACTION 和 LAST_CALL_TRANSACTION之间,所以我们给方法分配code的时候最好使用FIRST_CALL_TRANSACTION+n 这种方式
(2)data :Client传递过来的序列化数据包,Parcel类型
(3)reply: 如果Client端调用时需要返回值,Server通过这个对象将返回值传递回去,同样Parcel类型
(4)flag 用来区分这个调用是普通调用还是单边调用,普通调用时,Client端线程会阻塞,直到从Server端接收到返回值(所以如果Client端是主线程调用,其调用的Server端不宜做耗时操作,这会让造成Client的ANR),若flag==IBinder.FLAG_ONEWAY,则这次调用是单边调用,Client在传出数据后会立即执行下一段代码,此时两端异步执行,单边调用时函数返回值必须为void (也就是异步调用必须舍弃返回值,要返回值就必须阻塞等待)

那我们先来看一下Parcel 的用法 :
(1)Parcel的获取: Parcel parcle = Parcel.Obtain(); <Parcel的初始化也是由其对象池进行初始化的,Parcel是一个容器>
(2)向Parcel中传入一个Int型的数据 :parcel.writeString(String val);
(3)在完成了数据的写入之后,就需要进行数据的序列化:parcel.marshall();
(4)在经过上一步的处理之后,返回了一个byte数组,主要的IPC相关的操作主要就是围绕此byte数组进行的。
(5)parcel的销毁 :parcel.recycle();
(6)再进行了IPC的操作之后,一般读取出来的就是之前序列化的byte数组,所以,首先要进行一个反序列化操作,即如下的操作:
parcel.unmarshall(byte[] data, int offest, int length); 其中的参数分别是这个byte数组,以及读取偏移量,以及数组的长度。
(7)parcel.readInt();
(8)parcel.recycle();
详细请参考
链接: https://blog.csdn.net/rainbowchou/article/details/54294394?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163288163116780261933448%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163288163116780261933448&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-54294394.first_rank_v2_pc_rank_v29&utm_term=Parcel&spm=1018.2226.3001.4187
Service :

public class Stub extends android.os.Binder {
    //用于标识调用的Binder
    private static final java.lang.String DESCRIPTOR = "MyBinder";
    //方法标识,这里我们准备两个方法,一个无参,一个有参
    private static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    private static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    //服务端的data 是读,reply是写
    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
        switch (code) {
            case TRANSACTION_method0: {
                //Store or read an IBinder interface token
                //验证Binder标识
                data.enforceInterface(DESCRIPTOR);
                //调用实现方法
                this.method0();
                reply.writeNoException();
                return true;
            }
            case TRANSACTION_method1: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                //按写入的顺序读取数据
                _arg0 = data.readInt();           
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.method1(_arg0, _arg1);
                reply.writeNoException();
                //向Client写回返回值
                reply.writeInt(_result);          
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }
    //Server端真正实现业务的两个方法
    public void method0() throws RemoteException {
        Log.e("Server", Process.myPid() + "  " + Process.myTid() + "  " + Process.myUid() + "  " + "method0");
    }
    public int method1(int a, int b) throws RemoteException {
        Log.e("Server", Process.myPid() + "  " + Process.myTid() + "  " + Process.myUid() + "  " + "method1" + "  " + a + " " + b);
        return a + b;
    }
}

绑定服务否返回Binder对象: 通信的桥梁

    @Override
    public IBinder onBind(Intent intent) {
        return new Stub();
    }

client端 :
(1)绑定服务

   //定义常量
   //注意两个工程中对应的标识符必须相同
   static final String DESCRIPTOR = "MyBinder";
   //方法标识
   static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
   static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       Intent intent = new Intent();
       intent.setComponent(new ComponentName("com.xxx.server", "com.xxx.server.ServerService"));
       boolean b = bindService(intent, conn, BIND_AUTO_CREATE);
       Log.e("Client", "      "+b);
   }
    ```
  (2)bindService 的第二个参数,ServiceConnnection,在这个回调中我们取得IBinder对象,这个对象是Server端在Client中的一个代理对象
    ```
        ServiceConnection conn = new ServiceConnection() {
       @Override
       public void onServiceConnected(ComponentName name, IBinder iBinder) {
           //这个IBinder对象iBinder,可以调用c++层Binder代理并最终通过Binder驱动传递数据
           Parcel _data0 = Parcel.obtain();//申请传递参数的Parcel对象(从Parcel池中取出)
           Parcel _reply0 = Parcel.obtain();//申请接收返回值的Parcel对象,相当于数据载体
           Parcel _data1 = Parcel.obtain();
           Parcel _reply1 = Parcel.obtain();
           try {
               //调用第一个方法
               //写入Binder标识,以便服务端验证
               _data0.writeInterfaceToken(DESCRIPTOR);
               //传入方法标识,以便服务端知道我们要调用哪个方法,注意最后一个参数,就是上面提到的                  //flag,如果我们传入IBinder.FLAG_ONEWAY,则这次调用为单边调用,这个方法会立即返                //回,不会等服务端方法返回
               iBinder.transact(TRANSACTION_method0, _data0, _reply0, 0);
               _reply0.readException();
               //调用第二个方法
               _data1.writeInterfaceToken(DESCRIPTOR);
               //按顺序写入参数
               _data1.writeInt(1);
               _data1.writeInt(2);
               //从下面这行代码开始本线程会阻塞,直到服务端进程中调用的方法完成计算返回后这个线程继                 //续运行,计算的返回值放入_reply1中
               iBinder.transact(TRANSACTION_method1, _data1, _reply1, 0);
               _reply1.readException();
               int i = _reply1.readInt();//从reply中读取返回值,这里我们就得到了服务端计算后的结果
           } catch (RemoteException e) {
               e.printStackTrace();
           } finally {
               //回收Parcel
               _data0.recycle();
               _reply0.recycle();
               _data1.recycle();
               _reply1.recycle();
           }
           Log.e("Client", Process.myPid() + "  " + Process.myTid() + "  " + Process.myUid() + "  " + "method0");
       }
       @Override
       public void onServiceDisconnected(ComponentName name) {
       }
   };

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值