标签: android多线程

android多线程-AsyncTask之工作原理深入解析(下)

android多线程-AsyncTask之工作原理深入解析(下)

关联文章:
Android 多线程之HandlerThread 完全详解
Android 多线程之IntentService 完全详解
android多线程-AsyncTask之工作原理深入解析(上)
android多线程-AsyncTask之工作原理深入解析(下)

上篇分析AsyncTask的一些基本用法以及不同android版本下的区别,接着本篇我们就来全面剖析一下AsyncTask的工作原理。在开始之前我们先来了解一个多线程的知识点——Callable<V> 、Future<V>和FutureTask类

一、理解Callable<V> 、Future<V>以及FutureTask类

Callable<V>

Callable的接口定义如下:

public interface Callable<V> {   
      V   call()   throws Exception;   
}   

 

Callable接口声明了一个名称为call()的方法,该方法可以有返回值V,也可以抛出异常。Callable也是一个线程接口,它与Runnable的主要区别就是Callable在线程执行完成后可以有返回值而Runnable没有返回值,Runnable接口声明如下:

public interface Runnable {
    public abstract void run();
}

 

那么Callable接口如何使用呢,Callable需要和ExcutorService结合使用,其中ExecutorService也是一个线程池对象继承自Executor接口,对于线程池的知识点不了解可以看看我的另一篇文章,这里就不深入了,接着看看ExecutorService提供了那些方法供我们使用:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

 

  • submit(Callable task),传递一个实现Callable接口的任务,并且返回封装了异步计算结果的Future。
  • submit(Runnable task, T result),传递一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象。
  • submit(Runnable task),传递一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future。

因此我们只要创建好我们的线程对象(实现Callable接口或者Runnable接口),然后通过上面3个方法提交给线程池去执行即可。Callable接口介绍就先到这,再来看看Future时什么鬼。

Future<V>

Future接口是用来获取异步计算结果的,说白了就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操作。其方法如下:

public interface Future<V> {
    //取消任务
    boolean cancel(boolean mayInterruptIfRunning);

    //如果任务完成前被取消,则返回true。
    boolean isCancelled();

    //如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。
    boolean isDone();

    //获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。
    V get() throws InterruptedException, ExecutionException;

    // 获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,
    //如果阻塞时间超过设定的timeout时间,该方法将返回null。
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

}

 

总得来说Future有以下3点作用:

  • 能够中断执行中的任务
  • 判断任务是否执行完成
  • 获取任务执行完成后额结果。

但是Future只是接口,我们根本无法将其创建为对象,于官方又给我们提供了其实现类FutureTask,这里我们要知道前面两个接口的介绍都只为此类做铺垫,毕竟AsncyTask中使用到的对象是FutureTask。

FutureTask

先来看看FutureTask的实现:

public class FutureTask<V> implements RunnableFuture<V> {  
  • 1

显然FutureTask类实现了RunnableFuture接口,我们再看一下RunnableFuture接口的实现:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}
  • 1
  • 2
  • 3

从接口实现可以看出,FutureTask除了实现了Future接口外还实现了Runnable接口,因此FutureTask既可以当做Future对象也可是Runnable对象,当然FutureTask也就可以直接提交给线程池来执行。接着我们*关心的是如何创建FutureTask对象,实际上可以通过如下两个构造方法来构建FutureTask

public FutureTask(Callable<V> callable) {  
}  
public FutureTask(Runnable runnable, V result) {  
}  

 

从构造方法看出,我们可以把一个实现了Callable或者Runnable的接口的对象封装成一个FutureTask对象,然后通过线程池去执行,那么具体如何使用呢?简单案例,CallableDemo.java代码如下:


package com.zejian.Executor;
import java.util.concurrent.Callable;
/**
 * Callable接口实例 计算累加值大小并返回
 */
public class CallableDemo implements Callable<Integer> {

    private int sum;
    @Override
    public Integer call() throws Exception {
        System.out.println("Callable子线程开始计算啦!");
        Thread.sleep(2000);

        for(int i=0 ;i<5000;i++){
            sum=sum+i;
        }
        System.out.println("Callable子线程计算结束!");
        return sum;
    }
}

 

CallableTest.java测试代码如下:

