首页 > 技术文章 > Android中AsyncTask异步

Seven-cjy 2016-12-06 14:55 原文

今天我们学习了 AsyncTack, 这是一个异步任务。

那么这个异步任务可以干什么呢?

  因为只有UI线程,即主线程可以对控件进行更新操作。好处是保证UI稳定性,避免多线程对UI同时操作。

  同时要把耗时任务放在非主线程中执行,否则会造成阻塞,抛出无响应异常。

那么在Android中实现异步任务机制有两种方式,Handler和AsyncTask。今天主要讲的是 asyncTack.

  我们通过API 来学习下 整个 AsyncTack

1.为什么要异步任务

  • Android单线程模式
  • 耗时操作放在非主线程(UI线程)中执行

  我们都知道Android是单线程模式,只有主线程才能对UI操作,简称UI线程。

  当然这样的好处是:保证UI的稳定性、准确性,避免多线程同时对UI的操作,导致UI的混乱

  但同时Android是一个多线程的操作系统,不可能把全部的事情放在主线程。

  如果任务堵塞,当时间过长,会抛出ANR(Application Not Responding)错误。

  AsyncTask  能够适当的,简单的用于 UI 线程。这个类不需要操作线程(Thread)就可以完成后台操作将结果返回 UI

  异步任务的定义是一个在后台线程上运行,其结果是在UI线程上发布的计算。

 

2.AsyncTask为何而生

  • 子线程中更新UI
  • 封装、简化异步操作

  结构

    继承关系

      public abstract class AsyncTask extends Object 

    java.lang.Object

      android.os.AsyncTask <params,Progress,Result>

 

3.构建AsyncTask子类的参数
  AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承,继承AsyncTask需要指定如下三个泛型参数:

    Params : 启动任务时输入参数的类型。

    Progress : 后台任务执行中返回进度值的类型(后台人数执行的百分比)

    Result : 后台执行任务完成后返回结果的类型(后台计算的结果类型)

  注:在一个异步任务中,不是所有的类型总被用。假如一个类型不被使用,可以简单地使用void 类型。

 

4.构建AsyncTask子类的回调方法

  doInBackground :

    必须重写,异步执行后台程序将要完成的任务。

    用于在执行异步任务,不可以更改主线程中UI。

    当后台计算结束时,调用 UI线程。后台计算结果作为一个参数传递到这步。

    所有耗时的代码,写到这里来(数据库、蓝牙、网络服务)

  onPreExecute :

    执行后台耗时操作前被调用,通常用户完成一些初始化操作。

    用于在执行异步任务前,主线程做一些准备工作

    被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

    在UI线程上调用任务后立即执行。这步通常被用于设置任务,例如在用户界面显示一个进度条。

  onPostExecute :

    当doInBackground() 完成后,系统自动调用onPostExecute()方法,并将doInBackground方法返回的值传给该方法。

    用于异步任务执行完成后,在主线程中执行的操作

    当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

    当后台计算结束时,调用 UI线程。后台计算结果作为一个参数传递到这步。

  onProgressUpdate :

    在doInBackground() 方法调用publishProgress() 方法更新任务的执行进度后,就会触发该方法。

    用于更新异步执行中,在主线程中处理异步任务的执行信息

    此方法被执行,直接将进度信息更新到UI组件上。

    后台线程执行onPreExecute()完后立即调用,这步被用于执行较长时间的后台计算。异步任务的参数也被传到这步。

    计算的结果必须在这步返回,将传回到上一步。在执行过程中可以调用publishProgress(Progress...)来更新任务的进度。

 

在使用的时候,有几点需要格外注意:

  1.异步任务的实例必须在UI线程中创建。

  2.execute(Params... params)方法必须在UI线程中调用。

  3.不要手动调用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)这几个方法。

  4.不能在doInBackground(Params... params)中更改UI组件的信息。

  5.一个任务实例只能执行一次,如果执行第二次将会抛出异常。

 

接下来,我们来看看如何使用AsyncTask执行异步任务操作,我们先建立一个项目,结构如下:

结构相对简单一些,让我们先看看MainActivity.java的代码:

package com.example.multithreadind01;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private String fromDb_str1 = "";
    private Button btn;
    private Button btn2;
    private TextView tv;
    private ListView lv;
    private BaseAdapter adapter;
    private List<User> userList = new ArrayList<User>();
    private MyTask mt;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 模拟数据访问产生数据
        for (int i = 0; i < 5; i++) {
            User u = new User();
            u.setUsername("模拟" + i);
            u.setSex("女" + i);
            userList.add(u);
        }

        tv = (TextView) findViewById(R.id.textView1);
        btn = (Button) findViewById(R.id.button1);
        btn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                // 注意:
                // 1 每次需new一个实例,新建的任务只能执行一次,否则会出现异常
                // 2 异步任务的实例必须在UI线程中创建
                // 3 execute()方法必须在UI线程中调用。
                mt = new MyTask(MainActivity.this);
                mt.execute(userList, adapter);// 里面的参数是传给 doInBackground

            }
        });

        btn2 = (Button) findViewById(R.id.Button2);
        btn2.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                // 取消一个正在执行的任务,onCancelled()方法将会被调用
                mt.cancel(true);
                Toast.makeText(getApplicationContext(), "onCancelled()取消", 1)
                        .show();
            }
        });

        adapter = new BaseAdapter() {

            public int getCount() {
                return userList.size();// listView 循环数量
            }

            public View getView(int position, View convertView, ViewGroup parent) {
                LayoutInflater inflater = MainActivity.this.getLayoutInflater();
                View view;
                if (convertView == null) {
                    view = inflater.inflate(R.layout.item, null); // 创建convertView
                } else {
                    view = convertView; // 复用
                }

                TextView tv_username = (TextView) view
                        .findViewById(R.id.username);
                TextView tv_sex = (TextView) view.findViewById(R.id.sex);
                tv_username.setText(userList.get(position).getUsername());
                tv_sex.setText(userList.get(position).getSex());
                return view;
            }

            public Object getItem(int position) {
                return null;
            }

            public long getItemId(int position) {
                return 0;
            }
        };
        lv = (ListView) findViewById(R.id.listView1);
        lv.setAdapter(adapter);
    }

}
MainActivity.java

