androidP:IPC机制

一、Android中得多进程模式
1、开启多进程模式
在Android中使用多进程只有一种方法,那就是给四大组件在AndroidMenifest.xml中指定android:process属性。例如:
获取进程pid、uid

//activity.java 
private void getUid() {
        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        ApplicationInfo appinfo = getApplicationInfo();
        List<ActivityManager.RunningAppProcessInfo> run = am.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo runningProcess : run) {
            if ((runningProcess.processName != null) && runningProcess.processName.equals("com.example.service")) {
                Log.e(TAG, "processName = com.example.service" + ",uid = " + runningProcess.uid +  ",pid = " + runningProcess.pid);
                break;
            }
        }
    }

设置进程pid(方法一)

// AndroidManifest.xml
 <activity android:name=".view.showCustomViewActivity"
            android:excludeFromRecents="true"
            android:launchMode="standard"
            android:process=":test">
 </activity>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置进程pid(方法二)

<activity android:name=".view.showCustomViewActivity"
            android:excludeFromRecents="true"
            android:launchMode="standard"
            android:process="com.example.service.test">
        </activity>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
两钟方法得区别:
第一:“:”的含义是指要在当前的进程名前面附加上当前的包名,第二种是完整的命名方式。
第二:进程名以“:”开头的进程属于当前应用的私有进程,其他应用组件不可以和它跑在同一个进程中,而进程名不以“:”开头的进程属于全局进程,其他应用通过ShardUID方式可以和它跑在同一个进程中。
知识补充:
PID:为Process Identifier, PID就是各进程的身份标识,程序一运行系统就会自动分配给进程一个独一无二的PID。进程中止后PID被系统回收,可能会被继续分配给新运行的程序,但是在android系统中一般不会把已经kill掉的进程ID重新分配给新的进程,新产生进程的进程号,一般比产生之前所有的进程号都要大。
UID:一般理解为User Identifier,UID在linux中就是用户的ID,表明时哪个用户运行了这个程序,主要用于权限的管理。而在android 中又有所不同,因为android为单用户系统,这时UID 便被赋予了新的使命,数据共享,为了实现数据共享,android为每个应用几乎都分配了不同的UID,不像传统的linux,每个用户相同就为之分配相同的UID。(当然这也就表明了一个问题,android只能时单用户系统,在设计之初就被他们的工程师给阉割了多用户),使之成了数据共享的工具。
因此在android中PID,和UID都是用来识别应用程序的身份的,但UID是为了不同的程序来使用共享的数据。

2、多进程模式的运行机制
一般来说,使用多进程会造成如下几方面问题:
(1)静态成员和单例模式完全失败。(多进程共享内存会失败)
(2)线程同步机制完全失效。
(3)SharedPreferences的可靠性下降。
(4)Application会调用多次。

三、IPC基础概念介绍
1、Serializable([ˈsɪˌriəˌlaɪzəbl])接口
Serializable是java所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作。

//bean.java
package com.example.service.bean;

import java.io.Serializable;

public class UserBean implements Serializable {
    private static final long serialVersionUID = 10000L;
    public int userId;
    public String name;
    public int age;
    public int height;
}

//Activity.java
 //序列化
    private void testSerializable() {
        UserBean userBean = new UserBean();
        userBean.userId = 0;
        userBean.name = "lily";
        userBean.age = 32;
        userBean.height = 20;
        try{
            FileOutputStream outputStream = new FileOutputStream("/mnt/sdcard/catche.txt");
            ObjectOutputStream out = new ObjectOutputStream(outputStream);
            out.writeObject(userBean);
            out.close();
            Log.d(TAG,"testSerializable : id = " + userBean.userId + ",name = " + userBean.name + ",age = "+ userBean.age);
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

    //反序列化
    private void testDeSerialization() {
        try{
            FileInputStream inputStream = new FileInputStream("/mnt/sdcard/catche.txt");
            ObjectInputStream in = new ObjectInputStream(inputStream);
            UserBean userBean = (UserBean) in.readObject();
            in.close();
            Log.d(TAG,"testDeSerialization : id = " + userBean.userId + ",name = " + userBean.name + ",age = "+ userBean.age + ",height = " + userBean.height);
        }catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }
    }

序列化于反序列化代码实例。
重点讲一下SerialversionUID值。 在java对象序列化时,如果没有设置SerialversionUID,他会给一个默认的值,但是通常建议自定义一个serialVersionUID,因为默认的serialVersinUID对于class的细节非常敏感,反序列化时可能会导致InvalidClassException这个异常。
如果没有设置SerialversionUID值的话,在序列化后反序列化前对类进行了修改,类对应的SerialversionUID也会变化,而序列化和反序列化就是通过对比其SerialversionUID来进行的,一旦SerialversionUID不匹配,反序列化就无法成功。而如果我们在一个类中设置了SerialversionUID,在序列化后修改了类反序列化还是能成功(可以反序列化出部分数据)。
另外,静态成员变量属于类不属于对象,所以不会参与序列化过程;用transient关键字标记的成员变量不参与序列化过程。

2、Parcelable[paɾθe’laβle]接口
Parcelable也是一个接口,只要实现这个接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递。

package com.example.service.bean;

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

public class BookBean implements Parcelable {

    public int id;
    public String name;
    public int price;
    public boolean sale;

    //这个类也要实现Parcelable接口,否则报错
    public UserBean user;

    //构造函数
    public BookBean(int id, String name,int price,boolean sale) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.sale = sale;
    }
    
    protected BookBean(Parcel in) {
        id = in.readInt();
        name = in.readString();
        price = in.readInt();
        sale = in.readInt() == 1;
        user = in.readParcelable(Thread.currentThread().getContextClassLoader());
    }

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

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

    //几乎所有情况都返回0
    @Override
    public int describeContents() {
        return 0;
    }

    //序列化的过程
    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeInt(id);
        parcel.writeString(name);
        parcel.writeInt(price);
        parcel.writeInt(sale ? 1 : 0);
        parcel.writeParcelable(user,0);
    }
}

