Android服务Service(二)

来源:互联网 发布:花溪区4g网络基站建设 编辑:程序博客网 时间:2024/05/21 10:24

1)前言

本文是接着上篇没有介绍完的Android服务Service写的,主要是想对Bound式服务做一个总结,从学Android到现在,我很少用到Bound式服务,倒是Started式服务用得不少(可能太菜),借着这个机会,结合官方文档案例对Bound式服务做一个全面的了解。

2)基本用法

一个绑定服务,就是一个实现了类Service,并允许其它应用程序与其绑定及与之交互的的组件。提供一个绑定服务,你必须实现onBind()方法,这个方法返回一个IBinder对象,这个对象就是定义用来客户端与服务交互的接口。

客户端通过bindService()方法绑定服务。另外,客户端还必须提供一个ServiceConnection的实现对象,用以监控与服务Service间的连接情况。bindService()无返回值,但系统在客户端与服务端连接之间,会调用在ServiceConnect对象中的onServiceConnected()方法,并传递一个客户端与服务端可以交互的IBinder接口。

多个客户端同时连接Service时,系统只在第一个客户端连接时调用你的onBind()方法并返回一个IBinder对象,系统在其他客户端进行连接时返回相同的IBinder对象,而不再去调用onBind().

当最后一个客户端调用与服务解除绑定时,系统将销毁服务,除非这个服务启动时,也调用了startService()方法.

创建绑定服务,必须提供IBinder接口,有三种方式定义这个接口:

  • 1) 实现Binder类

如果这个service只是为当前应用服务,并且运行在相同的进程里,创建的接口必须继承Binder类,并且在onBind()方法中返回。客户可以通过它得到Binder对象,通过这个对象可以直接访问共有函数。

当service仅仅为自己的应用服务,这是首选技术。

  • 2) 使用Messenger

如果需要接口在不同的进程间使用,可以用Messenger 为service类创建接口。用这种方式, service对处理不同的Message 对象定义了一个Handler. 这个Handler是Messenger的基础,可以与客户分享IBinder,允许客户用message发送命令到service上。补充点,客户端可以自定义Messenger,因此service可以发送消息回来。

这是在进程间通信最简单的方式。因为Messenger队列所有的请求发生在一个线程上,因此不需要考虑service的线程安全。

  • 3) 使用AIDL

AIDL (Android Interface Definition Language) 可以把对象分解成系统能够识别的单元,已达到在进程间通信的能力。在前面的技术中,使用的messenger,实际上是以AIDL作为基础的结构。如上所述,Messenger在一个线程中创建了所有客户请求的一个队列。如果你想service能够同时处理多个请求,那么就可以直接用AIDL. 在这种案例中,service 必须要有处理多线程的能力,并且是线程安全的。

要想直接使用AIDL,必须创建定义程序的接口文件,后缀是.aidl。Android SDK工具可以通过这个文件生成一个实现了这个接口和处理IPC的抽象类,自定义的service需要继承自这个抽象类。

这种方式暂且不讨论

下面分别看下具体三种使用方法

3)实现Binder类

1、 在你的服务类中,创建一个满足下面条件的Binder实现类:
包含一个public方法,以便客户端可以调用
在一个public方法中返回当前服务实例给客户端使用

2、 在onBind()方法中,返回Binder对象实例
3、 在客户端,从onServiceConnected()回调方法接收Binder对象,并调用绑定服务中提供的方法。

下面来看下代码

//服务端public class BoundTest extends Service {    private static final String TAG = "chen";    private IBinder iBinder = new LocalBinder();    private Random mGenerator = new Random();    public class LocalBinder extends Binder{        public BoundTest getService(){            return BoundTest.this;        }    }    public int getRandomNumber() {        return mGenerator.nextInt(100);    }    @Override    public void onCreate() {        super.onCreate();        Log.i(TAG, "onCreate");    }    @Override    public IBinder onBind(Intent intent) {        Log.i(TAG, "onBind");        return iBinder;    }    @Override    public boolean onUnbind(Intent intent) {        Log.i(TAG, "onUnbind");        return super.onUnbind(intent);    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i(TAG, "onDestory");    }}

我们可以看见在服务端LocalBinder是一个内部类,其继承了Binder,里面有个公共方法,返回该服务的实例。并且有一个getRandomNumber()测试方法,供客户端调用。

//客户端public class MainActivity extends AppCompatActivity implements View.OnClickListener{    private Button btn_bound;    private Button btn_unbound;    private Button btn_getrandom;    private BoundTest mService;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btn_bound = (Button) findViewById(R.id.btn_bound);        btn_unbound = (Button) findViewById(R.id.btn_unbound);        btn_getrandom = (Button) findViewById(R.id.btn_getrandom);        btn_bound.setOnClickListener(this);        btn_unbound.setOnClickListener(this);        btn_getrandom.setOnClickListener(this);    }    @Override    public void onClick(View view) {        Intent intent = null;        switch (view.getId()){            case R.id.btn_bound:                intent = new Intent(MainActivity.this, BoundTest.class);                //绑定服务                bindService(intent, mConnection, Context.BIND_AUTO_CREATE);                break;            case R.id.btn_unbound:                intent = new Intent(MainActivity.this, BoundTest.class);                //解绑服务                unbindService(mConnection);                break;            case R.id.btn_getrandom:                int number = mService.getRandomNumber();                btn_getrandom.setText("获取随机数"+number);                break;        }    }    private ServiceConnection mConnection = new ServiceConnection() {        //绑定成功回调        @Override        public void onServiceConnected(ComponentName className,                                       IBinder service) {            BoundTest.LocalBinder binder = (BoundTest.LocalBinder) service;            mService = binder.getService();   //获取Binder对外提供的接口        }        //绑定失败回调        @Override        public void onServiceDisconnected(ComponentName arg0) {        }    };}//布局:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="com.chen.demo.MainActivity">    <Button        android:id="@+id/btn_bound"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="绑定"        />    <Button        android:layout_marginTop="20dp"        android:id="@+id/btn_unbound"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="解绑"        />    <Button        android:layout_marginTop="20dp"        android:id="@+id/btn_getrandom"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="获取随机数"        /></LinearLayout>

