zoukankan      html  css  js  c++  java
  • Android中AsyncTask异步

    今天我们学习了 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

  • 相关阅读:
    布隆过滤器(Bloom Filter)简要介绍
    利用skipList(跳表)来实现排序(待补充)
    rpc、socket、tcp/udp简要梳理
    微服务、rest/restful、springmvc、http简要梳理
    Kafka生产者producer简要总结
    相同数据源情况下,使用Kafka实时消费数据 vs 离线环境下全部落表后处理数据,结果存在差异
    kafka部分重要参数配置-broker端参数
    spring boot-- 三种启动方式
    spring boot --部署war到tomcat中
    阿里云服务上面部署redis + 本地Redis客户端连接方法
  • 原文地址:https://www.cnblogs.com/Seven-cjy/p/6137625.html
Copyright © 2011-2022 走看看