2.绑定型服务,同生共死(bindService

绑定型服务必须依附于应用的生命周期,一般是在最后退出的ActivityonDestroy方法中解绑。

   

看一个仅绑定的示例。

1)自定义服务

注意此时服务的生命周期发生变化了:

/**

 * 附属的,绑定型服务

 * @author cuiweiyou.com

 */

public class BindService extends Service {

    

    @Override

    public void onCreate() {

        Log.e("ard""服务create");

        super.onCreate();

    }

    

    /** 执行绑定的方法

     * Activity就靠这个方法和Service联络了 

     */

    @Override

    public IBinder onBind(Intent intent) {

        String data = intent.getStringExtra("data");

        Log.e("ard""服务绑定,启动," + data);

        

        NorthBinder binder = new NorthBinder();

        

        return binder;

    }

    

    /**

     * 再次绑定 

     */

    @Override

    public void onRebind(Intent intent) {

        super.onRebind(intent);

        String data = intent.getStringExtra("data");

        Log.e("ard""再次绑定服务," + data);

    }

    

    /**

     * 解除绑定

     */

    @Override

    public boolean onUnbind(Intent intent) {

          

        String data = intent.getStringExtra("data");

        Log.e("ard""解除绑定," + data);

        

        //return true; // 在仅bindService方式,返回值无所谓

        return super.onUnbind(intent); // false。结合startService时须区别

    }

    

    @Override

    public void onDestroy() {

        super.onDestroy();

        Log.e("ard""服务销毁");

    }

    

    /**

     * 绑定器,由Service通过onBind方法return给Activity

     * Binder完全实现了IBinder接口,没有必须重写的方法

     * 根据须要自定义功能即可

     * @author cuiweiyou.com

     */

    public class NorthBinder extends Binder {

        public void doNothing(){

            Log.e("ard""Binder被调用了");

        }

    }

}

 

这里面新加入了一个元素,自定义Binder对象。它就是ServiceActivity通讯的载体,如同Activity和子线程通讯时的Handler。只不过此情此景显得很有光芒。在这个光芒君里可以定义各种需要的功能,并在onBind方法中将光芒君对象返回即可。Activity会迎接到这个大号人物。

  

2)注册服务

无论是独立服务还是附属服务都须要在AndroidManifest.xml里注册。

android:name  -------------  服务类名

android:label  --------------  服务的自定义标签,如果此项不设置,那么默认显示的服务名则为类名

android:icon  --------------  服务的图标

android:permission  -------  申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务。android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。如果是android:process="remote",没有“:”冒号的,则创建全局进程,不同的应用程序共享该进程。

android:process  ----------  表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字

android:enabled  ----------  如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false

android:exported  ---------  表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false

  

3)绑定服务

此时对服务进行绑定的Activity须要实现一个ServiceConnection接口,继而实现2个功能,连接服务、断开服务。当然启动服务的方法也不一样了。

/**

 * 和Service进行通信的Activity

 * 实现ServiceConnection接口

 */

public class MainActivity extends Activity implements ServiceConnection {

     

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        

        findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                Intent i = new Intent(MainActivity.this, BindService.class);

                i.putExtra("data""顺便捎带点什么");

                

                // 绑定服务,此例唯一启动服务的方法(服务意图,回调对象,启动模式)

                bindService(i, MainActivity.this, Context.BIND_AUTO_CREATE);

            }

        });

    }

    

    @Override

    protected void onDestroy() {

        super.onDestroy();

        

        // 此时应用销毁

        unbindService(MainActivity.this); // 服务先解绑,再销毁

        

        // 绑定的服务必须在应用退出时解绑

        // 否则:ServiceConnectionLeaked: Activity has leaked ServiceConnection that was originally bound here

        // 且不可重复解绑

    }

    

    /**

     * ServiceConnection接口定义的与Service通讯连接成功的回调

     * @Parameters name:Service的名称,bindService绑定的自定义服务

     * @Parameters service:Service的onBind方法return来的Binder对象

     */

    @Override

    public void onServiceConnected(ComponentName name, IBinder service) {

        BindService.NorthBinder binder = (NorthBinder) service;

        Log.e("ard""服务连接了," + name.getClassName());

        

        binder.doNothing(); // 服务中的绑定器的功能

    }

    

    /**

     * 与Service连接断开的回调

     * 正常情况下是不被调用的,它的调用时机是当Service服务被异外销毁时。如内存不足

     * @Parameters name:服务名称

     */

    @Override

    public void onServiceDisconnected(ComponentName name) {

        Log.e("ard""意外连接断开 ");

    }

}

  