这里先说一下Parcel,Parcel内部包装了可序列化的数据,可以再Binder中自由传递。从上边代码可以看出,在序列化过程中需要实现的功能有序列化、反序列化和内容描述。序列化功能由writeToParcel方法来完成,最终时通过Parcel中的一系列write方法来完成的。反序列化功能由Creator来完成,其内部标明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程;内容描述功能由describeContents方法来完成,几乎在所有情况下这个方法都应该返回0,仅当当前对象中存在文件描述符时,此方法返回1。需要主义的是,在BookBean(Parcel in)方法中,由于UserBean是另一个可序列化对象,所以它反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到类的错误。

Serializable 和 Parcelable的区别:
Serializable是java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I/O操作,其作用可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable。而Parcelable是android中的序列化方式,因此适合用在android平台上,它的缺点就是使用起来稍微复杂,但是效率很高,在内存间数据传输时推荐使用Parcelable,如activity间传输数据。

3、Binder
Binder是Android中的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等等)和相应的ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,包括普通服务和基于aidl服务。

通过aidl服务实例,讲接Binder,看连接https://blog.csdn.net/zhuowalun8427/article/details/122983184 服务 Service -aidl应用

四、Android中的IPC通信
1、使用Bundle
我们知道,四大组件中的三大组件(Activity Service Ewceiver)都可以支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable,所以它可以方便地在不同的进程间传播。

2、使用文件共享(注意并发执行情况)
共享文件也是一种不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据。SharedPreferences也属于文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式下,系统对它的读写就变得不可靠,当面对高并发的读写访问,SharedPreferences有跟打几率会丢失数据,因此,不建议在进程间通信红使用SharedPreferences。

3、使用Messenger
Messenger可以翻译成信使,顾名思义,通过它可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。

//服务端

//service.java
package com.example.service.service;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.Nullable;

public class MessengerService extends Service implements Handler.Callback {
    private final String TAG = "MessengerService";
    private final int MSG_FROM_CLIENT = 0;
    private final int MSG_FROM_SERVICE = 1;

    private Handler mHandler;
    private Messenger mMessenger;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"===========>onCreate");
        mHandler = new Handler(this);
        mMessenger = new Messenger(mHandler);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }


    @Override
    public boolean handleMessage(Message message) {
        switch (message.what) {
            case MSG_FROM_CLIENT:
                //收到来自客户端消息
                Log.d(TAG,"receive msg from client:" + message.getData().getString("msg"));

                //向客户端回复消息
                Messenger messenger = message.replyTo;
                Message reply = Message.obtain(null,MSG_FROM_SERVICE);
                Bundle bundle = new Bundle();
                bundle.putString("reply","I have recived your message and I will reply you later!");
                reply.setData(bundle);
                Log.d(TAG,"messenger1 = "+ messenger);
                try {
                    messenger.send(reply);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
        }
        return true;
    }
}

//AndroidMenifest.xml
<service
            android:name="com.example.service.service.MessengerService"
            android:exported="true">
        </service>

客户端

package com.example.scoketclient.client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.telecom.Call;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.example.scoketclient.R;

public class MessengerClientActivity extends AppCompatActivity implements Handler.Callback {
    private final String TAG = "MessengerClientActivity";

    private final int MSG_FROM_CLIENT = 0;
    private final int MSG_FROM_SERVICE = 1;

    private Messenger mMessenger;
    private Messenger mGetReplyMessenger;

    private Handler mHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger_client);
    }

    @Override
    protected void onResume() {
        super.onResume();
        initParams();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }

    private void initParams() {
        Intent intent = new Intent();
        intent.setClassName("com.example.service","com.example.service.service.MessengerService");
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);

        mHandler = new Handler(this);
        mGetReplyMessenger = new Messenger(mHandler);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mMessenger = new Messenger(iBinder);
            Message msg = Message.obtain(null,MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg","Hellow,this is client!");
            msg.setData(data);

            //把接收服务端回复的Messenger通过Message的replyTo参数传递给服务端
            msg.replyTo = mGetReplyMessenger;
            try {
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    public boolean handleMessage(Message message) {
        switch (message.what) {
            case MSG_FROM_SERVICE:
                Log.d(TAG,"receiver msg from Service :" + message.getData().getString("reply"));
                break;
            default:
                break;
        }
        return true;
    }



}

Messenger的工作原理
在这里插入图片描述
4、使用AIDL
AIDL进程间接口调用,通过BInder实现。具体内容看链接
https://blog.csdn.net/zhuowalun8427/article/details/122983184 服务 Service -aidl应用.

5、使用ContentProvider
ContentProvider是android中提供的专门用于不同应用间进行数据共享的方式。ContentProvider的底层实现也是Binder。ContentProvider的使用查看链接:https://blog.csdn.net/zhuowalun8427/article/details/123770300 跨进程通信-ContentProvider应用及工作原理

6、使用Socket
后面再看吧,没用过socket

五、Binder连接池

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值