package com.zejian.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class CallableTest {

public static void main(String[] args) {
//*种使用方式
//      //创建线程池
//      ExecutorService es = Executors.newSingleThreadExecutor();
//      //创建Callable对象任务
//      CallableDemo calTask=new CallableDemo();
//      //提交任务并获取执行结果
//      Future<Integer> future =es.submit(calTask);
//      //关闭线程池
//      es.shutdown();

    //第二中使用方式

    //创建线程池
    ExecutorService es = Executors.newSingleThreadExecutor();
    //创建Callable对象任务
    CallableDemo calTask=new CallableDemo();
    //创建FutureTask
    FutureTask<Integer> futureTask=new FutureTask<>(calTask);
    //执行任务
    es.submit(futureTask);
    //关闭线程池
    es.shutdown();
    try {
        Thread.sleep(2000);
    System.out.println("主线程在执行其他任务");

    if(futureTask.get()!=null){
        //输出获取到的结果
        System.out.println("futureTask.get()-->"+futureTask.get());
    }else{
        //输出获取到的结果
        System.out.println("futureTask.get()未获取到结果");
    }

    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("主线程在执行完成");
}
}

 

代码非常简单,注释也很明朗,这里我们分析一下第2种执行方式,先前声明一个CallableDemo类,该类实现了Callable接口,接着通过call方法去计算sum总值并返回。然后在测试类CallableTest中,把CallableDemo实例类封装成FutureTask对象并交给线程池去执行,*终执行结果将封装在FutureTask中,通过FutureTask#get()可以获取执行结果。*种方式则是直接把Callable实现类丢给线程池执行,其结果封装在Future实例中,第2种方式执行结果如下:

Callable子线程开始计算啦!
主线程在执行其他任务
Callable子线程计算结束!
futureTask.get()-->12497500
主线程在执行完成

 

ok~,到此我们对Callable、Future和FutureTask就介绍到这,有了这个知识铺垫,我们就可以愉快的撩开AsyncTask的内部工作原理了。

二、AsyncTask的工作原理完全解析

在上篇中,使用了如下代码来执行AsyncTask的异步任务:

new AysnTaskDiff("AysnTaskDiff-1").execute("");
  • 1

从代码可知,入口是execute方法,那我们就先看看execute的源码:

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

 

很明显execute方法只是一个壳子,直接调用了executeOnExecutor(sDefaultExecutor, params),其中sDefaultExecutor是一个串行的线程池,接着看看sDefaultExecutor内部实现:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
 * An {@link Executor} that executes tasks one at a time in serial
 * order.  This serialization is global to a particular process.
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

//串行线程池类,实现Executor接口
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() { //插入一个Runnble任务
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        //判断是否有Runnable在执行,没有就调用scheduleNext方法
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
      //从任务队列mTasks中取出任务并放到THREAD_POOL_EXECUTOR线程池中执行.
      //由此也可见任务是串行进行的。
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

 

从源码可以看出,ArrayDeque是一个存放任务队列的容器(mTasks),任务Runnable传递进来后交给SerialExecutor的execute方法处理,SerialExecutor会把任务Runnable插入到任务队列mTasks尾部,接着会判断是否有Runnable在执行,没有就调用scheduleNext方法去执行下一个任务,接着交给THREAD_POOL_EXECUTOR线程池中执行,由此可见SerialExecutor并不是真正的线程执行者,它只是是保证传递进来的任务Runnable(实例是一个FutureTask)串行执行,而真正执行任务的是THREAD_POOL_EXECUTOR线程池,当然该逻辑也体现AsyncTask内部的任务是默认串行进行的。顺便看一下THREAD_POOL_EXECUTOR线程池的声明:

//CUP核数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心线程数量
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//*大线程数量
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//非核心线程的存活时间1s
private static final int KEEP_ALIVE = 1;
//线程工厂类
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
//线程队列,核心线程不够用时,任务会添加到该队列中,队列满后,会去调用非核心线程执行任务
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 * 创建线程池
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

 

ok~,关于sDefaultExecutor,我们先了解到这,回到之前execute方法内部调用的executeOnExecutor方法的步骤,先来看看executeOnExecutor都做了些什么事?其源码如下:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
   //判断在那种状态
   if (mStatus != Status.PENDING) {
       switch (mStatus) {
           case RUNNING:
               throw new IllegalStateException("Cannot execute task:"
                       + " the task is already running.");
           case FINISHED://只能执行一次!
               throw new IllegalStateException("Cannot execute task:"
                       + " the task has already been executed "
                       + "(a task can be executed only once)");
       }
   }

   mStatus = Status.RUNNING;
   //onPreExecute()在此执行了!!!
   onPreExecute();
   //参数传递给了mWorker.mParams
   mWorker.mParams = params;
   //执行mFuture任务,其中exec就是传递进来的sDefaultExecutor
   //把mFuture交给线程池去执行任务
   exec.execute(mFuture);

   return this;
    }

 

从executeOnExecutor方法的源码分析得知,执行任务前先会去判断当前AsyncTask的状态,如果处于RUNNING和FINISHED状态就不可再执行,直接抛出异常,只有处于Status.PENDING时,AsyncTask才会去执行。然后onPreExecute()被执行的,该方法可以用于线程开始前做一些准备工作。接着会把我们传递进来的参数赋值给 mWorker.mParams ,并执行开始执行mFuture任务,那么mWorker和mFuture到底是什么?先看看mWorker即WorkerRunnable的声明源码:

//抽象类
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {

    Params[] mParams;

}

 

WorkerRunnable抽象类实现了Callable接口,因此WorkerRunnable本质上也算一个Callable对象,其内部还封装了一个mParams的数组参数,因此我们在外部执行execute方法时传递的可变参数*终会赋值给WorkerRunnable的内部数组mParams,这些参数*后会传递给doInBackground方法处理,这时我们发现doInBackground方法也是在WorkerRunnable的call方法中被调用的,看看其源码如下:

public AsyncTask() {
   //创建WorkerRunnable mWorker,本质上就是一个实现了Callable接口对象
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            //设置标志
            mTaskInvoked.set(true);

         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //执行doInBackground,并传递mParams参数
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            //执行完成调用postResult方法更新结果
            return postResult(result);
        }
    };
