关于android:android多线程AsyncTaskyi一

43次阅读

共计 13109 个字符,预计需要花费 33 分钟才能阅读完成。

  明天剖析 android 的异步线程类 HandlerThread 与 IntentService,它们都是 android 零碎独有的线程类,而 android 中还有另一个比拟重要的异步线程类 AsyncTask。本文咱们就来剖析 AsyncTask。

  • AsyncTask 的惯例应用剖析以及案例实现
  • AsyncTask 在不同 android 版本的下的差别
  • AsyncTask 的工作原理流程

一、AsyncTask 的惯例应用剖析以及案例实现

  AsyncTask 是一种轻量级的异步工作类,它能够在线程池中执行后台任务,而后会把执行的进度和最终后果传递给主线程并更新 UI。AsyncTask 自身是一个抽象类它提供了 Params、Progress、Result 三个泛型参数,其类申明如下:

public abstract class AsyncTask<Params, Progress, Result> {

  由类申明能够看出 AsyncTask 抽象类的确定义了三种泛型类型 Params,Progress 和 Result,它们别离含意如下:

  • Params:启动工作执行的输出参数,如 HTTP 申请的 URL
  • Progress:后台任务执行的百分比
  • Result:后盾执行工作最终返回的后果类型

  如果 AsyncTask 不须要传递具体参数,那么这三个泛型参数能够应用 Void 代替。咱们当初创立一个类继承自 AsyncTask 如下:

package com.zejian.handlerlooper;

import android.graphics.Bitmap;
import android.os.AsyncTask;

/**
 * Created by zejian
 * Time 16/9/4.
 * Description:
 */
public class DownLoadAsyncTask extends AsyncTask<String,Integer,Bitmap> {

    /**
     * onPreExecute 是能够选择性覆写的办法
     * 在主线程中执行, 在异步工作执行之前, 该办法将会被调用
     * 个别用来在执行后台任务前对 UI 做一些标记和筹备工作,* 如在界面上显示一个进度条。*/
    @Override
    protected void onPreExecute() {super.onPreExecute();
    }

    /**
     * 形象办法必须覆写, 执行异步工作的办法
     * @param params
     * @return
     */
    @Override
    protected Bitmap doInBackground(String... params) {return null;}

    /**
     * onProgressUpdate 是能够选择性覆写的办法
     * 在主线程中执行, 当后台任务的执行进度产生扭转时,
     * 当然咱们必须在 doInBackground 办法中调用 publishProgress()
     * 来设置进度变动的值
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);
    }

    /**
     * onPostExecute 是能够选择性覆写的办法
     * 在主线程中执行, 在异步工作执行实现后, 此办法会被调用
     * 个别用于更新 UI 或其余必须在主线程执行的操作, 传递参数 bitmap 为
     * doInBackground 办法中的返回值
     * @param bitmap
     */
    @Override
    protected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);
    }

    /**
     * onCancelled 是能够选择性覆写的办法
     * 在主线程中, 当异步工作被勾销时, 该办法将被调用,
     * 要留神的是这个时 onPostExecute 将不会被执行
     */
    @Override
    protected void onCancelled() {super.onCancelled();
    }
}

  如代码所示,咱们创立一个继承自 AsyncTask 的异步线程类,在泛型参数方面,传递 String 类型 (Url) , Integer 类型(显示进度),Bitmap 类型作为返回值。接着重写了形象办法 doInBackground(),以及覆写了 onPreExecute()、onProgressUpdate()、onPostExecute()、onCancelled() 等办法,它们的次要含意如下:

  • (1)onPreExecute(), 该办法在主线程中执行,将在 execute(Params… params)被调用后执行,个别用来做一些 UI 的筹备工作,如在界面上显示一个进度条。
  • (2)doInBackground(Params…params), 形象办法,必须实现,该办法在线程池中执行,用于执行异步工作,将在 onPreExecute 办法执行后执行。其参数是一个可变类型,示意异步工作的输出参数,在该办法中还可通过 publishProgress(Progress… values)来更新实时的工作进度,而 publishProgress 办法则会调用 onProgressUpdate 办法。此外 doInBackground 办法会将计算的返回后果传递给 onPostExecute 办法。
  • (3)onProgressUpdate(Progress…), 在主线程中执行,该办法在 publishProgress(Progress… values)办法被调用后执行,个别用于更新 UI 进度,如更新进度条的以后进度。
  • (4)onPostExecute(Result), 在主线程中执行,在 doInBackground 执行实现后,onPostExecute 办法将被 UI 线程调用,doInBackground 办法的返回值将作为此办法的参数传递到 UI 线程中,并执行一些 UI 相干的操作,如更新 UI 视图。
  • (5)onCancelled(),在主线程中执行,当异步工作被勾销时, 该办法将被调用, 要留神的是这个时 onPostExecute 将不会被执行。

  咱们这里再强调一下它们的执行程序,onPreExecute 办法先执行,接着是 doInBackground 办法,在 doInBackground 中如果调用了 publishProgress 办法,那么 onProgressUpdate 办法将会被执行,最初 doInBackground 办法执行后完后,onPostExecute 办法将被执行。说了这么多,咱们还没说如何启动 AsyncTask 呢,其实能够通过 execute 办法启动异步线程,其办法申明如下:

public final AsyncTask<Params, Progress, Result> execute(Params... params)

  该办法是一个 final 办法,参数类型是可变类型,实际上这里传递的参数和 doInBackground(Params…params)办法中的参数是一样的,该办法最终返回一个 AsyncTask 的实例对象,能够应用该对象进行其余操作,比方完结线程之类的。启动范例如下:

new DownLoadAsyncTask().execute(url1,url2,url3);

  当然除了以上介绍的内容外,咱们在应用 AsyncTask 时还必须恪守一些规定,以防止不必要的麻烦。

  • (1) AsyncTask 的实例必须在主线程(UI 线程)中创立,execute 办法也必须在主线程中调用
  • (2) 不要在程序中间接的调用 onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个办法
  • (3) 不能在 doInBackground(Params… params)中更新 UI
  • (5) 一个 AsyncTask 对象只能被执行一次,也就是 execute 办法只能调用一次,否则屡次调用时将会抛出异样

  到此,AsyncTask 的惯例办法阐明和应用以及注意事项全副介绍完了,上面咱们来看一个下载案例,该案例是去下载一张大图,并实现下载实时进度。先来看看 AsynTaskActivity.java 的实现:

package com.zejian.handlerlooper;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.PowerManager;
import android.widget.Toast;

import com.zejian.handlerlooper.util.LogUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by zejian
 * Time 16/9/4.
 * Description:
 */
public class DownLoadAsyncTask extends AsyncTask<String, Integer, String> {
    private PowerManager.WakeLock mWakeLock;
    private int ValueProgress=100;
    private Context context;


    public DownLoadAsyncTask(Context context){this.context=context;}

    /**
     * sync method which download file
     * @param params
     * @return
     */
    @Override
    protected String doInBackground(String... params) {
        InputStream input = null;
        OutputStream output = null;
        HttpURLConnection connection = null;
        try {URL url = new URL(params[0]);
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();
            // expect HTTP 200 OK, so we don't mistakenly save error report
            // instead of the file
            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {return "Server returned HTTP" + connection.getResponseCode()
                        + " " + connection.getResponseMessage();}
            // this will be useful to display download percentage
            // might be -1: server did not report the length
            int fileLength = connection.getContentLength();
            // download the file
            input = connection.getInputStream();
            //create output
            output = new FileOutputStream(getSDCardDir());
            byte data[] = new byte[4096];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                // allow canceling with back button
                if (isCancelled()) {input.close();
                    return null;
                }
                total += count;
                // publishing the progress....
                if (fileLength > 0) // only if total length is known
                    publishProgress((int) (total * 100 / fileLength));
                //
                Thread.sleep(100);
                output.write(data, 0, count);
            }
        } catch (Exception e) {return e.toString();
        } finally {
            try {if (output != null)
                    output.close();
                if (input != null)
                    input.close();} catch (IOException ignored) { }
            if (connection != null)
                connection.disconnect();}

        return null;
    }

    @Override
    protected void onPreExecute() {super.onPreExecute();
        // take CPU lock to prevent CPU from going off if the user
        // presses the power button during download
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                getClass().getName());
        mWakeLock.acquire();
        //Display progressBar
