利用Messenger跨进程通信

在进行跨进程通信的时候,可能很多开发者一开始想到的就是AIDL。AIDL,中文名称是android接口描述语言,是android系统中用于进行跨进程通信必须了解的。至于如何生成远程对象进行AIDL通信,大家可以看我之前写的一篇文章------对Binder的浅显分析及AIDL的使用。但其实,如果我们进行的跨进程通信逻辑比较简单,我们完全没必要自己生成AIDL,因为android已经为开发者封装了一个天生的用于跨进程通信的类,叫Messenger。Messenger使用起来还算简单,但是如果不仔细去分析它在整个跨进程通信所起的作用,我们就很容易受困于某一些跨进程所产生的奇奇怪怪的错误。接下来,会通过介绍Message、Messenger、handler、ServiceConnection、Service来详细了解整个利用Messenger进行跨进程通信的流程。



(1)、Service



Service是android四大组件之一,是最顶层的应用容器,通常用于执行一些不用和用户交互的后台操作(播放歌曲)或者将本应用的某些功能提供给远程的客户端使用(跨进程通信),使用Service务必记住必须在AndroidManifest.xml文件中声明。启动一个Servcice的方法主要有两种,一种是startService,一种是bindService,通常来说前者是启动一个Service来执行一些后台任务,后者通常是进行远程通信使用。Service就和其他组件一样,同样依赖于宿主进程的主线程运行,所以如果我们需要做一些cpu密集型运算或者其他的耗时任务,就应该给Service开启一个工作线程,否则可能会导致ANR。我们要记住的两点是:

①:Service不是一个独立的进程,它和其他组件一样,默认情况下都是运行在当前的应用进程中(可以在AndroidManifest.xml文件单独指定进程)。

②:Service并不包含独立的工作线程,同样的,它也是运行在主线程中。

Service主要有两大功能:

①:告诉系统,应用希望在后台做一些工作(即使在没有和用户交互的情况下),相应的调用方法是startService,Service一旦运行就不会停止,除非显式的调用了stopService。

②:将本应用的一些方法暴露给远程应用,提供服务,相应的调用方法是bindService,这意味着该Servcice可以与客户端进行长链接并进行交互。

Service的生命周期:

当启动Service的时候,onCreate方法是最先被调用的,一般来将这个时候应该初始化一些全局变量,然后onStartCommand方法会被调用。如果Service是处于运行状态的,重复调用startServcice启动Service是不会起效果的,也就是onCreate只会被调用一次,但是onStartCommand会被重复调用。一旦Service运行起来了,除非明确指出要停止它,否则它将一直运行下去,除非系统回收了它所在的进程。

客户端通过调用bindService方法就可以连接Service,只要这个连接还存在,Service就会运行,并且,在Service的onBind方法里面,会给客户端返回一个IBinder对象,客户端通过IBinder对象就可以获取远程服务通道,进行远程操作。

拥有Service的进程的优先级:

默认情况下,一个只拥有Service的进程属于服务进程,它低于可见进程和前台进程,此进程往往在成为内存不足的情况下,会成为垃圾回收机制回收的候选对象。如果想要将一个只拥有Service的服务进程优先级提高,可以通过startForeground方法将其提升为前台进程。同时,我们应该知道,Service作为一个服务对象,是被客户端所依赖的,因此它 的进程的优先级必定要比客户端的高,因此,如果客户端所在进程是前台进程,那么当前进程也是前台进程。前台进程几乎不可能被回收,因为一个系统中位于前台进程的应用是很少的。

通过上述介绍,可以知道,对于使用Messenger进行远程通信来说,我们必须通过BindService方法启动远程服务,同时,Service的onBind方法会返回一个可进行通信的IBinder对象,通过它就可以和远程对象进行通信。



(2)、ServiceConnnection



ServiceConnection是用来管理连接远程Service的状态的,ServiceConnection是bindService的第二个参数,通过实现它,就可以在客户端中知道是否连接上了远程对象。ServiceConnection主要有两个方法,如下:


public interface ServiceConnection {
   
    public void onServiceConnected(ComponentName name, IBinder service);

    public void onServiceDisconnected(ComponentName name);
}


①:onServiceConnected:
当客户端与远程Service连接上时,此方法会回调,并且会获取到一个用于进行远程交流的IBinder对象Service。

②:onServiceDisconnected:
当客户端与远程Service断开连接时回调,name表示的是远程Service的具体名称。

(3)、Message


Message是一个实现了Parcelable接口的被设计来给Handler发送消息的类,它主要有两个Int数据和一个obj数据对象,如果只是进行简单的数据传输,Message可以在我们不需要进行任何配置的情况下进行异步通信。但进行远程通信时,由于跨进程通信最终是通过Parcel来传送的,因此要求所传送的数据对象必须是Parcelable子类。具体的将在后面讲解。通常获取Message对象的方法是Message.obtain和handler.obtainMessage方法,这两个方法获取的Message对象是从系统的一个可循环利用的再生池里面拿出来的,这也是系统提倡的用于获取Message对象的方法。关于Message主要有以下几点:

①:what属性:用于定义当前的Message的含义,通过它告诉接收者handler当前Message对象的作用。由于每个Handler都有各自的一块控件管理message,所以我们没必要担心Handler是否会造成冲突。

②:arg1、arg2属性:可供选择的且花销很小的用于存储发送给Handler的int数据。

③:obj属性:如果是在进程内通信,那么obj是可以存储任何数据对象的。但是如果是进程间通信,obj只可以存储一些系统实现的parcelable对象,并且只能是在android 2.2之后。

④:replyTo属性:这个字段在进程内通信基本没用,通常他是作为客户端附加在当前Message对象的提供个服务端进行回应的。会在客户端发送给服务端的时候,一起发送过去,服务端通过获取此字段就可以给客户端回应消息。

⑤:data属性:这是个Bundle对象,可以存储任何数据。也就是说如果想传输自定义的实现了Parcelable接口的对象,就可以通过此字段来存储对象信息并发送给服务端。

当获取Messege对象的时候,最好使用Message.obtain方法,这个方法有很多重载,这里就不多解释了,但是我们要知道,再使用完Message对象时,应该调用recycle方法释放Message对象,使它重新回收利用。



(4)、Handler



Handler对象允许你通过MessageQueue发送和处理Message和Runnable对象,每个Handler对象都会有一个单独的线程和消息队列用于处理相关消息,Handler中进行处理Message或者Runnable对象的主要有以下六个方法:





其中post系列的主要用于处理Runnable对象,send系列的用于处理Message对象,并且此方法会导致handleMessage方法的回调。对于我们了解Messenger的对调来说,我们只需要知道handlerMessage方法即可,由于每个Messenger对象都会关联一个handler,因此当进程间交流的时候,有一方收到通信消息,所关联的handlerj就会回调handlerMessage方法。

(5)、Messenger


Messenger封装了进程间通信的基本实现,因此只要我们在一个进程里定义了Messenger对象就可以把它发送给另一个进程,让另一个进程进行处理。由于Messenger是用于进程间交流的,且它不影响应用程序的生命周期,所以如果使用Messenger进行进程间交流,请务必把它放在四大组件里面,否则一旦进程被杀死,那么Messenger也会失去连接。

Messenger也是一个实现了Parcelable接口的类,它里面包含了一个IMessenger对象,这个对象是一个典型的aidl远程对象,因此Messenger自身既可以当成一个数据对象发送给远程对象,也可以作为连接客户端和服务端的桥梁。了解Messenger对象,有以下几点要注意:

①:Messenger的构造方法:主要有
 public Messenger(Handler target)
 public Messenger(IBinder target)

两个,通常来说,第一种用于实例化本地进程的Messenger对象,第二种用于客户端实例化远程IBinder代理对象。除此之外,我们还需要了解两个方法:
 public void send(Message message)

