IntentService 服务 工做线程 stopself MD

Markdown版本笔记 个人GitHub首页 个人博客 个人微信 个人邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

服务 IntentService 工做线程 stopself MDandroid


目录

介绍

Google 为方便开发者使用,提升开发效率,封装了 IntentService 和 HandlerThread。HandlerThread 继承自 Thread,内部封装了 Looper。git

IntentService 是继承自 Service 并处理异步请求的一个类,在 IntentService 内有一个工做线程来处理耗时操做,当任务执行完后,IntentService 会自动中止,不须要咱们去手动结束。若是启动 IntentService 屡次,那么每个耗时操做会以工做队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,执行完自动结束。github

同一个IntentService只会新建一个线程,由于其采用的就是相似主线程的机制。express

IntentService 中使用的 Handler、Looper、MessageQueue 机制把消息发送到线程中去执行的,因此屡次启动 IntentService 不会从新建立新的服务和新的线程,只是把消息加入消息队列中等待执行,而若是服务中止,会清除消息队列中的消息,后续的事件得不到执行。安全

IntentService 类
IntentService是Service的基类,可根据须要处理异步请求(表示为Intents)。客户端经过Context#startService(Intent)调用发送请求; 根据须要启动服务,使用工做线程依次处理每一个Intent,并在工做完成时自行中止。微信

IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through Context#startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.app

这种“工做队列处理器”模式一般用于从应用程序的主线程卸载任务。IntentService类的存在就是为了简化此模式并处理机制。要使用它,请扩展IntentService并实现onHandleIntent(Intent)。 IntentService将接收Intents,启动一个工做线程,并根据须要中止服务。less

This "work queue processor" pattern is commonly used to offload tasks from an application's main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.异步

全部请求都在一个工做线程上处理 - 它们可能须要运行很长时间(而且不会阻止应用程序的主循环),但一次只能处理一个请求。async

All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.

onHandleIntent 方法
在工做线程上调用此方法并请求处理。一次只处理一个Intent,但处理过程发生在独立于其余应用程序逻辑运行的工做线程上。所以,若是此代码须要很长时间,它将阻止对同一个IntentService的其余请求,但它不会阻止其余任何内容。处理完全部请求后,IntentService会自行中止,所以您不该该调用 stopSelf。

This method is invoked on the worker thread with a request to process. Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call stopSelf.

测试

参考

最佳应用场景:后台队列按顺序异步下载、下载完毕后自动结束服务!

测试代码

IntentService 源码

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            Log.i("bqt", "开始onHandleIntent");
            onHandleIntent((Intent) msg.obj);
            Log.i("bqt", "结束onHandleIntent,尝试stopSelf");
            stopSelf(msg.arg1);//处理完毕后自动调用stopSelf结束服务,注意若是此时还有未执行完的message则不会结束!
        }
    }

    public IntentService(String name) {
        super();
        mName = name; //Used to name the worker thread, important only for debugging.
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock 若是有一个部分唤醒锁的选项会更好
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId); //You should not override this method for your IntentService. Instead, override onHandleIntent
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        //每启动一次onStart方法,就会把消息数据发给mServiceHandler,至关于发送了一次Message消息给HandlerThread的消息队列
        //mServiceHandler会把数据传给onHandleIntent方法,因此每次onStart以后都会调用咱们重写的onHandleIntent方法去处理数据
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null; //Unless you provide binding for your service, you don't need to implement this method
    }

    protected abstract void onHandleIntent(Intent intent);
}

MyIntentService

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("包青天的工做线程");
    }

    @Override
    protected void onHandleIntent(Intent intent) {//注意,由于这里是异步操做,因此这里不能直接使用Toast。
        //全部请求的Intent记录会按顺序加入到【队列】中并按顺序【异步】执行,而且每次只会执行【一个】工做线程
        //当全部任务执行完后IntentService会【自动】中止
        Log.i("bqt", "onHandleIntent");
        try {
            int count = 0;
            sendThreadStatus("线程启动", count);
            Thread.sleep(500);
            while (count <= 100) {
                count++;
                Thread.sleep(30);
                sendThreadStatus("线程运行中...", count);
            }
            sendThreadStatus("线程结束", count);
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("bqt", "onCreate");
        sendServiceStatus("服务启动");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("bqt", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("bqt", "onDestroy");
        sendServiceStatus("服务结束");
    }

    // 发送服务状态信息
    private void sendServiceStatus(String status) {
        Intent intent = new Intent(MainActivity.ACTION_TYPE_SERVICE);
        intent.putExtra("status", status);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }

    // 发送线程状态信息
    private void sendThreadStatus(String status, int progress) {
        Intent intent = new Intent(MainActivity.ACTION_TYPE_THREAD);
        intent.putExtra("status", status);
        intent.putExtra("progress", progress);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
}

Activity

public class MainActivity extends ListActivity {

    public static final String ACTION_TYPE_SERVICE = "com.bqt.type.service";
    public static final String ACTION_TYPE_THREAD = "com.bqt.type.thread";

    ProgressBar progressBar;
    TextView tvServiceStatus;
    TextView tvThreadStatus;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = {"启动 IntentService",
            "结束 IntentService",
            "",};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Arrays.asList(array)));

        tvServiceStatus = new TextView(this);
        tvThreadStatus = new TextView(this);
        progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
        progressBar.setIndeterminate(false);
        progressBar.setMax(100);
        getListView().addFooterView(tvServiceStatus);
        getListView().addFooterView(tvThreadStatus);
        getListView().addFooterView(progressBar);

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_TYPE_SERVICE);
        intentFilter.addAction(ACTION_TYPE_THREAD);
        LocalBroadcastManager.getInstance(this).registerReceiver(new MyBroadcastReceiver(), intentFilter);
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        switch (position) {
            case 0:
                startService(new Intent(this, MyIntentService.class));// 像启动 Service 那样启动 IntentService
                break;
            case 1:
                stopService(new Intent(this, MyIntentService.class));
                break;
            case 2:

                break;
        }
    }

    class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case ACTION_TYPE_SERVICE:
                    tvServiceStatus.setText("服务状态:" + intent.getStringExtra("status"));
                    break;
                case ACTION_TYPE_THREAD:
                    int progress = intent.getIntExtra("progress", 0);
                    tvThreadStatus.setText("线程状态:" + intent.getStringExtra("status"));
                    progressBar.setProgress(progress);
                    break;
            }
        }
    }
}

测试情形

启动一次

点击[启动 IntentService],服务运行,线程计数完成后,服务和线程都结束。

15:18:45.488: onCreate
15:18:45.489: onStartCommand
15:18:45.489: onStart
15:18:45.489: 开始onHandleIntent
15:18:45.489: onHandleIntent        //【开始在工做线程执行任务】
15:18:49.651: 结束onHandleIntent,尝试stopSelf        //【线程执行完毕后调用stopSelf结束任务】
15:18:49.659: onDestroy        //【服务成功被结束】

启动一次后当即结束

点击[启动 IntentService],服务运行,线程计数完成前,点击[中止 IntentService],服务结束,线程计数完成后线程结束。

15:43:17.569: onCreate
15:43:17.570: onStartCommand
15:43:17.570: onStart
15:43:17.570: 开始onHandleIntent
15:43:17.570: onHandleIntent

15:43:18.895: onDestroy        //【调用stopService会当即结束服务,但工做线程并不会结束,而会正常的执行】

15:43:21.724: 结束onHandleIntent,尝试stopSelf        //【线程正常执行完毕,此前服务已经结束了】

连续启动两次

点击两次[启动 IntentService],服务运行,第一次线程计数完成后,进行第二次线程计数,两次完成后,服务和线程都结束。

14:36:30.794: onCreate
14:36:30.795: onStartCommand
14:36:30.795: 开始onHandleIntent
14:36:30.796: onHandleIntent

14:36:32.218: onStartCommand        //【在执行第一个任务时启动第二个任务】

14:36:34.986: 结束onHandleIntent,尝试stopSelf  //【由于此startId不是最新的startId,因此服务结束失败】
-----------------------------------------
14:36:34.993: 开始onHandleIntent
14:36:34.993: onHandleIntent
14:36:39.178: 结束onHandleIntent,尝试stopSelf //【第二个任务完成时的startId是最新的startId,因此能成功结束服务】
-----------------------------------------
14:36:39.188: onDestroy        //【服务结束】

连续启动两次后当即结束

点击两次[启动 IntentService],服务运行,在第一次线程计数完成前,点击[中止 IntentService],服务结束,第一次线程计数结束后不进行第二次计数。

16:34:05.052: onCreate
16:34:05.054: onStartCommand
16:34:05.054: onStart
16:34:05.055: 开始onHandleIntent
16:34:05.055: onHandleIntent

