Android创建服务之started service详细介绍_Android

创建started service

       应用组件(例如Activity)调用startService()来启动一个Service,将需要的参数通过Intent传给Service,Service将会在onStartCommand函数中获得Intent。

有两种方式可以创建started service,一种是扩展Service类,另外一种是扩展IntentService类

扩展Service
       这是所有服务的基类。扩展这个类的时候,特别重要的一点是,需要创建一个新的线程来做服务任务,因为service默认是运行在你的主线程(UI线程)中的,它会使你的主线程运行缓慢。

扩展IntentService
       这是一个service的子类,他可以在一个工作线程中处理所有的启动请求。如果你不需要同一时刻出来所有的服务请求,使用IntentService是一个很好的选择。你需要做的仅仅是实现onHandlerIntent()方法,通过这个函数处理接受的每一个启动请求。

下面我们学习如何扩展IntentService类和Service类

扩展IntentService类

IntentService做了什么?
1.创建一个独立于主线程的工作线程,用于执行通过onStartCommand()传来的所有的intent。
2.创建一个工作队列,将接受的所有intent一个一个的传给onHandlerIntent(),所以同一时间内你只处理一个intent,不用担心多线程的问题。
3.当所有的请求都处理完后,停止服务,所以你不需要手动调用stopSelf()。
4.提供onBind()函数的默认实现,返回null
5.提供onStartCommand()函数的默认实现,它把intent发送到工作队列中去,然后工作队列再发送到你的onHandlerIntent()函数中。

有了上面的基础,你仅仅要做的就是实现onHandlerIntent()。并且实现一个小小的构造函数。

参考下面的例子:

复制代码 代码如下:

public class HelloIntentService extends IntentService {

