AsyncTask小案例

△关于安卓里的异步任务

→安卓里的异步任务,可以当做java里面的多线程来看待了,在java语言里,想要启动多线程就只有两个方法:继承Thread类并重写run()方法,或者实现Runnable接口并且重写run()方法。

→安卓里面,想要启动多个线程可以通过:1,java原来有的两个方法(Thread类和Runnable接口);2,Handler类提供方法启动异步任务;3,AsyncTask类也能实现异步任务;4,IntentService类(Service的子类)也能实现异步任务,虽然这个和上述的相比有些勉强。



△为什么需要使用AsyncTask


Handler类提供很完整的异步任务机制,但是他的使用相对复杂,对于刚刚入手安卓的人来说,不好理解,AsyncTask的底层也是通过Handler来实现,通过封装,相对来说比较能让新手使用,简单方便。




△关于安卓的单线程模型


虽说AsyncTask封装的很好,能让你几乎在不了解安卓多线程工作的具体情况之下,照样使用异步任务,但是如果想理解好异步任务,还得说说安卓的某些线程的知识。理解这些知识以后,对于理解AsyncTask使用的注意事项,也更方便。

→主线程与UI线程:安卓应用在启动时,就是启动一个进程,这个进程可能会有若干线程,其中有个就是所谓“主线程”,他又叫做UI线程,首先记住:UI线程==主线程。

→安卓的单线程模型:详细情况你可以在网上查到,我在这里说说一定要记住的:

(1)不要阻塞UI线程:别在UI线程里面进行耗时操作(一般就是不要执行超过五秒钟的操作),否则将会出现ANR对话框(应用程序没有响应 )。

(2)只能够在主线程里更新UI:所有对UI的更新(比如修改TextView显示内容,修改ImageView的图片),都只能在主线程里进行,因为安卓对UI的操作是线程不安全的,如果你在非UI线程里更新UI,会抛异常。





△AsyncTask的简介

→他是个类,而且还是个抽象类,一个安卓封装好的用来简化异步任务的类,只有继承了并指定泛型参数,覆写指定的方法后才能使用,使用他,你能在几乎不了解安卓系统多线程的工作过程的前提下,使用安卓多线程的功能。

→类原型:public abstract class AsyncTask<Params, Progress,Result>,三个泛型参数的功能分别是:

(1)Params:启动后台任务时的输入参数,比如你要下载一些文件,那参数是这些文件的URI。

(2)Progress:任务执行过程中更新进度的参数,比如传给进度条的参数。

(3)Result:任务执行完成之后所返回的结果。

→主要方法:

(1)protected abstract Result doInBackground(Params... params);

这是必须要实现的方法,所有异步任务都在里面这些执行,同时,该方法的所有代码都运行在非UI线程里,根据刚才所说到的单线程的模型,这个方法里面绝对不能进行任何对UI的操作。  

(2)protected void onPreExecute();

异步任务执行前的准备操作,该方法在异步任务正式启动之前被回调(doInBackground()方法执行之前回调),该方法的所有代码都执行在UI线程里面,这里可以进行UI操作,比如,将进度条的进度值设置为零。但是这里不能进行耗时操作,因为是在UI线程。




(3)protected void onProgressUpdate(Progress... values);

任务执行过程中更新UI的操作,当你在"doInBackground()"方法里面调用函数"publishProgress(Progress... values)",实际执行的代码是"onProgressUpdate(Progress... values)"方法里面所写代码。这个方法里的所有代码都运行在UI线程里,可以进行更新UI操作,但是不能进行耗时操作。

(4)protected void onPostExecute(Result result);

当异步任务正常结束后(doInBackground()方法运行结束之后),这个方法将被回调,该方法的所有代码都运行在UI线程,可以在这进行完成任务相关提示,这里不能进行耗时操作,注意:如果异步任务是被取消掉的,这个方法不会执行。

AsyncTask类方法小结

(1)异步任务各个方法执行顺序:onPreExecute()→doInBackground()→onProgressUpdate()(如果调用过publishProgress方法)→onPostExecute()。

(2)除了doInBackground()方法里的代码是运行在子线程里面的,其与方法的代码是跑在UI线程里面。




△AsyncTask使用注意

→一个AsycnTask实例只能被执行一次(只能够execute()一次),第二次会出现异常。

→刚才提到所重写的四个方法,都不能有开发者所调用,都不能有开发者所调用,都不能有开发者所调用,重要的事情说三遍,只能重写,他们都有系统调用。




△示例代码


下面来看看实例了,模拟一个文件下载效果,布局文件很简单的,先来看看布局文件代码:


<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" android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_print_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="正下载第0个文件"
        android:gravity="center_horizontal"
        android:textSize="24sp" />

    <Button
        android:id="@+id/btn_star_asyncTask"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="启动一个异步任务"
        android:onClick="asyncTask"/>
    <Button
        android:id="@+id/btn_pauseAsyncTask"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="取消当前异步任务"
        android:onClick="asyncTask"/>
    

    <ProgressBar
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar" />
</LinearLayout>



再来看看窗体代码:

package com.example.masynctask;

import android.app.Activity;
import android.os.AsyncTask;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.io.File;
import java.io.InputStream;


public class MainActivity extends Activity {

    //准备数组待会传递异步任务
    private String[] strings = new String[]{"一号文件","二号文件","三号文件","四号文件"};
    private TextView textView = null;//这个用来更新窗体
    private ProgressBar progressBar = null;//进度
    private MAsyncTask mAsyncTask = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView)findViewById(R.id.tv_print_content);
        progressBar = (ProgressBar)findViewById(R.id.progressBar);

    }

    public void asyncTask(View view){
        switch(view.getId()) {
            case R.id.btn_star_asyncTask:
                mAsyncTask = new MAsyncTask();
                mAsyncTask.execute(strings);
                break;
            case R.id.btn_pauseAsyncTask:
                mAsyncTask.cancel(true);
                break;
        }
    }

//    第一参数:执行时传递进去的类型
//    第二参数:更新那时用的参数
//    第三参数:结束那是传递进去"onPostExecute"方法的参数
//AsyncTask里面的泛型,和上述所介绍哪些方法里的泛型,是对应的。
    private class MAsyncTask extends AsyncTask<String,Integer,Integer>{

        @Override
        protected Integer doInBackground(String... params) {
            Log.i("test", "doInBackGround");
            int i = 0;
            while(i<params.length) {
                publishProgress(i,params.length);
                try {
                    Thread.sleep(4000L);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                i++;
                if (isCancelled()){
                    break;
                }
            }
            Log.i("test","循环结束");
            return params.length;
        }

    //        这个方法将在子线程开始前执行
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Log.i("test","onPreExecute");
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
//        Log.i("test","正在下载第"+values[0]+"个文件");
        textView.setText("正下载第"+values[0]+"文件");
        progressBar.setProgress( (int)((values[0]+1)/(float)values[1]*100) );
    }

    //        这个方法将执行在异步任务结束之后
    @Override
    protected void onPostExecute(Integer integer) {
        super.onPostExecute(integer);
        Log.i("test", "onPostExecute");

    }

    @Override
    protected void onCancelled(Integer integer) {
        super.onCancelled(integer);
        Log.i("test", "onCancelled");
    }
}