利用Messenger跨进程通信
来源:互联网 发布:淘宝直播买翡翠靠谱吗 编辑:程序博客网 时间:2024/05/20 13:05
在进行跨进程通信的时候,可能很多开发者一开始想到的就是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
public interface ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service); public void onServiceDisconnected(ComponentName name);}
(3)、Message
(4)、Handler
(5)、Messenger
public Messenger(Handler target)和
public Messenger(IBinder target)
public void send(Message message)
public IBinder getBinder()
(6)、Messenger通信实例
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方法进行回收。
<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-----------
- 利用Messenger跨进程通信
- Android 利用Messenger进行跨进程通信
- Messenger 跨进程通信
- android Messenger 跨进程通信
- android Messenger 跨进程通信
- Android Messenger 跨进程通信!!!!!!!!!!!!!!!!!!
- Android Messenger 跨进程通信
- 【Android】Messenger 跨进程通信
- Android Messenger 跨进程通信
- Android Messenger 跨进程通信
- Android 跨进程通信--Messenger
- 跨进程通信之Messenger
- Messenger实现跨进程通信
- android Messenger跨进程通信
- Messenger:使用消息的跨进程通信
- Messenger:使用消息的跨进程通信
- Messenger:使用消息的跨进程通信
- Messenger:使用消息的跨进程通信
- 简单的网络通信程序 TCP
- ubuntu16.04 更行flashplayer 插件
- NYOJ204coin text
- package-info.java文件解读
- 循环输出数据到页面
- 利用Messenger跨进程通信
- 同时使用tcp和udp回射服务器
- IntelliJ IDEA 15 配置 Tomcat8
- [CTBS]许可证过期之激活
- POJ 3421 X-factor Chains (整数分解+组合数)
- 【Qt】标准int类型输入对话框
- 不规则多边形区域的面积计算算法
- ACM天梯赛 L3-001. 凑零钱
- 一点想法