AsyncTask的使用

Posted sq19920518

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AsyncTask的使用相关的知识,希望对你有一定的参考价值。

一、AsyncTask简单介绍

android中实现异步任务机制有两种方式,Handler和AsyncTask。

Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。

使用背景

1、开发Android应用时必须遵守单线程模型的原则:
Android UI操作并不是线程安全的,并且这些操作必须在UI线程中执行。

2、单线程模型中始终要记住两条法则:
1). 不要阻塞UI线程 ;
2). 确保只在UI线程中访问Android UI控件。
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),
主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

3、Android4.0以上版本中
主线程中不允许访问网络。涉及到网络操作的程序一般都是需要开一个新线程完成网络访问。但是在获得页面数据后,又不能将数据返回到UI界面中 。因为子线程(Worker Thread)不能直接访问UI线程中的成员,也就是说没有办法对UI界面上的内容进行操作,如果操作,将抛出异常:CalledFromWrongThreadException。

其实,android提供了几种在其他线程中访问UI线程的方法:
Activity.runOnUiThread( Runnable )
View.post( Runnable )
View.postDelayed( Runnable, long )
Handler消息传递机制

这些类或方法会使代码很复杂很难理解。为了解决这个问题,Android 1.5提供了一个工具类:AsyncTask,它使创建与用户界面长时间交互运行的任务变得更简单。AsyncTask更轻量级一些,适用于简单的异步处理,原理是线程和Handler的封装。

先看下面代码:

package com.danny_jiang.day07_asynctask_introduce;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

/**
 * 案例演示:AsyncTask的使用步骤
 * 1 自定义类继承AsyncTask,指定三个泛型
 * 2 复写抽象方法doInbackground完成耗时操作,或者其他生命周期方法
 * 3 调用AsyncTask.execute方法执行异步任务
 *
 */
public class MainActivity extends Activity 

    private TextView text;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.text);
    

    public void click(View view) 
        android.util.Log.e("danny", "按钮被点击了!!!");
        MyTask task = new MyTask();
        task.execute("abc");
    

    /**
     * 当继承AsyncTask时,需要指定三个泛型
     * 第一个    doInbackground方法的入参类型,也是执行AsyncTask时所需要传入
     *          的参数类型(AsyncTask.execute(参数)), 一般是String类型
     * 第二个    向用户提示进度信息时使用的参数类型,也就是publishProgress方法的入参类型
     *          可以指定为Void类型,说明不需要传参
     * 第三个    onPostExecute方法的传参类型,同时也是doInbackground方法的返回值类型
     *          一般情况下可以返回byte[]或者String
     *
     */
    class MyTask extends AsyncTask<String, Void, String> 
        /**
         * 当调用AsyncTask.execute方法之后,此方法立即被调用
         * 在主线程中执行,一般做一些准备工作(如UI操作)
         */
        @Override
        protected void onPreExecute() 
            super.onPreExecute();

            long id = Thread.currentThread().getId();
            String name = Thread.currentThread().getName();
            android.util.Log.e("TAG", "onPreExecute:" + id + " name is " + name);
        

        /**
         * 当onPreExecute方法执行完之后,此方法立即被调用
         * 在子线程中完成,一般在此方法中完成耗时操作
         * 返回值会被交给onPostExecute方法,进行UI刷新
         * (在执行过程中可以调用publishProgress(Progress... values)来更新进度信息)
         */
        @Override
        protected String doInBackground(String... params) 
            long id = Thread.currentThread().getId();
            String name = Thread.currentThread().getName();
            android.util.Log.e("TAG", "doInBackground:" + id + " name is " + name);

            for(int i = 0; i < 10; i++) 
                try 
                    Thread.sleep(1000);
                    android.util.Log.e("TAG", "任务执行到 " + i);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
            return "job finish";
        

        /**
         * 当doInbackground方法执行耗时操作之后,此方法会被自动调用
         * 入参就是doInbackground的返回值
         * 在主线程中执行,可以根据doInbackground返回的数据刷新UI控件
         */
        @Override
        protected void onPostExecute(String result) 
            super.onPostExecute(result);

            long id = Thread.currentThread().getId();
            String name = Thread.currentThread().getName();
            android.util.Log.e("TAG", "onPostExecute:" + id + " name is " + name);

            text.setText(result);
        
    

