当前位置: 代码迷 >> Android >> 创建一个BoundService——通译总结自developer.android.com
  详细解决方案

创建一个BoundService——通译总结自developer.android.com

热度:233   发布时间:2016-04-24 11:05:39.0
创建一个BoundService——翻译总结自developer.android.com

简述

继承Binder类

在onBind中创建一个继承Binder类,并返回。这样接到这个Binder的终端就可以直接使用它来访问Binder或者Service当中的共有方法。

当你的Service在幕后工作,并且仅仅被你自己的应用程序使用的时候,我们就推荐你使用这样的方法。如果你要使用你的服务可以被其他的应用程序访问或跨进程通信,你才需要用其他的方法。

使用Messenger

跨进程用这种方法。这种方式里面Service会定义一个Handler来处理不同类型的Message对象。Handler是Messager得以和client分享IBinder的基础,使得客户端可以使用Message对象向Service发送请求。客户端可以以自己定义一个Messenger,service也可以回送消息。

这是使用跨进程方式通信的最简单的办法,因为Messenger将所有的请求排队到一个单一的线程,不用费心去设计自己的service线程安全。

使用AIDL

Android接口定义语言,做了所有把对象分解为一个个操作系统能够识别的基础元素,并且可以使得他们跨进程。上一个Mesenger的技术,其实是使用AIDL来作为其基础的结构的。如之前提到的,Messenger创建一个所有客户端请求的队列,所以service可以一次接受一个请求。然而如果你想要service同时接收多个请求,那么你可以直接使用AIDL。在时你的Service要能够负载多线程并且线程安全。

想要直接使用AIDL,你必须创建一个 .ail文件,其中定义了编程的接口。Android sdk使用这个文件来生成一个抽象类,其中构建了结构并且处理IPC,在你的Service中拓展它。

提示:

大多数的app不应该使用AIDL来创建一个service,因为这需要多线程的负载能力,并且它的构建也更加复杂。所以,AIDL不适合于大多数的应用,并且在这里没有论述在你的service中怎样使用。如果你确定你想直接使用AIDL,查看AIDL的文档吧。


拓展Binder类

提示:比如一个音乐播放器需要将Activity和一个后台的Service进行绑定,就很适合使用这样的方法。

下面是开始的方法:

1.在你的Service中创建一个Binder的实例,至少包括以下一项:

- 客户端可以调用的公共方法。

- 返回当前service的实例,其中包含客户端可以调用的公有方法。

- 或者返回一个包含客户端可以调用的公有方法的service的内部类。

2.从onBind回调函数中返回Binder实例。

3.在客户端,从onServiceConnected回调方法中接收Binder对象,并且调用其中的方法来和service通信。


提示:之所以要在一个应用程序里面的原因,是这样的话客户端才可以获取到返回的对象并且调用API。Servie和客户端必须在相同的进程当中,因为这个技术不可以进程。

取个例子,下面是一个通过构建一个Binder使得客户端可以和servise通信的例子。

