关于android:android多线程AsyncTaskyi一

  明天剖析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 如有侵权,请分割删除。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理