//把mWorker(即Callable实现类)封装成FutureTask实例
//*终执行结果也就封装在FutureTask中
    mFuture = new FutureTask<Result>(mWorker) {
        //任务执行完成后被调用
        @Override
        protected void done() {
            try {
             //如果还没更新结果通知就执行postResultIfNotInvoked
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occurred while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                //抛异常
                postResultIfNotInvoked(null);
            }
        }
    };
}

 

可以看到在初始化AsyncTask时,不仅创建了mWorker(本质实现了Callable接口的实例类)而且也创建了FutureTask对象,并把mWorker对象封装在FutureTask对象中,*后FutureTask对象将在executeOnExecutor方法中通过线程池去执行。给出下图协助理解:
这里写图片描述

AsynTask在初始化时会创建mWorker实例对象和FutureTask实例对象,mWorker是一个实现了Callable线程接口并封装了传递参数的实例对象,然后mWorker实例会被封装成FutureTask实例中。在AsynTask创建后,我们调用execute方法去执行异步线程,其内部又直接调用了executeOnExecutor方法,并传递了线程池exec对象和执行参数,该方法内部通过线程池exec对象去执行mFuture实例,这时mWorker内部的call方法将被执行并调用doInBackground方法,*终通过postResult去通知更新结果。关于postResult方法,其源码如下:

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

 

显然是通过Handler去执行结果更新的,在执行结果成返回后,会把result封装到一个AsyncTaskResult对象中,*后把MESSAGE_POST_RESULT标示和AsyncTaskResult存放到Message中并发送给Handler去处理,这里我们先看看AsyncTaskResult的源码:

private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

 

显然AsyncTaskResult封装了执行结果的数组以及AsyncTask本身,这个没什么好说的,接着看看AsyncTaskResult被发送到handler后如何处理的。