  /**
   * A constructor is required, and must call the super IntentService(String)
   * constructor with a name for the worker thread.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // 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) {
              }
          }
      }
  }
}

如果你要实现其他的回调函数,例如 onCreate , onStartCommand ,onDestroy 一定记得调用父类相应的函数,这样IntentService才能正确的处理工作线程。

例如,你需要在onStartCommand函数中弹出一个提示,那么你可以这样写:

复制代码 代码如下:

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

有一点例外的是,如果你需要其他组件绑定服务,那么你的onBind函数不需要调用父类的onBind。

在下一节中,你将会看到通过扩展service类来实现与本节相同的服务,所不同的是代码会更多,但是同样意味着更灵活,特别是你需要同时处理多个请求时,比较适合直接扩展Service。

扩展Service类

如同你在上一节看到的一样,使用IntentService来实现一个started service非常简单。如果你需要你的service来执行多个线程,那么你需要扩展Service类去处理每个intent。

作为对比,下面的例子用Service类实现了和上一节中一样功能的服务。

复制代码 代码如下:

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

  // Handler that receives messages from the thread
  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() {
    // Start up the thread running the service.  Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block.  We also make it
    // background priority so CPU-intensive work will not disrupt our UI.
    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) {
      // We don't provide binding, so return null
      return null;
  }

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

如同你看到的,比IntentService的例子多了好多代码。

因为你自己处理每次的onStartcommand(),所以你可以在同一时间处理多个请求。这个例子没有这样做,但是如果你愿意,你完全可以每收到一个请求的时候,创建一个新线程并且立即运行,而不是等到上一个请求处理完成后再运行。

注意,onStartCommand()函数必须返回一个整数。这个整数描述了当系统杀死该服务时将要如何处理该服务。返回值必须是下面几个值:
START_NOT_STICKY
在onStartCommand()返回后,系统杀死服务,服务将不会重启,除非有pending intents(目前笔者还不懂什么是pending Intents)要投递。这比较适合非常容易就可以重新启动未完成任务的情况。

START_STICKY
        在系统杀死服务后,重启服务并且调用onStartCommand(),但是不重新投递上一次的intent。系统会传给onStartCommand()函数null,除非有pending intent来启动服务,会传给onStartCommand()相应的intent。这种做法比较适合媒体播放服务,不需要执行命令,但是独立运行,常处于等待任务的服务。

START_REDELIVER_INTENT
在系统杀死服务后,重启服务并且调用onStartCommand(),参数传入上一次的intent,然后接下来是pending intent传入onStartCommand()。这比较适合于正在执行一项工作,它不能被立刻恢复,例如下载文件。

启动Service

你可以从一个Activity或者其他组件调用startService(intent)来启动服务。Android系统会调用服务的onStartCommand()函数并且传给它intent。

用上一节的HelloService来做个例子:
Intent intent = new Intent(this, HelloService.class);
startService(intent);
startService()会立刻返回,Android系统会调用服务的onStartCommand()函数。如果这是服务没有在运行,系统会首先调用onCreate()函数,然后会紧接着调用onStartCommand()函数。

如果服务没有提供绑定,那么通过startService()函数传入的intent就是应用组件和服务进行交互的唯一途径。如果你想服务发送结果给客户组件,那么客户组件需要在启动服务时,创建一个用于广播的PendingIntent,通过intent投递给服务。这样,服务就可以利用广播把结果发送给客户组件。

多个请求会导致服务的onStartCommand()被调用多次。但是,只需要一个停止请求(stopSelf() 或 stopService())就会使服务停止。

停止服务

一个启动的服务必须管理自己的生命周期。因为除非系统需要回收资源的时候,系统不会停止或者销毁服务。所以,服务必须通过调用stopSelf()来停止自己,或者有其他组件调用stopService()。

一担收到stopSelf()或stopService()的停止请求,系统会立刻销毁服务。

如果你的服务同时处理多个服务请求,当某个请求完成时就不能马上停止服务,因为你可能已经又接受了一个新的请求(在第一个请求完成时结束服务会终止第二个请求)。为了解决这个问题,你可以调用stopSelf(int)函数来保证你的停止请求总是基于最近的一次服务请求。这是因为,调用stopSelf(int)函数
会把onStartCommand()函数传入的startId传给停止请求。当你的startId和最近一次接受的服务请求不匹配时,服务不会结束。

注意:stopSelf(int)函数只是简单的与最近一次收到的startId比较,如果你的服务处理过程是多线程的,可能后收到的服务请求先完成,那stopSelf(int)的方案不适合你,你应该手动管理接受到的服务请求和完成的服务,比如在onStartCommand()函数中把startId记录到一个表中,在完成服务任务时在表中记录完成状态,在确保表中任务都完成的情况下直接调用stopSelf()来停止服务。

在前台运行服务

一个前台服务是用户能够很明显意识到的服务,当可用内存比较低的时候,系统不会杀掉前台服务。一个前台服务必须提供一个状态栏通知,放在正在运行条目里,这意味着只要服务没有停止或者一直是前台服务,这个通知就不会被消失。

举个例子,一个音乐播放器服务应该被设置为前台运行,因为用户非常明显知道他在运行。这个状态栏通知可以显示正在播放的歌曲,并且可以允许用户点击进入音乐播放界面。

要使服务运行在前台只要调用startForeground()。这个方法有两个参数:一个通知的整数ID和一个状态栏通知。代码片段:

复制代码 代码如下:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
        System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);

使用stopForeground()函数可以把服务从前台移除。这个函数带一个布尔值参数,来表示是否从状态栏移除通知。这个函数不会使服务停止。如果把前台服务停止掉,那么通知也会同时被移除。

时间: 2024-10-27 00:32:34

Android创建服务之started service详细介绍_Android的相关文章

Android入门:多线程断点下载详细介绍_Android

本案例在于实现文件的多线程断点下载,即文件在下载一部分中断后,可继续接着已有进度下载,并通过进度条显示进度.也就是说在文件开始下载的同时,自动创建每个线程的下载进度的本地文件,下载中断后,重新进入应用点击下载,程序检查有没有本地文件的存在,若存在,获取本地文件中的下载进度,继续进行下载.当下载完成后,自动删除本地文件. 一.多线程断点下载介绍 所谓的多线程断点下载就是利用多线程下载,并且可被中断,如果突然没电了,重启手机后可以继续下载,而不需要重新下载: 利用的技术有:SQLite存储各个线程的

Android中程序的停止状态详细介绍_Android

很多人遇到过广播收不到的问题,比如Google Play推广安装广播没有收到等,诸如这些问题,又都是什么原因呢,这篇文章将进行回答. 从Android 3.1(HoneyComb) 也就是API 12开始,Android引入了一套新的启动控制,这就是程序的停止状态.那让我们看一下Google对于程序的停止状态的描述. 什么是程序的停止状态 复制代码 代码如下: Starting from Android 3.1, the system's package manager keeps track

Android adb logcat 命令查看日志详细介绍_Android

Android 开发的程序员开发程序的时候,一定为log而苦恼过吧.Eclipse老是Log找不到,是不是很让人不爽,虽然Android Studio的Logcat功能很不错,但也没有在命令行terminal中打印出来更方便. 使用 logcat 命令         查看和跟踪系统日志缓冲区的命令logcat的一般用法是: [adb] logcat [<option>] ... [<filter-spec>] ... 下文介绍过滤器和命令选项,详细内容可参见Listing of

android listview优化几种写法详细介绍_Android

这篇文章只是总结下getView里面优化视图的几种写法,就像孔乙己写茴香豆的茴字的几种写法一样,高手勿喷,勿笑,只是拿出来分享,有错误的地方欢迎大家指正,谢谢. listview Aviewthatshowsitemsinaverticallyscrollinglist. 一个显示一个垂直的滚动子项的列表视图在android开发中,使用listview的地方很多,用它来展现数据,成一个垂直的视图.使用listview是一个标准的适配器模式,用数据--,界面--xml以及适配器--adapter,

Android 布局控件之LinearLayout详细介绍_Android

LinearLayout是线性布局控件,它包含的子控件将以横向或竖向的方式排列,按照相对位置来排列所有的widgets或者其他的containers,超过边界时,某些控件将缺失或消失.因此一个垂直列表的每一行只会有一个widget或者是container,而不管他们有多宽,而一个水平列表将会只有一个行高(高度为最高子控件的高度加上边框高度).LinearLayout保持其所包含的widget或者是container之间的间隔以及互相对齐(相对一个控件的右对齐.中间对齐或者左对齐). API说明

Android 各国语言缩写及简称详细介绍_Android

android资源文件夹的写法规则: 语言缩写-国家地区缩写 语言缩写请参阅: 国家地区缩写 en 英文 en_US 英文 (美国) ar 阿拉伯文 ar_AE 阿拉伯文 (阿拉伯联合酋长国) ar_BH 阿拉伯文 (巴林) ar_DZ 阿拉伯文 (阿尔及利亚) ar_EG 阿拉伯文 (埃及) ar_IQ 阿拉伯文 (伊拉克) ar_JO 阿拉伯文 (约旦) ar_KW 阿拉伯文 (科威特) ar_LB 阿拉伯文 (黎巴嫩) ar_LY 阿拉伯文 (利比亚) ar_MA 阿拉伯文 (摩洛哥) a

Android Service总结及详细介绍_Android

1.Service的种类 按运行地点分类: 类别 区别  优点 缺点   应用 本地服务(Local) 该服务依附在主进程上,  服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL.相应bindService会方便很多.  主进程被Kill后,服务便会终止.  非常常见的应用如:HTC的音乐播放服务,天天动听音乐播放服务. 远程服务(Remote) 该服务是独立的进程,  服务为独立的进程,对应进程名格式为所在包名

Android Intent传递数据底层分析详细介绍

Android  Intent传递数据底层分析详细介绍 我们知道在Activity切换时,如果需要向下一个ActivityB传递数据,可以借助Intent对象的putExtra方法. 但是不知各位有没有想过这样一个问题:ActivityB中获取到的对象跟上一个Activity中的那个对象有什么关系? 换句话说就是,我在ActivityB中通过Intent获取的对象跟ActivityA中的那个对象,有没有可能是同一个对象? 按照常理来说,博主提出一个设想后续的就是证明过程了,但是我要遗憾的告诉你,

Android onCreate( )方法详细介绍_Android

onCreate( )方法是android应用程序中最常见的方法之一,那么,我们在使用onCreate()方法的时候应该注意哪些问题呢?     先看看Google Android Developers官网上的解释:     onCreate(Bundle) is where you initialize your activity. Most importantly, here you will usually call setContentView(int) with a layout re