public class LocalService extends Service {    // Binder given to clients    private final IBinder mBinder = new LocalBinder();    // Random number generator    private final Random mGenerator = new Random();    /**     * Class used for the client Binder.  Because we know this service always     * runs in the same process as its clients, we don't need to deal with IPC.     */    public class LocalBinder extends Binder {        LocalService getService() {            // Return this instance of LocalService so clients can call public methods            return LocalService.this;        }    }    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    /** method for clients */    public int getRandomNumber() {      return mGenerator.nextInt(100);    }}
在LocalBinder中定义了一个getService方法,可以让客户端检索到LocalServce的实例。这使得客户端可以调用service中的公有方法。例如,客户端可以调用来自service的getRandomNumber方法。

下面是一个binds了LocalService的Activity,当按钮按下时候调用getRandomNumber。

public class BindingActivity extends Activity {    LocalService mService;    boolean mBound = false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);    }    @Override    protected void onStart() {        super.onStart();        // Bind to LocalService        Intent intent = new Intent(this, LocalService.class);        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        // Unbind from the service        if (mBound) {            unbindService(mConnection);            mBound = false;        }    }    /** Called when a button is clicked (the button in the layout file attaches to      * this method with the android:onClick attribute) */    public void onButtonClick(View v) {        if (mBound) {            // Call a method from the LocalService.            // However, if this call were something that might hang, then this request should            // occur in a separate thread to avoid slowing down the activity performance.            int num = mService.getRandomNumber();            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();        }    }    /** Defines callbacks for service binding, passed to bindService() */    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className,                IBinder service) {            // We've bound to LocalService, cast the IBinder and get LocalService instance            LocalBinder binder = (LocalBinder) service;            mService = binder.getService();            mBound = true;        }        @Override        public void onServiceDisconnected(ComponentName arg0) {            mBound = false;        }    };}

上面这个例子展示了客户端是怎样使用一个ServiceConnection的构造和回调onServiceConnected来绑定service的。下一个部分我们将介绍更多绑定service 的方法。

提示:在上面的例子里面,onStop方法将client从service上面解绑。客户端应该在合适的时机从service上面解绑,入宫在Additional Notes中讨论的那样。

更多的示例代码可以查看LocalSercie.java和LocalServiceAcitivities.java,在ApiDemos中。


使用一个Messenger

如果需要你的service跟远程的进程通信,那么你可以使用一个Messenger来提供你的service的接口。这个技术让你不使用AIDL就可以进行IPC。

下面是怎样使用Messenger的一个总结:

- 在service中构建一个Handler,其可以从客户端接受每一个调用的回调。

- Handler被用来创建一个Message对象(其实是一个Handler的引用)

- Messenger创建一个IBinder,也就是在onBind中service返回给客户端的那个。

- 客户端使用IBinder来初始化Messenger(它引用了服务的Handler),客户端使用这个Messsenger来给service发送Message对象。

- service在它的Handler中接收每一个Message。具体而言实在handleMessge方法中。


用这种方法,就没有了客户端能够调用的service的方法了。取而代之的是,客户端发送messages,也即是Message对象,service它的Handler中接收。

下面是使用Messenger接口的一个简单的service示例。

public class MessengerService extends Service {    /** Command to the service to display a message */    static final int MSG_SAY_HELLO = 1;    /**     * Handler of incoming messages from clients.     */    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);            }        }    }    /**     * Target we publish for clients to send messages to IncomingHandler.     */    final Messenger mMessenger = new Messenger(new IncomingHandler());    /**     * When binding to the service, we return an interface to our messenger     * for sending messages to the service.     */    @Override    public IBinder onBind(Intent intent) {        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();        return mMessenger.getBinder();    }}
注意到这个例子中并没有膳食出srevice是如何应答客户端的,如果你要service去应答,那么你需要在客户端里面创建一个Messenger。那么当客户单接收到onServiceConnected的回调的时候,他就会想service发送Message,其中在send函数的reply参数中包含客户端的Messenger。

你可以在例子中看见怎样提供一种双工通信,在MessengerService.java(Service)和MessengerServiceActivities.java(client)的例子中。


绑定一个Service

应用组件(客户端)可以通过调用bindeService来绑定service。Android 系统随后调用service的onBinde方法,它返回一个用来和service交互的IBinder。绑定是异步的过程。bindService即时返回,并且不向客户端返回IBinder。为了接受IBinder,客户端必须创建一个ServiceConnection实例,并且把它传入给bindService。ServiceConnection中包含一个回调方法,系统调用它来传送IBinder。

提示:只有activity,service和content providers可以绑定一个service,你不能让一个broadcast receiver来绑定service。

所以,从你的客户端去绑定service,你必须做:

1.构建ServiceConnection

你的构建必须重写两个回调函数:

onServiceConnected()

系统通过调用它来传递service的onBind返回的IBinder。

onServiceDisconnected()

Android系统在和service 的连接以外地中断时候调用该函数。比如说当service崩溃了或者被杀掉了。当客户端解绑的时候,这个方法不会被调用。

2.调用bindService,将构建的ServiceConnection传入。

