第九章--进程和进程间的通信

时间:2020-12-15 04:25:34

1、进程相关的知识

1.1、什么叫进程

说到进程,很多人包括我会联想到线程,以为这两个东西之间有什么关系,但是这一周的学习,至少让我知道,多进程和单线程,多进程和多线程,单进程和多线程这样的组合都可以。借用老师的比喻,线程比作流水线,进程就是一个车间,而我们的系统就是工厂,然而它们的数量之间不一定是现实中的。进程是系统进行资源分配以及调度的基本单位,这个意思就是会所同一个进程里内存资源是共享的,然而不同进程里的资源是不共享的,这就涉及到进程间通信的问题,后面会讲到。

1.2、创建进程

创建一个进程很简单,只需要在Androidmenifest中添加process标签,就可以创建一个进程,命名格式一般为包名,如:

android:process="com.example.w"

如果只是想创建一个远程服务,可以用”:”创建一个子进程程如:

android:process=":remote"

1.3、进程间的等级

进程可有如下等级:

  1. 前台进程;用户正在使用的进程,简单理解就是用户正在交互的对象。一直存在的前台进程是极少的,而且前台进程一般是最后内存小的连前台进程都没有地方运行的时候,才会杀掉进程。
  2. 可见进程:如弹出一个对话框的Acitivity,后面的Activity是可见的。后面可见的Activity可以被认为是一个可见进程。
  3. 服务进程:一个通过startService()启动的service,用户虽然看不到然而进行着和用户关注的任务,如下载,播放音乐
  4. 后台进程:按了home键之后不可见的activity,一般是执行了onStop()方法之后,在内存不足的情况下可能会被回收
  5. 空进程:里面没有任何可以运行的程序组件,这些进程存在就是作为一个缓存,为下一次启动程序缩短时间。

参考:google文档 左边的翻译

2、多进程之间的通信

2.1、多进程的优点

  • 稳定安全:一个子进程如果因为某些异常结束之后,不会直接导致主程序的崩溃。
  • 扩大内存空间:android系统对于一个应用程序的内存占用是有限制的,当使用多进程的时候,可以减少主进程占用的内存,这样降低被系统kill的概率。

2.2、创建多进程时候注意的问题

  • 每个进程初始化的时候都会执行一遍Application的onCreate()方法,在这个方法中一般是进行一些全局初始化的操作,所以进程初始化的过程多次执行onCreate()是浪费资源,解决方法,获取当前的进程名,只初始化当前的进程,如下代码:
public class MyApplication extends Application {

@Override
public void onCreate() {
super.onCreate();
//获取当前pid
int pid = android.os.Process.myPid();

Log.i("haha", "application pid is :" + pid);

ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
//进程列表
List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();

if (processInfos != null && !processInfos.isEmpty()){
for (ActivityManager.RunningAppProcessInfo appProcessInfo : processInfos){
//匹配当前进程的pid
if (appProcessInfo.pid == pid){
//匹配进程名
if (TextUtils.equals(appProcessInfo.processName,getPackageName())){
Log.i("haha", "主进程被初始化了");

}else if (TextUtils.equals(appProcessInfo.processName, "com.text.messenger.service")){
Log.i("haha", "其他进程被初始化了");
}
}
}
}
}
}

每个进程都有自己的初始化代码,这样就不会被初始化多次了。

  • 进程不是越多越好,当进程很多的时候就要考虑耗电等其他问题

以上参考:进程初始化中Application多次初始化的问题

2.3、进程间的通信ipc

inter process communication

正因为进程间内存不共享,所以就需要方法来进行进程间数据的传递。

(单进程,多线程)->handler
(多进程,单线程)->Messenger
(多线程,多进程)->AIDL(android interface definition language)

这里推荐一个最近发现的可在国内用的开发者网站:android developers , 感谢群里胖大哥。

方法1:Messenger

