基于 Binder 的跨进程通信以及 Service(二):Java 层

来源:互联网 发布:江苏域名备案 编辑:程序博客网 时间:2024/06/09 03:40

同进程使用Service(一)

  • 比如,你做了一个音乐播放 app,里面有一个 MusicService 负责后台播放音乐,对外提供 play(),pause() 的接口
  • 你在一个 Activity 里想调用这个 Service 的 play(),怎么搞?
  • 必须在这个 Activity 里拿到刚才启动的 MusicService 的实例,但这是不可能的。Service 实例是由安卓OS维护的,你拿不到。(启动 Service 可不是 new MusicService() new 出来的,而是调用安卓系统的 startService()/bindService(),这些函数不给你返回被启动的Service实例)
  • 我们可以想个办法把 Service 的实例返回去:首先要了解 Service 和客户端的回调机制,这里涉及一个重要的接口类:IBinder。

一、Service机制

  • 服务端都继承自 Service 类,Service 类有个 onBind() 方法。当有客户端来使用 Service 的时候,安卓OS会调用该 Service 的 onBinde()。不过因为 onBind() 的返回类型并不是 Service,所以我们不能直接把 Service 的实例返回去。
  • 并且,就算你返回去了也没用,因为客户端不是 onBind() 的调用者,安卓OS才是。客户端调的是 bindService()。
  • 客户端调用 bindService() 去绑定 Service,以便能够使用 Service 的功能。函数原型是:boolean bindService(Intent intent, ServiceConnection conn, int flags)
  • 第一个参数 intent 指定要绑定的是哪个 Service,我们这里就是: new Intent(this, MusicService.class);
  • 第三个参数 flags 一般用 BIND_AUTO_CREATE 即可。表示如果被绑定的Service还没启动,则安卓OS会自动启动它。
  • 重点是第二个参数 conn。
    • 它是 ServiceConnection 类型的
    • 当 Service 绑定好了之后(Service 的 onBind() 返回了),安卓OS会回调 conn.onServiceConnected(ComponentName name, IBinder service)
    • 安卓OS会在这个回调里,传来一个 IBinder 类型的对象(第二个参数),它就是 Service 端的 onBind() 方法 return 的东西。
  • 但这里还是没有拿到 Service 的实例,因为 onBind() 返回的并不是 Service 实例,而是一个 IBinder。
  • 不过既然知道了服务端和客户端之间交互的流程,就可以想办法让客户端拿到 Service 实例了。

二、让客户端拿到Service的实例

  • Service 端通过重写 onBind() 方法,让它返回一个可以拿到 Service 实例的 IBinder。比如给这个 IBinder 添加一个方法,getService().
