Android开发-AsyncTask

现在一直在做安卓的教学,一些人对于一些比较基本的东西还是比较模糊
所以最近只要有时间就会总结一些常用的知识
希望对读者有所帮助
下面有具体示例代码

简介

AsyncTask是 API 3提供的轻量级异步类(代码轻量一些,实际上与Hanler相比更加的消耗资源),效果相当于Thread+Handler。与之相比,AsyncTask在使用上更加简便快捷且过程可控,但当多个异步操作进行UI变更的时候,就变得比较复杂。

首先明确为什么会有AsyncTask或者handler的存在,其实很简单,就是程序在正常情况下只存在一个线程(主线程也叫UI线程),而当处理耗时操作如网络请求等等,就会阻塞主线程,造成UI界面的卡顿,这是不被允许的。所以才会出现异步处理(AsyncTask、Thread)。

这也就是为什么我们会使用AsyncTask或者Thread的原因。

AsyncTask是一个抽象类,如需使用它则必须显示的继承或者使用匿名内部类。

public abstract class AsyncTask<Params, Progress, Result> 

其中实现子类或者匿名内部类的时候需要设置三个泛型参数
Params:执行后台任务所需要设置的参数,如:网络请求的URL
Progress:后台任务执行的进度信息,如:实现下载网络图片的进度,就需要通过它
Result:后台任务执行完毕最终返回的结果,如:下载网络图片最终返回的图片

AsyncTask提供的方法

该方法运行在UI线程,当AsyncTask执行execute()方法,onPreExecute()就会被执行。
可以再这个方法中做一些准备工作,如初始化进度条值等等

protected void onPreExecute() {
        super.onPreExecute();
    }

在onPreExecute()方法执行后马上执行,该方法运行在子线程中,负责执行耗时操作。
在执行耗时操作的过程中你可以不断地调用publishProgress()方法,这样可以使onProgressUpdate()不断地被调用

protected Result doInBackground(Params... params) {
        return null;
    }

调用publishProgress()方法后,就会导致该方法被执行,方法运行在UI线程,
可以更新进度条的显示等等关于进度的信息

protected void onProgressUpdate(Progress... values) {
        super.onProgressUpdate(values);
    }

这是最终的方法
doInBackground()方法执行后的返回结果会传给该方法,该方法运行在UI线程,在该方法中你可以显示处理结果。

    protected void onPostExecute(Result s) {
        super.onPostExecute(s);
    }

用户取消操作执行的方法

 protected void onCancelled() {
        super.onCancelled();
    }

这个方法不可以复写,多数在doInBackground()中调用,用来及时发布后台任务

publishProgress(Integer... Values)

下面是我写的一个下载图片的例子

MainActivity

public class MainActivity extends AppCompatActivity {
    private ImageView imageView;
    private ProgressBar progressBar;
    final String imageUrl = "http://img4.duitang.com/uploads/item/201502/01/20150201163209_NNruz.jpeg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.main_imageview);
        progressBar = (ProgressBar) findViewById(R.id.main_progress);

    }
    // 通过按钮点击事件开启Asynctask任务
    public void buttonAction(View view) {
        new  MyAsyncTask().execute(imageUrl);
    }

    // 使用匿名内部类的方式使用AsyncTask
    // 三个泛型分别为1.传入的网址类型 2.不需要监听进度所以使用Void 3.返回的值类型
    class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {


        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // 使progressBar可见
            progressBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            // 网络请求图片
            String imageUrl = params[0];
            Bitmap bitmap = null;
            InputStream inputStream = null;
            HttpURLConnection connection = null;

            try {
                URL url = new URL(imageUrl);
                connection = (HttpURLConnection) url.openConnection();
                if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    inputStream = connection.getInputStream();
                    bitmap = BitmapFactory.decodeStream(inputStream);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (inputStream != null)
                    inputStream.close();
                    connection.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if (bitmap != null) {
                // 成功之后返回的数据
                imageView.setImageBitmap(bitmap);
                progressBar.setVisibility(View.GONE);
            }
        }
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.wjn.asynctaskdemo.MainActivity">

    <ImageView
        android:id="@+id/main_imageview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <ProgressBar
        android:id="@+id/main_progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/main_imageview"
        android:visibility="gone"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/main_progress"
        android:onClick="buttonAction"
        android:text="缓存图片"
        />

</RelativeLayout>

这样就完成了一个简单的AsyncTask的使用。能懂就好。

下面介绍下AsyncTask常见的问题:

最多可执行数量
在API11之前的版本,内部的线程池只能执行5个线程。超过的只能等待。也就是说AsyncTask的实例超过5个的话,那么之后的线程只能等待前五个执行完成之后才能执行。这就产生了一个比较大的限制。所以出现这种情况之后,只能自己创建线程池管理Thread。
但在之后Google意识到了AsyncTask的局限性,从Android 3.0开始对AsyncTask的API做出了一些调整:使用execute()提交的任务,增加了二个预定义的线程池SERIAL_EXECUTOR(默认为序列线程池,即一次只执行一个线程任务)和THREAD_POOL_EXECUTOR(API11之前使用的线程池)
他们两个最大的区别就是SERIAL_EXECUTOR为执行完一个在执行下一个,而THREAD_POOL_EXECUTOR和以前没有任何区别,一次崔铎执行5个,5个之后的只能等待。

所以有的时候会遇到网络图片需要等待很久才能出现,这就是因为有其他的AsyncTask还没有执行完毕。所以遇到这种问题解决很简单,要么直接使用Thread要么创建一个单独的线程池(Executors.newCachedThreadPool())

生命周期
其实对于AsyncTask很多人有这样一个误解,就是一个在Activity中的AsyncTask会随着Activity的销毁而销毁。然而事实并非如此。AsyncTask会一直执行doInBackground()方法直到方法执行结束

如果cancel(boolean)调用了,则执行onCancelled(Result)方法
如果cancel(boolean)没有调用,则执行onPostExecute(Result)方法

内存问题

在Activity中使用非静态匿名内部AsyncTask类,由于Java内部类的特点,AsyncTask内部类会持有外部类的隐式引用。但由于AsyncTask的生命周期可能比Activity的长,当Activity进行销毁AsyncTask还在执行时,所以AsyncTask还会持有Activity的引用,这样就会导致Activity对象无法回收,进而产生内存泄露。

有可能出现结果丢失

在屏幕旋转等造成Activity重新创建时AsyncTask数据丢失的问题。当Activity销毁并创新创建后,还在运行的AsyncTask会持有一个Activity的非法引用即之前的Activity实例。这样就导致了onPostExecute()没有任何作用。

最后给一些自己的建议

1.少用异步 - 原因很简单,异步任务开销大,难维护等等
2.出现异步的情况可以使用 - Loaders
3.大量线程执行任务的时候一定要使用线程池
4.对于没有与UI交互的线程就用Thread吧

希望有所帮助,感谢阅读!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值