zoukankan      html  css  js  c++  java
  • 转 Android 多线程:手把手教你使用AsyncTask

    转自:https://www.jianshu.com/p/ee1342fcf5e7

    前言

    • 多线程的应用在Android开发中是非常常见的,常用方法主要有:

      1. 继承Thread类
      2. 实现Runnable接口
      3. Handler
      4. AsyncTask
      5. HandlerThread
    • 今天,我将献上一份AsyncTask使用教程,希望大家会喜欢


    目录

     
    示意图

    1. 定义

    • 一个Android 已封装好的轻量级异步类
    • 属于抽象类,即使用时需 实现子类
    public abstract class AsyncTask<Params, Progress, Result> { 
     ... 
     }
    

    2. 作用

    1. 实现多线程
      在工作线程中执行任务,如 耗时任务
    2. 异步通信、消息传递
      实现工作线程 & 主线程(UI线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作

    从而保证线程安全


    3. 优点

    • 方便实现异步通信
      不需使用 “任务线程(如继承Thread类) + Handler”的复杂组合
    • 节省资源
      采用线程池的缓存线程 + 复用线程,避免了频繁创建 & 销毁线程所带来的系统资源开销

    4. 类 & 方法介绍

    4.1 类定义

    AsyncTask类属于抽象类,即使用时需 实现子类

    public abstract class AsyncTask<Params, Progress, Result> { 
     ... 
    }
    
    // 类中参数为3种泛型类型
    // 整体作用:控制AsyncTask子类执行线程任务时各个阶段的返回类型
    // 具体说明:
        // a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数
        // b. Progress:异步任务执行过程中,返回下载进度值的类型
        // c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致
    // 注:
        // a. 使用时并不是所有类型都被使用
        // b. 若无被使用,可用java.lang.Void类型代替
        // c. 若有不同业务,需额外再写1个AsyncTask的子类
    }
    

    4.2 核心方法

    • AsyncTask 核心 & 常用的方法如下:
     
    示意图
    • 方法执行顺序如下
     
    示意图

    5. 使用步骤

    • AsyncTask的使用步骤有3个:
    1. 创建 AsyncTask 子类 & 根据需求实现核心方法
    2. 创建 AsyncTask子类的实例对象(即 任务实例)
    3. 手动调用execute(()从而执行异步线程任务
    • 具体介绍如下
    /**
      * 步骤1:创建AsyncTask子类
      * 注: 
      *   a. 继承AsyncTask类
      *   b. 为3个泛型参数指定类型;若不使用,可用java.lang.Void类型代替
      *   c. 根据需求,在AsyncTask子类内实现核心方法
      */
    
      private class MyTask extends AsyncTask<Params, Progress, Result> {
    
            ....
    
          // 方法1:onPreExecute()
          // 作用:执行 线程任务前的操作
          // 注:根据需求复写
          

    6. 实例讲解

    下面,我将用1个实例讲解 具体如何使用 AsyncTask

    6.1 实例说明

    1. 点击按钮 则 开启线程执行线程任务
    2. 显示后台加载进度
    3. 加载完毕后更新UI组件
    4. 期间若点击取消按钮,则取消加载

    如下图


     
    示意图

    6.2 具体实现

    建议先下载源码再看:Carson_Ho的Github地址:AsyncTask

    • 主布局文件:activity_main.xml
    <?xml version="1.0" encoding="utf-8"?>
    <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:gravity="center"
        tools:context="com.example.carson_ho.handler_learning.MainActivity">
    
        <Button
            android:layout_centerInParent="true"
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="点我加载"/>
    
        <TextView
            android:id="@+id/text"
            android:layout_below="@+id/button"
            android:layout_centerInParent="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="还没开始加载!" />
    
        <ProgressBar
            android:layout_below="@+id/text"
            android:id="@+id/progress_bar"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:progress="0"
            android:max="100"
            style="?android:attr/progressBarStyleHorizontal"/>
    
        <Button
            android:layout_below="@+id/progress_bar"
            android:layout_centerInParent="true"
            android:id="@+id/cancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="cancel"/>
    </RelativeLayout>
    
    • 主逻辑代码文件:MainActivity.java
    public class MainActivity extends AppCompatActivity {
    
        // 线程变量
        MyTask mTask;
    
        // 主布局中的UI组件
        Button button,cancel; // 加载、取消按钮
        TextView text; // 更新的UI组件
        ProgressBar progressBar; // 进度条
        
        /**
         * 步骤1:创建AsyncTask子类
         * 注:
         *   a. 继承AsyncTask类
         *   b. 为3个泛型参数指定类型;若不使用,可用java.lang.Void类型代替
         *      此处指定为:输入参数 = String类型、执行进度 = Integer类型、执行结果 = String类型
         *   c. 根据需求,在AsyncTask子类内实现核心方法
         */
        private class MyTask extends AsyncTask<String, Integer, String> {
    
            // 方法1:onPreExecute()
            // 作用:执行 线程任务前的操作
            @Override
            protected void onPreExecute() {
                text.setText("加载中");
                // 执行前显示提示
            }
    
    
            // 方法2:doInBackground()
            // 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
            // 此处通过计算从而模拟“加载进度”的情况
            @Override
            protected String doInBackground(String... params) {
    
                try {
                    int count = 0;
                    int length = 1;
                    while (count<99) {
    
                        count += length;
                        // 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
                        publishProgress(count);
                        // 模拟耗时任务
                        Thread.sleep(50);
                    }
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                return null;
            }
    
            // 方法3:onProgressUpdate()
            // 作用:在主线程 显示线程任务执行的进度
            @Override
            protected void onProgressUpdate(Integer... progresses) {
    
                progressBar.setProgress(progresses[0]);
                text.setText("loading..." + progresses[0] + "%");
    
            }
    
            // 方法4:onPostExecute()
            // 作用:接收线程任务执行结果、将执行结果显示到UI组件
            @Override
            protected void onPostExecute(String result) {
                // 执行完毕后,则更新UI
                text.setText("加载完毕");
            }
    
            // 方法5:onCancelled()
            // 作用:将异步任务设置为:取消状态
            @Override
            protected void onCancelled() {
    
                text.setText("已取消");
                progressBar.setProgress(0);
    
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // 绑定UI组件
            setContentView(R.layout.activity_main);
    
            button = (Button) findViewById(R.id.button);
            cancel = (Button) findViewById(R.id.cancel);
            text = (TextView) findViewById(R.id.text);
            progressBar = (ProgressBar) findViewById(R.id.progress_bar);
    
            /**
             * 步骤2:创建AsyncTask子类的实例对象(即 任务实例)
             * 注:AsyncTask子类的实例必须在UI线程中创建
             */
            mTask = new MyTask();
    
            // 加载按钮按按下时,则启动AsyncTask
            // 任务完成后更新TextView的文本
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
    
                    /**
                     * 步骤3:手动调用execute(Params... params) 从而执行异步线程任务
                     * 注:
                     *    a. 必须在UI线程中调用
                     *    b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
                     *    c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
                     *    d. 不能手动调用上述方法
                     */
                    mTask.execute();
                }
            });
    
            cancel = (Button) findViewById(R.id.cancel);
            cancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 取消一个正在执行的任务,onCancelled方法将会被调用
                    mTask.cancel(true);
                }
            });
    
        }
    
    }
    
    • 运行结果
     
    示意图

    7. 使用时的注意点

    在使用AsyncTask时有一些问题需要注意的:

    7.1 关于 生命周期

    • 结论
      AsyncTask不与任何组件绑定生命周期
    • 使用建议
      ActivityFragment中使用 AsyncTask时,最好在ActivityFragmentonDestory()调用 cancel(boolean)

    7.2 关于 内存泄漏

    • 结论
      AsyncTask被声明为Activity的非静态内部类,当Activity需销毁时,会因AsyncTask保留对Activity的引用 而导致Activity无法被回收,最终引起内存泄露
    • 使用建议
      AsyncTask应被声明为Activity的静态内部类

    7.3 线程任务执行结果 丢失

    • 结论
      Activity重新创建时(屏幕旋转 / Activity被意外销毁时后恢复),之前运行的AsyncTask(非静态的内部类)持有的之前Activity引用已无效,故复写的onPostExecute()将不生效,即无法更新UI操作
    • 使用建议
      Activity恢复时的对应方法 重启 任务线程

    8. 源码分析



    作者:Carson_Ho
    链接:https://www.jianshu.com/p/ee1342fcf5e7
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    SoapUI 使用笔记
    git 使用笔记(二)
    git 使用笔记(一)
    jquery 拓展
    hdu 1024 Max Sum Plus Plus (DP)
    hdu 2602 Bone Collector (01背包)
    hdu 1688 Sightseeing (最短路径)
    hdu 3191 How Many Paths Are There (次短路径数)
    hdu 2722 Here We Go(relians) Again (最短路径)
    hdu 1596 find the safest road (最短路径)
  • 原文地址:https://www.cnblogs.com/mwl523/p/14061048.html
Copyright © 2011-2022 走看看