结束AsyncTask和Thread的有效方案

如何结束AsyncTask

上周在项目中碰见一个问题,AsyncTask调用了cancel(true)方法后任务没有取消,造成的结果就是线程继续运行把不该返回的值返回了,源码中对cancel()的说明如下:

/**
     * 

Attempts to cancel execution of this task. This attempt will * fail if the task has already completed, already been cancelled, * or could not be cancelled for some other reason. If successful, * and this task has not started when cancel is called, * this task should never run. If the task has already started, * then the mayInterruptIfRunning parameter determines * whether the thread executing this task should be interrupted in * an attempt to stop the task.

* *

Calling this method will result in {@link #onCancelled(Object)} being * invoked on the UI thread after {@link #doInBackground(Object[])} * returns. Calling this method guarantees that {@link #onPostExecute(Object)} * is never invoked. After invoking this method, you should check the * value returned by {@link #isCancelled()} periodically from * {@link #doInBackground(Object[])} to finish the task as early as * possible.

* * @param mayInterruptIfRunning true if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete. * * @return false if the task could not be cancelled, * typically because it has already completed normally; * true otherwise * * @see #isCancelled() * @see #onCancelled(Object) */ public final boolean cancel(boolean mayInterruptIfRunning) { mCancelled.set(true); return mFuture.cancel(mayInterruptIfRunning); }

大概是说AsyncTask的cancel方法被调用后并不会真正将AsyncTask任务结束,而是将isCancelled置为true。
下面是一个demo,在AsyncTaskTest.java文件中重写doInBackground()方法,在这个方法中写一个死循环;

AsyncTaskTest.java

@Override
    protected Void doInBackground(Integer... integers) {
        if (true) {
            while(true){
                integers[0]++;
                Log.i(TAG,"doInBackground is called for "+integers[0]+"times");
                try{
                    Thread.sleep(1000L);

                }catch(Exception e){

                }
            }
        }
        return null;
    }
@Override
    protected void onCancelled(Void aVoid) {
        super.onCancelled(aVoid);
        Log.i(TAG,"AsyncTask is abnormally terminated ");
    }

在MainActivity.java中执行AsyncTaskTest任务

private void init(){
        asyncTaskTest = new AsyncTaskTest();
        asyncTaskTest.execute(0);
        cancelButton = findViewById(R.id.cancelled);
        cancelButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                asyncTaskTest.cancel(true);
            }
        });
    }

点击cancelButton时调用asyncTaskTest.cancel(true),但是Logcat还是会输出doInBackground()函数中的日志,同时onCancelled()函数也会被调用。这里证实源码中“AsyncTask的cancel方法被调用后并不会真正将AsyncTask任务结束,而是将isCancelled置为true。”的结论。

然后将AsyncTaskTest.java文件中的doInBackground()修改如下(其实就添加如果isCancelled()为true就return的逻辑):

@Override
    protected Void doInBackground(Integer... integers) {
        if (true) {
            while(true){
                if(isCancelled()){
                    return null;
                }
                integers[0]++;
                Log.i(TAG,"doInBackground is called for "+integers[0]+"times");
                try{
                    Thread.sleep(1000L);

                }catch(Exception e){

                }
            }
        }
        return null;
    }

再去点击cancelButton后发现doInBackground()函数不会再输出日志了。所以在需要终止AsyncTask时,要在doInBackground()和onPostExecute()中周期性去判断isCancelled的值,然后在业务层自己控制。

为什么要这么麻烦呢?调用AsyncTask中cancel()直接结束任务就完事儿不好吗?这里就需要我们了解AsyncTask在系统中是怎么运行的,需要去看看源码,我也不知道为什么,等我研究完了再更新。

如何结束Thread

Thread的结束和AsyncTask结束的方式如出一辙,有这三个方法interrupt()、isInterrupted()这些个名字有点秀,interrupt()类似于AsyncTask中的cancel(),isInterrupted()类似于AsyncTask中的isCancelled()。

你可能感兴趣的:(Android)