AsyncTask使用详解

AsyncTask定义

和Handler作用相似,AsyncTask也是为了处理异步任务的。代码操作上AsyncTask比Handler轻量级,但是资源实现上Handler比AsyncTask轻量级。AsyncTask不需要Handler和新线程的介入即可完成异步任务与UI之间的更新操作。Handler不限定运行环境,而AsyncTask必须在主线程调用。

AsyncTask实际上是一个线程池,最大只支持5个并发。如果有线程长时间占用,且没有空闲,则其他线程只能处于等待状态,会造成阻塞。

异步操作更新UI的几种方式

  1. Handler机制
  2. AsyncTask机制
  3. Activity.runOnUiThread(Runnable)
  4. View.post(Runnable)
  5. View.postDelayed(Runnable,long)

可以看到他们基本都是用新线程来处理耗时操作。AsyncTask相比后三种,简化了操作步骤,常用这种方式来进行异步任务的处理。
**

AsyncTask运行原理

AsyncTask主要有二个部分:一是与主线程的交互,二是线程的管理调度。一个应用可以有多个AsyncTask的子类的实例,但是AsyncTask的内部Handler和ThreadPoolExecutor都是进程范围内共享的,其都是static的,也是属于类的,类的属性的作用范围是CLASSPATH,因为一个进程是一个VM虚拟机,所以AsyncTask控制着进程范围内所有的子类实例。 

  AsyncTask内部会创建一个进程作用域的线程池来管理要运行的任务,也就就是说当你调用了AsyncTask的execute()方法后,AsyncTask会把任务交给线程池,由线程池来管理创建Thread和运行Therad。所以我们新建任务时对于Handler来说是立即执行的,而对于AsyncTask来说可能不会立即执行,总的来说AsyncTask适合处理短时间的任务,比如json数据获取,不适合大数据的获取,比如网络电影下载,这种实现最好是自定义Thread来实现。

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

**
官方文档建议最多不要超过几秒钟。推荐耗时任务使用Executor。为什么呢?

  • AsyncTask的生命周期没有跟Activity的生命周期同步:Activity意外销毁重建的话,AsyncTask与先前绑定的Activity就不存在了,如果你原本想在onPostExecute()中更新UI的话,这时的AsyncTask将不会更新新的Activity,并且这个操作会引发一个异常:java.lang.IllegalArgumentException
  • 容易内存泄露:在Activity中作为内部类创建AsyncTask很方便。因为AsyncTask在执行完成或者执行中需要使用Activity中的view,因为内部类可以直接访问外部类的域(也就是变量)。然而这意味着内部类会持有外部类的一个引用。当长时间运行时,因为AsyncTask持有Activity的引用,所以即使当该Activity不再显示时Android也无法释放其占用的资源。
  • 容易阻塞:AsyncTask是一个线程池,最多支持5个并发操作,如果这5个操作运行时间过长,其他后加入的任务只能处于等待状态,对于用户体验来说将会很糟糕。

AsyncTask的构造

三个参数

  • Params:启动任务时指定的参数类型,可为空。
  • Progress:后台任务完成进度值得类型,可为空。
  • Result:后台任务执行完毕返回类型,可为空。

四个方法

  • onPreExecute():UI线程中调用,在调用excute()方法后会立即被调用,这个方法适用于初始化task,比如显示后台任务进度条;
  • doInBackground(Object[] params):非UI线程中调用,在onPreExecute执行后调用,这个方法适用于执行耗时的操作,excute方法中的Params参数就是通过这个方法传递的,该方法的返回值就是Task执行的结果,在doInBackground方法执行过程中,可以通过publishProgress方法来更新后台任务的执行进度;
  • onProgressUpdate(Object[] values):UI线程中调用,在publishProgress方法后被调用,这个方法用于在后台任务执行过程中显示任务的进度UI,比如它可以用于显示进度条动画或者显示进度文本;
  • onPostExecute(Object o):UI线程中调用,后台任务执行完成后被调用,Task返回的结果以参数的形式传递到该方法中。

开发中,最少要覆写doInBackground(Object[] params)和onPostExecute(Object o)两个方法,如果执行途中我们想要取消任务可以调用onCancelled(Boolean result)方法,就中断任务并不会执行onPostExecute(Object o)

AsyncTask实例

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private Button mButton;
    private TextView mTextView;

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


        mTextView = (TextView) findViewById(R.id.textView);

        mButton = (Button) findViewById(R.id.btn_click);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    new DownloadFilesTask().execute(new URL("http://blog.csdn.net/z_dinghao/article/details/71190014"));
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private class DownloadFilesTask extends AsyncTask<URL, Integer, String> {
        ProgressDialog progressDialog;
        //已读取行数
        int hasRead = 0;
        protected String doInBackground(URL... params) {
            StringBuilder sb = new StringBuilder();
            try {
                URLConnection coon = params[0].openConnection();
                BufferedReader br = new BufferedReader(
                        new InputStreamReader(coon.getInputStream(),"utf-8"));
                String line = null;
                while ((line = br.readLine())!= null){
                    sb.append(line + "/n");
                    hasRead++;
                    //为了便于观察我们延迟100ms
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    publishProgress(hasRead);
                }
                return sb.toString();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        protected void onProgressUpdate(Integer... progress) {
            //实时更新进度
            mTextView.setText("已经读取了"+progress[0]+"行");
            progressDialog.setProgress(progress[0]);
        }

        @Override
        protected void onPreExecute() {
            progressDialog = new ProgressDialog(MainActivity.this);
            progressDialog.setTitle("下载中...");
            //设置对话框不可取消
            progressDialog.setCancelable(false);
            //设置任务最大容量
            progressDialog.setMax(380);
            //设置对话框样式
            progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            //设置进度条风格显示是否不确定,想要他显示进度就false,否则他会处于一直读取状态,不显示进度
            progressDialog.setIndeterminate(false);
            progressDialog.show();
        }

        protected void onPostExecute(String result) {
            //执行完毕后执行
            progressDialog.dismiss();
            Toast.makeText(MainActivity.this,"下载完毕!",Toast.LENGTH_SHORT).show();
        }
    }
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.alphathink.asynctaskdemo.MainActivity">

    <Button
        android:id="@+id/btn_click"
        android:text="click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/btn_click"
        android:text="TextView" />


</RelativeLayout>

记得在AndroidManifest.xml文件加入权限:

<uses-permission android:name="android.permission.INTERNET"/>

这里我们做的是读取网页内容,实时更新读取内容的行数,测试中加入了休眠100ms是为了便于观察,否则网页内容少,瞬间完成不容易看到效果。具体效果如下:
这里写图片描述

除了excute方法外,还可以调用executeOnExecutor(其实excute方法也是调用的executeOnExecutor方法,只是线程池是在AsyncTask中默认定义好的),如果使用executeOnExecutor方法,可以在外部自定义线程池,解决不能并发执行异步任务的问题。

LinkedBlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService exec = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, blockingQueue);
new DownloadFileTask().executeOnExecutor(1);
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值