private static class InternalHandler extends Handler {
    public InternalHandler() {
        //获取主线程的Looper传递给当前Handler,这也是为什么AsyncTask只能在主线程创建并执行的原因
        super(Looper.getMainLooper());
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
    //获取AsyncTaskResult
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            //执行完成
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
                //更新进度条的标志
            case MESSAGE_POST_PROGRESS:
            //执行onProgressUpdate方法,自己实现。
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

 

从Handler的源码分析可知,该handler绑定的线程为主线线程,这也就是为什么AsyncTask必须在主线程创建并执行的原因了。接着通过handler发送过来的不同标志去决定执行那种结果,如果标示为MESSAGE_POST_RESULT则执行AsyncTask的finish方法并传递执行结果给该方法,finish方法源码如下:

private void finish(Result result) {
        if (isCancelled()) {//判断任务是否被取消
            onCancelled(result);
        } else {//执行onPostExecute(result)并传递result结果
            onPostExecute(result);
        }
        //更改AsyncTask的状态为已完成
        mStatus = Status.FINISHED;
    }

 

该方法先判断任务是否被取消,如果没有被取消则去执行onPostExecute(result)方法,外部通过onPostExecute方法去更新相关信息,如UI,消息通知等。*后更改AsyncTask的状态为已完成。到此AsyncTask的全部流程执行完。
这里还有另一种标志MESSAGE_POST_PROGRESS,该标志是我们在doInBackground方法中调用publishProgress方法时发出的,该方法原型如下:

protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
    //发送MESSAGE_POST_PROGRESS,通知更新进度条
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

 

ok~,AsyncTask的整体流程基本分析完,*后来个总结吧:当我们调用execute(Params… params)方法后,其内部直接调用executeOnExecutor方法,接着onPreExecute()被调用方法,执行异步任务的WorkerRunnable对象(实质为Callable对象)*终被封装成FutureTask实例,FutureTask实例将由线程池sExecutor执行去执行,这个过程中doInBackground(Params… params)将被调用(在WorkerRunnable对象的call方法中被调用),如果我们覆写的doInBackground(Params… params)方法中调用了publishProgress(Progress… values)方法,则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress… values)方法将被调用;*后如果FutureTask任务执行成功并返回结果,则通过postResult方法发送一条MESSAGE_POST_RESULT的消息去执行AsyncTask的finish方法,在finish方法内部onPostExecute(Result result)方法被调用,在onPostExecute方法中我们可以更新UI或者释放资源等。这既是AsyncTask内部的工作流程,可以说是Callable+FutureTask+Executor+Handler内部封装。结尾我们献上一张执行流程,协助大家理解整个流程:
这里写图片描述
好~,本篇到此结束。。。

android多线程-AsyncTask之工作原理深入解析(上)

android多线程-AsyncTask之工作原理深入解析(上)

关联文章:
Android 多线程之HandlerThread 完全详解
Android 多线程之IntentService 完全详解
android多线程-AsyncTask之工作原理深入解析(上)
android多线程-AsyncTask之工作原理深入解析(下)

前两篇我们分析android的异步线程类HandlerThread与IntentService,它们都是android系统独有的线程类,而android中还有另一个比较重要的异步线程类,它就是AsyncTask。本篇我们将从以下3点深入分析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)
  • 1

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

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

当然除了以上介绍的内容外,我们在使用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~,*后看看我们的运行结果:
%title插图%num
效果符合预期,通过这个案例,相信我们对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打印如下:
%title插图%num
在 android 6.0上执行Log打印如下:
%title插图%num
从打印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,"");
  • 1

*个参数传递是线程池,一般使用AsyncTask内部提供的线程池即可(也可以自己创建),第二个参数,就是*终会传递给doInBackground方法的可变参数,这里不传,所以直接给了空白符。执行效果就不再演示了,大家可以自行测试一下。
ok~,到此AsyncTask在不同android版本中的差异也分析完,感觉文章有点长了,那么AsyncTask工作原理分析就放到下篇吧。

Android多线程(Handler篇)

先放流程图:

%title插图%num

由于Android中的耗时操作不能放入主线程中,所以实现多线程是必须的。今天的主角是Handler,本文将从使用及源码来分析探索其奥秘。

使用
步骤:

创建Handler对象,实现handlMessage()方法
创建Runnable线程
此时产生一个Looper,并自动创建一个消息队列MessageQueue()
Looper轮询MessageQueue交给Handler
Handler做处理
其中1、2、5为使用步骤,其他在后面分析源码时会讲到
使用方法

public class MainActivity extends AppCompatActivity {
@SuppressLint(“HandlerLeak”)
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
// 处理事件
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
// 耗时操作
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
}).start();
}
}