public class MusicService extends Service {    // 用于让 onBind() 返回的类,它从 Binder 继承,即实现了 IBinder 接口    // 我们给它加了一个方法,getService(),用于返回 MusicService 的实例    public class MyBinder extends Binder {        MusicService getService() {            return MusicService.this;        }    }    // 重写 onBind 方法,返回一个 MyBinder 的实例,    // 而 MyBinder 的 getService() 可以拿到 MusicService 实例    @Override    public IBinder onBind(Intent intent) {        return new MyBinder();    }    public void play() {      Log.d("testing", "playing");    }}
  • 客户端准备一个 ServiceConnection,用来绑定 Service 并接收 Service onBind() 返回的那个 IBinder。
  • 重写这个 ServiceConnection 的 onServiceConnected() 方法,把安卓OS回传过来的 IBinder 用起来,得到 Service 的实例。
import com.zsl.musicserver.MusicService.MyBinder;public class MainActivity extends Activity {    MusicService mService;    // 准备一个 ServiceConnection,用来绑定 Service 并接收 Service onBind() 返回的那个 IBinder。    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className,IBinder service) {            // 注意 !!!! 这里要进行强转,才能使用 getService() 方法 !!!!            MyBinder binder = (MyBinder) service;            mService = binder.getService();        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent(this, MusicService.class);        bindService(intent, mConnection, BIND_AUTO_CREATE);    }    public void onButtonClick(View v) {        // 调用 Service 的业务函数        mService.play();    }}
  • 以上程序写在同一个工程里可以正确运行,点击 play 按钮后 logcat 输出 playing。

三、小结

  • Service 重写 onBind() 方法,返回一个 IBinder 的子类。
  • 客户端用 conn 的 onServiceConnected() 方法接收到这个返回的对象。
  • 这样,客户端和 Service 的联系就建立起来了。

四、问题

  • 首先,上面这种做法很难做到跨进程,先不说原理,就从代码层面看也不行
  • 上例中客户端需要拿到Service的实例,这就要求客户端和服务端必须在一个工程里,如果是两个 app 根本没法搞:
MusicService mService; // 客户端代码里必须有 MusicService 这个类
  • 理论上服务端的 MusicService 类对客户端应该是透明的,客户端只需要使用 Service 的功能就可以了。
  • 所以我们稍微更进一步,把 MusicService 抽象出一个接口:MusicInterface。服务端和客户端都有接口类 MusicInterface,但分别实现,客户端是给服务端发消息,服务端真的干活。

同进程使用Service(二)

  • 这一节跟上一节没有本质区别,只是在上节基础上做了一点包装。
  • 让 Service 的 onBind() 函数返回的不光是一个 IBinder,还是一个 MusicInterface。
  • MusicInterface 是个接口,统一封装了 MusicService 的业务函数。客户端通过该接口使用业务函数。
package com.zsl.musicservice;public interface MusicInterface {    void play();    void pause();}

服务端

  • onBind() 返回的东西是 extends Binder implements MusicInterface
  • 下面例子中,MusicService 的内部类 MusicServiceProxy,就是用来返回给客户端的 proxy。
package com.zsl.musicservice;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.util.Log;public class MusicService extends Service {    // proxy只负责分发,把活交给 Service 的业务函数干    class MusicServiceProxy extends Binder implements MusicInterface {        @Override        public void play() {            MusicService.this.play();        }        @Override        public void pause() {            MusicService.this.pause();        }    }    // 模拟播放的业务逻辑    public void play() {        Log.d("zsl", "Playing");    }    // 模拟暂停的业务逻辑    public void pause() {        Log.d("zsl", "Paused");    }    @Override    public IBinder onBind(Intent intent) {        return new MusicServiceProxy();    }}

客户端

  • 客户端代码如下:
package com.zsl.musicclient;import com.zsl.musicplayer0.R;import com.zsl.musicservice.MusicInterface;import com.zsl.musicservice.MusicService;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.view.View;public class PlayerActivity extends Activity {    private MusicInterface mMusicServiceProxy;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_player);        Intent intent = new Intent(this, MusicService.class);        bindService(intent, new MusicServiceConn(), BIND_AUTO_CREATE);    }    class MusicServiceConn implements ServiceConnection{        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            mMusicServiceProxy = (MusicInterface) service;        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    }    public void btn_play_clicked(View v){        mMusicServiceProxy.play();    }    public void btn_pause_clicked(View v){        mMusicServiceProxy.pause();    }}

小结

  • 服务端需要提供的有:

    • 接口类 MusicInterface
    • 继承自 Binder 并实现了 MusicInterface 的 proxy 类,即 MusicServiceProxy,它的业务函数,实际上只是调用 MusicService 的业务函数
    • 通过 onBind() 函数 return proxy 对象,即 new MusicInterface()
  • 客户端需要做的事情:

    • 搞个类 MusicServiceConn,实现 ServiceConnection 接口,用于接收 Service onBind() 返回的 IBinder,即 proxy
    • MusicServiceConn 实现 onServiceConnected() 方法时,把第二个参数(带出参数)数强转成 MusicInterface

为何不能跨进程?

  • 客户端拿到 proxy 后,强转那句,不能跨进程。即:
mMusicServiceProxy = (MusicInterface) service;
  • 这个 service 是安卓OS传过来的,它的本质是Server进程里的一个对象,即一堆二进制数据。安卓OS拿他当 IBinder 类型看待。
  • 如果你的客户端是另一个进程,你没办法成功进行强转的。哪怕你把 MusicInterface.java 拷贝到了客户端进程。
  • 虽然这样客户端可以 import MusicInterface,并且使用它,编译的时候也没问题。但运行时,强转的时候还是会报错如下:
java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.zsl.musicclient.MusicInterface
  • 如果还是不服,把 MusicInterface 类的包也改成跟服务端一样的,还是报错:
java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.zsl.musicserver.MusicInterface
  • 其根本原因就是一个进程里的对象,是不可能被另一个进程认识。

如果你想做实验,见证真的不能跨进程

