Android四大组件之Service

不羁岁月 提交于 2020-01-24 05:06:30

在 Android 四大组件之中,除了 Activity 之外,最常用的就是 Service 了。先来看一下官方对 Service 的介绍 :

    Service是一个可以在后台执行需要长时间运行的操作的应用组件,它不提供用户界面。其它组件可以启动一个Service ,之后即使用户切换到别的应用里,这个Service 也将继续在后台运行。此外,一个组件可以与一个Service绑定来与之交互,甚至可以进行进程间通信。服务可以在后台执行很多任务,比如处理网络事务,播放音乐,文件读写或者与一个内容提供者交互等等。
 
由此可见,Service 的用途还是十分广泛的,我们在开发中经常会用到 Service,所以应该对 Service 有一定的了解。
 
      Service 有一个非常需要注意的地方就是它其实是运行在主线程中的,如果是刚了解 Service 的人,很容易会误以为 Service 是另开一个新线程运行的。所以我们一定要注意,不要在 Service 中执行一些耗时操作,从而导致线程阻塞。
 
      想要了解Service,那么就要先了解Service的生命周期,幸运的是,Service的生命周期比起Activity要简单的多。如下 :
 

 

上图展示了 Service 在两种形式下的生命周期。下面说明 Service 的两种形式 :

未绑定形式 Service:

    该形式的 Service 是通过 startService() 方法来启动的,该方法需要传入一个 intent 用于通知要启动哪一个 Service。处于未绑定形式下的 Service 在启动后会调用 onCreate() 方法和 onStartCommand() 方法,然后会去执行自己的任务。处于该状态下的 Service 不会和 Activity 之间有太多关联,我们也无法再 Actvity 中去调用 Service 中的方法。这种 Service 一般用于处理不需要返回数据给调用者的操作,如从网络中下载图片到本地等。

    这种形式下的 Service 只会在刚启动时调用一次 onCreate() 方法,之后只要 Service 未停止,无论再多次调用 startService() ,也都不会再调用 onCreate() 方法。也就是说相同的 Service 只会运行一个,不会同时运行两个同样的 Service 。不过虽然 Service 不会再被创建,即 onCreate() 不会再被调用,但每次 startService() ,它的 onStartCommand() 都会被调用。如果想要停止 Service ,则调用 stopService()或stopSelf(),同样传入一个 intent 即可。stopService() 之后 Service 会在后台保持停止状态,然后很快就会被系统回收销毁。

 

未绑定形式 Service 的使用

(1)创建一个子类继承 Service 类,并实现 onBind() 方法(非绑定的 Service 用不到该方法,返回 null 即可)。我们可以在 onCreate() 方法或 onStartCommand() 方法中执行各种操作。再次强调,如果是耗时操作,则放到子线程中执行。

public class TestService extends Service{

    //该方法只会在Service第一次启动时调用
    @Override
    public void onCreate() {
        super.onCreate();
    }

    //该方法在Service每次启动后都会被调用
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 只要继承Service必定要实现该方法
     * 如果是未绑定形式的Service,则不需要使用到该方法,返回null即可
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    //Service终止时调用该方法
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

 

(2)不要忘了注册Service

    <!--在AndroidManifest.xml中注册Service-->
    <service android:name=".service.TestService"/>

 

(3)启动或停止 Service :

    //启动Service
    private void start() {
        Intent intent = new Intent(this, TestService.class);
        startService(intent);
    }
    //停止Service
    private void stop(){
        Intent intent = new Intent(this, TestService.class);
        stopService(intent);
    }

 

绑定形式 Service:

    绑定形式的 Service 的生命周期在上面的图片中也见到了。它在 Service 第一次绑定并启动后会调用 onCreate() 方法和 onBind() 方法,绑定的 Service 不会调用onStartCommand() 方法。之后只要该 Service 未结束,无论几次调用 BindService ,都不会再调用 onCreate() 方法和 onBind() 方法。绑定的 Service 可与多个Activity进行绑定,但是当所有的Activity都与Service 解绑之后,该 Service 就会被销毁。

 

绑定形式的 Service 使用起来稍微复杂些,在要绑定的 Service 和 Activity 中都需要先进行一些准备操作。

首先 Service 方面 :

(1)先在 Service 中创建一个内部类继承 Binder 类,里面可以写一些方法,用于对 Service 进行操作。这里假设要创建一个后台播放音乐的 Service ,那么 Binder 就可以这么写:

