Java 中可以使用 Thread 对象来处理异步逻辑:
Thread thread = new Thread(new Runnable() {
@override
public void run(){
// logic code...
}
});
thread.start();
但是使用 Thread 时需要处理的逻辑比较复杂,比如:
- 线程的创建和释放;
- 线程的开启和停止;
- 线程池管理;
所以,类似于 iOS 中也一般不适用 NSThread,在 Android 中一般也不使用 Thread 对象来处理一步逻辑,因为封装度不够,实用性太差;
2. AsynTask使用步骤:
- 创建一个类,继承自 AsyncTask;
- 设置 AsyncTask 的第一个参数,表示参数类型。不能使用 java 原始数据类型,比如 int、char,要使用对象类型,比如 String、Integer、model,当然也可以使用 Void 来表示无参数;
- 第二个参数表示进度,即向外界传递出去的数据类型,即 onProgressUpdate 方法中的入参类型;
- 第三个参数表示返回数据的类型;
- 创建并调用 execute(param)方法;
AsyncTask 中方法的含义:
- onPreExcute:线程任务执行之前,进行一些初始化操作;
- OnPostExcute:线程任务执行完毕之后,做一些清除的工作,比如数据库连接。入参类型和方法中的返回参数类型相对应,可以获取到线程任务的执行结果;
- doInBackground:唯一一个在异步线程执行的方法,异步代码需要写在该方法中;
- publishProgress:可以使用在 doInBackground 中,实时向外界(主线程)传送任务的执行进度;
- onProgressUpdate:调用 publishProgress 方法后可以在该方法中获取进度的回调,进而可以执行一些 UI 操作;
- onPostExecute:任务执行完毕,即 doInBackground 执行完毕后该方法会被调用,且 doInBackground 中 return 的值会被传递到该方法中;
示例代码如下:
public class MainActivity extends AppCompatActivity {
MainAsyncTask asyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
asyncTask = new MainAsyncTask();
// 入参会被传递到doInBackground方法中,可以传递多个值
asyncTask.execute(100);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (asyncTask != null) {
asyncTask.cancel(true);
}
}
public class MainAsyncTask extends AsyncTask {
TextView textView;
@Override
protected void onPreExecute() {
super.onPreExecute();
// 该方法在线程任务开始执行之前被调用
textView = findViewById(R.id.textView);
}
@Override
protected String doInBackground(Integer... integers) {
// 只有该方法在子线程执行,所以耗时逻辑需要在这里实现
for (int i = 0; i < integers[0]; i++) {
// 将当前任务的进度传递出去,这里可以传递多个值
publishProgress(i);
// 模拟耗时
SystemClock.sleep(100);
}
return "Task Finished";
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 该方法会接收到publishProgress中传递的进度
// values数组元素个数和publishProgress传递的个数一样
textView.setText("loading:" + values[0] + "%......");
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// 任务执行完毕,即doInBackground方法return,之后会调用该方法,入参为doInBackground方法return的值
textView.setText(s);
}
}
}
特别注意:
- 只有 doInBackground 方法在其他线程执行,剩余方法都是在 UI 线程执行;
因此,可以直接在 onProgressUpdate 或者是 onPostExecute 方法中进行 UI 操作;
3. AsyncTask中内存泄漏的处理当 Activity 被 Destory 时,需要主动 cancle 对应的 AsyncTask,否则会造成内存泄漏,需要在 onDestroy 方法中主动清除:
@Override
protected void onDestroy() {
super.onDestroy();
if (asyncTask != null) {
if (!asyncTask.isCancelled()) {
asyncTask.cancel(true);
}
}
}
PS:有资料说即使 cancel 方法的参数 mayInterruptIfRunning 为 True,AsyncTask 也不会中断,此时需要在 doInBackground 中加入 while() 判断:
@Override
protected String doInBackground(Integer... integers) {
// 只有该方法在子线程执行,所以耗时逻辑需要在这里实现
while(!this.isCancelled()){
for (int i = 0; i < integers[0]; i++) {
// 将当前任务的进度传递出去,这里可以传递多个值
publishProgress(i);
// 模拟耗时
SystemClock.sleep(100);
}
}
return "Task Finished";
}
当然,还可以将 AsyncTask 标记为 static,此时内部类变成了静态内部类。Java 中 文档中指出,一旦内部类使用static修饰,那么此时这个内部类就升级为顶级类。所以,从某种意义上说,静态内部类已经不是内部类的一种了。所以:
- 静态内部类不能再使用外部类中的实例方法、属性;
- 本质是静态内部类不再引用外部类了,因此也不存在 Activity 无法被释放的情况,也就不会造成内存泄漏,最多就是 Activity 被回收,AsyncTask 仍在执行;
AsyncTask之所以可能会产生内存泄露,是因为:
- 我们一般在组件内部,以内部类的方式创建AsyncTask,而java里面,内部类是默认持有外部类的引用。
- 我们的加载数据是在子线程中执行,但是java里面有没有提供很好的中断机制来中断线程 (有中断方法,但是如果你不针对性的处理,这个方法也没什么卵用),这样就导致组件生命周期已经结束,应该被GC回收,但是由于子线程正在执行,内部类无法回收,进而导致组件无法正常回收,所以造成了内存泄露 (Handler导致内存泄露也是类似的道理)。
来源:Android 中更安全的使用 AsyncTask



