目录

1. 定义

1.1 AsyncTask 的三个泛形参数

1.2 AsyncTask的4个核心方法

1.3 取消任务

2. 简单Demo上手

2.1 Java 版本

2.2 Kotlin 版本

3. AsyncTask 如何防止内存泄露


1. 定义

官方解释:AsyncTask可以正确及方便地使用UI线程。此类允许您执行后台操作并在UI线程上发布结果,而无需通过操作Thread和Handler。AsyncTasks应该用于短操作(最多几秒钟)。异步任务在后台线程运行计算,其结果在UI线程上发布。

1.1 AsyncTask 的三个泛形参数

java.lang.Object
   ↳	android.os.AsyncTask<Params, Progress, Result>

1. Params(传入参数):在执行 execute(Params... params) 任务方法时传入。

2. Progress(执行进度):在后台计算期间发布的进度单位。

3. Result(执行结果):后台的计算结果。 这三个参数不是每次都必须被定义使用,如果你不想使用,就定义为 Void。 如果三个参数都不使用,如下所以:

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

1.2 AsyncTask的4个核心方法

当 AsyncTask 被执行时,会经历4个步骤,分别是onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute()。 分别是:

1、onPreExecute():在 UI 线程上工作,在任务执行 doInBackground() 之前调用。此步骤通常用于设置任务,例如在用户界面中显示进度条。

2、doInBackground(Params...params):在子线程中工作,在 onPreExecute() 方法结束后执行,这一步被用于在后台执行长时间的任务,Params 参数通过 execute(Params) 方法被传递到此方法中。任务执行结束后,将结果传递给 onPostExecute(Result) 方法,同时我们可以通过 publishProgress(Progress) 方法,将执行进度发送给 onProgressUpdate(Progress) 方法。

3、onProgressUpdate(Progress...values):在 UI 线程上工作,会在 doInBackground() 中调用 publishProgress(Progress) 方法后执行,此方法用于在后台计算仍在执行时(也就是 doInBackgound() 还在执行时)将计算执行进度通过 UI 显示出来。例如,可以通过动画进度条或显示文本字段中的日志,从而方便用户知道后台任务执行的进度。

4、onPostExecute(Result result):在 UI 线程上工作,在任务执行完毕(即 doInBackground(Result) 执行完毕)并将执行结果传过来的时候工作。

1.3 取消任务

一个任务可以通过调用 cancel() 方法在任何时间取消。调用此方法后再调用 isCancelled() 方法,返回 true。调用此方法后,onPostExecute() 方法将在 onCancel() 方法返回后调用,而不是在 doInBackground() 返回结果后调用。为了确保任务被尽快取消,你应该在 doInBackground() 方法中进行周期性地检查 isCancelled() 的返回值,如果可能的话(例如在一个循环中进行检查),比如下面例子:

@Override
protected String doInBackground(String... strings) {
    for (int i = 1; i <= 10; i++) {
		···
        //在for循环中进行周期性检查,检查异步任务是否被取消
        if (isCancelled()) {
            break;
        }
        ···
    }

    ···
    return result;
}

2. 简单Demo上手

点击 Start AsyncTask 按钮,开始执行异步任务,点击 Stop AsyncTask 按钮,取消任务执行。效果如下所示:

android AsyncTask 出现卡死 android asynctask内存泄漏_AsyncTask 防止内存泄露例子

2.1 Java 版本

详细代码:

public class AsyncTaskActivity extends AppCompatActivity implements View.OnClickListener {
    private ProgressBar progressBar;
    private TextView displayTv;
    private Button startBtn, stopBtn;
    private MyAsyncTask myAsyncTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task);

        progressBar = findViewById(R.id.progress_bar);
        displayTv = findViewById(R.id.display_tv);
        startBtn = findViewById(R.id.start_btn);
        stopBtn = findViewById(R.id.stop_btn);
        myAsyncTask = new MyAsyncTask();

        startBtn.setOnClickListener(this);
        stopBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.start_btn:
                myAsyncTask.execute("Jere test");
                //AsyncTask只能执行一次,如果你在AsyncTask执行过程中再次调用 execute() 执行它,就会跳IllegalStateException异常
                //java.lang.IllegalStateException: Cannot execute task: the task is already running.
                startBtn.setEnabled(false);
                break;
            case R.id.stop_btn:
                //中断线程执行
                myAsyncTask.cancel(true);
                break;
            default:
                break;
        }
    }

    /**
     * 创建MyAsyncTask类, 继承AsyncTask类 -> AsyncTask<Params, Progress, Result>
     * 3个泛型参数指定类型:
     * Params(输入参数): String类型
     * Progress(执行进度): Integer类型
     * Result(执行结果): String类型
     */
    private class MyAsyncTask extends AsyncTask<String, Integer, String> {

        /**
         * 在后台任务开始计算执行前执行onPreExecute()操作
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            displayTv.setText("onPreExecute: start! ");
            Toast.makeText(AsyncTaskActivity.this, "onPreExecute", Toast.LENGTH_SHORT).show();
        }

        /**
         * 接收输入参数,执行后台任务中的计算工作(耗时操作),计算结束,返回计算结果
         * @param strings Params输入参数,在主线程执行execute()传入的参数
         * @return 返回执行结果给onPostExecute()
         */
        @Override
        protected String doInBackground(String... strings) {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //通过调用publishProgress()方法,将执行进度传递给onProgressUpdate()
                publishProgress(i);
            }

            String result;
            result = Arrays.toString(strings) + " return from doInBackground";
            return result;
        }

        /**
         * 接收后台计算执行进度,在UI线程中通过ProgressBar现实执行进度。
         * @param values : 执行进度,通过publishProgress()传入
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            //得到一个整型数组,但每次里面只有一个元素,所有通过values[0]就可以拿到当前的执行进度
            progressBar.setProgress(values[0]);
            displayTv.setText("onProgressUpdate: value = " + values[0]);
        }

        /**
         * 接收后台任务计算结束返回的结果,在UI线程上显示出来
         * @param s :后台计算结束返回的执行结果,由doInBackground()返回
         */
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            displayTv.setText("onPostExecute: " + s);
            Toast.makeText(AsyncTaskActivity.this, "onPostExecute", Toast.LENGTH_SHORT).show();
        }

        /**
         * 在主线程中调用cancel()方法,执行onCancelled(),取消执行后台任务
         *
         * 当任务计算完成时,无法取消任务,或者已经取消任务后不可再次取消
         */
        @Override
        protected void onCancelled() {
            super.onCancelled();
            displayTv.setText("onCancelled");
            progressBar.setProgress(0);
            Toast.makeText(AsyncTaskActivity.this, "onCancelled", Toast.LENGTH_SHORT).show();
        }

    }

}

点击 “Stop AsyncTask” 按钮, 执行 onCancelled() 方法,实现中断线程任务的效果如下:

android AsyncTask 出现卡死 android asynctask内存泄漏_Android多线程之AsyncTask_02

2.2 Kotlin 版本

class TestAsyncTaskActivity : AppCompatActivity(), View.OnClickListener {
    private var mAsyncTask: MyAsyncTask? = null

    companion object {
        const val TAG = "JereTest"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_async_task)

        mAsyncTask = MyAsyncTask(this)

        startBtn.setOnClickListener(this)
        cancelBtn.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.startBtn -> {
                mAsyncTask?.execute("jereTest")
                startBtn.isEnabled = false
            }
            R.id.cancelBtn -> mAsyncTask?.cancel(true)
        }
    }

    class MyAsyncTask(activity: TestAsyncTaskActivity) : AsyncTask<String, Int, String>() {
        private var weakRef: WeakReference<TestAsyncTaskActivity>? = null

        init {
            weakRef = WeakReference(activity)
        }

        override fun onPreExecute() {
            super.onPreExecute()
            val activity = weakRef?.get()
            if (activity != null && !activity.isFinishing) {
                Toast.makeText(activity, "MyAsyncTask onPreExecute()", Toast.LENGTH_SHORT).show()
            }
        }

        override fun doInBackground(vararg params: String?): String {
            for (i in 0..10) {
                try {
                    Thread.sleep(500)
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }

                //通过调用publishProgress()方法,将执行进度传递给onProgressUpdate()
                publishProgress(i)
                if (isCancelled) {
                    break
                }
            }
            return params[0]!!
        }

        override fun onProgressUpdate(vararg values: Int?) {
            super.onProgressUpdate(*values)
            val activity = weakRef?.get()
            if (activity != null && !activity.isFinishing) {
                activity.progressBar.progress = values[0]!!
                activity.displayTv.text = "onProgressUpdate: value = " + values[0]
            }
        }

        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)
            val activity = weakRef?.get()
            if (activity != null && !activity.isFinishing) {
                Toast.makeText(activity, "MyAsyncTask onPostExecute()" + result, Toast.LENGTH_SHORT)
                    .show()
            }
        }

        override fun onCancelled() {
            super.onCancelled()
            Log.e(TAG, "click cancel button, cancel task execute")
            val activity = weakRef?.get()
            if (activity != null && !activity.isFinishing) {
                Toast.makeText(activity, "cancel task", Toast.LENGTH_SHORT).show()
            }
        }

    }

}

 

3. AsyncTask 如何防止内存泄露

如上方 Demo 所示的代码,我们在 Activity 中创建了 MyAsyncTask 内部类, 来进行异步处理,然后引用 Activity 中的全局变量 progressBar 及 displayTv 来显示异步处理完的结果。这样的用法带来的结果就是,在实际使用中,我们都是将耗时的操作放到 AsyncTask 中的 doInBackground() 子线程中处理,其操作处理周期会超出该 Activity 的生命周期,但由于其一直引用着该 Activity,导致该 Activity 不会被垃圾回收机制回收,从而导致内存泄露。所以 Android Studio 也给了我们的警告提示!如下图所示:

android AsyncTask 出现卡死 android asynctask内存泄漏_AsyncTask 防止内存泄露例子_03

This AsyncTask class should be static or leaks might occur (这个AsyncTask类应该是静态的,否则可能会发生泄漏),解决的方法呢,就是将该内部类写成静态的内部类,然后对 Activity(或者的需要的 Context)采用 WeakReference 弱引用, 方法如下所示:

// 1. 对 Activity 进行弱引用
private WeakReference<Activity> activityWeakReference;

MyAsyncTask(Activity activity) {
    activityWeakReference = new WeakReference<>(activity);
}


// 2. 对 Context 进行弱引用
private WeakReference<Context> contextWeakReference;

MyAsyncTask(Context context) {
    contextWeakReference = new WeakReference<>(context);
}

下面,我们将刚刚的 MyAsyncTask.java 进行重构,将 MyAsyncTask 改成静态的内部类,然后对 AsyncTaskActivity 进行弱引用,如下所示:

private static class MyAsyncTask extends AsyncTask<String, Integer, String> {
    private WeakReference<AsyncTaskActivity> asyncTaskActivityWeakReference;

    MyAsyncTask(AsyncTaskActivity asyncTaskActivity) {
        asyncTaskActivityWeakReference = new WeakReference<>(asyncTaskActivity);
    }

    /**
     * 在后台任务开始计算执行前执行onPreExecute()操作
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        AsyncTaskActivity asyncTaskActivity = asyncTaskActivityWeakReference.get();
        //只有在 AsyncTaskActivity 没被销毁的时候才显示 displayTv 及 Toast.
        if (asyncTaskActivity != null && !asyncTaskActivity.isFinishing()) {
            asyncTaskActivity.displayTv.setText("onPreExecute: start! ");
            Toast.makeText(asyncTaskActivity, "onPreExecute", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 接收输入参数,执行后台任务中的计算工作(耗时操作),计算结束,返回计算结果
     * @param strings Params输入参数,在主线程执行execute()传入的参数
     * @return 返回执行结果给onPostExecute()
     */
    @Override
    protected String doInBackground(String... strings) {
        for (int i = 1; i <= 10; i++) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //通过调用publishProgress()方法,将执行进度传递给onProgressUpdate()
            publishProgress(i);
        }

        String result;
        result = Arrays.toString(strings) + " return from doInBackground";
        return result;
    }

    /**
     * 接收后台计算执行进度,在UI线程中通过ProgressBar现实执行进度。
     * @param values : 执行进度,通过publishProgress()传入
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        AsyncTaskActivity asyncTaskActivity = asyncTaskActivityWeakReference.get();
        //只有在 AsyncTaskActivity 没被销毁的时候才显示 progressBar 及 displayTv.
        if (asyncTaskActivity != null && !asyncTaskActivity.isFinishing()) {
            //得到一个整型数组,但每次里面只有一个元素,所有通过values[0]就可以拿到当前的执行进度
            asyncTaskActivity.progressBar.setProgress(values[0]);
            asyncTaskActivity.displayTv.setText("onProgressUpdate: value = " + values[0]);
        }
    }

    /**
     * 接收后台任务计算结束返回的结果,在UI线程上显示出来
     * @param s :后台计算结束返回的执行结果,由doInBackground()返回
     */
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        AsyncTaskActivity asyncTaskActivity = asyncTaskActivityWeakReference.get();
        //只有在 AsyncTaskActivity 没被销毁的时候才显示 displayTv 及 Toast.
        if (asyncTaskActivity != null && !asyncTaskActivity.isFinishing()) {
            asyncTaskActivity.displayTv.setText("onPostExecute: " + s);
            Toast.makeText(asyncTaskActivity, "onPostExecute", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 在主线程中调用cancel()方法,执行onCancelled(),取消执行后台任务
     *
     * 当任务计算完成时,无法取消任务,或者已经取消任务后不可再次取消
     */
    @Override
    protected void onCancelled() {
        super.onCancelled();
        AsyncTaskActivity asyncTaskActivity = asyncTaskActivityWeakReference.get();
        //只有在 AsyncTaskActivity 没被销毁的时候才显示 progressBar displayTv Toast.
        if (asyncTaskActivity != null && !asyncTaskActivity.isFinishing()) {
            asyncTaskActivity.displayTv.setText("onCancelled");
            asyncTaskActivity.progressBar.setProgress(0);
            Toast.makeText(asyncTaskActivity, "onCancelled", Toast.LENGTH_SHORT).show();
        }
    }

}

 

具体代码请看 -> 源代码

Peace~