一、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连接池