Android学习笔记2 四大组件之Service

四大组件中,我们最熟悉的而且最常用的应该是Activity了,它的主要作用是提供用户交互的界面,那么如果有一些任务只需要在后台执行而不需要界面,那应该怎么办呢。今天总结归纳的是Service。

目录
一、Service概述
二、Service的基本用法
   1.启动Service
   2.绑定Service
三、Service生命周期
四、Service的几点说明
五、IntentService使用
六、相关参考

一、概述

首先我们看下官方对Service的介绍:

Service是一个可以在后台执行需要长时间运行的操作的应用组件,它没有用户界面。其它组件可以启动一个Service,之后即使用户切换到别的应用里,这个Service也将继续在后台运行。此外,一个组件可以与一个Service绑定来与之交互,甚至可以执行IPC进程间通信。服务可以在后台执行很多任务,比如处理网络事务,播放音乐,文件读写或者与一个内容提供者交互,等等。

Service主要有两种形式:启动状态和绑定状态。

启动状态(Started)

当一个应用组件比如activity通过调用startService()来启动一个服务的时候,服务便处于启动状态。一旦启动,服务可以在后台无限期地运行下去,即使当启动它的组件已经销毁。通常情况下,一个启动的service执行一个单一的操作并且不会返回任何结果给调用者。例如,服务可能通过网络下载或者上传一个文件,当操作完成,服务会自己停止。

绑定状态(Bound)

当一个应用组件通过调用bindService()来与一个服务绑定时,服务便处于绑定状态。一个绑定的服务提供了一个客户端-服务器端接口来允许组件与服务进行交互,发送请求,得到结果甚至通过IPC进程间通信来完成操作。只有当其它组件与服务绑定时,服务才会处于绑定状态。多个组件可以同时与服务绑定,但是当他们全部都解除绑定时,服务就会销毁。

二、Service的基本用法

对于Activity的使用,我们应该都比较熟悉了,一般是先新建一个继承Activity的自己的Activity,然后在里面写自己的逻辑,如果要启动一个Activity的话可以通过调用StartActivity()传入一个Intent对象来完成,在清单文件里进行Activity注册也是必不可少的一步。与之类似,Service基本用法也很简单,下面简单介绍启动Service的步骤,之后会有详细的介绍。

启动Service

  1. 新建Service
public class TestService extends Service {

    private final String TAG = "TestService";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "---onCreate()---");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "---onStartCommand()---");
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "---onDestroy()---");
    }
}
  1. 注册Service


    清单文件里注册Service.png

3.启动Service

点击按钮后启动Service

从上面这张图可以看到,新建一个Intent对象并把它传给startService,这样就可以启动一个Service了,启动Service后如果想要停止它,可以使用stopService方法。

停止Service.png

绑定Service

下面是组件与Service绑定的步骤。

  1. 首先我们修改一些之前的Service.
public class TestService extends Service {

    private final String TAG = "TestService";

    //实例化"粘合剂" 可返回给与Service绑定的组件
    private MyBinder mMyBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "---onCreate()---");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "---onStartCommand()---");
        return super.onStartCommand(intent, flags, startId);
    }

    //绑定后调用此方法
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind");
        return mMyBinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "---onDestroy()---");
    }

    //这里执行任务
    public class MyBinder extends Binder {
        public void startTask() {
            Log.i(TAG, "Task starts...");
        }
    }
}

