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

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.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容