8.3. 在 Service 中循环

优质
小牛编辑
126浏览
2023-12-01

8.3.在 Service 中循环

根据设计,我们的 Service 需要被频繁地唤醒,检查消息更新,然后再次“睡眠”一段时间。这一过程会持续进行下去,直到 Service 停止。实现时可以将这一过程放在一个循环中,每迭代一次就暂停一段时间。在这里可以利用 Java 提供的Thread.sleep()方法,可以让当前进程以毫秒为单位暂停一段时间,并让出 CPU。

在这里还有一点需要考虑,那就是Service连接到服务端获取数据这一行为本身,需要花费相当长的时间。网络操作的执行效率直接受网络接入方式、服务端的响应速度以及一些不可预知的因素影响,延迟是很常见的。

要是把检查更新的操作放在主线程的话,网络操作中的任何延时都会导致用户界面僵死,这会给用户留下一个很不好的印象。甚至很可能会让用户不耐烦,直接调出 "Force Close or Wait" 对话框(参见‘Android的线程机制’一节)把我们的应用杀死。

解决这一问题的最好办法,就是利用Java内置的线程支持,把网络相关的操作放到另一个线程里。Service在控制UI的主线程之外执行,这使得它们与用户交互的代码总是分离的。这点对Yamba这样与网络交互的界面而言尤为重要,而且对其它场景也同样适用,哪怕它花费的时间不长。

例 8.5. UpdaterService.java, version 2

package com.marakana.yamba3;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.util.Log;

public class UpdaterService2 extends Service {

private static final String TAG = "UpdaterService";

static final int DELAY = 60000; // a minute

private boolean runFlag = false; //

private Updater updater;

@Override

public IBinder onBind(Intent intent) {

return null;

}

@Override

public void onCreate() {

super.onCreate();

this.updater = new Updater(); //

Log.d(TAG, "onCreated");

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

super.onStartCommand(intent, flags, startId);

this.runFlag = true; //

this.updater.start();

Log.d(TAG, "onStarted");

return START_STICKY;

}

@Override

public void onDestroy() {

super.onDestroy();

this.runFlag = false; //

this.updater.interrupt(); //

this.updater = null;

Log.d(TAG, "onDestroyed");

}

/**

* Thread that performs the actual update from the online service

*/

private class Updater extends Thread { //

public Updater() {

super("UpdaterService-Updater"); //

}

@Override

public void run() { //

UpdaterService2 updaterService = UpdaterService2.this; //

while (updaterService.runFlag) { //

Log.d(TAG, "Updater running");

try {

// Some work goes here...

Log.d(TAG, "Updater ran");

Thread.sleep(DELAY); //

} catch (InterruptedException e) { //

updaterService.runFlag = false;

}

}

}

} // Updater

}

  1. 声明一个常量,用以表示网络更新的时间间隔。我们也可以把它做在选项里,使之可以配置。
  2. 这个标志变量用以方便检查 Service 的执行状态。
  3. Updater 在另一个线程中进行网络更新。这个线程只需创建一次,因此我们在onCreate()中创建它。
  4. 在 Service 启动时,它会调用onStartCommand()方法,我们就在这里启动 Updater 线程再合适不过。同时设置上标志变量,表示 Service 已经开始执行了。
  5. 与之对应,我们可以在onDestroy()中停止 Updater 线程。再修改标志变量表示 Service 已经停止。
  6. 我们通过调用interrupt()来停止一个线程执行,随后设置变量的引用为 null ,以便于垃圾收集器清理。
  7. 在这里定义 Updater 类。它是个线程,因此以 Java 的 Thread 类为基类。
  8. 给我们的线程取一个名字。这样便于在调试中辨认不同的线程。
  9. Java 的线程必须提供一个run()方法。
  10. 简单得到对 Service 的引用。 Updater 是 Service 的内部类。
  11. 这个循环会一直执行下去,直到 Service 停止为止。记着 runFlag 变量是由onStartCommand()与onDestroy()修改的。
  12. 调用Thread.sleep()暂停Updater线程一段时间,前面我们将DELAY设置为1分钟。
  13. 对执行中的线程调用interrupt(),会导致run()中产生一个InterruptException异常。 我们捕获这个异常,并设置 runFlag 为 false ,从而避免它不断重试。