2.假设我们要在一个Activity里通过点击一个按钮来绑定Service,那么我们要先在Activity里新建一个ServiceConnection的实例。下面代码是在Activity里抽取出来的。

    private TestService.MyBinder mMyBinder;

    /**
     * 服务连接
     */
    private ServiceConnection mServiceConnection = new ServiceConnection() {

        /**
         * 当服务连接后 返回binder
         * @param name
         * @param binder
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            mMyBinder = (TestService.MyBinder) binder;
            mMyBinder.startTask();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

    };

3.这样我们在Activity里点击一个按钮来绑定Service的时候,可以调用BindService来完成,详细如下图。

绑定Service

启动后的Service可以用stopService来停止服务,绑定后的Service可以用unbindService来取消绑定。

取消绑定

Service用法小结

  1. 上面的基本用法其实展示了Service最基本的用法,包括启动Service,停止Service,绑定Service以及取消绑定Service,Service里的逻辑很简单,实际上在平时的开发过程中,在后台执行时应该会比较复杂,考虑的东西也比较多。

  2. 启动Service的逻辑其实比较简单,就是新建一个Intent对象,然后调用startService方法把对象传进去,之后就可以启动了,停止的时候也是传一个Intent对象给stopService方法。

  3. 绑定Service的话逻辑较为复杂,这里我们是将Activity与Service建立绑定。我们可以看到几点注意事项:
    (1) 首先是在Service里,我们定义了一个继承Binder的子类,用来执行任务,Binder我觉得可以理解为"粘合剂",我们可以理解为它是其它组件和Service的之间的结合物,之后实例化这个Binder并在回调方法里返回这个Binder给绑定Service的组件,就可以通过它让组件控制Service了。
    (2) 然后是在Activity里实例化了一个ServiceConnection,并重写了onServiceConnected方法,这个方法会在建立连接后返回Service的Binder,然后通过Binder执行Service里的任务。
    (3) 绑定Service的时候,调用bindService方法,这里传入三个参数,一个是Intent对象,说明要与哪个Service绑定,一个是刚才的ServiceConnection实例,第三个参数是常量,我们这里传入BIND_AUTO_CREATE,表示绑定时是否自动创建Service。

  4. 销毁绑定的Service的时候只需要在使用unbindService方法时传入参数ServiceConnection实例即可。

  5. 如果既点击了Start Service按钮,又点击了Bind Service按钮,这样的话不管是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。

三、Service生命周期

Service的生命周期比Activity的简单很多,但是更加要引起重视,毕竟Service可能是在用户不知情的情况下在后台运行的。下面这张图很清楚地为我们展示了两种不同的方式,Service的生命周期。

Paste_Image.png

看这张图,我们可以初步对Service之前的几个回调方法有所了解,启动和绑定Service都会调用onCreate()方法来创建Service,如果是启动的话会调用onStartCommand()方法,如果是绑定的话会调用onBind()方法,当启动的Service停止后会调用onDestroy()方法销毁,绑定的Service在全部都解除绑定后调用onUnbind()方法,最后调用onDestroy()方法销毁。

四、Service的几点说明

  1. A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.Service不是一个单独的进程,Service与它所在应用位于同一进程中。

  2. A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).Service不是一个线程,所以为了避免ANR错误(用户无响应),我们应该在Service里开启新的线程来处理耗时任务。

  3. 我们可以看到Service只是一个在后台运行的没有用户界面的组件,使用它做些耗时的任务时,是需要开启新线程的。这里如果有需要的话,可以详细去了解下IntentService这个类,它可以很方便地处理一些多线程问题。

五、IntentService

Service这个组件默认情况下仍然运行在应用的主线程中,所以如果我们要执行耗时或者阻塞操作,那么为了避免ANR,应该要在Service内创建一个新的线程。IntentService是Service类的一个子类,用来处理异步请求。

下面是一个IntentService使用示例:

//实现倒计时5秒的服务

public class MyIntentService extends IntentService {

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     */
    public MyIntentService() {
        super("count down 5");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("ssssss", "倒计时服务创建...");
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        long endTime = System.currentTimeMillis() + 5 * 1000;

        while (System.currentTimeMillis() < endTime) {
            synchronized (this) {
                try {
                    wait(endTime - System.currentTimeMillis());
                } catch (Exception e) {
                } finally {
                    Log.e("ssssss", "倒计时结束...");
                }
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("ssssss", "倒计时服务销毁...");
    }
}


与普通Service一样,我们可以通过startService(Intent)方法传递请求给IntentService。通过查看源码可知,IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来处理所有Intent请求对象所对应的任务,这样以免事务处理阻塞主线程。执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service;否则执行下一个Intent请求所对应的任务。

IntentService详解

官方建议我们使用IntentService来完成异步请求的处理,那么IntentService内部具体都做了哪些东西呢?大概如下:

  1. 创建一个独立于应用主线程的新的线程来执行所有传递给onStartCommand()方法中的请求。

  2. 创建一个工作队列,一次传递一个请求到onHandleIntent()方法中。

  3. 当所有请求处理完成后,停止服务,不需要我们手动调用stopSelf()。

  4. 提供onBind()方法的默认实现,并且返回空。

  5. 帮我们实现了将请求从onStartCommand()传递到工作队列,然后传递到onHandleIntent()。

为了对比地看出Service和IntentService在使用上的区别,下面展示一个使用Service的例子:


public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

六、相关参考

  1. Service基本使用
    Android Service完全解析,关于服务你所需知道的一切(上)

  2. Service高级用法
    Android Service完全解析,关于服务你所需知道的一切(下)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • [文章内容来自Developers] Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。...
    岳小川阅读 857评论 0 7
  • 服务基本上分为两种形式 启动 当应用组件(如 Activity)通过调用 startService() 启动服务时...
    pifoo阅读 1,254评论 0 8
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-androi...
    eddy_wiki阅读 3,252评论 0 20
  • 思 文/迷恋深秋 雾蒙蒙 青山碧水迷雾间 可人儿 可辨得出那绿的模样 路漫漫 蜿蜒陡峭云深处 可人儿 可记得那回家...
    迷恋深秋阅读 243评论 0 1