3.当系统调用你的onServieConnected的回调方法的时候,你可以开始和service通信 ,使用你在接口中定义的方法。

4.要断开和service的连接,就调用unbindeService()。

如果你的客户端在你的应用销毁客户端的时候仍然和service连接,那么销毁会使客户端解绑。在客户端完成和service的交互的时候解绑,这样做更好。


例如,下面的代码片展示了从客户单到service的连接,servise使用上面的extending the Binder 类的方法构建,所以所有他要做的事情就是获取返回的IBinder并传递到LocalService类中,并且请求LocalService实例。


private ServiceConnection mConnection = new ServiceConnection() {    // Called when the connection with the service is established    public void onServiceConnected(ComponentName className, IBinder service) {        // Because we have bound to an explicit        // service that is running in our own process, we can        // cast its IBinder to a concrete class and directly access it.        LocalBinder binder = (LocalBinder) service;        mService = binder.getService();        mBound = true;    }    // Called when the connection with the service disconnects unexpectedly    public void onServiceDisconnected(ComponentName className) {        Log.e(TAG, "onServiceDisconnected");        mBound = false;    }};
有了ServiceConnetion,客户端可以通过传递以这个对象给bindService方法来绑定一个service。例如:

Intent intent = new Intent(this, LocalService.class);bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- bindService的第一个参数是一个Intent,显式地说明了要绑定的service的名字。(尽管intent也可以是隐式)

- 第二个参数是ServiceConnection的对象。

- 第三个参数是一个标志位,它指示了绑定的选项。一般为BIND_AUTO_CREATE,用来创建一个service,如果这个service还没有创建。

- 其他的可能值为BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或者是0表示什么也不做。


附加提示

下面是一些关于绑定服务的重点:

- 你应该一直注意捕捉DeadObjectException的异常,当connection断开的时候就可能捕获。这是远程方法唯一可能抛出的异常。

- 对象是被认为是跨进程的引用。

- bind和unbind应该成双成对,当你在客户端的声明周期里面建立匹配和断开匹配的时候。举个例子:

 * 如果你只在你的activity可见的时候和service通信,你应该在onStart方法中bind,在onStop中unbind。

* 如果你想要你的activity在他stop在后台的时候,仍然接收回应,那么你可以在onCreate方法中bind,在onDestroy方法中unbind。你要意识到,这意味着你的activity需要在他运行的时候一直都使用service,包括在background的时候,所以如果service在其他的进程中,那么你增大了这个进程的负担,它很有可能被系统回收掉。


提示:请不要在onResume和onPause中来bind和unbind,因为这些调用在每一个生命周期转换的时候都会发生,而你应该把发生在转换过程中发生的进程数量控制到最小。并且如果你的应用中的多个activity都bind了同一个service,并且其中的两个activity之间发生跳转,那么当当前的(在pause的时候)activity解绑和下一个绑定(在resume时候)之间,服务将会被销毁并且重新创建。(Activity之间的转化过程如何协调,可以参见Activity文档)。


更多关于如何bind service的示例代码,请查看ApiDemos里面的RemoteService。


管理一个绑定的Service的生命周期

当一个service从所有的客户端解绑的时候,Android系统会销毁它( 除非他使用了onStartCommand启动的)。这样的话,你不需要管理你的service的生命周期,如果它只是单纯的一个绑定的service,android系统会帮你管理它,基于是够有客户端跟他绑定。

但是,如果你选择构建一个onStartCommand的回调方法,那么你必须显式地停止你的service,因为这个service现在被当做started(已启动)。在这种情况下,这个service会一直运行,除非他自己停止,比如说用stopSelf方法,或者另一个组件调用了stopService方法,无论其是否和客户端绑定。


除此之外,如果你的service启动了并且接受了bind,当系统调用了你的onUnbind方法时候,你可以选择返回true,如果你想在下次有客户端绑定service的时候被调用onRebind方法。onRebind返回一个空,但是客户单仍然在他的onSericeConnected回调方法中接收到IBinder。下面的图1表示了这种生命周期的逻辑。


关于service生命周期的更多信息,请看Service文档。


  相关解决方案