   /**
     * 在Service内部创建一个Binder
     */
    public static class TestBinder extends Binder{
        //播放音乐
        public void playMusic(){
             //这里可以调用MediaPlay,播放音乐
        }
        //停止播放音乐
        public void stopMusic(){
             //这里写停止播放音乐的逻辑
        }
    }

 

(2)创建好自己的 Binder 类后,我们还需要创建一个实例化 Binder 对象,并且在 onBinder() 方法中将该对象返回。返回的 Binder 对象会被传递到 Actvity 中。

public class TestService extends Service{

    //该方法只会在Service第一次启动时调用
    @Override
    public void onCreate() {
        super.onCreate();
    }

    //创建Binder对象
    TestBinder binder = new TestBinder();

    /**
     * 如果是绑定形式的Service,则需要返回一个Binder对象
     * 在Activity的ServiceConnection类中可以获取到该对象
     */
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    //Service终止时调用该方法
    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    /**
     * 在Service内部创建一个Binder
     */
    public static class TestBinder extends Binder{
        //播放音乐
        public void playMusic(){

        }
        //停止播放音乐
        public void stopMusic(){

        }
    }
}

 

 (3)最后不要忘了注册 Service

    <!--在AndroidManifest.xml中注册Service-->
    <service android:name=".service.TestService"/>

这样 Service 方面的准备操作就算是完成了。

 

Actvity 方面:

(1)在需要绑定的 Actvity 中,我们要创建 ServiceConncetion 类,并实现它内部的两个方法

   private ServiceConnection connection = new ServiceConnection(){
        //当Service被绑定后会调用该方法
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //获取到Service传来的Binder对象
            mBinder = (TestService.TestBinder) iBinder;
            //有了Binder对象,可以调用它的方法
            mBinder.playMusic();
            mBinder.stopMusic();
        }

        //Service解绑后会调用该方法
        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

      onServiceConected() 方法和 onServiceDisconnceted() 方法分别会在 Service 被绑定时和解绑时调用。在 onServiceConnected() 方法中可以得到 Service 中传来的 Binder 对象。我们有了 Binder 对象,就可以调用它的方法,对 Service 进行操作。

 

(2)有了上面的 ServiceConnection 对象,我们接下来就只需要将 Activity 绑定 Service 即可。通过 bindService() 可以与 Service 进行绑定,如下:

 //绑定Service
    private void bind() {
        Intent intent = new Intent(this, TestService.class);
        //该方法第一个参数传入一个intent,第二个参数传入ServiceConnection对象
        // 第三个参数是表示Service的绑定模式,BIND_AUTO_CREATE代表Service在绑定后就会自动启动
        bindService(intent,connection,BIND_AUTO_CREATE);
    }

      要注意的是,如果绑定了 Service 就要记得在 Actvivity 被销毁时调用 unBindService() 解绑 Service ,以免造成内存泄漏。在 Activity 的 onDestory() 中 :

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //一般我们会在Activity销毁时解绑服务,以免造成Activity不能释放内存
        unbindService(connection);
    }

 

      当然还有一种情况需要我们注意。如果我们启动 Service 时既使用了 startServcie() 方法,又使用了 bindService() 方法,那么该 Service 会同时处于两种状态,也就是说如果我们想要停止这个 Service ,必须即调用 unBindService() ,又要调用 stopService() 才行。

      之前也说过了 Service 其实是运行在主线程中的。如果我们想要进行耗时操作,最好创建新的线程。这其实很容易让人产生疑惑。如果 Service 也要创建子线程才可以运行耗时的操作,那么它相比 Activity 的优势在哪里呢?因为我们如过使用 Activity 创建子线程,由于 Activity 会被用户频繁的操作,当 Activity 被销毁时,就无法再拿到该线程的实例了,也就无法再对子线程进行操作,而且在一个 Activity 中创建的线程,其他的 Activity 也无法对其进行操作。但是如果使用 Service ,由于其进程优先级较高,所以可以长期在后台运行,Activity 可以在创建时通过 bindService()方法绑定 Service ,然后就可以调用Service中方法来控制线程。多个 Activity 也可以绑定同一个 Service,这样就可以多个Activity对同一线程进行操作。

      

      除了我们自己在 Service 中开辟线程外,还有其他的解决方法。比如使用官方的 IntentService ,又或是给 Service 另外开辟一个进程,然后再通过 IPC 机制进行通信。