//        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    protected void onPostExecute(String values) {super.onPostExecute(values);
        mWakeLock.release();
        if (values != null)
            LogUtils.e("Download error:"+values);
        else {Toast.makeText(context, "File downloaded", Toast.LENGTH_SHORT).show();}
    }

    /**
     * set progressBar
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);
//        progressBar.setmProgress(values[0]);
        //update progressBar
        if(updateUI!=null){updateUI.UpdateProgressBar(values[0]);
        }
    }

    /**
     * get SD card path
     * @return
     */
    public File getSDCardDir(){if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
            // 创立一个文件夹对象,赋值为内部存储器的目录
            String dirName = Environment.getExternalStorageDirectory()+"/MyDownload/";
            File f = new File(dirName);
            if(!f.exists()){f.mkdir();
            }
            File downloadFile=new File(f,"new.jpg");
            return downloadFile;
        }
        else{LogUtils.e("NO SD Card!");
            return null;

        }

    }

    public UpdateUI updateUI;


    public interface UpdateUI{void UpdateProgressBar(Integer values);
    }

    public void setUpdateUIInterface(UpdateUI updateUI){this.updateUI=updateUI;}
}

  简略阐明一下代码,在 onPreExecute 办法中,能够做了一些筹备工作,如显示进度圈,这里为了演示不便,进度圈在常态下就是显示的,同时,咱们还锁定了 CPU,避免下载中断,而在 doInBackground 办法中,通过 HttpURLConnection 对象去下载图片,而后再通过 int fileLength =connection.getContentLength(); 代码获取整个下载图片的大小并应用 publishProgress((int) (total * 100 / fileLength)); 更新进度,进而调用 onProgressUpdate 办法更新进度条。最初在 onPostExecute 办法中开释 CPU 锁,并告诉是否下载胜利。接着看看 Activity 的实现:
activity\_download.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:customView="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.zejian.handlerlooper.util.LoadProgressBarWithNum
        android:id="@+id/progressbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        customView:progress_radius="100dp"
        android:layout_centerInParent="true"
        customView:progress_strokeWidth="40dp"
        customView:progress_text_size="35sp"
        customView:progress_text_visibility="visible"
        customView:progress_value="0"
        />

    <Button
        android:id="@+id/downloadBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="start download"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/progressbar"
        android:layout_marginTop="40dp"
        />
</RelativeLayout>

AsynTaskActivity.java

package com.zejian.handlerlooper;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.widget.Button;

import com.zejian.handlerlooper.util.LoadProgressBarWithNum;
import com.zejian.handlerlooper.util.LogUtils;

/**
 * Created by zejian
 * Time 16/9/4.
 * Description:AsynTaskActivity
 */
public class AsynTaskActivity extends Activity implements DownLoadAsyncTask.UpdateUI {
    private static int WRITE_EXTERNAL_STORAGE_REQUEST_CODE=0x11;
    private static String DOWNLOAD_FILE_JPG_URL="http://img2.3lian.com/2014/f6/173/d/51.jpg";
    private LoadProgressBarWithNum progressBar;

    private Button downloadBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);
        progressBar= (LoadProgressBarWithNum) findViewById(R.id.progressbar);
        downloadBtn= (Button) findViewById(R.id.downloadBtn);
        //create DownLoadAsyncTask
        final DownLoadAsyncTask  downLoadAsyncTask= new DownLoadAsyncTask(AsynTaskActivity.this);
        //set Interface
        downLoadAsyncTask.setUpdateUIInterface(this);
        //start download
        downloadBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //execute
                downLoadAsyncTask.execute(DOWNLOAD_FILE_JPG_URL);
            }
        });

        //android 6.0 权限申请
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            //android 6.0 API 必须申请 WRITE_EXTERNAL_STORAGE 权限
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    WRITE_EXTERNAL_STORAGE_REQUEST_CODE);
        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        doNext(requestCode,grantResults);
    }

    private void doNext(int requestCode, int[] grantResults) {if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission Granted
                LogUtils.e("Permission Granted");
            } else {
                // Permission Denied
                LogUtils.e("Permission Denied");
            }
        }
    }

    /**
     * update progressBar
     * @param values
     */
    @Override
    public void UpdateProgressBar(Integer values) {progressBar.setmProgress(values);;
    }
}

  在 AsynTaskActivity 中实现了更新 UI 的接口 DownLoadAsyncTask.UpdateUI,用于更新主线程的 progressBar 的进度,因为应用的测试版本是 android6.0,波及到内部 SD 卡读取权限的申请,所以在代码中对 SD 卡权限进行了非凡解决(这点不深究,不明确能够 google 一下),LoadProgressBarWithNum 是一个自定义的进度条控件。ok~,最初看看咱们的运行后果:

  成果合乎预期,通过这个案例,置信咱们对 AsyncTask 的应用已相当清晰了。根本应用到此,而后再来聊聊 AsyncTask 在不同 android 版本中的差别。