使用就写这么多,接下来重点分析Handler原理。

原理分析
首先列一下将会讲到的几个对象

ThreadLocal
Looper
MessageQueue
Handler
Message
ThreadLocal
关于他我看了几篇博客,很大一部分写的是错的,所以
注意:

他并不是解决共享对象的多线程访问问题的!!!
他并没有创建对象的拷贝或副本!!!
目的:他只是为了保证每个线程都拥有同一个类的不同对象
实质:每个线程里都new了同一个类的对象
作用:你或许觉得这样做很傻,但是如果使用全局变量就要考虑线程安全问题,而线程安全是以消耗性能为前提的,所以这种设计可以说很巧妙
场景:每个线程都需要相同类型的对象,又各自独立,并且与他相关的操作不少时,如,Looper,ActivityThread,AMS 等

private static final ThreadLocal tSession = new ThreadLocal();
public static Session getSession() throws Exception{
Session s = tSession.get();
try{
if(s==null){
s = getSessionFactory.openSession();
tSession.set(s);
}
}catch(Exception e){

}
return s;
}

我们看一下ThreadLocal的源码,主要分析get()和set()

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings(“unchecked”)
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

set、get操作的都是ThreadLocalMap,key=当前线程,value=线程局部变量缓存值。可以看到get()实际上都是调用getMap(传入当前线程)来取得当前线程的ThreadLocalMap对象

set(),实际上是调用了ThreadLocalMap的set(),ThreadLocalMap的set()涉及到哈希散列算法,我会在之后博客里详细分析这里先不提算法的事。
get(),从当前线程中获取ThreadLocalMap,查询当前ThreadLocal变量实例对应的Entry,如果不为null,获取value,如果map为null,走初始化方法
由此看出不是如很多博主写的各线程用了同一个对象又相互独立那么神奇,只不过是用线程当做键在其中维护了一个私有变量而已。得到ThreadLocalMap后如何得到维护的变量呢,在这一句

ThreadLocalMap.Entry e = map.getEntry(this);
//this指代的ThreadLocal对象

所以过程就很清晰了,我们来总结一下:

声明一个全局公用的ThreadLocal实例作为key
在线程中new一个或取出已经存了的对象作为value
将此key-value放入ThreadLocalMap中
将当前线程作为key,ThreadLocalMap作为值放入ThreadLocal中
比较绕多读几遍就能明白了,再放一张图加深理解

%title插图%num
Looper
两件事

创建消息队列
在队列中循环取消息
两个方法

prepare()
loop()
两个作用

保证当前线程只有一个looper和一个MessageQueue
从MessageQueue中取消息交给target的dispatchMessage
下面我们分析源码

#####构造方法

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

很容易看出当Looper构造时创建了一个消息队列

#####prepare()方法

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}
sThreadLocal.set(new Looper(quitAllowed));
}

通过前面ThreadLocal的分析可知sThreadLocal.get()得到了Looper对象,当Looper存在是报错,不存在是创建一个存入ThreadLocal中,保证线程里有且只有一个Looper对象。

#####loop()

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);
}
final MessageQueue queue = me.mQueue;

// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(“>>>>> Dispatching to ” + msg.target + ” ” +
msg.callback + “: ” + msg.what);
}

final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end – start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, “Dispatch took ” + time + “ms on ”
+ Thread.currentThread().getName() + “, h=” +
msg.target + ” cb=” + msg.callback + ” msg=” + msg.what);
}
}

if (logging != null) {
logging.println(“<<<<< Finished to ” + msg.target + ” ” + msg.callback);
}

// Make sure that during the course of dispatching the
// identity of the thread wasn’t corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, “Thread identity changed from 0x”
+ Long.toHexString(ident) + ” to 0x”
+ Long.toHexString(newIdent) + ” while dispatching to ”
+ msg.target.getClass().getName() + ” ”
+ msg.callback + ” what=” + msg.what);
}

msg.recycleUnchecked();
}
}

1)拿到ThreadLocal中的Looper,若没有则prepare()
2)拿到Looper的MessageQueue
3)进入死循环,调用msg.target.dispatchMessage(msg),发给Handler
4)释放资源

MessageQueue
一个单链表结构的消息队列

Handler
#####构造函数