官方文档上有介绍,对于大多数应用来说,多进程单线程的操作已经能够满足很多需求,而且使用Messenger比使用AIDL简单。

文档里的代码:

public class MessengerService extends Service {

static final int MSG_SAY_HELLO = 1;

class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
//接收到客户端的消息
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}

final Messenger mMessenger = new Messenger(new IncomingHandler());

@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}

首先创建一个Handler,用于处理客户端发过来的消息,然后创建Messenger对象,这个对象用于在客户端如Activity中给Service这个进程发送消息,通过onBind()方法返回给客户端Binder对象,在客户端中使用IBinder对象将Messenger实例化,然后使用这个Messenger给服务端发送消息。那么客户端(这里是Activity)代码如下:(来自文档)

public class ActivityMessenger extends Activity {

Messenger mService = null;
//是否连接成功
boolean mBound;

private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
//实例化Messenger对像
mService = new Messenger(service);
mBound = true;
}

public void onServiceDisconnected(ComponentName className) {

mService = null;
mBound = false;
}
};

public void sayHello(View v) {
if (!mBound) return;

Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
//使用该Messenger发送消息
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

@Override
protected void onStart() {
super.onStart();
// 绑定服务
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
super.onStop();
// 解绑服务
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}

上面的代码和前面说的流程差不多,得到Messenger对象就可以发送消息了。还有还有别忘记在AndroidMenifest中注册这个Service,并声明android:process这个属性。

这个例子只是简单的客户端给服务端发送消息,显然功能不能满足,有来有往才符合交互。接下来就是服务端给客户端发送消息,文档里有这样一段话:

If you want the service to respond, then you need to also create a Messenger in the client. Then when the client receives the onServiceConnected() callback, it sends a Message to the service that includes the client’s Messenger in the replyTo parameter of the send() method.

我英语不好开始看的时候用某度翻译了一下,也看不懂,然后它贴心的告诉我有个例子,我立马就去看了。最后一句话的意思才懂了一些。
首先,上面英语告诉我们,得先在客户端新建一个Messenger,那么问题来了,这个Messenger是要传给服务端来给客户端发送消息的,如果有很多的客户端怎么办?我们可以考虑在服务端用一个Map或者List来保存这些不同客户端的Messenger,如:

ArrayList<Messenger> mClients = new ArrayList<>();

服务端的代码:

/**
*
* 多进程,单线程
* Created by W on 2016/9/4.
*/

public class MessengerService extends Service {


public static final int MSG_REGISTER_CLIENT = 1;

public static final int MSG_UNREGISTER_CLIENT = 2;

public static final int MSG_SET_VALUE = 3;

int mValue = 122;

ArrayList<Messenger> mClients = new ArrayList<>();
//接收传过来的消息
class InComingHandler extends Handler{
@Override
public void handleMessage(Message msg) {

switch (msg.what){
case MSG_REGISTER_CLIENT:
Log.i("haha", "添加了");
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
Log.i("haha", "移除");
break;
case MSG_SET_VALUE:
try {
Log.i("haha", "发送给Activity");
mClients.get(0).send(Message.obtain(null, MSG_SET_VALUE, mValue,0));
} catch (RemoteException e) {
e.printStackTrace();
mClients.remove(0);
}
break;
default:
super.handleMessage(msg);
}
}
}

Messenger mMessenger = new Messenger(new InComingHandler());


@Override
public void onCreate() {
super.onCreate();
}

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

然后客户端的代码:

/**
*
* Created by W on 2016/9/4.
*/

public class MessengerActivity extends AppCompatActivity{


boolean mBound = false;
private Messenger mMessenger;

//该Activity的Messenger
Messenger messenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MessengerService.MSG_SET_VALUE:
Log.i("haha", "从服务传过来的:" + msg.arg1 + "");
break;
default:
super.handleMessage(msg);
}
}
});

private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
mBound = true;
Log.i("haha", "服务连接成功");

