Android(安卓)AsyncTask使用方法(防止内存泄露)
目录
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
1. Params(传入参数):在执行 execute(Params... params) 任务方法时传入。
2. Progress(执行进度):在后台计算期间发布的进度单位。
3. Result(执行结果):后台的计算结果。 这三个参数不是每次都必须被定义使用,如果你不想使用,就定义为 Void。 如果三个参数都不使用,如下所以:
private class MyTask extends AsyncTask { ... }
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() 的返回值,如果可能的话(例如在一个循环中进行检查),比如下面例子:
@Overrideprotected String doInBackground(String... strings) { for (int i = 1; i <= 10; i++) {··· //在for循环中进行周期性检查,检查异步任务是否被取消 if (isCancelled()) { break; } ··· } ··· return result;}
2. 简单Demo上手
点击 Start AsyncTask 按钮,开始执行异步任务,点击 Stop 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 * 3个泛型参数指定类型: * Params(输入参数): String类型 * Progress(执行进度): Integer类型 * Result(执行结果): String类型 */ private class MyAsyncTask extends AsyncTask { /** * 在后台任务开始计算执行前执行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() 方法,实现中断线程任务的效果如下:
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() { private var weakRef: WeakReference? = 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 也给了我们的警告提示!如下图所示:
This AsyncTask class should be static or leaks might occur (这个AsyncTask类应该是静态的,否则可能会发生泄漏),解决的方法呢,就是将该内部类写成静态的内部类,然后对 Activity(或者的需要的 Context)采用 WeakReference 弱引用, 方法如下所示:
// 1. 对 Activity 进行弱引用private WeakReference activityWeakReference;MyAsyncTask(Activity activity) { activityWeakReference = new WeakReference<>(activity);}// 2. 对 Context 进行弱引用private WeakReference contextWeakReference;MyAsyncTask(Context context) { contextWeakReference = new WeakReference<>(context);}
下面,我们将刚刚的 MyAsyncTask.java 进行重构,将 MyAsyncTask 改成静态的内部类,然后对 AsyncTaskActivity 进行弱引用,如下所示:
private static class MyAsyncTask extends AsyncTask { private WeakReference 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~
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用
- python list.sort()根据多个关键字排序的方法实现
- android上一些方法的区别和用法的注意事项
- android实现字体闪烁动画的方法
- Android中dispatchDraw分析
- Android四大基本组件介绍与生命周期
- Android(安卓)MediaPlayer 常用方法介绍
- 在Fragment中设置控件点击方法,执行失败。