使用IntentService
1.Service缺陷
由于Service本身存在以下两个问题:
(1)Service不会专门启动一条单独的进程,Service与他所在应用位于同一进程中;
(2)Service也不是专门一条新的线程,如果我们在Service中直接处理耗时的任务,那么就会导致应用程序出现假性"卡死"。如果我们需要在Service处理耗时任务,也可以在Service的onCreate()方法中启动一条新线程来处理该耗时任务(如上例)。但是,问题来了,启动Service的Activity可能随时被用户退出,如果在子线程还没有结束的情况下,Activity已经被用户退出了,此时那些子线程所在的进程就变成了空进程(即没有任何活动组件的进程),系统需要内存时可能会优先终止该进程。如果宿主进程被终止,那么该进程内的所有子线程也会被终止,这样就可能导致子线程无法执行完成。
2.IntentService原理
IntetnService是Service的子类,它不是普通的Service,通过IntentService恰好弥补了Service上述的两个不足。IntentService主要使用队列来管理请求Intent,每当客户端代码通过Intent请求启动IntentService时,IntentService会将该Intent加入队列中,然后开启一条新的worker线程来处理该Intent。对于异步的startService()请求,IntentService会按次序依次处理队列中的Intent,该线程保证同一时刻只处理一个Intent。由于IntentService使用新的worker线程处理Intent请求,因此IntentService不会阻塞主线程,所有IntentService自己就可以处理耗时任务了。
3.IntentService使用特征
(1)IntentService会创建单独的worker线程来处理所有的Intent请求;
(2)IntentService会创建单独的worker线程来处理onHandleIntent()方法实现的代码,因此开发者无需处理多线问题;
(3)当所有请求处理完成后,IntentService会自动停止,因此开发者无须调用StopSelf()方法来停止该Service。
(4)为Service的onBind()方法提供了默认实现,默认实现的onBind()方法返回null;
(5)为Service的onStartCommand()方法提供了默认实现,该实现会将请求Intent添加到队列中。
注释:扩展IntentService实现Service无须重写onBind()、onStartCommand()方法,只要重写onHandleIntent()方法即可。
4.源码实战
实现:分别启动普通Service和IntentService,且同时处理耗时任务,对比两者效果。
(1)\src\com\example\android_intentservice\ServiceAndIntentService.java
实现:通过两个按钮分别启动普通Service和IntentService
package com.example.android_intentservice; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; public class ServiceAndIntentService extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } //1.启动普通Service按钮方法 public void startService(View source) { Intent intent = new Intent(this,MyService.class); //创建需要启动的service的Intent startService(intent); //启动Intent指定的Service } //2.启动IntentService按钮方法 public void startIntentService(View source) { Intent intent = new Intent(this,MyIntentService.class); startService(intent); } }
(2)\res\layout\main.xml
实现:设置Button属性android:onClick,为按钮绑定响应方法
...... <Button android:onClick="startService" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="启动普通Service" /> <Button android:onClick="startIntentService" android:layout_width="186dp" android:layout_height="wrap_content" android:text="启动IntentService" />
(3)\src\com\example\android_intentservice\MyService.java
实现:实现一个普通Service执行耗时任务(20s),观察是否导致ANR异常(Application Not Responding)
package com.example.android_intentservice; import android.app.Service; import android.content.Intent; import android.os.IBinder; public class MyService extends Service { @Override public IBinder onBind(Intent intent) { return null; } //当启动Service,调用该方法执行相应代码 @Override public int onStartCommand(Intent intent, int flags, int startId) { long endTime = System.currentTimeMillis()+20*1000; System.out.println("onStart"); while(System.currentTimeMillis()<endTime) { synchronized(this) { try { wait(endTime-System.currentTimeMillis()); }catch(Exception e) { } } } System.out.println("---普通Service耗时任务执行完成---"); return START_STICKY; } }
(4)\src\com\example\android_intentservice\MyIntentService.java
实现:实现一个IntentService执行耗时任务(20s),观察是否导致ANR异常
package com.example.android_intentservice; import android.app.IntentService; import android.content.Intent; public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); } //IntentService会使用单独的线程来执行方法的代码 @Override protected void onHandleIntent(Intent intent) { //该方法内可以执行任何耗时任务,比如下载文件等,此处只是让线程暂停20s long endTime = System.currentTimeMillis()+20*1000; System.out.println("onStart"); while(System.currentTimeMillis()<endTime) { synchronized(this) { try { wait(endTime-System.currentTimeMillis()); }catch(Exception e) { } } } System.out.println("---IntentService耗时任务执行完成---"); } }
(5)AndroidManifest.xml
实现:在工程文件中为MyService、MyIntentService配置<service../>信息
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity .......... </activity> <!--配置service--> <service android:name=".MyService"/> <service android:name=".MyIntentService"/> </application>
效果演示:
a.当点击"启动普通service"时的效果
b.连续点击7次"启动IntentService"的效果
升华笔记:
1.普通Service是在onStartCommand()方法内执行耗时任务;IntentService在onHandleIntent()方法内执行耗时任务;
2.普通Service在没有创建一个新线程的情况下,其执行耗时任务主要是在应用程序的主线程中完成的,由于普通Service的执行会阻塞主线程,因此启动该Service执行耗时任务会导致程序出现ANR异常;
3.IntentService主要使用队列来管理请求Intent,每当客户端代码通过Intent请求启动IntentService时,IntentService会将该Intent加入队列中,然后开启一条新的worker线程来处理该Intent。由于IntentService会使用单独的线程来完成该耗时任务,因此启动MyIntentService不会阻塞前台线程,程序界面就不会失去响应。
参考:http://wear.techbrood.com/reference/android/app/Service.html