二、AsyncTask 在不同 android 版本的下的差别

  这里咱们次要辨别一下 android3.0 前后版本的差别,在 android 3.0 之前,AsyncTask 解决工作时默认采纳的是线程池里并行处理工作的形式,而在 android 3.0 之后,为了防止 AsyncTask 解决工作时所带来的并发谬误,AsyncTask 则采纳了单线程串行执行工作。然而这并不意味着 android 3.0 之后只能执行串行工作,咱们依然能够采纳 AsyncTask 的 executeOnExecutor 办法来并行执行工作。接下来,编写一个案例,别离在 android 2.3.3 和 android 6.0 上执行,而后打印输出日志。代码如下:

package com.zejian.handlerlooper;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.zejian.handlerlooper.util.LogUtils;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by zejian
 * Time 16/9/5.
 * Description:
 */
public class ActivityAsyncTaskDiff extends Activity {
    private Button btn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_diff);
        btn= (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {new AysnTaskDiff("AysnTaskDiff-1").execute("");
                new AysnTaskDiff("AysnTaskDiff-2").execute("");
                new AysnTaskDiff("AysnTaskDiff-3").execute("");
                new AysnTaskDiff("AysnTaskDiff-4").execute("");
                new AysnTaskDiff("AysnTaskDiff-5").execute("");
            }
        });
    }

    private static class AysnTaskDiff extends AsyncTask<String ,Integer ,String>{
        private String name;
        public AysnTaskDiff(String name){super();
            this.name=name;
        }

        @Override
        protected String doInBackground(String... params) {
            try {Thread.sleep(2000);
            }catch (Exception ex){ex.printStackTrace();
            }

            return name;
        }

        @Override
        protected void onPostExecute(String s) {super.onPostExecute(s);
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            LogUtils.e(s+"execute 执行实现工夫:"+df.format(new Date()));
        }
    }

}

  案例代码比较简单,不过多剖析,咱们间接看在 android 2.3.3 和 android 6.0 上执行的后果,其中 android 2.3.3 上执行 Log 打印如下:

在 android 6.0 上执行 Log 打印如下:

   从打印 log 能够看出 AsyncTask 在 android 2.3.3 上的确是并行执行工作的,而在 android 6.0 上则是串行执行工作。那么理解这点有什么用呢?其实以前我也只是晓得这回事而已,不过最近在 SDK 开发中遇到了 AsyncTask 的开发问题,产生问题的场景是这样的,咱们团队在 SDK 中应用了 AsyncTask 作为网络申请类,因为当初大部分零碎都是在 Android 3.0 以上的零碎运行的,所以默认就是串行运行,一开始 SDK 在海外版往外提供也没有呈现什么问题,直到前面咱们提供国内一个 publisher 海内版本时,问题就呈现了,该 publisher 接入咱们的 SDK 后,他们的利用网络加载速度变得非常慢,起初他们始终没排查出啥问题,咱们这边也在懵逼中……直到咱们单方都找到一个点,那就是 publisher 的利用和咱们的 SDK 应用的都是 AsyncTask 作为网络申请,那么问题就来,咱们 SDK 是在在 Application 启动时触发网络的,而他们的利用也是启动 Activity 时去拜访网络,所以 SDK 比利用先加载网络数据,然而!!!AsyncTask 默认是串行执行的,所以!!只有等咱们的 SDK 网络加载实现后,他们利用才开始加载网络数据,这就造成利用的网络加载提早非常重大了。前面咱们 SDK 在外部把 AsyncTask 改为并行任务后问题也就解决了(当然这也是 SDK 的一个 BUG,思考欠佳)。在 Android 3.0 之后咱们能够通过上面代码让 AsyncTask 执行并行任务,其 AsyncTask.THREAD\_POOL\_EXECUTOR 为 AsyncTask 的外部线程池。

new AysnTaskDiff("AysnTaskDiff-5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");

   第一个参数传递是线程池,个别应用 AsyncTask 外部提供的线程池即可(也能够本人创立),第二个参数,就是最终会传递给 doInBackground 办法的可变参数,这里不传,所以间接给了空白符。执行成果就不再演示了,大家能够自行测试一下。

 到此 AsyncTask 在不同 android 版本中的差别也剖析的差不多了。

本文转自:http://blog.csdn.net/javazejian/article/details/52462830 如有侵权,请分割删除。

正文完
 0