源码探索系列15---那个AsyncTask里面的FutureTask

很久前在写源码探索系类2–AsyncTask时候有提及到这个类,现在在这里把FutureTaskFutureCallback,这三个火枪手的关系温习下

起航

就让我们进入主题,开始说说这个FutureTask吧。

在安装开发过程中,系统限制我们对于耗时的任务是不能执行在主线程的,必须单独开一个线程去做。
所以我们在开发过程的一种写法是下面这样

1. 用Runnable

    new Thread(new Runnable() {
            @Override
            public void run() {

             //do something
             ...

             myHandler.sendMessage(msg);   
            }
    }).run();

但有时候我们需要这个线程的运算结果,可我们没办法直接获取,因此安卓配套了一个Handler给我们用,利用他发送消息会我们的主线程,执行一些更新任务等。

2.用Callable

除了使用Runnable,我们还可以使用Callable,示例如下

ExecutorService executor = Executors.newCachedThreadPool();
Future future= executor.submit(new MyCallableTask());
System.out.println(" result=" + future3.get());

class MyCallableTask implements Callable<String> {
    @Override
    public String call() throws Exception {                          
        return "call-result";
    }
}

我们的Callable和Runnable的一点区别是可以有返回值了,而且能抛出异常。
不过他只能用ExecutorService来执行,不能用在新线程中new Thread(Runnable r)
但获得他的返回值,好像不是很方便。而且这个get操作是堵塞线程的,例如改成下面这样

System.out.println("before" + System.currentTimeMillis());
System.out.println(" result=" + future3.get());
System.out.println("after" + System.currentTimeMillis());

打印的结果是:

before1451383552900
result=call-result
after1451383555900

时间刚好差了3秒钟。

很显然,有时候我们需要异步的,希望等运行结束了通知下我,我去获取结果,然后做点什么,改怎办呢?如果不用AsyncTask,Thread+Handler的方式?看下这个FutureTask能不能帮我们点什么

FutureTask

我们先看下示例代码:

ExecutorService executor = Executors.newCachedThreadPool();
MyFutureTask futureTask = new MyFutureTask(new MyCallableTask());
executor.submit(futureTask);

class MyCallableTask implements Callable<String> {
     @Override
     public String call() throws Exception {
         Thread.sleep(3000);
         return "call-result";
     }
 }


class MyFutureTask extends FutureTask<String> {

    public MyFutureTask(Callable<String> callable) {
        super(callable);
    }

    @Override
    protected void done() {
        try {
            System.out.println("执行完毕,结果是:"+get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我们的done()函数会被调用,当这个任务结束返回结果的话。
小小问题来了,为何这个FutureTask可以被Executor执行?我们看下他的构造

 public class FutureTask<V> implements RunnableFuture<V>
 public interface RunnableFuture<V> extends Runnable, Future<V>

他实现了RunnableFuture,是个混血儿!混血儿!混血儿!Amazing
就像是AsyncTask封装好了,帮我们解脱这些繁琐的事情一样,有用!

既然这样,我们去看下他的内部的run方法吧

 public void run() {

    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        ...
    }
}

他会去调用我们的callable.call()函数,然后把结果扔给Set()函数,如果一切正常的话。
我们看下set里面的

protected void set(V v) {
    if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
        outcome = v;
        U.putOrderedInt(this, STATE, NORMAL); // final state
        finishCompletion();
    }
}

最后调用的是finishComPletion()

private void finishCompletion() {
    // assert state > COMPLETING;    
    ....

    done();
    callable = null;        // to reduce footprint
}

我们看到他调用了done函数了,而且最后把callbale清清清清清清清了,因为我们拿到结果了。
这个done() 函数里面什么也没有,主要是通知我们计算完毕,我们可以在这个时候去调用get()函数去获取结果了。


说到这,想提下,知道为何AsyncTask不能够执行两次吗?和这个FutureTask有关系吗?

后记

这次没有后记的内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Android中,线程倒计时可以通过多种方式实现,包括使用postDelayed、runOnUiThread和AsyncTask。 1. 使用postDelayed方式实现线程倒计时: ``` final Handler handler = new Handler(); handler.postDelayed(new Runnable() { int count = 10; @Override public void run() { // 更新UI tvCountDown.setText("" + count); count--; if (count >= 0) { handler.postDelayed(this, 1000); // 一秒后再次执行 } else { // 倒计时结束 } } }, 1000); // 延迟一秒后执行 ``` 2. 使用runOnUiThread方式实现线程倒计时: ``` new Thread(new Runnable() { int count = 10; @Override public void run() { while (count >= 0) { runOnUiThread(new Runnable() { @Override public void run() { // 更新UI tvCountDown.setText("" + count); } }); try { Thread.sleep(1000); // 暂停一秒 } catch (InterruptedException e) { e.printStackTrace(); } count--; } // 倒计时结束 } }).start(); ``` 3. 使用AsyncTask方式实现线程倒计时: ``` private class CountDownTask extends AsyncTask<Void, Integer, Void> { @Override protected Void doInBackground(Void... voids) { int count = 10; while (count >= 0) { publishProgress(count); try { Thread.sleep(1000); // 暂停一秒 } catch (InterruptedException e) { e.printStackTrace(); } count--; } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); // 更新UI tvCountDown.setText("" + values[0]); } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); // 倒计时结束 } } // 启动AsyncTask new CountDownTask().execute(); ``` 需要注意的是,以上三种实现方式都有可能会出现卡顿的情况,特别是在倒计时时间较长的情况下。为了避免卡顿,可以考虑使用CountDownTimer类来实现线程倒计时。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值