注意:
1.异步任务的实例必须在UI线程中创建。
2.execute(Params… params)方法必须在UI线程中调用。
3.不要手动调用onPreExecute(),doInBackground(Params…params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法。
4.不能在doInBackground(Params… params)中更改UI组件的信息。
5.一个任务实例只能执行一次,如果执行第二次将会抛出异常。

二、实例演示

package com.danny_jiang.day07_asynctask_progress;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class MainActivity extends Activity 

    private String url = "http://img1.ph.126.net/BmSbVg7ncmVYe4c_pdO77Q=="
            + "/2622783833007290111.png";

    private ProgressDialog pd = null;
    private ImageView image;
    private Button download;
    private Button cancle;
    private MyTask mTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        download = (Button) findViewById(R.id.download);
        cancle = (Button) findViewById(R.id.cancle);
        image = (ImageView) findViewById(R.id.image);
    

    public void download(View view) 
        // 注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
        mTask = new MyTask();
        mTask.execute(url);

        download.setEnabled(false);
        cancle.setEnabled(true);
    

    public void cancle(View view) 
        // 取消一个正在执行的任务,onCancelled方法将会被调用
        mTask.cancel(true);
    

    class MyTask extends AsyncTask<String, Integer, byte[]> 

        // onPreExecute方法用于在执行后台任务前做一些UI操作
        @Override
        protected void onPreExecute() 
            super.onPreExecute();
            // 创建对话框
            pd = new ProgressDialog(MainActivity.this);
            // 设置对话框水平样式
            pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            // 设置titile
            pd.setTitle("提示:");
            // 设置message
            pd.setMessage("Downloading...");
            // 显示对话框
            pd.show();
        

        // doInBackground方法内部执行后台任务,不可在此方法内修改UI(子线程中执行)
        @Override
        protected byte[] doInBackground(String... params) 
            String imageUrl = params[0];

            InputStream is = null;
            byte[] buffer = new byte[1024];
            int len = 0;

            // 用来代表所下载图片的总数据
            int totalLength = 0;
            // 用来代表当前下载的进度
            int currentProgress = 0;

            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            /**
             * 以下是真正的网络请求操作
             */
            try 
                URL newUrl = new URL(imageUrl);

                HttpURLConnection conn = (HttpURLConnection) newUrl.openConnection();

                if (conn != null && conn.getResponseCode() == 200) 

                    // 通过conn获取图片的总数据长度
                    totalLength = conn.getContentLength();
                    // 设置进度条最大的进度
                    pd.setMax(totalLength);

                    is = conn.getInputStream();

                    while ((len = is.read(buffer)) != -1) 
                        baos.write(buffer, 0, len);
                        baos.flush();
                        // 将当前所读取的进度赋值给currentProgress
                        currentProgress += len;
                        // 调用publishProgress公布进度,最后onProgressUpdate方法将被执行
                        publishProgress(currentProgress);
                        // 为了演示进度,休眠50毫秒
                        Thread.sleep(50);

                        // pd.setProgress(currentProgress);
                    
                    return baos.toByteArray();
                
             catch (Exception e) 
                e.printStackTrace();
            
            return null;
        

        // onProgressUpdate方法用于更新进度信息
        @Override
        protected void onProgressUpdate(Integer... values) 
            pd.setProgress(values[0]);
        

        // onPostExecute方法用于在执行完后台任务后更新UI,显示结果
        @Override
        protected void onPostExecute(byte[] result) 
            super.onPostExecute(result);
            Bitmap bm = BitmapFactory.decodeByteArray(result, 0, result.length);
            image.setImageBitmap(bm);
            // 关闭对话框
            if (pd.isShowing()) 
                pd.dismiss();
            
        

        // onCancelled方法用于在取消执行中的任务时更改UI
        @Override
        protected void onCancelled() 
            pd.setProgress(0);
            download.setEnabled(true);
            cancle.setEnabled(false);
        
    

更多介绍见详解AsyncTask

以上是关于AsyncTask的使用的主要内容,如果未能解决你的问题,请参考以下文章

Android中AsyncTask的使用

如何获取AsyncTask 异步处理的返回值

201709013工作日记--Android异步通信AsyncTask

为啥我的 AsyncTask 会冻结我的 UI 线程?

AsyncTask的基本使用和各个参数的说明

AsyncTask - 基本原理 后台线程和UI线程的交互