AsyncTask解析

大家都知道,使用AsyncTask类,可以很方便的从子线程切换到UI线程。我们今天从源码来了解一下它的原理。

AsyncTask是一个抽象类,我们先看看AsyncTask里面的四个方法:

  • onPreExecute()(不必重写)
    在doInBackground方法之前执行,用于执行一些初始化操作,如显示一个等待框。

  • doInBackground(Params...)(必须重写)
    主要处理一些耗时操作,在子线程运行。执行过程中可以用publishProgress更新UI,如更新进度。使用publishProgress会执行onProgressUpdate方法(需要我们重写)。

  • onProgressUpdate(Progress...)(不必重写)
    在doInBackground内使用publishProgress方法会执行此处代码,可以用于更新UI。

  • onPostExecute(Result)(不必重写)
    doInBackground执行完之后就会调用这个方法,我们可以根据doInBackground返回的数据,进行UI操作。

以上是AsyncTask比较重要的方法,掌握了它,你能很好的应用这个AsyncTask类。但是身边的朋友都不推荐使用,最后我会解释一下为什么。我们先跟着源码了解一下这个类。

我们look两眼构造函数:

public AsyncTask() { 
    mWorker = new WorkerRunnable<Params, Result>() { 
        public Result call() throws Exception { 
            mTaskInvoked.set(true);   
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 
            return postResult(doInBackground(mParams)); 
        } 
    }; 
    mFuture = new FutureTask<Result>(mWorker) { 
        @Override 
        protected void done() { 
            try { 
                final Result result = get(); 
                postResultIfNotInvoked(result); 
            } catch (InterruptedException e) { 
                android.util.Log.w(LOG_TAG, e); 
            } catch (ExecutionException e) { 
                 throw new RuntimeException("An error occured while executing doInBackground()", e.getCause()); 
            } catch (CancellationException e) { 
                postResultIfNotInvoked(null); 
            } catch (Throwable t) { 
                throw new RuntimeException("An error occured while executing " + "doInBackground()", t); 
            }
        } 
    }; 
} 

很简单,只是初始化了mWorker和mFurure两个参数。
如果我们要启动一个任务,我们需要执行execute(Params...)方法,我们look这个方法:

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

接着看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(); 
    // 这里其实是doInBackground(Params...)的参数
    mWorker.mParams = params; 
    // 执行后台任务
    exec.execute(mFuture); 
    return this; 
} 

我们先解释一下sDefaultExecutor这个值,因为要了解 exec.execute(mFuture),我们的先知道exec,即sDefaultExecutor的值。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

我们继续追下去,看SerialExecutor这个类:

  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() { 
              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); 
        } 
    } 
} 

因为调用了exec.execute(mFuture)这个,对照源码,参数r就是我们的mFuture了,mFuture执行了run方法,这个run方法其实是调用了innerRun方法,我们直接看到innerRun这个方法:

void innerRun() { 
    if (!compareAndSetState(READY, RUNNING)) 
        return; 
    runner = Thread.currentThread(); 
    if (getState() == RUNNING) { 
        // recheck after setting thread  
        V result; 
        try { 
            result = callable.call(); 
        } catch (Throwable ex) { 
            setException(ex); 
            return; 
        } 
        set(result); 
    } else { 
        releaseShared(0); // cancel  
    } 
} 

callable是个什么鬼?其实就是我们构造函数传进来的mWorker。看到构造函数,call()方法中调用了postResult(doInBackground(mParams)),这就调用了我们重写的doInBackground(mParams)了。我们可以look一下postResult这个方法:

private Result postResult(Result result) { 
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); 
    message.sendToTarget(); 
    return result; 
} 

这里就是把获取的结果,通过handler机制,把result发用到UI线程处理。我们可以看看这个自定义的hander长咋样:

private static class InternalHandler extends Handler { 
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) 
    @Override public void handleMessage(Message msg) { 
        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: 
        result.mTask.onProgressUpdate(result.mData); 
        break; 
        } 
    } 
} 

private void finish(Result result) { 
    if (isCancelled()) { 
        onCancelled(result); 
    } else { 
        onPostExecute(result); 
    } 
    mStatus = Status.FINISHED; 
} 

到这里,我们之前说的4个重要的方法也都全部调用了。还有一个方法的实现,就是在用于在doInBackground方法里通知更新UI的publishProgress,代码:

protected final void publishProgress(Progress... values) { 
    if (!isCancelled()) { 
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); 
    } 
} 

从源码可以知道,也是通过handler来更新UI的。

以上是AsyncTask整个源码的分析。我之前说过,身边很多朋友都不推荐使用这个类,因为啥呢??我来解释一下。

  • 生命周期不稳定
    大家都以为,我创建了一个AsyncTask,等Activity销毁会自动销毁,我们不用care。并非如此,如果activity销毁时,正在子线程执行耗时任务,那么子线程会继续do下去。如果我们不去手动cancel()它的话,doInBackground()会把结果通过handler发送到UI处理,可是UI已经销毁(为空)了,这时会报crash。
    再有,我们旋转屏幕的时候,他会重新去创建,又会重新执行一遍,这样会浪费资源,还取消不掉。

  • 内存泄露
    如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。这个Handler泄露差不多一样。因为Activity要销毁了,此时AsyncTask还在执行子线程耗时操作,还是持有Activity的引用,这样Activity无法被回收。

以上也就大神们不推荐使用这个类的原因。还能用吗??看你怎么使用咯。我在这里推荐使用rxjava代替AsyncTask,这个开源框架可以不但可以帮我们很好的解决问题,还给我们带很多方便。这个框架我以后我也会总结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容