我们在客户端通过bindService()绑定服务,通过unbindService()解绑服务,其中bindService()需要三个参数意思分别是:

第一个变量是通过Intent指定要绑定的service.
第二个参数是 ServiceConnection 对象.
第三个参数是设置绑定的选项。通常是BIND_AUTO_CREATE,当service不在运行时,会自动创建。其他值有BIND_DEBUG_UNBIND 和BIND_NOT_FOREGROUND, 或者是0(没有任何意义).

而unbindService(),需要一个参数ServiceConnection

我们看下运行结果:

这里写图片描述

结果是最后一个Button的Text后面的数字

4)使用Messenger

如果service需要与远程的进程通讯,可以用Messenger为service提供一个接口。这种技术可以处理进程间的通讯。

下面是使用messenger的简介:

1)service 要实现Handler,它可以收到每一个client调用的回调。
2)Handler用于创建Messenger对象。 (which is a reference to the Handler).
3)Messenger创建一个IBinder,client调用onBind()时,IBinder要返回给client的
4)Clients 通过IBinder实例化Messenger(that references the service’s Handler),Messenger为了client发送Message对象到service。
5)service 通过Handler收到每一个Message, 在 handleMessage() 方法明确的处理.

利用这种方式,service中没有方法可一个被Client调用。client通过传递消息(messages)到service的Handler中。

我们简单看下服务端代码

//服务端public class MessengerService extends Service {    private static final String TAG = "chen";    public static final int MSG_SAY_HELLO = 1;    public 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 void onCreate() {        super.onCreate();        Log.i(TAG, "onCreate");    }    @Override    public IBinder onBind(Intent intent) {        Log.i(TAG, "onBind");        return mMessenger.getBinder();    }    @Override    public boolean onUnbind(Intent intent) {        Log.i(TAG, "onUnbind");        return super.onUnbind(intent);    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i(TAG, "onDestory");    }}

注意:Handler 的handleMessage()方法收到Message,并且根据Message的what变量 确定需要做什么。

Client的需要实现的是,基于返回的IBinder创建Messenger对象,并且通过send()发送Message给service的Handler。

//客户端public class MessengerActivity extends AppCompatActivity implements View.OnClickListener{    private Button btn_bound;    private Button btn_unbound;    private Button btn_getrandom;    Messenger mService = null;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_messenger);        btn_bound = (Button) findViewById(R.id.btn_bound);        btn_unbound = (Button) findViewById(R.id.btn_unbound);        btn_getrandom = (Button) findViewById(R.id.btn_getrandom);        btn_bound.setOnClickListener(this);        btn_unbound.setOnClickListener(this);        btn_getrandom.setOnClickListener(this);    }    @Override    public void onClick(View view) {        Intent intent = null;        switch (view.getId()){            case R.id.btn_bound:                intent = new Intent(MessengerActivity.this, MessengerService.class);                //绑定服务                bindService(intent, mConnection, Context.BIND_AUTO_CREATE);                break;            case R.id.btn_unbound:                //解绑服务                unbindService(mConnection);                break;            case R.id.btn_getrandom:                Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);                try {                    mService.send(msg);                } catch (RemoteException e) {                    e.printStackTrace();                }                break;        }    }    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(mConnection);    }    private ServiceConnection mConnection = new ServiceConnection() {        //绑定成功回调        @Override        public void onServiceConnected(ComponentName className,                                       IBinder service) {            mService = new Messenger(service);        }        //绑定失败回调        @Override        public void onServiceDisconnected(ComponentName arg0) {        }    };}

注意: 这个例子没有体现service是如何响应client。如果想service响应client,需要在client里面创建一个Messenger。当client收到onServiceConnected()回调,就会发送Message到service,send()方法的Message变量replyTo包括客户端的Messenger,从而实现双向通讯。

看下运行结果:

这里写图片描述

这个Toast是服务端打印的结果。

5)生命周期管理

当service没有被任何的client绑定,android系统会杀死它(除非中途被其他的组件调用了onStartCommand())。按照这种原理, 绑定service是不需要管理它的生命周期的-android系统会根据绑定原则自动管理service。

然而,如果选择onStartCommand()方法实现,就必须明确的停止service,因为服务当前状态被认为是started。在这种案例下,service一直运行直到调用stopSelf()或者其他的组件调用stopService()方法,不管他绑定任何clients.这里的意思是说:当一个绑定service通过onStartCommand()启动,需要stopSelf()和stopService()方法来停止它,即使是中间绑定其他的客户端。

补充点,如果service已经启动并且接受了client的绑定,那么当系统调用onUnbind()方法时,如果想client下次绑定service的时候调用onRebind(),可以选择返回true,(而不是重新调用onBind()). onRebind() 返回 void, 但是client在onServiceConnected()还是接受IBinder. 下面的图阐明了上面的逻辑。

这里写图片描述

6)最后

服务基本内容也就这么多,还有一个AIDL没有介绍,下次准备结合Android Studio详细介绍下

7) Ref

1 )Android 官方文档
2 )http://blog.163.com/cazwxy_12/blog/static/898763720122106483898/
3 )http://blog.csdn.net/luhuajcdd/article/details/8756541

0 0
原创粉丝点击