  • 拆分成两个工程,一个 Sever(com.zsl.musicserver), 一个 Client(com.zsl.musicclient)
  • Server 工程包括如下源文件: MusicService.java,MusicInterface.java
  • Client 工程包括如下源文件: MainActivity.java,MusicInterface.java
  • Client 需要知道 Server 的包名和 Service 的类名:
Intent intent = new Intent();intent.setClassName("com.zsl.musicserver",  "com.zsl.musicserver.MusicService");  
  • 问题:Client 绑定 Service 时报错,不让绑定
java.lang.SecurityException: Not allowed to bind to service Intent
  • 解决方式是在 Client 里先 startService(intent) 然后再 bindService(intent, ...)
  • 最后,你会遇到不让强转的问题

跨进程使用Service(一):Binder

  • Binder 既然是被设计用来跨进程通信的,那么利用 Binder 一定可以跨进程。
  • 我们上面的例子之所以不能跨进程,是因为我们没有使用到 Binder 跨进程的核心函数:transact() 和 onTransact()

一、Binder 跨进程原理

  • 对于安卓 OS 来说,Binder 对象是可以跨进程使用的,即一个进程里的 Binder 对象,可以被安卓 OS 传递到另一个进程里。
  • Binder 有两个函数:transact() 发送消息,onTransact() 接收消息。
  • Binder 实现了 IBinder 接口,transact() 是 IBinder 的,onTransact() 是 Binder 的。
  • 利用 transact() 和 onTransact(),我们可以实现跨进程通信。

二、使用 Binder 跨进程调用 Service

1. 服务端