下面来介绍一下 IntentService : 

IntentService 和普通的 Service 的区别是,你不需要亲自去创建一个子线程去执行异步操作,它的内部会为你创建一个子线程,且在任务结束后,你不需要手段的去调用 stopService() 方法去结束服务,它会自动终止。

不过要想真正了解 IntentService ,那么首先得了解 HandlerThread ,因为它的内部就是封装了 HandlerThread 。

 

HandlerThread浅析:

HandlerThread 继承自 Thread,它和普通的 Thread 区别在于其 run() 方法中通过 Looper.prepare() 创建了一个消息队列,并且通过 Looper.loop() 开启了一个消息循环。

HandlerThread 的 run() 方法实现如下 :

  @Override
    public void run() {
        mTid = Process.myTid();
        //创建Looper,并创建消息队列
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        //开启消息循环
        Looper.loop();
        mTid = -1;
    }

      如果你对 Handler 消息处理机不清楚,建议先去弄懂它。其实 HandlerThread 内部就是使用 Handler 消息通信机制,因为 HandlerThread 内部拥有了 Looper,所以我们可以在该线程中创建 Handler 对象,并通过 Handler 在不同线程中发送和处理消息。HandlerThread比一般线程的好处是,它的性能较高,比如我们需要执行耗时操作时,创建了一个线程来执行耗时操作,当执行完后,线程就被销毁。如果频繁的需要进行耗时操作,这时创建和销毁线程的话,就会带来极大的性能损耗,这时我们就可以用HandlerThread来处理这种情况。

      HandlerThread和线程池的区别是,HandlerThread是串行的处理耗时操作,只有一个线程存在;而线程池是并发处理耗时操作,是多个线程同时处理耗时任务。

 

简单分析了 HandlerThread 后,再来看看 IntentServcie 。

IntentService 的使用 :

      IntentService 和 Service 一样,是一个抽象类,我们要先创建一个子类继承它 :

public class TestService extends IntentService{

    public TestService(String name) {
        super(name);
    }

    //该方法为抽象方法,子类必须实现
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {

    }

}

      如上,我们必须实现 onHandlerIntent() 方法。onHandlerIntent() 方法是 IntentService 的核心方法,由于该方法是运行在子线程中的,故我们可以在该方法中直接处理耗时操作,而不用担心发现 ANR 。

 

      在 IntentService 的 onCreate() 方法中使用到了 HandlerThread 和 Handler ,如下:

    @Override
    public void onCreate() {
        super.onCreate();
        
        //创建HnadlerThread,并启动它
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        //获得HandlerThread线程中的Looper对象
        mServiceLooper = thread.getLooper();
        //使用该Looper对象,创建一个Handler,故该Handler是属于HandlerThread线程的
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

 

      上面代码中的 ServiceHandler 其实就是一个 Handler 。了解 Handler 消息机制的话就会知道,我们在创建 Handler 时如果传入 Looper ,如果 Looper 是属于线程 A 的话,那么创建出来的 Handler 也属于线程 A,也就是说Handler 和 Looper 属于同一线程 。如果我们使用该 Handler 发送消息的话,那么该消息会被送到 Handler 所属的线程中并被处理。所以只要是通过上面的 mServiceHandler 所发送的消息,最后都会在 HandlerThread 中执行。如下 :

  private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

发送的消息(即Intent)会被放入消息队列中,然后再被逐一取出,传送到 handlerMessage() 方法中,而该方法中会调用 onHandleIntent() 方法,就是需要我们重写的那个方法。由此我们知道了 onHandlerIntent()方法确实不是运行在主线程中,而是运行在 HandlerThread 线程中的。

IntentService 的 onStart() 方法中使用了 ServiceHandler 发送一个消息,它的 onStartCommand() 方法中调用了 onStart() 方法,所以每次调用该方法同意会发送消息到 HandlerThread() 中。

因此我们要注意的是,使用 IntentService 时最好使用 startServcie() 方式来启动它,不要使用 bindService() 启动方式。这是因为 IntentServcie 默认 onBind() 方法返回的是 null,如果我们用绑定的方式来使用它,需要重写 onBind() 方法,最关键是它的 onStartCommand() 方法将不再被调用,也就是说 onHandlerIntent() 方法将失去作用,那么就和使用平常的绑定 Service 没有区别了。

 

    

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!