//以下的代码,就是一旦连接成功,以下的代码,就是一旦连接成功,就把该activity的Messenger传给服务端
Message message = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT);
message.replyTo = messenger;
try {
mMessenger.send(message);
Log.i("haha", "给服务发消息成功");
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName name) {
mMessenger = null;
mBound = true;
}
};



@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.messenger);
Log.i("haha", "onCreate()");

Button button = (Button) findViewById(R.id.send_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message message = Message.obtain(null, MessengerService.MSG_SET_VALUE);
message.replyTo = messenger;
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}

}
});

}

@Override
protected void onStart() {
super.onStart();
Log.i("haha", "开始绑定服务");
bindService(new Intent(this, MessengerService.class), mServiceConnection, Context.BIND_AUTO_CREATE);

}

@Override
protected void onStop() {
super.onStop();
if (mBound){
if (mMessenger != null){
Message message = Message.obtain(null, MessengerService.MSG_UNREGISTER_CLIENT);
message.replyTo = messenger;
Log.i("haha", "给服务发送消息解除监听");
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mServiceConnection);
mBound = false;
}
}

@Override
protected void onDestroy() {
super.onDestroy();
Log.i("haha", "onDestroy");
}
}

Activity创建,走onCreate(),onStart(),在start中绑定服务,然后回调的接口返回了Service的Messenger对象,Activity也把自己的Messenger对象通过msg.replyTo传给了服务端,同时mClients添加这个Messenger。这样之后它们就可以互相发消息了。可能我讲的不是很好,如果在真机上调试看Log,就基本上能理清这个流程。

哎呀不用不知道,Messenger有一个特别坑的地方,发现不能传一个序列啊,这周作业本来是打算把一个对象传过去,然而不行啊。还有一个在进程间全局变量没什么作用啊。果然只有实践才知道好多东西

方法2.AIDL

我感觉没用这个aidl都已经忘的差不多了。
首先在android studio中main目录下新建一个AIDL文件夹,如图:
第九章--进程和进程间的通信
基本上是一路下一步。我们看到生成的文件里面有一个.aidl的文件:

// IMyAidlInterface.aidl
package com.example.myactionbardemo;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/

void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);

String getName(String aName);
}

这里生成了一个接口,里面的抽象方法是需要实现的。
然后点任务栏上的tools->make project。就会在如下菜单下生成一个接口:
第九章--进程和进程间的通信
重点关注有个Stub类实现了IMyAidlInterface接口,然后有个asInterface()方法可以生成接口对象。这就是我们做的前期工作,还有如果重新修改了.aidl文件一定要重新make project。
然后就是开始使用了,定义一个服务端:

public class AIDLService extends Service{

IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
//需要实现的两个方法,或者说会回调的方法。
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

}

@Override
public String getName(String aName) throws RemoteException {
return aName + "style";
}
};


@Nullable
@Override
public IBinder onBind(Intent intent) {
//返回的Stub对象
return mStub;
}
}

这个就会传到客户端,这里仍然用Activity代替客户端:

    IMyAidlInterface mIMyAidlInterface;

ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};

以上的代码应该是比较熟悉了,得到了接口对象,就能回调她的方法,相当于把客户端的数据传给服务端进行操作了,最重要的事它可以传对象啊,这点就足以了。

另,进程间传递的对象需要序列化,在此android studio有个parcelable插件可以自动给一个对象序列化。

再另,messenger底层也是用aidl实现的。

写的有点多,下面的英语看看就好。

If you only need to interact with the service while your activity is visible, you should bind during onStart() and unbind during onStop().
If you want your activity to receive responses even while it is stopped in the background, then you can bind during onCreate() and unbind during onDestroy(). Beware that this implies that your activity needs to use the service the entire time it’s running (even in the background), so if the service is in another process, then you increase the weight of the process and it becomes more likely that the system will kill it.