此方法用于将消息对象发送给远程对象(既可以是客户端发送给服务端也可以是反过来)。
 public IBinder getBinder()

此方法用于Service端将本地IBinder对象发送给客户端。



(6)、Messenger通信实例



接下来讲解客户端通过Messenger调用服务端的方法,并获取数据的实例。主要内容是发送User信息给服务端,并添加到服务端的list对象里面,然后获取服务端的list对象的消息返回给客户端。代码如下:
首先定义一个User对象,此对象必须实现Parcelable接口,否则无法进行远程通信的传递。

package com.example.messanger;

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

import java.io.Serializable;

/**
 * Created by Myy on 2016/7/15.
 */
public class User implements Parcelable {

    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return  "name:" + name + ",age:" + age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
}

接着定义一些常量,用于识别调用的方法类别(实例主要有两个方法,分别是添加user和获取user),代码如下:
package com.example.messanger;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Myy on 2016/7/15.
 */
public class Constant {

    public final static int AddUser = 0;//添加用户信息标签
    public final static int getUser = 1;//获取用户信息标签

    public static ArrayList<User> list = new ArrayList<User>();//用于存储客户端传递过来的用户信息
}
接下来是Service端的实现,代码如下:
package com.example.messanger;

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.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by Myy on 2016/7/15.
 */
public class RemoteService extends Service {

    private Handler handler = new Handler() {
        Messenger messenger = null;
        Message replyMsg = null;

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Constant.AddUser:
                    Bundle bundles = msg.getData();
//                   Log.i("服务端",bundles.getClassLoader().toString());
                    bundles.setClassLoader(Thread.currentThread().getContextClassLoader());
                    User user = bundles.getParcelable("user");
                    Constant.list.add(user);
                    messenger = msg.replyTo;
                    replyMsg = Message.obtain(null, Constant.AddUser);
                    try {
                        messenger.send(replyMsg);
                        Log.i("服务端", "成功执行添加用户操作");
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }finally {
                        replyMsg.recycle();
                    }
                    break;
                case Constant.getUser:
                    messenger = msg.replyTo;
                    replyMsg = Message.obtain(null, Constant.getUser);
                    Bundle bundle = new Bundle();
                    bundle.putParcelableArrayList("userList", Constant.list);
                    replyMsg.setData(bundle);
                    try {
                        messenger.send(replyMsg);
                        Log.i("服务端", "获取用户操作执行完毕");
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }finally {
                        replyMsg.recycle();
                    }
                    break;
            }
        }
    };

    private Messenger messenger = new Messenger(handler);

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //将底层的IMessanger对象返回
        return messenger.getBinder();
    }
}
我们可以发现Messenger对象的初始化引用了handler对象,handler对象进行了一些数据处理。难点在与handler的实现,下面讲解:

 Bundle bundles = msg.getData();
//                   Log.i("服务端",bundles.getClassLoader().toString());
  bundles.setClassLoader(Thread.currentThread().getContextClassLoader());
  User user = bundles.getParcelable("user");
这段话用于获取客户端发送过来的User信息,难点在于bundles为什么要设置当前环境的类的加载器,前面说过,Messenger远程通信,归根到底是Parcel在起作用,因此传递的对象必须是实现了Parcelable接口的对象,而ClassLoader类并没有实现,因此在传递过程中会丢失,所以需要重新加载当前环境的类的加载器去寻找User类别进行实例化。如果不设置,会报ClassNotFoundException错误。如果我们把上面的Log语句的注释去掉,系统会报空指针异常,很好的证明了ClassLoader类的消息并不能跨进程传输。

 messenger = msg.replyTo;
 replyMsg = Message.obtain(null, Constant.AddUser);
 try {
         messenger.send(replyMsg);
         Log.i("服务端", "成功执行添加用户操作");
       } catch (RemoteException e) {
         e.printStackTrace();
       }finally {
           replyMsg.recycle();
       }