其中,绑定服务的启动模式有以下几种:

 

看一下此时启动服务,而后又退出应用的日志:

 

4startServicebindService结合使用

Service的生命周期里还有个特殊的onRebind方法,出现的情况也比较特殊。首先由startService创建启动了服务,而后bindService进行了绑定;而后解绑服务又再次绑定服务的时候执行,从而跳过onBind方法。

  

这个情况必须有onUnbind方法的配合:

/**

 * 解除绑定

 */

@Override

public boolean onUnbind(Intent intent) {

    Log.e("ard""解除绑定");

    

    return true// 再次绑定服务时执行onRebind方法

    //return super.onUnbind(intent); // false。结合startService时须区别

}

onUnbind方法必须返回true

  

首先用startService方法创建启动服务,然后用bindService方法绑定服务,退出应用,解绑服务,再次运行应用,再次绑定服务。看一下日志,“再次绑定服务”由onRebind方法打印。

  

如果仅须启动一个后台服务长期进行某项任务(播放器)那么使用 startService 便可以了。

如果服务只是公开一个远程接口供客服端远程调用执行方法。可以只用bindService,在第一次bindService的时候才会创建服务的实例运行它,节约资源,尤其是Remote Service

如果须要与正在运行的Service取得联系,那么有两种方法:一种是使用Broadcast;另外就是使用bindService绑定服务。广播的缺点:若交流较为频繁,容易造成性能上的问题,并且BroadcastReceiveronReceive中代码的执行时间不能超过5s(也许执行到一半,后面的代码便不会执行)。服务则没有这些问题。

Activity中更新Service的某些运行状态,需同时使用 startService 和 bindService

 

下图是2种服务的生命周期演示:

 

试题

1startServicebindService区别

首先按照服务的使用场景可分2种:

本地服务(Local Service):

用于实现应用程序自己的一些耗时任务,比如查询升级信息、播放器,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。startService()启动,stopService()结束。在服务自身内部可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。无论调用了多少次startService(),都只需调用一次stopService()来停止。

远程服务(Remote Sercie):

用于系统内应用程序之间访问。如天气预报服务、内容提供者,其他应用程序不需要再写这样的服务,调用已有的即可。一个应用可以作为服务端定义接口并把接口暴露出来,以便其他应用作为客户端进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服务。bindService()方法建立连接启动,unbindService()关闭连接。多个客户端可以绑定至同一个服务。

 

startService创建后持续后台运行,不依附于应用的生命;应用退出时无须stop服务,多用于本地服务。

bindService创建后在后台运行,但依附于应用的生命;应用退出时必须解绑,否则会抛出异常,常用于远程服务。

  

2ServiceThread的区别

ThreadThread 是程序执行的最小单元,它是分配CPU资源的基本单位。可以用Thread来执行一些异步的操作。

ServiceServiceandroid的一种机制,当它运行的时候如果是Local Service,那么对应的Service是运行在主进程的main线程上的。如:onCreateonStart这些函数在被系统调用的时候都是在主进程的main线程上运行的。如果是Remote Service,那么对应的Service则是运行在独立进程的main线程上。

  

Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。当 Activity 被 finish 之后,你不再持有该 Thread 的引用,因此无法手动结束线程。另一方面,无法在不同的Activity中对同一Thread进行控制。

因此在Service里面创建、运行并控制Thread,这样便解决了2问题任何Activity都可以控制同一Service,而系统也只会创建一个对应Service的实例。

  

可以把Service理解为一种消息服务,而你可以在任何有Context的地方调用Context.startServiceContext.stopServiceContext.bindServiceContext.unbindService,来控制它可以在Service里注册BroadcastReceiver,在其他地方通过发送 broadcast来控制它

    

3判断服务是否正在运行

Android系统提供了一个函数ActivityManager.getRunningServices,可以列出当前正在运行的后台服务线程

private boolean isServiceRunning() {

    ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);

    for (android.app.ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {

        if (service.service.getClassName().equals("目标服务类名")) {

            return true;

        }

    }

    return false;

}

  

4ServiceUI之间的通信方式

第一种方法就不演示了,后面4种见下一节。

启动服务时通过Intent捎带数据

流程:UI —> Service

操作:使用Intent进行数据传递,通过服务中的onStartCommandonBind方法进行接受Activity间传递方式一样 

  

Handler+Message

  

使用Broadcast(广播)进行信息的双向传递

流程:UI <——> Service

操作:在服务里注册广播收音机Activity发广播。通过广播来进行2者间通信

注意:在服务退出的时候记得解除广播。

  

AIDL