public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, “The following Handler class should be static or leaks might occur: ” +
klass.getCanonicalName());
}
}

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
“Can’t create handler inside thread that has not called Looper.prepare()”);
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

构造时通过Looper.myLooper获取当前线程保存的Looper实例,再获取这个Looper的MessageQueue

#####发送消息

public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}

/**
* Sends a Message containing only the what value.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}

/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
* @see #sendMessageDelayed(android.os.Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}

发送消息时所有方法都实际调用了sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + ” sendMessageAtTime() called with no mQueue”);
Log.w(“Looper”, e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

是获取MessageQueue调用enqueueMessage();
#####接下来看enqueueMessage中

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

将msg的target属性赋值为Handler自己,实现了Message与Handler的绑定,并调用了MessageQueue中的enqueueMessage()方法

boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException(“Message must have a target.”);
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + ” This message is already in use.”);
}

synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + ” sending message to a Handler on a dead thread”);
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}

msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don’t have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

对前一个方法进行补充,把msg放入MessageQueue中,这时候轮询取消息(在前面Looper已经分析),调用dispatchMessage()

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

*终调用handlerMessage()方法

/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

一个空方法,在这里处理操作,end

Android多线程方式

1、前言
在Android开发中经常会使用到多线程,这里主要是总结Android开发中常见的多线程实现方式,以及这些多线程实现方式的一些特点
多线程实现方式主要有:

实现Thread的run()方法或者实现Runable接口
HandlerThread
AsyncTask
LoaderManager
2、Thread方式
一般使用异步操作*常见的一种方式,我们可以继承Thread,并重写run()方法,如下所示:

Thread syncTask = new Thread() {
@Override
public void run() {
// 执行耗时操作
}
};

syncTask.start();

还有另外一种启动线程的方式,即在创建Thread对象时,传入一个实现了Runable接口的对象,如下所示:

Thread syncTask = new Thread(new Runnable() {
@Override
public void run() {
// 执行耗时操作
}
});

syncTask.start();

Thread类中有几个方法的作用有些模糊,这里给出说明:

interrupt( ):
我们一般会使用该方法中断线程的执行,但该方法并不会中断线程,它的作用只是设置一个中断标志位,我们还得在run( )方法中判断这个标志位,并决定是否继续执行,通过这样的方式来达到中断的效果。 但该方法根据线程状态的不同,会有不同的结果。结果如下:
当线程由于调用wait( )、join( )、sleep( )而阻塞时,中断标志位将会被清空,并接收到InterruptedException异常。
当线程由于正在进行InterruptibleChannel类型的I/O操作而阻塞时,中断标志位将会置位,并接收到ClosedByInterruptException异常(I/O流也会自动关闭)
当线程由于进行Selector操作而阻塞时,中断标志位将会置位,但不会接收到异常
interrupted( )和isInterrupted( )的区别:
两个方法都是判断当前线程的中断标志位是否被置位,但调用interrupted( )方法后,中断标志位将会重置,而isInterrupted()不会被重置。

join( )和sleep( )的区别:两个方法都会让线程暂停执行
join()方法是让出执行资源(如:CPU时间片),使得其它线程可以获得执行的资源。所以调用join()方法会使进入阻塞状态,该线程被唤醒后会进入runable状态,等待下一个时间片的到来才能再次执行。
sleep( )不会让出资源,只是处于睡眠状态(类似只执行空操作)。调用sleep()方法会使进入等待状态,当等待时间到后,如果还在时间片内,则直接进入运行状态,否则进入runable状态,等待下个时间片。

3、HandlerThread
有些需求需要子线程不断的从一个消息队列中取出消息,并进行处理,处理完毕以后继续取出下一个处理。对于这个需求我们可以使用*种方式,实现一个Thread对象,并创建一个消息队列,在Thread对象的run方法中不断的从消息队列中取出消息进行处理。多以该线程的这些特点有点像一个Looper线程,我们可复用Handler机制提供的消息队列MessageQueue,而无需自己重新创建。
HandlerThread的内部实现机制很简单,在创建新的线程后,使该线程成为一个Looper线程,让该线程不断的从MessageQueue取出消息并处理。我们看一下HandlerThread的实现:

public class HandlerThread extends Thread {
int mPriority;
Looper mLooper;

/**
* Constructs a HandlerThread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}

@Override
public void run() {
// 要想让某个线程成为Looper线程,先调用Looper.prepare()为该线程创建一个Looper对象,并初始化MessageQueue对象
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
// 调用Looper.loop(),让该线程的Looper实例循环从MessageQueue中取出Message进行处理
Looper.loop();
}
}

4、AsyncTask
这是我们*经常使用的一种异步方式,在前面的两种多线程方式中,如果在子线程中进行了耗时的处理操作(如:网络请求、读写数据库等),当操作完毕后,我们需要更新UI上的显示状态,但在Android开发中我们是不能在子线程中更新UI界面的,所以还得在子线程中发送一个通知到主线程,让主线程去更新UI。这样的操作流程有些复杂,且都是重复性的工作。所以Android sdk中为我们抽象出AsyncTask这个类。

public class CustomAsyncTask extends AsyncTask<String, Integer, String> {
@Override
protected void onPreExecute() {
// 在开始执行异步操作前回调,该方法在主线程中执行
}

@Override
protected String doInBackground(String… strings) {
// 在该方法中进行异步操作,参数strings是在启动异步任务时execute(…)传递进来的
// 该异步任务放回的结果类型为String
return null;
}

@Override
protected void onProgressUpdate(Integer… values) {
// 该方法用户通知用户doInBackground()方法的处理进度,在主线程中被回调,所以可在该方法中更新UI
// 参数values用于指示处理进度
}

@Override
protected void onPostExecute(String result) {
// 该方法是在异步操作doInBackground()处理完毕后回调,参数result是doInBackground()的处理结果
// 该方法在主线程中被回调,可直接更新UI
}

@Override
protected void onCancelled(String result) {
super.onCancelled(result);

// 当调用cancel(boolean), 则在doInBackground()完成后回调该方法
// 注意: 参数result可能为null,
}
}

AsyncTask的内部使用了两个线程池,我们大概看一下AsyncTask的内部实现

// 顺序执行任务的线程池,注意这个线程池是静态的,每个AsyncTask对象共用这个线程池
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

// 我们启动异步任务的三个方法,都是向SerialExecutor.execute(runable)传递一个runable对象
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
return executeOnExecutor(sDefaultExecutor, params);
}

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params… params) {

exec.execute(mFuture);

return this;
}

public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}

看一下SerialExecutor的实现

private static class SerialExecutor implements Executor {
// 存储待执行的异步任务
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;

public synchronized void execute(final Runnable r) {
// 其实并没有马上执行,而是添加到队列mTasks中, 进行一个排队
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
// 一个任务执行完后,再执行下一个
scheduleNext();
}
}
});

// 当前没有异步任务执行时,启动开始执行
if (mActive == null) {
scheduleNext();
}
}

protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
// 使用另外一个线程池分配线程,并执行任务
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}

所以在使用AsyncTask执行异步操作时,会先在SerialExecutor进行一个顺序排队, 后再用ThreadPoolExcutor线程池为你分配一个线程并执行。而整个应用的AsyncTask任务都在排同一条队,有可能等待排队的任务很多,所以一般不会使用AsyncTask执行一些优先级比较高的异步任务。
当然我们是可以跳过不需要进行排队,直接就通过线程池分配一个线程并执行异步任务,但需要注意同时执行太多的异步任务,会影响用户体验,我想Google就是为了限制同时创建太多的线程才会采用一个排队机制的

/** @hide */
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}

