Android基础之AsyncTask

背景

AsyncTask是Android中异步请求的基础类。由于在Android系统是单线程模型,即只能在主线程更新UI。之所以这样设计,主要是为了避免多个线程对UI同时操作造成的混乱。另一方面,Android是一个多线程的操作系统,我们不可能把所有操作都放在主线程中操作,如网络请求,读取存储的数据,等等。否则会抛出ANR异常。所以,有必要把耗时操作放在非UI线程中执行,而仅在UI线程更新UI。这样既保证了Android系统的单线程模型,又避免了程序的ANR。

AsyncTask是Android封装好的用于实现异步请求的类。利用这个类,可以很方便的在子线程中更新UI,同时简化异步操作。

AsyncTask基础


AsyncTask是一个抽象类,它包含了三个泛型参数。一般会继承该类。

AsyncTask<Params,Progress,Result>

其中:

  • 泛型参数一:Params。它是启动任务时的输入参数类型;

  • 泛型参数二:Progress。它是后台任务执行时返回的进度值的类型;

  • 泛型参数三:Result。后台任务执行完成后返回的结果类型。


AsyncTask中必须重写的方法:

  • doInBackground():该方法必须重写,用于执行后台的异步任务。所有耗时操作都在该方法中执行。

  • onPreExecute():执行耗时操作前被调用。完成一些初始化操作。该方法在主线程中执行。

  • onPostExecute():在doInBackground()方法执行完毕后,该方法会被调用,并将返回的值传递至该方法中。该方法在主线程中执行;

  • onProgressUpdate():在 doInBackground()中调用publishProgress()方法,可将当前后台执行的进度发送到该方法中。该方法在主线程中执行。


新建一个MyAsyncTask类继承自AsyncTask,并重写上述方法,打印Log,各方法的调用顺序如下:

public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    public static final String TAG = "MyAsyncTask";

    @Override
    protected Void doInBackground(Void... params) {
        Log.e(TAG, "doInBackground");
        return null;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Log.e(TAG, "onPreExecute");
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Log.e(TAG, "onPostExecute");


    }

    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
        Log.e(TAG, "onProgressUpdate");

    }
}

调用顺序:
这里写图片描述


顺序为:onPreExecute()—>doInBackground()—>onPostExecute()


若在doInBackground()中加入方法 publishProgress()方法,再次运行程序,打印的Log为:
这里写图片描述


顺序为:onPreExecute()—>doInBackground()—>onProgressUpdate()—>onPostExecute()


demo1:使用AsyncTask加载一张网络图片


在使用AsyncTask加载完成之前,会实现一个progressBar,提示用户等待。加载完毕后,隐藏progressBar。

布局代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_image_load"
    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.demo.lenovo.asynctasktest.ImageLoadActivity">

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ProgressBar
        android:id="@+id/pb_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:visibility="gone" />

</RelativeLayout>

首先将ProgressBar设为Gone,即不显示。

接着是Activity:

public class ImageLoadActivity extends AppCompatActivity {
    private static final String URL = "https://img-my.csdn.net/uploads/201609/14/1473820894_3292.png";
    private ImageView iv_image;
    private ProgressBar pb_progress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_load);
        iv_image = (ImageView) findViewById(R.id.iv_image);
        pb_progress = (ProgressBar) findViewById(R.id.pb_progress);
        new ImgLoadAsyncTask().execute(URL);

    }

    private class ImgLoadAsyncTask extends AsyncTask<String, Void, Bitmap> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pb_progress.setVisibility(View.VISIBLE);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String url = params[0];

            URLConnection urlConnection = null;
            Bitmap bitmap = null;
            InputStream inputStream = null;
            BufferedInputStream bufferedInputStream = null;
            try {
                urlConnection = new URL(url).openConnection();
                inputStream = urlConnection.getInputStream();
                bufferedInputStream = new BufferedInputStream(inputStream);
                bitmap = BitmapFactory.decodeStream(bufferedInputStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    if (bufferedInputStream != null) {
                        bufferedInputStream.close();

                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            iv_image.setImageBitmap(bitmap);
            pb_progress.setVisibility(View.GONE);
        }
    }
}

在Activity中,将AsyncTask设为内部类,并将泛型参数分别设为String, Void, Bitmap。其中String表示加载的图片URL地址,Void表示不需要在加载过程中通知主线程更新进度,Bitmap表示加载完毕后返回一张Bitmap图。

在onPreExecute()方法中,首先将ProgressBar设为可见状态;在doInBackground()方法中从泛型数组String中获取url地址,通过URLConnection和InputStream网络请求基础类请求url,最终转化为Bitmap图像并返回;最后,在onPostExecute()方法中将Bitmap设置到ImageView上并将ProgressBar关闭。(在doInBackground()方法中加入一个Thread.sleep()以模拟网络请求的延迟。)

demo2:使用AsyncTask模拟进度条

在demo中,将模拟进度条的更新等待操作。

首先,布局就是一个横向的ProgressBar:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_progress_load"
    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.demo.lenovo.asynctasktest.ProgressLoadActivity">

    <ProgressBar
        android:id="@+id/pb_load_progress"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />

</RelativeLayout>

接着是Activity:

public class ProgressLoadActivity extends AppCompatActivity {
    private ProgressBar pb_load_progress;
    private ProgressLoadAsyncTask mProgressLoadAsyncTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_progress_load);
        pb_load_progress = (ProgressBar) findViewById(R.id.pb_load_progress);
        mProgressLoadAsyncTask = new ProgressLoadAsyncTask();
        mProgressLoadAsyncTask.execute();

    }

    private class ProgressLoadAsyncTask extends AsyncTask<Void, Integer, Void> {

        @Override
        protected Void doInBackground(Void... params) {
            for (int i = 0; i < 100; ++i) {

                //根据AsyncTask指定的泛型,该方法可传入一个Integer变长数组作为参数
                publishProgress(i);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            pb_load_progress.setProgress(values[0]);
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            pb_load_progress.setVisibility(View.GONE);
        }
    }
}

在Activitiy中使用AsyncTask模拟进度条的更新,由于不需要传入参数,所以泛型参数一是Void,而在doInBackground中,调用publishProgress(),传入的值将传给onProgressUpdate()方法,所以泛型参数二是Integer类型。该操作也不需要返回参数,所以泛型参数三也是Void类型。

最后,在onPostExecute()中隐藏ProgressBar。


取消Task

在运行过程中,会遇到一个问题:

  • 当进度条未执行完毕时,退出该Activity,并立即再次启动,发现进度条并没有从上次退出的进度继续执行,也没有从头执行。

原因:

  • 由于AsyncTask的底层实现是线程池,只有当一个线程执行完毕以后,下一个线程才会开始,所以,只有当doInBackground()方法的内容全部执行完毕以后,才能得到下一次执行。

解决方法:

  • 使AsyncTask的生命周期与Activity的生命周期一致。

具体解决方式:
在Activity的onPause()中判断AsyncTask的状态并将其停止:

@Override
    protected void onPause() {
        super.onPause();
        //当AsyncTask不为空且AsyncTask的状态为正在运行
        if (mProgressLoadAsyncTask != null && mProgressLoadAsyncTask.getStatus() == AsyncTask.Status.RUNNING) {
            //只是将对应的AsyncTask标记为cancel状态,并没有真正取消该线程。
            mProgressLoadAsyncTask.cancel(true);
        }
    }

需要注意的是cancel()方法只是将对应的AsyncTask标记为cancel状态,并没有真正取消该线程。下面是cancel()方法的文档:
这里写图片描述

其中第二段说:调用cancel()方法将会触发onCancelled(Object)在doInBackground()方法结束后调用(并且onCancelled(Object)是在UI线程中调用),同时您还要确保onPostExecute(Object) 不会被调用。最后还需要不断在doInBackground方法中检查isCancelled() 并及时手动停止该AsyncTask。

所以,除了在onPause中调用cancel方法外,还需要在doInBackground方法中调用isCancelled()判断状态,并将onPostExecute方法中的逻辑复制到onCancelled()方法中(如您需要在onCancelled()中直线自己的操作,如关闭进度条等,请不要调用super.onCancelled()):
这里写图片描述

在doInBackground和onProgressUpdate方法中,不断判断isCancelled()方法,若为true即终止任务:

 @Override
        protected Void doInBackground(Void... params) {
            for (int i = 0; i < 100; ++i) {
                if (isCancelled()) {
                    break;
                }

                //根据AsyncTask指定的泛型,该方法可传入一个Integer变长数组作为参数
                publishProgress(i);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            if (isCancelled()) {
                return;
            }
            pb_load_progress.setProgress(values[0]);
        }

重新运行程序,当中途退出Activity,并再次进入后,可以进度条从头加载。


总结

  • 必须在UI线程中创建AsyncTask实例;

  • 必须在UI线程中调用AsyncTask.execute()方法;

  • 不能手动调用AsyncTask的回调方法;

  • 只能调用AsyncTask.execute()一次,不可重复调用。

  • 只有doInBackground()方法运行在子线程中,其余回调方法均运行在主线程中。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android是一种基于Linux操作系统和开放源代码的移动设备操作系统,由Google开发和维护,是目前全球最流行的移动操作系统之一。以下是Android基础知识: 1. Android系统架构:Android系统架构分为四个层次,分别是应用层、应用框架层、系统运行库层和Linux内核层。 2. Android应用:Android应用可以通过Java编程语言开发,使用Android SDK(软件开发工具包)提供的API进行开发,也可以使用其他编程语言如C++、Python等。 3. Android应用生命周期:Android应用生命周期指应用从启动到终止的整个过程,包括四种状态:运行状态、暂停状态、停止状态和销毁状态。 4. Android布局:Android布局指应用中各个控件的排列方式,包括线性布局、相对布局、表格布局等多种布局方式。 5. Android控件:Android控件是应用中的基本元素,包括文本框、按钮、复选框、列表等各种控件。 6. Android Intent:Intent是Android中用于启动应用组件、传递数据和启动服务等的机制。 7. Android存储:Android中提供了多种存储方式,包括SharedPreferences、文件存储、SQLite数据库等。 8. Android网络编程:Android中可以使用HttpURLConnection、HttpClient等工具进行网络编程,也可以使用第三方库如Volley、OkHttp等。 9. Android多线程编程:Android中可以使用AsyncTask、Handler等工具进行多线程编程。 10. Android调试:Android中可以使用Logcat、DDMS等工具进行调试,也可以使用Android Studio提供的调试功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值