MyTask.java的代码:

package com.example.multithreadind01;

import java.util.List;

import android.os.AsyncTask;
import android.widget.BaseAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

//构造函数AsyncTask<Params, Progress, Result>参数说明: 
//Params   启动任务执行的输入参数
//Progress 后台任务执行的进度
//Result   后台计算结果的类型

public class MyTask extends AsyncTask {

    private BaseAdapter adapter;
    private List<User> userList;
    private MainActivity activity;
    private ProgressBar progressBar;

    public MyTask(MainActivity activity) {
        this.activity = activity;
    }

    // 1.所有耗时的代码,写到这里来(数据库、蓝牙、网络服务)
    // 2.绝对不能碰UI
    // doInBackground()方法用于在执行异步任务,不可以更改主线程中UI
    protected Object doInBackground(Object... params) {

        System.out.println("调用doInBackground()方法--->开始执行异步任务");
        userList = (List<User>) params[0];
        adapter = (BaseAdapter) params[1];
        for (int i = 0; i < userList.size(); i++) {
            try {
                // 为了演示进度,休眠1000毫秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            userList.get(i).setUsername("更改" + i);
            userList.get(i).setSex("男" + i);
            // publishProgress()为AsyncTask类中的方法
            // 常在doInBackground()中调用此方法 用于通知主线程,后台任务的执行情况.
            // 此时会触发AsyncTask中的onProgressUpdate()方法
            publishProgress(i);
        }

        // userlist,adapter

        // 返回给前端
        return "天气:22度";
    }

    // 准备
    // onPreExecute()方法用于在执行异步任务前,主线程做一些准备工作
    protected void onPreExecute() {
        Toast.makeText(activity, "开始准备", Toast.LENGTH_SHORT).show();
        System.out.println("调用onPreExecute()方法--->准备开始执行异步任务");
    }

    // 做完后执行
    // onPostExecute()方法用于异步任务执行完成后,在主线程中执行的操作
    protected void onPostExecute(Object result) {
        String r = result.toString();
        TextView tv = (TextView) activity.findViewById(R.id.textView1);
        // textView显示请求结果
        tv.setText("访问完成!" + r);
        System.out.println("调用onPostExecute()方法--->异步任务执行完毕");
    }

    // 分步完成
    // onProgressUpdate()方法用于更新异步执行中,在主线程中处理异步任务的执行信息
    protected void onProgressUpdate(Object... values) {

        // 0,1,2,3,4
        int bar = Integer.parseInt(values[0].toString());
        bar = (bar + 1) * 20;
        progressBar = (ProgressBar) activity.findViewById(R.id.progressBar1);
        // 更改ProgressBar
        progressBar.setProgress(bar);
        adapter.notifyDataSetChanged();
        System.out.println("调用onProgressUpdate()方法--->更新异步执行中");
    }

    // onCancelled()方法用于异步任务被取消时,在主线程中执行相关的操作
    protected void onCancelled() {

        progressBar = (ProgressBar) activity.findViewById(R.id.ProgressBar2);
        // 更改进度条进度为0
        progressBar.setProgress(0);

        System.out.println("调用onCancelled()方法--->异步任务被取消");
    }

}
MyTask.java

User.java的代码:

package com.example.multithreadind01;

public class User {
    private String username;
    private String sex;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }

}
User.java

布局文件activity_main.xml代码如下:

<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.multithreadind01.MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:layout_marginTop="44dp" >
    </ListView>

    <Button
        android:id="@+id/Button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/listView1"
        android:layout_alignTop="@+id/button1"
        android:text="取消异步任务" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="15dp"
        android:text="开始异步任务" />

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/listView1"
        android:layout_alignRight="@+id/button1"
        android:layout_below="@+id/button1" />

    <ProgressBar
        android:id="@+id/ProgressBar2"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/progressBar1"
        android:layout_alignLeft="@+id/Button2"
        android:layout_alignRight="@+id/Button2"
        android:layout_below="@+id/Button2" />

</RelativeLayout>
activity_main.xml

布局文件item.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <TextView 
        android:id="@+id/username"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="45dp"
        />

    <TextView 
        android:id="@+id/sex"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="45dp"
        />
</LinearLayout>
item.xml

我们来看一下运行时的界面:

 

以上几个截图分别是初始界面、执行异步任务时界面、执行成功后界面、取消任务后界面。执行成功后,整个过程日志打印如下:

 如果我们在执行任务时按下了“取消异步任务”按钮,日志打印如下:

 

 可以看到onCancelled()方法将会被调用,onPostExecute(Result result)方法将不再被调用。

 

详解Android中AsyncTask的使用: http://blog.csdn.net/liuhe688/article/details/6532519

 关于asynctask的取消操作: http://blog.csdn.net/isamu/article/details/9381139

 

推荐阅读