当前位置: 首页 > 工具软件 > AsynTask > 使用案例 >

异步任务AsynTask

尚河
2023-12-01

之所以需要异步任务,因为当进行一些耗时操作在主线程时,超过20秒会引起ANR(所谓application not respond)异常。

安卓中已经封装好了一个异步处理的方法,即AsyncTask。

AsyncTask <Params,Progress,Result> //是一个抽象类

//Params:启动任务输入参数的类型
//Progress:后台任务完成进度值的类型
//Result:后台任务返回的结果

AsynTask需要实现以下四个方法:

doInBackground:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成.

onPreExecute:执行后台耗时操作前被调用,通常用于进行初始化操作.

onPostExecute:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新.

onProgressUpdate:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度.

下面通过代码演示一个典型的异步处理的实例–加载网络图片.网络操作作为一个不稳定的耗时操作,从4.0开始就被严禁放入主线程中.所以在显示一张网络图片时,我们需要在异步处理中下载图片,并在UI线程中设置图片:

这里先准备一张自己拍的照片地址:
https://drscdn.500px.org/photo/149628981/q%3D80_m%3D1000/ab0758214aa0193240eb74e48bfb8aca

由于涉及到网络操作,需要在AndroidManifest.xml中添加网络操作权限:

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

在MainActivity布局中加入一个按钮,点击跳转到所要测试的Activity中去:

package com.chase.cn.myapplication;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    private Button imageTest;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageTest = (Button) findViewById(R.id.imageAsync);
        imageTest.setOnClickListener(new View.OnClickListener() {//点进进入ImageTest Class进行测试
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this,ImageTest.class));
            }
        });
    }
}

xml文件比较简单就不在这里贴出。

下面是跳转进入的ImageTest Activity:

package com.chase.cn.myapplication;

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;

/**
 * Created by Chase on 2016/11/17.
 */

public class ImageTest extends Activity {
    private ImageView mimageView;
    private ProgressBar mprogressBar;
    //准备图片的网址
    private static String URL = "https://drscdn.500px.org/photo/149628981/q%3D80_m%3D1000/ab0758214aa0193240eb74e48bfb8aca";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_test);
        mimageView = (ImageView) findViewById(R.id.Myimage);
        mprogressBar = (ProgressBar) findViewById(R.id.Image_Prograss);

        //通过调用execute方法开始处理异步任务.相当于线程中的start方法.
        new MyAsyncTask().execute(URL);
    }

    class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {

        //onPreExecute用于异步处理前的操作
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //此处将progressBar设置为可见.
            mprogressBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];//因为只传递进来一个参数,所以取出第0位
            Bitmap bitmap = null;
            URLConnection connection;
            InputStream is;
            try {
                connection = new URL(url).openConnection();//打开connection连接对应流,在下面将它包装成BufferedReader
                is = connection.getInputStream();
                //为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
                Thread.sleep(3000);
                BufferedInputStream bis = new BufferedInputStream(is);
                //通过decodeStream方法解析输入流
                bitmap = BitmapFactory.decodeStream(bis);
                is.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return bitmap;//返回bitmap
        }

//        //此方法在doInBackground中调用publishPrograss(Prograss...values)方法更新执行进度
//        @Override
//        protected void onProgressUpdate(Void... values) {
//            super.onProgressUpdate(values);
//        }

        //onPostExecute用于UI的更新.此方法的参数为doInBackground方法返回的值.
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            mprogressBar.setVisibility(View.GONE);
            mimageView.setImageBitmap(bitmap);
        }
    }
}

布局文件为:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp"
    android:gravity="center"
   >

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

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

</RelativeLayout>

进度条的模拟,进度条模拟用for循环。如果正在加载一半点退出,再进去就不会加载,为什么呢?
AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.所以必须等到第一个task的for循环结束后,才能执行第二个task.我们知道,当点击BACK键时会调用Activity的onPause()方法.为了解决这个问题,我们需要在Activity的onPause()方法中将正在执行的task标记为cancel状态,在doInBackground方法中进行异步处理时判断是否是cancel状态来决定是否取消之前的task.

同样在MainActivity中加一个按钮 到新的测试Activity中:

即PrograssTest

package com.chase.cn.myapplication;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;

/**
 * Created by Chase on 2016/11/17.
 */
public class PrograssTest extends Activity{
    private ProgressBar progressBar;
    private MyAsyncTask myAsyncTask;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.prograsstest);
        progressBar = (ProgressBar) findViewById(R.id.mprograssbar);
        myAsyncTask = new MyAsyncTask();
        //启动异步任务的处理
        myAsyncTask.execute();
    }

    //AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.
    @Override
    protected void onPause() {
        super.onPause();
        if (myAsyncTask != null && myAsyncTask.getStatus() == AsyncTask.Status.RUNNING) {
            //cancel方法只是将对应的AsyncTask标记为cancelt状态,并不是真正的取消线程的执行.
            myAsyncTask.cancel(true);
        }
    }

    class MyAsyncTask extends AsyncTask<Void,Integer,Void> {
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            //通过publishProgress方法传过来的值进行进度条的更新.
            progressBar.setProgress(values[0]);
        }

        @Override
        protected Void doInBackground(Void... params) {
            //使用for循环来模拟进度条的进度.
            for (int i = 0;i < 100; i ++){
                //如果task是cancel状态,则终止for循环,以进行下个task的执行.
                if (isCancelled()){
                    break;
                }
                //调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新.
                publishProgress(i);
                try {
                    //通过线程休眠模拟耗时操作
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
}

最后的注意事项:
1.必须在UI线程中创建AsyncTask的实例.

2.只能在UI线程中调用AsyncTask的execute方法.

3.AsyncTask被重写的四个方法是系统自动调用的,不应手动调用.

4.每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常.

 类似资料: