Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制。不过为了更加方便我们在子线程中更新UI元素,Android从1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程。 AsyncTask 很早就出现在Android的API里了,所以我相信大多数朋友对它的用法都已经非常熟悉。不过今天我还是准备从AsyncTask的基本用法开始讲起, 然后我们再来一起分析下AsyncTask的并行执行情况
AsyncTask的基本用法
首先来看一下AsyncTask的基本用法,由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。使用时需要继承这个类,然后调用execute()方法。注意继承时需要设定三个泛型Params,Progress和Result的类型,如AsyncTask<Void,Inetger,Void>:
- Params是指调用execute()方法时传入的参数类型和doInBackgound()的参数类型
- Progress是指更新进度时传递的参数类型,即publishProgress()和onProgressUpdate()的参数类型
- Result是指doInBackground()的返回值类型
- doInBackgound() 这个方法是继承AsyncTask必须要实现的,运行于后台,耗时的操作可以在这里做
- publishProgress() 更新进度,给onProgressUpdate()传递进度参数
- onProgressUpdate() 在publishProgress()调用完被调用,更新进度
布局文件如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="显示异步的执行情况" android:id="@+id/tv_show"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动一个异步任务"
android:id="@+id/bt_start_AsyncTask" android:layout_below="@+id/tv_show" android:layout_alignLeft="@+id/tv_show"
android:layout_marginTop="12dp" android:focusableInTouchMode="true"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动一个异步 长耗时 任务"
android:id="@+id/bt_start_long_AsyncTask"
android:focusableInTouchMode="true"
android:layout_above="@+id/bt_toast" android:layout_alignLeft="@+id/tv_show2"
android:layout_marginBottom="13dp"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="弹出对话框"
android:id="@+id/bt_toast"
android:focusableInTouchMode="true"
android:layout_centerVertical="true" android:layout_alignLeft="@+id/bt_start_long_AsyncTask"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="显示长耗时情况"
android:id="@+id/tv_show2"
android:layout_above="@+id/bt_start_long_AsyncTask" android:layout_alignLeft="@+id/bt_start_AsyncTask"
android:layout_marginBottom="11dp"/>
</RelativeLayout>
源代码如下:
package cn.iigrowing.android.MyAsyncTask;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private Button bt_start_AsyncTask = null;
private Button bt_start_long_AsyncTask = null;
private Button bt_toast = null;
private TextView tv = null;
private TextView tv2 = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_start_AsyncTask = (Button) findViewById(R.id.bt_start_AsyncTask);
bt_start_long_AsyncTask = (Button) findViewById(R.id.bt_start_long_AsyncTask);
bt_toast = (Button) findViewById(R.id.bt_toast);
tv = (TextView) findViewById(R.id.tv_show);
tv2 = (TextView) findViewById(R.id.tv_show2);
bt_start_AsyncTask.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
update();
}
});
bt_start_long_AsyncTask.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
update2();
}
});
bt_toast.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Toast.makeText(MainActivity.this, "点击了按钮", Toast.LENGTH_LONG).show();
}
});
}
private void update() {
UpdateTextTask updateTextTask = new UpdateTextTask(this, 1, tv);
updateTextTask.execute();
}
private void update2() {
UpdateTextTask updateTextTask = new UpdateTextTask(this, 10, tv2);
updateTextTask.execute();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
class UpdateTextTask extends AsyncTask<Void, Integer, Integer> {
private Context context;
private TextView mytv = null;
private int sleepTime = 1;
UpdateTextTask(Context context, int sleepSecond, TextView tv) {
this.context = context;
sleepTime = sleepSecond;
this.mytv = tv;
}
/**
* 运行在UI线程中,在调用doInBackground()之前执行
*/
@Override
protected void onPreExecute() {
Toast.makeText(context, "开始执行", Toast.LENGTH_SHORT).show();
}
/**
* 后台运行的方法,可以运行非UI线程,可以执行耗时的方法
*/
@Override
protected Integer doInBackground(Void... params) {
int i = 0;
while (i < 10) {
i++;
publishProgress(i);
try {
Thread.sleep(1000 * sleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 运行在ui线程中,在doInBackground()执行完毕后执行
*/
@Override
protected void onPostExecute(Integer integer) {
Toast.makeText(context, "执行完毕", Toast.LENGTH_SHORT).show();
}
/**
* 在publishProgress()被调用以后执行,publishProgress()用于更新进度
*/
@Override
protected void onProgressUpdate(Integer... values) {
this.mytv.setText(this.mytv.getText() + " " + values[0]);
}
}
}
通过上面的例子发现, 在同时点击两个测试按钮时, 两个文本框不能同时更新, 证明两个异步任务没有并行执行!!
查找网络资料有如下:研 究过Android系统源码的同学会发现:AsyncTask在android2.3的时候线程池是一个核心数为5线程,队列可容纳10线程,最大执行 128个任务,这存在一个问题,当你真的有138个并发时,即使手机没被你撑爆,那么超出这个指标应用绝对crash掉。 后来升级到3.0,为了避免并发带来的一些列问题,AsyncTask竟然成为序列执行器了,也就是你即使你同时execute N个AsyncTask,它也是挨个排队执行的。 这一点请同学们一定注意,AsyncTask在3.0以后,是异步的没错,但不是并发的。关于这一点的改进办法,我之前写过一篇《Thread并发请求封装——深入理解AsyncTask类》没有看过的同学可以看这里,本文是在这个基础上对AsyncTask做进一步的优化。参考:http://my.oschina.net/kymjs/blog/350565
程序源代码
链接: http://pan.baidu.com/s/1Eqw3O 密码: 6kll