16:34:05.463: onStartCommand
16:34:05.463: onStart

16:34:05.985: onDestroy        //【调用stopService会当即结束服务,但工做线程并不会结束,而会正常的执行】

16:34:09.237: 结束onHandleIntent,尝试stopSelf        //【由于服务已经结束,因此不会再执行未开启的任务】

Service 的 stopself 方法详解

结论

  • stopSelf():直接中止服务,和调用 Context#stopService 的效果彻底一致。
  • stopSelf(int startId):在其参数 startId 跟最后启动该 service 时生成的 ID 相等时才会执行中止服务。
  • stopSelfResult(int startId):和 stopSelf(int startId) 的区别仅仅是有返回值,当成功中止服务时返回 true,不然返回 false。
  • 每次调用 startService 时 Service 都会从新赋值 startId 值,从1开始,若是 Service 没有被关闭,那这个值会顺序增长。
  • IntentService 在调用 stopSelf(int startId) 结束本身时,老是拿最新的 startId 和上一次传入的 startId 做比较,若是相等则结束本身,若是不相等则不结束,而IntentService 中维护着一个消息队列,要想结束本身必须等到消息队列没有消息了。

使用场景
若是同时有多个服务启动请求发送到onStartCommand(),不该该在处理完一个请求后调用stopSelf(),由于在调用此函数销毁service以前,可能 service 又接收到新的启动请求,若是此时 service 被销毁,新的请求将得不处处理。此状况应该调用stopSelf(int startId),例如 IntentService 中,在执行完异步任务后,就是经过调用stopSelf(int startId)来结束服务的。

源码
先看看 onStartCommand 方法。
每次经过 startService 方式启动服务时,都会调用 onStartCommand 方法,此方法中有一个 startId,这个 startId 表明启动服务的次数,也表明着某一特定启动请求。
文档中明确说明了,这个 startId 其实就是在 stopSelfResult(int) 中用的。

//startId:A unique integer representing this specific request to start. Use with stopSelfResult(int).
public int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
    onStart(intent, startId);
    return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}

再看看 stopSelf() 方法,能够看到其只是调用了stopSelf(-1)

//Stop the service, if it was previously先前 started. 
//This is the same as calling Context#stopService for this particular特定 service.
public final void stopSelf() {
    stopSelf(-1);
}

再看看 stopSelf(int startId) 方法,文档中没有什么解释,只说是旧版本中stopSelfResult的无返回值形式。

//Old version of stopSelfResult that doesn't return a result.
public final void stopSelf(int startId) {
    if (mActivityManager == null) return;
    try {
        mActivityManager.stopServiceToken(new ComponentName(this, mClassName), mToken, startId);
    } catch (RemoteException ex) {
    }
}
public final boolean stopSelfResult(int startId) {
    if (mActivityManager == null) return false; //中止失败
    try {
        return mActivityManager.stopServiceToken(new ComponentName(this, mClassName), mToken, startId);
    } catch (RemoteException ex) {
    }
    return false; //中止失败
}

如下是文档中的详细描述:

  • 若是最近启动服务时的ID就是此startId,则中止服务。

    Stop the service if the most recent time it was started was startId.

  • 这与对此特定服务调用Context#stopService的效果相同,但若是有一个来自客户端的启动请求(在执行此方法时这个请求还在onStart中,因此你没发现这个请求),则容许您安全地避免中止服务。

    This is the same as calling Context#stopService for this particular service ,but allows you to safely avoid stopping ,if there is a start request from a client ,that you haven't yet seen in #onStart.

  • 要当心调用此函数后的顺序。

    Be careful about ordering of your calls to this function.

  • 若是在为以前收到的ID调用以前使用最近收到的ID调用此函数,则不管如何都会当即中止该服务。

    If you call this function with the most-recently received ID before you have called it for previously received IDs, the service will be immediately stopped anyway.

  • 若是您最终可能无序处理ID(例如经过在不一样的线程上调度它们),那么您有责任按照收到它们的顺序中止它们。

    If you may end up processing IDs out of order (such as by dispatching them on separate threads), then you are responsible for stopping them in the same order you received them.

参数与返回值:

  • startId:The most recent start identifier received in #onStart. onStart中收到的最新启动标识符
  • Returns true if the startId matches the last start request and the service will be stopped, else false.

2019-2-13

相关文章
相关标签/搜索