Android AsyncTask基础

作者: Angelicas | 来源:发表于2016-09-18 16:40 被阅读149次

    AsyncTask是Android为了简化异步操作而封装的异步任务操作抽象类。当我们需要在程序中执行耗时的异步操作时,我们可以考虑使用AsyncTask来实现。

    AsyncTask的使用比较简单,由于AsyncTask是一个抽象类,我们需要写一个类来继承AsyncTask,继承AsyncTask需要指定三个泛型参数,参数分别表示为:
    (1)Params:在执行AsyncTask时需要传入的参数,比如我们输入的String类型URL
    (2)Progress:后台任务执行中返回的进度值类型,如果需要在界面上显示当前进度,则就用Progress指定的泛型作为进度单位。
    (3)Result:耗时操作完成后返回的结果,如果需要对结果进行返回,那么就用这里指定的泛型作为返回的类型。

    实现AsyncTask类还需要重写相应的回调方法,一般我们用到的有如下四个方法,他们都是自动被调用的,记得不要手动的调用:
    (1)doInBackground(Void... params):必须重写,异步执行耗时操作
    (2)onPreExecute():执行耗时操作前被调用,通常用来完成一些初始化操作
    (3)onProgressUpdate(Void... values):将doInBackground方法返回的值传递给该方法,在doInBackground中调用publishProgroess()方法可以且来更新进度
    (4)onPostExecute(Void result):耗时的异步任务完成后回调该方法

    需要说明一点:这几个方法中,只有doInBackground方法是在子线程中执行的,其他的方法都是在主线程中执行的。

    启动和取消这个异步任务的方法分别是:
    (1)myAsyncTask.execute() 其中该任务
    (2)myAsyncTask.cancel() 取消该任务

    我们已经大概了解了AsyncTask的基础知识,接下来就以如下三个点做一些实例,加深一下印象。


    这里写图片描述

    (一)观察AsyncTask子类方法的执行顺序
    首先写一个最简单的AsyncTask子类,传入的参数都是空值, 然后在每一个方法中都打印出一句话,主要是为了便于观察他们的执行顺序。

    package com.adan.asynctaskdome;
    
    import android.os.AsyncTask;
    import android.util.Log;
    
    /**
     * @author: xiaolijuan
     * @description:
     * @projectName: AsyncTaskDome
     * @date: 2016-03-04
     * @time: 12:47
     */
    public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            Log.d("TAG", "doInBackground");
            return null;
        }
    
        @Override
        protected void onPreExecute() {
            Log.d("TAG", "onPreExecute");
            super.onPreExecute();
        }
    
        @Override
        protected void onPostExecute(Void aVoid) {
            Log.d("TAG", "onPostExecute");
            super.onPostExecute(aVoid);
        }
    
        @Override
        protected void onProgressUpdate(Void... values) {
            Log.d("TAG", "onProgressUpdate");
            super.onProgressUpdate(values);
        }
    }
    
    

    MainActivity的代码

    package com.adan.asynctaskdome;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    
    
    public class MainActivity extends Activity implements View.OnClickListener {
        private Button button1, button2, button3;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            new MyAsyncTask().execute();
        }
    
    }
    

    运行结果:


    这里写图片描述

    这里的 方法没有执行,是因为我们需要在doInBackground(Void... params)中提供publishProgress(Progress...)方法用于显示进度信息,如下所示:

    package com.adan.asynctaskdome;
    
    import android.os.AsyncTask;
    import android.util.Log;
    
    /**
     * @author: xiaolijuan
     * @description:
     * @projectName: AsyncTaskDome
     * @date: 2016-03-04
     * @time: 12:47
     */
    public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            Log.d("TAG", "doInBackground");
            publishProgress();
            return null;
        }
    
        @Override
        protected void onPreExecute() {
            Log.d("TAG", "onPreExecute");
            super.onPreExecute();
        }
    
        @Override
        protected void onPostExecute(Void aVoid) {
            Log.d("TAG", "onPostExecute");
            super.onPostExecute(aVoid);
        }
    
        @Override
        protected void onProgressUpdate(Void... values) {
            Log.d("TAG", "onProgressUpdate");
            super.onProgressUpdate(values);
        }
    }
    
    

    运行效果:


    这里写图片描述

    从打印的结果很容易就看出方法的执行顺序了吧,如下:

    onPreExecute——>onProgressUpdate(调用了publishProgress方法才会调用该方法)——>doInBackground——>onPostExecute
    (二)异步加载网络图片
    首先一点先别忘记了,我们既然是要访问网络,千万不能忘记了在配置文件中添加网络权限哦

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

    LoadBitmapActivity,我们使用一个ImageView用来加载我们的这张网络图片的,然后再在中间放置一个进度条,用来显示进程
    LoadBitmapActivity

    package com.adan.asynctaskdome;
    
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.ProgressBar;
    
    import java.io.BufferedInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URL;
    import java.net.URLConnection;
    
    /**
     * @author: xiaolijuan
     * @description: 加载图片
     * @projectName: AsyncTaskDome
     * @date: 2016-03-04
     * @time: 13:12
     */
    public class LoadBitmapActivity extends Activity {
        private ImageView img;
        private ProgressBar progressBar;
        private static final String url = "https://www.baidu.com/img/bd_logo1.png";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_load_bitmap);
    
            img = (ImageView) findViewById(R.id.img);
            progressBar = (ProgressBar) findViewById(R.id.progressbar);
            new MyAsyncTask().execute(url);
        }
    
        class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {
            /**
             * 将url所对应的图像解析成Bitmap
             *
             * @param params
             * @return
             */
            protected Bitmap doInBackground(String... params) {
                String url = params[0];// 获取传递进来的参数
                Bitmap bitmap = null;
                URLConnection connection;//定义网络连接对象
                InputStream is;//用于获取数据的输入流
                try {
                    //访问网络操作,耗时操作
                    connection = new URL(url).openConnection();//获取网络连接对象
                    is = connection.getInputStream();//获取输入流
                    BufferedInputStream bis = new BufferedInputStream(is);
                    bitmap = BitmapFactory.decodeStream(bis);//通过decodeStream()方法解析输入流,从而转换成一张bitmap图片
                    is.close();
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //将bitmap作为返回对象
                return bitmap;
            }
    
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                //显示进度条
                progressBar.setVisibility(View.VISIBLE);
            }
    
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                try {
                    Thread.sleep(2000);//为了观看效果,休眠2秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                super.onPostExecute(bitmap);
                //关闭进度条和更新UI
                progressBar.setVisibility(View.GONE);
                img.setImageBitmap(bitmap);
            }
        }
    }
    
    

    activity_load_bitmap.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ImageView
            android:id="@+id/img"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_centerInParent="true" />
    
        <ProgressBar
            android:id="@+id/progressbar"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_centerInParent="true" />
    
    </RelativeLayout>
    

    效果图如下:


    这里写图片描述

    (三)显示进度
    ShowProgressActivity,用来显示这个进度条,在这个类里面我们就使用AsyncTask让进度条动起来。

    package com.adan.asynctaskdome;
    
    import android.app.Activity;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.widget.ProgressBar;
    
    /**
     * @author: xiaolijuan
     * @description: 显示进度
     * @projectName: AsyncTaskDome
     * @date: 2016-03-04
     * @time: 13:12
     */
    public class ShowProgressActivity extends Activity {
        private ProgressBar progressBar;
        private MyAsyncTask mTask;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_show_progress);
    
            progressBar = (ProgressBar) findViewById(R.id.progressBar);
            mTask = new MyAsyncTask();
            mTask.execute();
        }
    
        class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
    
            protected Void doInBackground(Void... params) {
                for (int i = 0; i < 100; i++) {
                    publishProgress(i);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
    
            //根据任务执行情况更新UI,其实就是更新进度条
            protected void onProgressUpdate(Integer... values) {
                super.onProgressUpdate(values);
                // 进度条数值显示
                progressBar.setProgress(values[0]);
            }
    
        }
    }
    
    

    activity_show_progress,布局文件很简单,里面就放置一个横向的进度条而已

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center">
    
        <ProgressBar
            android:id="@+id/progressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="5dp" />
    
    </LinearLayout>
    

    效果图如下:


    这里写图片描述

    不知道大家发现一个问题没,当我第一次点击界面时候,进度条还没运行完,我就返回了,当再一次进去时候,等待了好久进度条才有开始启动起来。这是因为我们之前的doInBackground里的线程还在进行,循环还在继续,所以AsyncTask还在执行。因此此刻重新进入进度条界面时,就不可能立刻启动新的AsyncTask,而旧的呢又因为之前我们返回到按钮界面就没法重新绘制了。所以只有等旧的AsyncTask执行完毕,才会开启新的。就像队列一样,只有运行完之后才运行下一个。这时候你会想,那就麻烦了,如果我需要大量地处理一些耗时操作呢,这样给用户的体验性是不好的。我们能不能在我们点击返回的时候就取消了这个任务呢,答案当然是可以的,只不过我们需要将AsyncTask与Activity的生命周期相关联。
    我把修改后的代码贴一下:

    package com.adan.asynctaskdome;
    
    import android.app.Activity;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.widget.ProgressBar;
    
    /**
     * @author: xiaolijuan
     * @description: 显示进度
     * @projectName: AsyncTaskDome
     * @date: 2016-03-04
     * @time: 13:12
     */
    public class ShowProgressActivity extends Activity {
        private ProgressBar progressBar;
        private MyAsyncTask mTask;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_show_progress);
    
            progressBar = (ProgressBar) findViewById(R.id.progressBar);
            mTask = new MyAsyncTask();
            mTask.execute();
        }
    
        @Override
        protected void onPause() {
            // 判断当前异步任务是否在进行中
            if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
                // cancel()方法:取消该任务,但只是将对应的AsyncTask状态标记为cancel状态,并没有真正取消异步操作
                mTask.cancel(true);
            }
            super.onPause();
        }
    
        class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
    
            protected Void doInBackground(Void... params) {
                for (int i = 0; i < 100; i++) {
                    if (isCancelled()) {//如果异步任务为取消状态,立刻break,跳出循环
                        break;
                    }
                    publishProgress(i);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
    
            //根据任务执行情况更新UI,其实就是更新进度条
            protected void onProgressUpdate(Integer... values) {
                super.onProgressUpdate(values);
                // 进度条数值显示
                progressBar.setProgress(values[0]);
            }
    
        }
    }
    
    

    接下来我们再看看效果图:


    这里写图片描述

    最后我们来总结一下使用AsyncTask类需要注意的几点:
    (1)AsyncTask实例必须在UI Thread中创建;
    (2)execute()方法也必须在UI Thread中调用;
    (3)cancel()方法:取消该任务,该方法只是将对应的AsyncTask状态标记为cancel状态,并没有真正取消异步操作;
    (4)每个AsyncTask的execute()方法只能被执行一次,否则多次调用时将会出现异常;
    (5)重写的四个方法是系统自动调用,不能手动更改;

    如果存在理解偏差甚至错误的地方,请多多交流指正!谢谢各位!
    点击下载源代码:Android AsyncTask基础

    相关文章

      网友评论

        本文标题:Android AsyncTask基础

        本文链接:https://www.haomeiwen.com/subject/hvhgettx.html