该方法是隐藏,但可使用反射,设置一个线程池。

5、Loader&LoaderManager
上面三种异步方式都可以用来加载一些耗时的数据,但有时我们加载数据的过程与Activity、Fragment的生命息息相关的。所以在使用上面说的那几种异步方式进行异步数据加载时,是需要去考虑Activity(Fragment)的生命周期是处于哪个阶段的。于是Android在Android 3.0以后引入了LoaderManager,主要用于执行一些耗时的异步数据加载操作,并根据Activity生命周期对异步处理进行调整,LoaderManager可以解决的问题包括:

加载的数据有变化时,会自动通知我们,而不自己监控数据的变化情况,如:用CursorLoader来加载数据库数据,当数据库数据有变化时,可是个展示变化的数据
数据的请求处理时机会结合Activity和Fragment的生命周期进行调整,如:若Acivity销毁了,那就不会再去请求新的数据
使用该方法加载数据涉及到两个类重要的类,Loader和LoaderManager:

Loader:该类用于数据的加载 ,类型参数D用于指定Loader加载的数据类型

public class Loader<D> {
}

一般我们不直接继承Loader,而是继承AsyncTaskLoader,因为Loader的加载工作并不是在异步线程中。而AsyncTaskLoader实现了异步线程,加载流程在子线程中执行。注意:对该类的调用应该在主线程中完成。