其中messenger对象是客户端发送过来的,用于服务器进行回应的对象,replyMsg就是回应的消息。在使用完此对象记得调用recycle方法进行回收。
为了模拟远程通信,我们需要声明RemoteService并为其指定一个独立进程。
 <service
            android:name=".RemoteService"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.hy.blog.remote" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

其中android:process用于指定当前容器所运行的进程。:RemoteService的意思是,当前Service会运行在名字为"包名:RemoteService”进程里面,用了:号的,表示该进程是当前应用的独立进程,别的应用的组件不可以和它共同运行在同一个组件里面。当然我们可以指定一个完整的名字,比如:android:process="com.hy.bolg.remoteService“,这样的话表示Service会运行在名字为com.hy,blog.remoteService的进程里。


接下来是客户端代码:

package com.example.messanger;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    //执行添加user信息和获取user信息的远程连接
    private ServiceConnection addConnection, getConnection;
    private Messenger addMessenger, getMessenger;
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initMessenger();
        initAddServiceConnection();
        initGetServiceConnection();
        startAddServiceConnection();
    }

    /**
     * 执行添加user远程连接
     */
    private void startAddServiceConnection() {
        Intent intent = new Intent();
        //android5.0开始隐式意图启动Service必须加上当前的包命。
        intent.setPackage(getPackageName());
        intent.setAction("com.example.hy.blog.remote");
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        bindService(intent, addConnection, BIND_AUTO_CREATE);
    }

    /**
     * 绑定消息获得反馈
     */
    private void initMessenger() {
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case Constant.AddUser:
                        Log.i("客户端", "服务端返回消息:" + "成功");
                        //向服务端获取user信息
                        startGetServiceConnection();
                        break;
                    case Constant.getUser:
                        Bundle bundle = msg.getData();
                        //因为Messenger传输信息归根到底是通过parcel传递的,而ClassLoader类并不是Parcelable的子类,在传输过程中或丢失掉。所以必须给当前的Bundle
                        //提供类的加载器,才能找到相应的类并初始化。否则会报ClassNotFoundException
                        bundle.setClassLoader(Thread.currentThread().getContextClassLoader());
                        ArrayList<User> list = bundle.getParcelableArrayList("userList");
                        Log.i("客户端", "服务端返回消息:" + list.toString());
                        break;
                }
            }
        };
        addMessenger = new Messenger(handler);
        getMessenger = new Messenger(handler);
    }

    /**
     * 连接获取user服务
     */
    private void startGetServiceConnection() {
        Intent intent = new Intent();
        intent.setAction("com.example.hy.blog.remote");
        intent.setPackage(getPackageName());
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        bindService(intent, getConnection, BIND_AUTO_CREATE);
    }

    /**
     * 初始化添加user的连接对象
     */
    private void initAddServiceConnection() {
        addConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Messenger messenger = new Messenger(service);
                Message msg = Message.obtain(null, Constant.AddUser);
                Bundle bundle = new Bundle();
                bundle.putParcelable("user", new User("hy", 23));
                msg.setData(bundle);
                msg.replyTo = addMessenger;
                try {
                    messenger.send(msg);
                    Log.i("客户端", "执行添加用户操作");
                } catch (RemoteException e) {
                    e.printStackTrace();
                } finally {
                    msg.recycle();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };
    }

    /**
     * 初始化获取user的连接
     */
    private void initGetServiceConnection() {
        getConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Messenger messenger = new Messenger(service);
                Message msg = Message.obtain(null, Constant.getUser);
                msg.replyTo = getMessenger;
                try {
                    messenger.send(msg);
                    Log.i("客户端", "执行读取用户操作");
                } catch (RemoteException e) {
                    e.printStackTrace();
                } finally {
                    msg.recycle();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };
    }
}

其中的逻辑和服务端是一样的,读者自行理解并无难度,此处不再解析。运行,会打印如下LOG:


可以看到user信息成功发送给服务端,并执行了添加User信息操作,同时获取了服务端返回的User信息。


---------文章写自:HyHarden---------

--------博客地址:http://blog.csdn.net/qq_25722767/article/details/51895992-----------




  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值