public abstract class IntentService extends Service {
.......
}
IntentService是一个抽象类,继承Service。
IntentService因为是一个Service,在后台不会轻易被系统杀死。他能够在onHandleIntent中接收intent请求。然后在子线程中按顺序处理。
特点:
优先级比较高的,用于串行执行异步任务,并且是一个会自尽的Service(stopSelf())。当任务执行完成以后,IntentService会自动停止。
启动IntentService与启动普通的Service一样。可以启动IntentService多次。每一个耗时的操作都会以工作队列的方式在IntentService的OnHandleIntent回调中执行,并且只有一个工作线程。(由HandlerThread开启一个新的线程)
请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。 那么,用 IntentService 有什么好处呢?首先,我们省去了在 Service 中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止 Service。
而Service 是长期运行在后台的应用程序组件。
Service 不是一个单独的进程,它和应用程序在同一个进程中,Service 也不是一个线程,它和线程没有任何关系,所以它不能直接处理耗时操作。如果直接把耗时操作放在 Service 的 onStartCommand() 中,很容易引起 ANR .如果有耗时操作就必须开启一个单独的线程来处理。
源码解析:
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
//内部创建的 Handler
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//调用这个方法处理数据
onHandleIntent((Intent)msg.obj);
//处理完就自尽了
stopSelf(msg.arg1);
}
}
//子类需要重写的构造函数,参数是服务的名称
public IntentService(String name) {
super();
mName = name;
}
//设置当前服务被意外关闭后是否重新
//如果设置为 true,onStartCommand() 方法将返回 Service.START_REDELIVER_INTENT,这样当
//当前进程在 onHandleIntent() 方法返回前销毁时,会重启进程,重新使用之前的 Intent 启动这个服务
//(如果有多个 Intent,只会使用最后的一个)
//如果设置为 false,onStartCommand() 方法返回 Service.START_NOT_STICKY,当进程销毁后也不重启服务
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
//创建时启动一个 HandlerThread
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//拿到 HandlerThread 中的 Looper,然后创建一个子线程中的 Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
//将 intent 和 startId 以消息的形式发送到 Handler
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit(); //值得学习的,在销毁时退出 Looper
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
工作流程:
HandlerThread
默认的工作线程HandlerThread
的 Looper
创建了一个 Handler
,这个 Handler
执行在子线程onStartCommand()
中调用 onStart()
,然后在 onStart()
中将 intent 和 startId 以消息的形式发送到 HandlerHandler
中将消息队列中的 Intent
按顺序传递给 onHandleIntent()
方法stopSelf()
这个 stopSelf()
方法传递了一个 id,这个 id 是启动服务时 IActivityManager
分配的 id,当我们调用 stopSelf(id)
方法结束服务时,IActivityManager
会对比当前 id 是否为最新启动该服务的 id,如果是就关闭服务。只有当最后一次启动 IntentService 的任务执行完毕才会关闭这个服务。IntentService 中除了 onHandleIntent
方法其他都是运行在主线程的
案例:
public class DownLoadService extends IntentService {
private static final String TAG = "DownloadService";
public static final String DOWNLOADURL = "download_url";
public static final int DOWN_START = 0;
public static final int DOWN_FINISH = 1;
private static Handler mUIHandler;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public DownLoadService() {
super(TAG);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
String url = intent.getStringExtra(DOWNLOADURL);
if (mUIHandler != null) {
//下载开始,通知主线程
Message startMsg = mUIHandler.obtainMessage(DOWN_START, "\n 开始下载 @" + System.currentTimeMillis() + "\n" + url);
mUIHandler.sendMessage(startMsg);
}
SystemClock.sleep(4000); //模拟下载
if (mUIHandler != null) {
//下载完成,通知主线程
Message finishMsg = mUIHandler.obtainMessage(DOWN_FINISH, "\n 下载完成 @" + System.currentTimeMillis() + "\n" + url);
mUIHandler.sendMessage(finishMsg);
}
}
public static void setUiHandler(Handler handler) {
mUIHandler = handler;
}
}
Activity:
public class MainActivity extends RxAppCompatActivity implements View.OnClickListener, Handler.Callback {
TextView mTvStartMsg;
TextView mTvFinishMsg;
Button mBtnStartDownload;
private Handler mUIHandler;
private List urlList = Arrays.asList("https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg",
"https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg",
"https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvStartMsg = (TextView) findViewById(R.id.tv_start_msg);
mTvFinishMsg = (TextView) findViewById(R.id.tv_finish_msg);
mBtnStartDownload = (Button) findViewById(R.id.btn_start_download);
mBtnStartDownload.setOnClickListener(this);
mUIHandler = new Handler(this);
DownLoadService.setUiHandler(mUIHandler);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_start_msg:
break;
case R.id.tv_finish_msg:
break;
case R.id.btn_start_download:
Intent intent = new Intent(this,DownLoadService.class);
for (String url : urlList) {
intent.putExtra(DownLoadService.DOWNLOADURL, url);
startService(intent);
SystemClock.sleep(4000);
Log.i("onion","startService:"+url);
}
mBtnStartDownload.setEnabled(false);
break;
}
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case DownLoadService.DOWN_FINISH:
Log.i("onion","开始下载: "+ msg.obj);
break;
case DownLoadService.DOWN_START:
Log.i("onion","下载完成: "+ msg.obj);
break;
}
return true;
}
}
结果:
07-18 08:29:19.942 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
07-18 08:29:23.944 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:27.945 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg
07-18 08:29:27.948 4418-4418/lib.com.myapplication I/onion: 下载完成:
开始下载 @1531916967948
https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
07-18 08:29:31.949 4418-4418/lib.com.myapplication I/onion: 开始下载:
下载完成 @1531916971948
https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
下载完成:
开始下载 @1531916971949
https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:35.949 4418-4418/lib.com.myapplication I/onion: 开始下载:
下载完成 @1531916975949
https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:35.950 4418-4418/lib.com.myapplication I/onion: 下载完成:
开始下载 @1531916975950
https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg
07-18 08:29:39.951 4418-4418/lib.com.myapplication I/onion: 开始下载:
下载完成 @1531916979951
https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg
IntentService内部的HandlerThread 继承自 Thread,内部封装了 Looper,在这里新建线程并启动,所以启动 IntentService 不需要新建线程。IntentService 中使用的 Handler、Looper、MessageQueue 机制把消息发送到子线程中去执行的,所以多次启动 IntentService 不会重新创建新的服务和新的线程,只是把消息加入消息队列中等待执行,而如果服务停止,会清除消息队列中的消息,后续的事件得不到执行。