Android之AsyncTask


多线程那些事儿

何为线程、多线程?

线程
  把一个进程比喻为一个车间,那么线程就是车间里面的一条条流水线。一个车间可以有多条流水线,流水线属于车间。一个车间的工作过程是一个进程,一个流水线的工作过程是一个线程。进程是操作系统资源分配的最小单位(生产科3号车间负责生产3万件包装纸箱),线程是cup调度的最小单位(3号车间里面的每一条生产线负责生产纸箱这个具体的任务)。

多线程
  就是一个进程中存在多条线程。多线程是数据共享的(共享进程中的空间地址啥的),即3号车间有多条流水线,都共用了一个车间的资源。

多线程的应用举例
  办公三件套之word为例,打开word软件就开启了一个进程,这个进程至少要干几件事儿:1监控键盘输入;2处理文字;3定时自动保存文字信息等。这几件事儿操作的都是同一块数据,所以不能使用多进程(进程间数据隔离啊),只能在一个进程里并发的开启三个线程,如果是单线程,那就只能是键盘输入时,不能处理文字和自动保存,自动保存时又不能打字。

参考资料


ANR(Application Not Responding)

应用程序未响应

原因: 在安卓中,定义了一个UI线程,与控件、界面相关,一旦线程做一件事卡住了(比如:下载音乐,耗时2分钟),10秒内未下载完成,就算卡住了。在下载的过程中,点击控件无响应,界面卡死了,这时候就ANR了
在这里插入图片描述

解决: 另开一个线程,在该线程中下载音乐在这里插入图片描述


什么时候使用多线程?

不想ANR(有耗时操作时),就使用多线程


Main/UI Thread和Worker Thread

之间的通信
Thread/Runnable线程安全
普通的主线程更新
1、

Activity.runOnUiThread(Runnable)

2、

View.post(Runnable)

3、

View.postDelayed(Runnable,long)

Handler


AsyncTask

目的:方便后台线程中操作后更新UI

实现:Thread和Headler进行了封装

实质:Handler异步消息处理机制


AsyncTask(抽象类)

1、 泛型参数 <Params,Progress,Result>
在这里插入图片描述

Params:参数
Progress:显示当前进度
Result:结果

2、需要重写的4个方法:
onPreExecute:在后台任务开始执行之前调用,用于进行一些界面上的初始化操作
doInBackground(Params…):在子线程中执行,处理耗时任务
onProgressUpdate(Progress…):对UI进行操作
onPostExecute(Result…):后台任务执行完毕后返回结果


UI操作:onPreExecute、onPostExecute

1、onPreExecute

/**
* 在异步任务之前,在主线程中
*/
@Override
protected void onPreExecute() {
   super.onPreExecute();

   //可操作UI
}

2、onPostExecute

/**
* 在主线程中
*/
@Override
protected void onPostExecute(Boolean aBoolean) {
	  super.onPostExecute(aBoolean);
	
	  //执行结果处理(doInBackground返回的Boolean结果)
}

后台线程操作doInBackground

继承AsyncTask必须实现doInBackground()方法:

public class DownloadAsyncTask extends AsyncTask<String,Integer,Boolean>{
  /**
   * 在另外一个线程中处理参数
   * @param params 入参
   * @return 结果
   */
   @Override
   protected Boolean doInBackground(String... strings) {
       return null;
   }
}

具体代码

package com.example.asynctaskstudy;