  • 新建一个 Android 工程作为服务端。
  • 服务端 onBind() 返回的东西仍然继承自 Binder,不过我们这次不必给他添加个函数让客户端拿到 Service 实例了。
  • 我们只需要重写 Binder 的 onTransact() 即可,因为 onTransact() 能收到客户端发来的消息:
package com.zsl.musicservicebinderipc;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.Parcel;import android.os.RemoteException;import android.util.Log;public class MusicService extends Service {    // 用于让 onBind() 返回的类    // 重写 Binder 的 onTransact(), 用于接收客户端进程发来的消息    public class MyBinder extends Binder {        @Override        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {        switch(code){            // 客户端的 transact() 第一个参数是 1 的时候,进行 play()            case 1:{                play();                return true;            }            }            return super.onTransact(code, data, reply, flags);        }    }    // 重写 onBind 方法,返回一个 MyBinder 的实例,    // 而 MyBinder 的 onTransact() 一旦收到 transact() 发来的 1,将会调用 play()    @Override    public IBinder onBind(Intent intent) {        return new MyBinder();    }    public void play() {        Log.d("testing", "playing");    }}
  • 我们让 onBind() 返回一个 MyBinder。客户端拿到 MyBinder 后,只需调用它的 transact() 方法,第一个参数传1,就能实现调用服务端 play() 的目的。

2. 客户端

  • 新建一个 Android 工程作为客户端。
  • 客户端不再需要强转,它调用服务端 play() 的方式变成:调用从服务端得到的 IBinder 的 transact() 方法,并且第一个参数传1.
  • 这个 1 是服务端和客户端约定好的,代表是 play()。
  • 客户端代码如下:
package com.zsl.musicclientbinderipc;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.Parcel;import android.os.RemoteException;import android.view.View;public class MainActivity extends Activity {    IBinder mService;    // 准备一个 ServiceConnection,用来绑定 Service 并接收 Service onBind() 返回的那个 IBinder。    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className,IBinder service) {            // 把服务端进程返回的 IBinder 保存起来,后续调用它的 transact() 给服务端发消息            mService = service;        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent();        intent.setClassName("com.zsl.musicservicebinderipc", "com.zsl.musicservicebinderipc.MusicService");        startService(intent);        bindService(intent, mConnection, BIND_AUTO_CREATE);    }    // 让服务端 play()    public void play(){        // 调用 transact() 必须提供这两个参数,目前先不必理会        Parcel data = Parcel.obtain();        Parcel reply = Parcel.obtain();        try {            // IBinder 的 transact() ,给服务端发消息,服务端的 onTransact() 会处理消息            mService.transact(1, data, reply, 0);        } catch (RemoteException e) {            e.printStackTrace();        }    }    public void btn_play_clicked(View v) {        play();    }}

三、小结

  • 客户端程序员需要知道调用 play() 要 transact() 哪些参数,调用 pause() 要传递哪些。。。 要为每个需要用到的方法写一套 transact()
  • 不应该让客户端看到那么多细节,用起来不方便。服务端应该把一切都封装好,让客户端用起来方便。
  • 这样服务端需要封装几个类,把 transact()、onTransact() 包装起来。
  • 安卓为了简化这一个过程,为我们准备了 AIDL

跨进程使用Service(二):AIDL

一、AIDL 怎么用

1.书写 AIDL 文件,定义接口

  • 新建一个 Android 工程,作为服务端。
  • 新建一个 Interface,起名叫 MusicInterface。MusicInterface.java 内容如下:
package com.zsl.musicservice2aidl;public interface MusicInterface {    void play();    void pause();}
  • 直接把 MusicInterface.java 改成 MusicInterface.aidl,并且把 public 去掉,因为 AIDL 语法不支持 public。MusicInterface.aidl 内容如下:
package com.zsl.musicservice2aidl;interface MusicInterface {    void play(String f);    void pause();}

2. 实现服务端

  • 新建一个 MusicService。这次我们让 onBind() 返回的类不用自己搞一个 extends Binder implements MusicInterface 的内部类了,AIDL 已经为我们搞定了。我们只需要搞一个内部类,让它 extends Stub 即可。Stub 是 AIDL 为我们生成的一个类,它已经 extends Binder implements MusicInterface 了。
  • 服务端代码如下:
package com.zsl.musicservice2aidl;// 把 AIDL 生成的 Stub 类 import 进来import com.zsl.musicservice2aidl.MusicInterface.Stub;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;public class MusicService extends Service {    // 用于让 onBind() 返回的类,只需要从 Stub 继承即可,比原来简单了些    class MusicServiceProxy extends Stub {        @Override        public void play() {            MusicService.this.play();        }        @Override        public void pause() {            MusicService.this.pause();        }    }    // 模拟播放的业务逻辑    public void play() {        Log.d("zsl", "Playing");    }    // 模拟暂停的业务逻辑    public void pause() {        Log.d("zsl", "Paused");    }    @Override    public IBinder onBind(Intent intent) {        return new MusicServiceProxy();    }}
  • 记得在项目的 Manifest 文件里写上这个 Service。
  • 另外为了能被远程启动和绑定,最好把该 Service 的 export 搞成 true。

3. 实现客户端

  • 新建一个 Android 工程,作为客户端。
  • 客户端也需要 MusicInterface 这个接口,把 MusicInterface.aidl 拷贝到客户端项目。注意!! 一定要在客户端建一个跟服务端一样的包,把 MusicInterface.aidl 放到这个包里。
  • 客户端的代码如下:
package com.zsl.musicclient2aidl;// 注意包名,跟服务端是一致的import com.zsl.musicservice2aidl.MusicInterface;import com.zsl.musicservice2aidl.MusicInterface.Stub;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.view.View;public class MainActivity extends Activity {    private MusicInterface mMusicServiceProxy;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent();        intent.setClassName("com.zsl.musicservice2aidl", "com.zsl.musicservice2aidl.MusicService");        startService(intent);        bindService(intent, new MusicServiceConn(), BIND_AUTO_CREATE);    }    class MusicServiceConn implements ServiceConnection{        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            // 这里不直接强转了,而是使用 Stub 的 asInterface() 方法            mMusicServiceProxy = Stub.asInterface(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    }    public void btn_play_clicked(View v){        try {            mMusicServiceProxy.play();        } catch (RemoteException e) {            e.printStackTrace();        }    }}
  • 可以看到客户端代码跟原来区别很小,最关键的一点就是: 在把 IBinder 强转成 MusicInterface 时,不再直接强转,而是使用 Stub 的 asInterface() 方法。
  • 部署服务端再部署客户端,实验,可以跨进程调用。
  • 可以看到,借助 AIDL 我们实现了跨进程通信,并且我们并没有显式的使用 transact() 和 onTransact() 方法。这就是 AIDL 带给我们的方便。
  • 下面我们看一下 AIDL 做了哪些包装来避免让程序员直接调用 transact() 、onTransact()

二、AIDL的原理

  • MusicInterface.aidl 文件写好后,会在 gen 目录下生成 MusicInterface.java,其内容如下(经过精简):
package com.zsl.musicservice2aidl;public interface MusicInterface extends IInterface {    // 1. Stub 类继承了 Binder,实现了 MusicInterface,Service 的 onBind() 只需要返回 Stub 的子类即可    // 2. Stub 类是 abstract 的,它并没有实现 MusicInterface 的业务函数 play() 和 pause(),这要交给 Service 去做    // 3. Stub 类提供了一个 static 方法 asInterface(),我们在这个方法的注释中再细看,注意它是 static 的    public static abstract class Stub extends Binder implements MusicInterface {        // Stubd 类的 asInterfasce() 方法,这个方法是供客户端使用的,注意它是 static 的,它 return 一个 Proxy        // 注意它需要一个参数 IBinder,它根据这个 IBinder new 一个 Proxy,返回给客户端        public static MusicInterface asInterface(IBinder obj) {            return new MusicInterface.Stub.Proxy(obj);        }        @Override        // Stub 类实现 onTransact(),根据客户端传来的 code,调用 this.play()、this.pause()        // 注意,Stub 的 this.play()、this.pause() 是虚函数,因为 Stub 不负责业务,这两函数交给Stub 的继承者去实现        // 继承 Stub 的类,即 Service 设计者要实现的,用于让 onBind() 返回的类        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            switch (code) {                case TRANSACTION_play: {                    this.play();                    return true;                }                case TRANSACTION_pause: {                    this.pause();                    return true;                }            }        }        // Proxy 是客户端使用的类,它的 play() 函数其实就是用 mRemote 的 transact() 发消息        // mRemote 就是从服务端得到的 IBinder,构造 Proxy 需要它        private static class Proxy implements MusicInterface {            private IBinder mRemote;            Proxy(IBinder remote) {                mRemote = remote;            }            @Override            public void play() throws android.os.RemoteException {                mRemote.transact(Stub.TRANSACTION_play, _data, _reply, 0);            }            @Override            public void pause() throws android.os.RemoteException {                mRemote.transact(Stub.TRANSACTION_pause, _data, _reply, 0);            }        }        static final int TRANSACTION_play = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_pause = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    } // end of class Stub    public void play() throws RemoteException;    public void pause() throws RemoteException;}
  • 我们把 AIDL 文件复制到客户端工程,客户端也会生成如上的 java 文件。
  • 其实拷贝 AIDL 文件就相当于拷贝了一个 .java 的接口文件。只不过 AIDL 的写法要简洁的多。
  • 服务端负责继承 Stub,实现它的业务函数。而 Stub 本身已经做好了 onTransact(),它把客户端的 play() 分发给继承者的 play().
  • 客户端只需要在绑定 Service 之后,利用安卓OS传回来的 IBinder 作为参数,调用 Stub.asInterface() 就能得到一个 MusicInterface。
  • 后续直接使用它的 play() 即可,感觉就像使用本地函数一样。
  • 客户端程序员不必知道,其实这个 MusicInterface 的 play(),底层调用的是 binder.transact(TRANSACTION_play,..)

三、小结

  • AIDL 定义的是接口,服务端和客户端使用同样的接口。两端的工程里只要有相同的 AIDL 文件即可。
  • AIDL 书写简单,服务端程序员和客户端程序员都省去了很多麻烦。
  • 其本质上还是使用 Binder 的 transact() 和 onTransact() 进行跨进程通信。

四、扩展:传递复杂数据类型:用 AIDL 定义类型

  • 我们上面的例子省去了跨进程传递参数的部分
  • 这部分不是本文关注的重点,作为扩展可以自行查资料

五、AIDL 与 Native 层 的 Bp、Bn、呼应

  • AIDL 挺方便,是 Android 为我们准备的好东西。类似于 Native 层里那些 Android 为我们准备的好东西。
  • asInterface(),相当于 Native 层的 interface_cast,让客户端一调用,就能把一个 binder 转换成 IXXX 这种业务类。
  • 而 asInterface() 返回的 Proxy 类,其实就对应 Native 层的 BpXXX。
  • Stub 类则对于 Native 层的 BnXXX,它负责用 onTransact() 分发客户端请求。
  • AIDL 的方便之处在于,它连 Stub 类(即 BnXXX )的 onTransact() 都给你实现好了,包括用于辨别指令的 CODE 都自动给你生成了。
  • 而 Native 层的程序员一般需要自己写 onTransact()

跨进程使用Service(二):Message

一、回忆一下 Handler 和 Message

  • 在UI编程时,我们经常用 Handler 和 Message 处理高耗时任务。
  • 具体方法:
    • Activity 里弄个 Handler 类型的成员 mHandler。
    • mHandler 的 handleMessage() 负责处理 Message
    • 在 Activity 里起一个线程去做高耗时任务,该线程通过 mHandler.sendMessage() 给 mHander 发送 Message。
  • 要点: Activity 里的工作线程能拿到 mHander。这很容易,内部类可以通过类名访问外部类。

二、 Messenger

  • UI 里只使用了两个类: Handler、Message。 UI 里都是调用 Handler 的 sendMessage(Message msg) 方法,发送一个 Message。然后 Handler 的 handleMessage() 会收到这个 Message 参数。
  • 说白了,这种传递 Message 的方式,是用同一个对象(mHandler)的两个方法(sedMessage(),handleMessage())在传递 Message。
  • 而同一个对象不可能横跨两个进程,所以我们需要一个新类:Messenger。
  • Messager 是基于 Binder 构建的,所以可以跨进程。 Messenger 的构造函数需要一个 IBinder 参数:
Messenger(IBinder target)// Create a Messenger from a raw IBinder, which had previously been retrieved with getBinder(). 
  • 以上是 API 文档里对该构造函数的描述,他说那个 IBinder 参数必须是之前用 getBinder() 拿到的。这是啥意思?
  • Messenger 类有个 getBinder() 方法,能返回这个 Messenger 与对应的 Handler 通信用的 IBinder。
  • 对应的 Handler 在哪里? Messager 还有一个构造函数,以 Handler 为参数。
Messenger(Handler target)//Create a new Messenger pointing to the given Handler. 
  • 用 Messenger 发送 Message 的时候,直接发给该 Handler。
  • 要点:
    1. Messenger 有两个构造函数,一个以 Handler 为参数,一个以 IBinder 为参数。
    2. Messenger 有个 getBinder() 方法,能返回与它对应的 Handler 通信用的 IBinder。
  • 这样就可在服务端弄一个 Handler,用来处理客户端发的 Message。然后基于它构建一个 Messenger,用其 getBinder() 方法得到 Messenger 与 Handler 通信的 IBinder。
  • 然后服务端的 onBind() 返回这个 IBinder。
  • 客户端得到这个 IBinder 后,再基于它构建一个自己的 Messenger,这个 Messenger 发的消息,就会被服务端收到了。
  • 注意: 服务端和客户端各有自己的 Messenger 对象,而不是同一个对象。服务端的 Messenger 是基于 Handler 构建,客户端的是基于 IBinder 构建。

三、利用 Messenger 跨进程使用 Service

1. 服务端

  • 服务端代码如下:
package com.zsl.musicserver1message;public class MusicServer extends Service {    static final int MSG_PLAY = 1;    class ServiceHandler extends Handler  {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_PLAY:                    MusicServer.this.play();                    break;                default:                    super.handleMessage(msg);            }        }    }    final Messenger mMessenger = new Messenger(new ServiceHandler());    @Override    public IBinder onBind(Intent intent) {        //返回给客户端一个 IBinder 实例,客户端基于它构建自己的 Messager,然后发消息过来        return mMessenger.getBinder();    }    public void play(){        Log.d("Messenger Test", "Playing");    }}
  • Service 的 Manifest 里要加上这个: android:exported="true"

2. 客户端

  • 客户端代码如下:
package com.zsl.musicclient1message;public class MainActivity extends Activity {    static final int MSG_PLAY = 1;    Messenger mService = null;    private ServiceConnection mConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className, IBinder service) {            //接收onBind()传回来的IBinder,并用它构造Messenger            mService = new Messenger(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    public void play() {        Message msg = Message.obtain(null, MSG_PLAY, 0, 0);        try {            mService.send(msg);        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent();        intent.setClassName("com.zsl.musicserver1message", "com.zsl.musicserver1message.MusicServer");        startService(intent);        bindService(intent, mConnection, BIND_AUTO_CREATE);    }    public void btn_play_clicked(View v){        play();    }}

Messenger 和 AIDL 哪个好?

  • 本质都是 Binder
  • 都是 Android 推荐的方法
  • 孰优孰劣还是看需求
  • AIDL 相对来说使用起来更复杂,不过能传递的数据类型更丰富
0 0
原创粉丝点击