zoukankan      html  css  js  c++  java
  • Develop系列-API Guides-应用组件-Services

    Services

    Services:可在后台长时间运行,无UI界面。

    Started:

    Services通过其他组件调用startService启动,能在后台一直运行,即使启动它的组件已经销毁。一般这种服务执行特定的操作而且没有返回值,比如通过网络下载或上传一个文件,一旦操作结束,服务需要停止自己。

    Bound:

    Service通过其他组件调用bindService绑定,提供一个CS架构的接口允许其他组件与Service交互、发消息、获取返回值、甚至跨进程通信。bound service与绑定它的组件共存亡,多个组件可以绑定一个服务,当这个服务所有绑定它的组件都解绑后,服务会被销毁。

    Caution:Service在宿主进程的主线程运行,也就是说Service不会创建自己的线程,也不会运行在另一个进程(除非你指定)。这就意味着,如果Service执行一些高CPU负载或阻塞界面操作的任务时,你需要创建新的线程来运行它,以降低ANR的风险。

    (使用service还是thread?如果你只是需要在与用户交互时,在主线程之外执行额外的任务,那么你需要创建一个新线程而不是Service。比如在activity在前台显示的时候播放音乐,你需要在onCreate里面创建线程,在onStart执行线程,在onStop里停止线程,当然也可以使用AsyncTask或者HandleThread来替代线程类。)

    基础

    创建Service的子类,覆写Service生命周期中的关键回调函数,如果有必要,提供绑定Service的机制。最重要的回调函数如下:

    onStartCommand

    系统在其他组件调用startService启动Service时执行,之后,Service一直在后台执行。如果你实现了这个方法,那么你有责任在Service完成其工作时通过stopSelf或stopService来停止Service。(如果只是提供绑定Service,可以不用实现这个方法)

    onBind

    系统在其他组件调用bindService绑定Service时执行,在这个方法的实现中,你需要返回一个IBider接口用于clients与service交互。如果你不需要绑定,可以直接返回null

    onCreate

    Service第一次创建时执行,在onStartCommand和onBind之前,用于执行一次性需要初始化好的数据。

    onDestroy

    service销毁时执行,用于清理资源,类似线程、注册监听器,receiver等。

    系统会在低内存时强制停止service。如果service绑定在一个有用户焦点的activity,那么它很少会被销毁;如果服务定义为在前台运行的,那么它永远不会被销毁。

    在manifest定义service

    <manifest ... >
      ...
      <application ... >
          <service android:name=".ExampleService" />
          ...
      </application>
    </manifest>

    android:name是唯一必填的属性,其他可以选填。一旦你的应用发布出去了,那么android:name永远不要再变。

    为了安全性,请使用显式Intent启动或绑定服务,而不是定义filiters。如果确实允许多个服务来响应,那么请务必在Intent中通过setPackage来指定报名,以降低目标服务匹配出意外值得概率。

    设置android:exported为false,可以避免其他应用启动这个服务。

    创建service

    Service

    所有服务的基类,如果你扩展这个类,那么你可能需要创建一个新的线程,因为这个服务室在app的主线程中执行,有可能会降低activity的流畅度

    IntentService

    Service 的子类,通过工作线程来处理所有请求,每次处理一个。如果你的service不需要同时处理多个请求,IntentService是最佳的选择。

    扩展IntentService

    因为大部分服务不需要同时处理多个请求(实际上是个多线程场景),IntentService是最好的推荐

    IntentService工作流程如下:

    • 创建一个默认的新工作线程来执行所有分发给onStartCommand的intents
    • 创建一个工作队列来依次传递intent去执行onHandleIntent具体实现,所以不需要考虑多线程场景
    • 所有请求执行完之后,自动销毁,你不需要执行stopSelf
    • 提供默认的onBind实现:直接返回null
    • 提供onStartCommand默认实现:发送intent给工作线程执行onHandleIntent实现

    实际上,你需要做的就是实现onHandleIntent,如下例子:

      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才能处理工作线程的生命周期。onHandleIntent和onBind是例外。

    扩展Service

    如果服务需要支持多线程(而不是通过工作队列来一一处理),那么你要扩展Service来处理每一个intent。

    作为对比,如下代码和上面代码功能一样。

    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必须返回一个整型,这个值用来描述系统如何处理这个服务,当它被异常销毁时。

    START_NOT_STICKY

    如果服务执行完onStartCommand后被异常销毁,不需要重新创建服务,除非还有未处理的intents。避免服务被不必要地启动,你的应用能够很容易地重新启动未完成的工作,选择这个。

    START_STICKY

    如果服务执行完onStartCommand后被异常销毁,重新创建服务并执行onStartCommand,但是不会使用发给这个服务的最后一个intent,而是通过一个null intent来执行onStartCommand,除非还有未处理的intents。这种情况比较适合不执行命令,一直在后台运行或者等待任务的媒体播放。

    START_REDELIVER_INTENT

    如果服务执行完onStartCommand后被异常销毁,重新创建服务,用发给这个服务的最后一个intent调用onStartCommand,其他未处理的intents依次分发。这种情况适合执行的任务需要理解恢复的场景,比如下载文件。

    启动服务

    startService –> onStartCommand

    Intent intent = new Intent(this, HelloService.class);
    startService(intent);

    startService方法立即返回。

    如果service不能绑定,只能通过startService启动,而且需要发送返回值时,可以通过创建带延迟广播的PendingIntent(用getBroadcast)来启动服务,服务能用里面的广播来分发结果。

    停止服务

    如果服务同步地处理多个请求,那么不能在处理完一个请求直接销毁,有可能其他的请求刚发过来或者还在处理中。为了避免这种情况,你可以通过stopSelf(int)来确保关闭的服务就是刚处理完请求的,而不是其他正在处理的服务,整型参数唯一标识服务实例。

    创建绑定服务

    详见下一章节

    发送Notifications给用户

    服务一旦运行,可以通过Toast Notifications和Status Bar Notifications提醒用户。

    在前台运行服务

    在前台运行的服务必须提供一个状态栏的notification,来呈现运行状态,除非服务被停止或者移除,否则notification不会消失。

    比如:播放音乐,在状态栏有个notification来告诉用户当前播放的歌曲,提供用户进入音乐播放器的入口

    服务需要在前台运行,需要调用startForground,第一个参数是notification的唯一标示(不能为0),第一参数就是notification,比如:

    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。

    实现生命周期回调

    public class ExampleService extends Service {
        int mStartMode;       // indicates how to behave if the service is killed
        IBinder mBinder;      // interface for clients that bind
        boolean mAllowRebind; // indicates whether onRebind should be used
    
        @Override
        public void onCreate() {
            // The service is being created
        }
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            // The service is starting, due to a call to startService()
            return mStartMode;
        }
        @Override
        public IBinder onBind(Intent intent) {
            // A client is binding to the service with bindService()
            return mBinder;
        }
        @Override
        public boolean onUnbind(Intent intent) {
            // All clients have unbound with unbindService()
            return mAllowRebind;
        }
        @Override
        public void onRebind(Intent intent) {
            // A client is binding to the service with bindService(),
            // after onUnbind() has already been called
        }
        @Override
        public void onDestroy() {
            // The service is no longer used and is being destroyed
        }
    }

    Note:不需要执行父类的实现。

    完整的生命周期:onCreate —> onDestroy

    活动的生命周期:onStartCommand/onBind —> onDestroy/onUnbind

  • 相关阅读:
    C#对SQLite、Access数据库操作的封装,很好用的~
    如何使用MFC连接Access数据库
    字节、十六进制字符串相互转换(asc2hex、hex2asc)
    Oracle数据库模式关系和数据备份导出导入
    Oracle数据库sql常用
    Oracle数据库的函数,存储过程,程序包,游标,触发器
    收集一下
    JS 获取随机颜色值
    IDEA webapp文件夹不识别解决方案
    使用postman请求响应Invalid CORS request
  • 原文地址:https://www.cnblogs.com/konger/p/3920866.html
Copyright © 2011-2022 走看看