LoaderManager:
LoaderManager用于管理与Activity和Fragment关联的Loader实例,LoaderManager负责根据的Activity的生命周期对Loader的数据加载器进行调度,所以这里分工明确,Loader负责数据加载逻辑,LoaderManager
负责Loader的调度,开发者只需要自定义自己的Loader,实现数据的加载逻辑,而不再关注数据加载时由于Activity销毁引发的问题。

注意:其实AsyncTaskLoader内部实现异步的方式是使用AsyncTask完成的,上面我们说过AsyncTask的内部是有一个排队机制,但AsyncTaskLoader内部使用AsyncTask进行数据异步加载时,异步任务并不进行排队。而直接又线程池分配新线程来执行。

6、总结
我们来总结一下异步处理的方式,以及每种处理方式适合什么样的场景

直接使用Thread实现方式,这种方式简单,但不是很优雅。适合数量很少(偶尔一两次)的异步任务,但要处理的异步任务很多的话,使用该方式会导致创建大量的线程,这会影响用户交互。
HandlerThread,这种方式适合子线程有序的执行异步操作,异步任务的执行一个接着一个。
AsyncTask, 通常用于耗时的异步处理,且时效性要求不是非常高的那种异步操作。如果时效性要求非常高的操作,不建议使用这个方式,因为AsyncTask的默认实现是有内部排队机制,且是整个应用的AsyncTask的任务进行排队,所以不能保证异步任务能很快的被执行。
LoaderManager,当请求处理时机需要根据Activity的生命周期进行调整,或需要时刻监测数据的变化,那LoaderManager是很不错的解决方案。

谈谈你对android多线程的理解

在android中,需要处理一些耗时的操作,这些操作不能运行在UI线程中,不然会造成线程的阻塞,所以就需要多线程操作

在日常项目中比较常用的多线程操作主要有:

1.Handler

2.AsyncTask

3.IntentService

一:handler的理解

优点:对于对后台任务时,简单清晰

缺点:对于操作单个后台任务,代码过于繁琐

具体操作:

在主线程中创建Handler对象并实现handlmessage()方法,
创建runnable线程,先在线程中执行耗时操作,
开启一个线程会相应的产生一个looper,在初始化looper的时候会创建一个消息队列MessageQueue();
执行完耗时操作,通过handler将消息发送到消息队列中、、looper轮询消息队列将消息取出来交给Handler,
Handler接收到取出来的消息,并根据消息类型做出相应的处理

二:AsyncTask的理解

优点:操作简单方便,过程可控

缺点:对于多异步操作更新UI会变得很繁琐

具体操作:

onPreExecute()运行在主线程中,开启线程前的准备操作,
doInBackground()运行在子线程中,
onPreExecute()之后的操作,用于处理耗时操作,通过调用publishProcess()向 onProcessUpdata()推送消息
onProcessUpdata()运行在主线程中,当调用 publishProcess()方法时就会开启此方法,接收到推送过来的数据,更新UI进度页面
onPostExecute()运行在主线程中,当子线程耗时操作执行完毕后会调用此方法, doInBackground()返回的参数传递到这里来用于更新UI
调用execute()方法开启AsyncTask,类似runnable的start()方法

三:IntentService的理解

IntentService和普通的Service区别在于,IntentService在oncreate()方法中单独开启一个线程用于耗时操作
通过onHandleIntent(Intent intent)方法来处理耗时操作
在耗时操作执行完毕之后,会自动关闭service不用手动关闭
如果同时new出多个IntentService对象进行耗时操作,oncreate()和ondestory()方法会执行一次,onstart()、onstartcommand()、onHandleIntent()会执行多次。
执行完毕自动关闭service

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速