不管是何种Service,它默认都是在应用程序的主线程(亦即UI线程)中运行的。所以,如果你的Service将要运行非常耗时或者可能被阻塞的操作时,你的应用程序将会被挂起,甚至会出现ANR错误。为了避免这一问题,你应该在Service中重新启动一个新的线程来进行这些操作。现有两种方法共大家参考:
① 直接在Service的onStartCommand()方法中重启一个线程来执行,如:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyServiceActivity.updateLog(TAG + " ----> onStartCommand()");
new Thread(new Runnable() {
@Override
public void run() {
// 此处进行耗时的操作,这里只是简单地让线程睡眠了1s
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
return START_STICKY;
}
② Android SDK 中为我们提供了一个现成的Service类来实现这个功能,它就是IntentService,它主要负责以下几个方面:
- Creates a default worker thread that executes all intents delivered to
onStartCommand()
separate from your application's main thread. 生成一个默认的且与主线程互相独立的工作者线程来执行所有传送至 onStartCommand() 方法的Intetnt
- Creates a work queue that passes one intent at a time to your
onHandleIntent()
implementation, so you never have to worry about multi-threading. 生成一个工作队列来传送Intent对象给你的onHandleIntent()方法,同一时刻只传送一个Intent对象,这样一来,你就不必担心多线程的问题。
- Stops the service after all start requests have been handled, so you never have to call
stopSelf()
. 在所有的请求(Intent)都被执行完以后会自动停止服务,所以,你不需要自己去调用stopSelf()方法来停止该服务
- Provides default implementation of
onBind()
that returns null. 提供了一个onBind()方法的默认实现,它返回null
- Provides a default implementation of
onStartCommand()
that sends the intent to the work queue and then to youronHandleIntent()
implementation 提供了一个onStartCommand()方法的默认实现,它将Intent先传送至工作队列,然后从工作队列中每次取出一个传送至onHandleIntent()方法,在该方法中对Intent对相应的处理
以上,英文来自官方SDK,中文为我所译。
从以上看来,你所需要做的就是实现 onHandleIntent() 方法,在该方法内实现你想进行的操作。另外,继承IntentService时,你必须提供一个无参构造函数,且在该构造函数内,你需要调用父类的构造函数,如下:
public HelloIntentService() { super("HelloIntentService"); }
下面给出一例,来解释一下:
// activity 的onCreate() @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startSer1 = (Button) findViewById(R.id.startSer1); stopSer1 = (Button) findViewById(R.id.stopSer1); startSer2 = (Button) findViewById(R.id.startSer2); stopSer2 = (Button) findViewById(R.id.stopSer2); log = (TextView) findViewById(R.id.log); logView = (ScrollView) findViewById(R.id.logView); startSer1.setOnClickListener(btnListener); stopSer1.setOnClickListener(btnListener); startSer2.setOnClickListener(btnListener); stopSer2.setOnClickListener(btnListener); intent = new Intent(MyServiceActivity.this, IntentServiceDemo.class); // 打印出主线程的ID long id = Thread.currentThread().getId(); updateLog(TAG + " ----> onCreate() in thread id: " + id); }
// service 代码 package com.archer.rainbow; import java.text.SimpleDateFormat; import java.util.Date; import android.app.IntentService; import android.content.Intent; public class IntentServiceDemo extends IntentService { private static final String TAG = "IntentServiceDemo"; private static final SimpleDateFormat SDF_DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.SSS"); public IntentServiceDemo() { super(TAG); MyServiceActivity.updateLog(TAG + " ----> constructor"); } @Override public void onCreate() { super.onCreate(); // 打印出该Service所在线程的ID long id = Thread.currentThread().getId(); MyServiceActivity.updateLog(TAG + " ----> onCreate() in thread id: " + id); } @Override public void onDestroy() { super.onDestroy(); MyServiceActivity.updateLog(TAG + " ----> onDestroy()"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { MyServiceActivity.updateLog(TAG + " ----> onStartCommand()"); // 记录发送此请求的时间 intent.putExtra("time", System.currentTimeMillis()); return super.onStartCommand(intent, flags, startId); } @Override public void setIntentRedelivery(boolean enabled) { MyServiceActivity.updateLog(TAG + " ----> setIntentRedelivery()"); super.setIntentRedelivery(enabled); } @Override protected void onHandleIntent(Intent intent) { // 打印出处理intent所用的线程的ID long id = Thread.currentThread().getId(); MyServiceActivity.updateLog(TAG + " ----> onHandleIntent() in thread id: " + id); long time = intent.getLongExtra("time", 0); Date date = new Date(time); try { // 打印出每个请求对应的触发时间 MyServiceActivity.updateLog(TAG + " ----> onHandleIntent(): 下载文件中..." + SDF_DATE_FORMAT.format(date)); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }
应用启动时,界面如下:
从此图可以看出,主线程(UI线程)的ID是1。接,连续点击三次Start Service 1 按钮,得如下画面:
从此图中可以看出,IntentServiceDemo的onCreate()所处的线程ID仍为1,说明它是在主线程中被执行的,且只被执行一次。然后,我每点击一次按钮,它都会触发一下onStartCommand()方法。仔细看第二次与第三次的onCommand()方法以及onHandleIntent()打印出来的语句,你会发现,第二、三两次点击按钮与第一次点击按钮的时间是没有超过3秒钟的,它们是连续被执行的,这说明了什么呢?说明,在第一个intent被处理时(即onHandleIntent()处于运行中),该Service仍然可以接受新的请求,但接受到新的请求后并没有立即执行,而是将它们放入了工作队列中,等待被执行。这就是 IntentService 的简单用法。但你若是想在Service中让多个线程并发的话,就得另想法子喽。比如,使用第一种方法,在Service内部起多个线程,但是这样的话,你可要处理好线程的同步哦~~~