import androidx.appcompat.app.AppCompatActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new DownloadAsyncTask().execute("imooc","good");
    }

    public class DownloadAsyncTask extends AsyncTask<String,Integer,Boolean> {

        /**
         * 在另外一个线程中处理参数
         * @param params 入参
         * @return 结果
         */
        @Override
        protected Boolean doInBackground(String... params) 						 {
            for (int i = 0; i <10000 ; i++) {
                Log.i(TAG, "doInBackground: "+params[0]);
            }

            try {
                Thread.sleep(100*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return true;
        }
    }
}

其中,休眠100秒,测试会不会卡死:

try {
	Thread.sleep(100*1000);
	} catch (InterruptedException e) {
	    e.printStackTrace();
}

休眠100秒也不会卡死,界面仍然正常应用,因为是在另外一个线程中sleep()

结果:
在这里插入图片描述
10000遍的

02-15 03:15:37.765 8297-8316/com.example.asynctaskstudy I/MainActivity: doInBackground: imooc


进度条显示 onProgressUpdate

/**
* 在主线程中
*/
@Override
protected void onProgressUpdate(Integer... values) {
   super.onProgressUpdate(values);

   //收到进度,处理
}

具体代码

目标:
在这里插入图片描述

具体步骤:

  • 1、网络上请求数据:申请网络权限 申请读写存储权限
  • 2、布局layout
  • 3、下载之前我们要做什么? 处理UI
  • 4、下载中我们要做什么? 处理数据
  • 5、下载后我们要做什么? 处理UI

网络上请求数据:申请权限权限

网络权限

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

读写存储权限

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

布局layout

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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"
    tools:context=".MainActivity"
    android:orientation="vertical"
    >


    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="match_parent"
        android:layout_height="15dp"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        />

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="点击下载"
        />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        />

</LinearLayout>

MainActivity

package com.example.asynctaskstudy;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * 1、网络上请求数据:申请网络权限 申请读写存储权限
 * 2、布局layout
 * 3、下载之前我们要做什么? 处理UI
 * 4、下载中我们要做什么? 处理数据
 * 5、下载后我们要做什么? 处理UI
 */
public class MainActivity extends AppCompatActivity {


    private static final String TAG = "MainActivity";//快捷键【logt】
    private ProgressBar mProgressBar;
    private Button mDownloadButton;
    private TextView mResultTextView;
    private static final int INIT_PROGRESS = 0;
    private static final String APK_URL = "http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk";
    private static final String FILE_NAME = "imooc3.apk";

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

        //初始化视图
        initView();

        //设置点击监听
        setListener();

        //初始化UI数据
        setData();

    }

    /**
     * 初始化视图
     */
    private void initView() {

        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        mDownloadButton = (Button) findViewById(R.id.button);
        mResultTextView = (TextView) findViewById(R.id.textView);

    }

    /**
     * 设置点击监听
     */
    private void setListener() {

        mDownloadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //做下载的事情
                // TODO: 2020-02-15  下载任务
                DownloadAsyncTask asyncTask = new DownloadAsyncTask();
                asyncTask.execute(APK_URL);
            }
        });

    }

    /**
     * 初始化UI数据
     */
    private void setData() {

        mProgressBar.setProgress(INIT_PROGRESS);
        mDownloadButton.setText(R.string.click_download);
        mResultTextView.setText(R.string.download_text);

    }


    /**
     * String 入参
     * Integer 进度
     * Boolean 返回值
     */
    public class DownloadAsyncTask extends AsyncTask<String, Integer, Boolean> {

        private String mFilePath;

        /**
         * 在异步任务之前,在主线程中
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            //可操作UI  类似于淘米,之前的准备工作
            mDownloadButton.setText(R.string.downloading);
            mResultTextView.setText(R.string.downloading);
            mProgressBar.setProgress(INIT_PROGRESS);
        }

        /**
         * 在另外一个线程中处理参数
         *
         * @param params 入参 煮米
         * @return 结果
         */
        @Override
        protected Boolean doInBackground(String... params) {

            //下载中
            if (params != null && params.length > 0) {
                String apkUrl = params[0];

                try {
                    //构造URL
                    URL url = new URL(apkUrl);

                    //构造连接,并打开
                    URLConnection urlConnection = url.openConnection();

                    //
                    InputStream inputStream = urlConnection.getInputStream();

                    //获取了下载内容的总长度
                    int contentLength = urlConnection.getContentLength();

                    //下载地址准备 File.separator:/
                    mFilePath = Environment.getExternalStorageDirectory()
                            + File.separator + FILE_NAME;


                    //对下载地址进行处理
                    File apkFile = new File(mFilePath);
                    if (apkFile.exists()) {
                        boolean result = apkFile.delete();
                        if (!result) {//删除失败
                            return false;
                        }
                    }

                    //已下载的大小
                    int downloadSize = 0;

                    //byte数组
                    byte[] bytes = new byte[1024];

                    int length;

                    //创建一个输入管道
                    OutputStream outputStream = new FileOutputStream(mFilePath);

                    /**
                     * read(byte[] b) :
                     * 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。
                     */
                    //不断的一车一车挖土,直到挖不到为止
                    while ((length = inputStream.read(bytes)) != -1) {
                        //挖到的放到我们的文件管道里
                        outputStream.write(bytes, 0, length);
                        //累加我们的大小
                        downloadSize += length;
                        // 发送进度
                        publishProgress(downloadSize * 100 / contentLength);
                    }

                    //关闭流,防止内存泄漏
                    inputStream.close();
                    outputStream.close();


                } catch (IOException e) {
                    e.printStackTrace();
                    return false;
                }

            } else {
                return false;
            }

            return true;
        }


        /**
         * 在主线程中
         */
        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);

            //执行结果处理(doInBackground返回的Boolean结果)
            mDownloadButton.setText(result ? getString(R.string.download_finish) : getString(R.string.download_fail));
            mResultTextView.setText(result ? getString(R.string.download_finish) + mFilePath : getString(R.string.download_fail));
        }


        /**
         * 在主线程中
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            //收到进度,然后处理
            if (values != null) {
                mProgressBar.setProgress(values[0]);
            }
        }

        @Override
        protected void onCancelled(Boolean aBoolean) {
            super.onCancelled(aBoolean);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AsyncTaskAndroid 开发中用于在后台执行耗时操作的一个类,可以进行异步操作以避免在主线程上执行耗时操作导致界面卡顿的情况。在 Kotlin 中使用 AsyncTask 的用法与 Java 中基本一致。 使用 AsyncTask 需要创建一个继承于 AsyncTask 的子类来进行异步操作。在 Kotlin 中,可以使用 object 关键字创建一个匿名内部类来实现 AsyncTask 的子类。下面是一个基本的 AsyncTask 使用示例: ```kotlin // 创建一个继承自 AsyncTask 的子类 val task = object : AsyncTask<Void, Void, String>() { // 在后台线程执行耗时操作 override fun doInBackground(vararg params: Void): String { // 执行耗时操作,例如网络请求或数据库查询 return "AsyncTask 执行完毕" } // 在耗时操作结束后更新 UI override fun onPostExecute(result: String) { // 在这里更新 UI,例如更新 TextView 的内容 } } // 执行 AsyncTask task.execute() ``` 在上面的示例中,首先创建了一个继承于 AsyncTask 的子类,并实现了三个方法:doInBackground、onPostExecute 和 onPostExecute。doInBackground 方法用于在后台线程执行耗时操作,例如网络请求或数据库查询,并返回结果给 onPostExecute 方法。onPostExecute 方法在耗时操作结束后会被调用,你可以在这个方法中更新 UI。 最后,通过调用 AsyncTask 的 execute 方法来执行异步操作。 需要注意的是,Kotlin 推荐使用更现代化的异步操作方式,例如使用 Kotlin 协程来替代 AsyncTask。Kotlin 协程提供了更简洁、可读性更高的代码,且在处理异步操作上更加灵活和强大。因此,如果在新的项目中,可以考虑使用 Kotlin 协程来替代 AsyncTask
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值