在研究AsyncTask源代码的时候发现它的内部使用了FutureTask、Future、Callable类,然后就学习了一下。

1.Runnable、Callable和Future
Executor框架使用Runnable作为其基本的任务表示形式。Runnable是一种有很大局限的抽象,它不能返回一个值或抛出一个受检查的异常。
Runnable接口:
public interface Runnable {
public abstract void run();
}
由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。

许多任务实际上都存在延迟的计算,对于这些任务,Callable是一种更好的抽象:它会返回一个值,并可能抛出一个异常。
Callable接口:
public interface Callable< V> {
V call() throws Exception;
}
可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。

Runnable和Callable描述的都是抽象的计算任务。这些任务通常是有生命周期的。Executor执行的任务有4个生命周期阶段:创建、提交、开始和完成。由于有些任务可能要执行很长时间,因此通常希望可以取消这些任务。在Executor框架中,已提交但尚未开始的任务可以取消,对于已经开始执行的任务,只有当它们响应中断时才能取消。

Future是一个接口,表示一个任务的生命周期,并提供了方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。
Future接口:
public interface Future< V> {
boolean cancel(boolean
mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
在Future接口中声明了5个方法,下面依次解释每个方法的作用:
①cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
②isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
③isDone方法表示任务是否已经完成,若任务完成,则返回true;
④get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
⑤get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
也就是说实际上Future提供了三种功能:
①判断任务是否完成;
②中断任务;
③获取任务执行结果。

Future只是一个接口,无法直接创建对象,因此有了FutureTask。

2.FutureTask
FutureTask是一个用来执行异步任务的类,同时当程序执行完成之后还会返回运算的结果。
我们之前经常使用Thread+Runnable来执行异步任务的,但是使用这种方式不能获取到执行的结果。FutureTask解决了这个问题。

FutureTask用法:
public class FutureTaskActivity extends Activity implements View.OnClickListener {

//创建一个实现了Callable接口并且在call()方法中做耗时操作的类
class WorkTask implements Callable< Integer> {
@Override
public Integer call() throws Exception {
//使用线程sleep来模拟耗时的操作
Thread.sleep(5000);
//将执行的结果返回出去
return 1000;
}
}

private void executeTask() {
//创建一个实现了Callable的类的对象,并且当作参数传入到FutureTask中
WorkTask workTask = new WorkTask();
FutureTask< Integer> futureTask = new FutureTask< Integer>(workTask) {
@Override
protected void done() {
try {
//该方法是在线程运行结束或者被取消后回调的,用get方法获取call方法中返回的结果
int result = get();
Log.i(“LOH”, “result…” + result);
Thread.currentThread().getName();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
//将FutureTask作为参数传入到Thread函数中执行
Thread thread = new Thread(futureTask);
//启动线程执行任务
thread.start();
}
}
其实FutureTask的使用跟Runnable的使用差不多的,只是多出了一个用来实现Callable接口的类作为参数传入到 FutureTask中,并且能监听线程执行完成。

总结一下使用线程来执行异步任务的几种方式:
①使用new Thread(new Runnable()).start()的方法来执行异步任务,也就是说将实现Runnable接口的类当作参数传入到Thread 类中,然后使用thread.start来启动线程执行
②通过继承Thread类并且重写该类的run方法,在该方法中执行耗时的操作,同样也是使用 Thread.start()来启动线程执行
③使用线程池来执行实现Runnable接口的类,这样子也可以达到执行异步任务的目的。
④AsyncTask
⑤HandlerThread
⑥IntentService
⑦FutureTask
其实用这些方法实现异步任务的本质是实现 Runnable() 接口,所以FutureTask也是实现了该接口的,不然就无法执行异步任务的。

3.FutureTask源码解析
先来看下FutureTask的实现:
public class FutureTask< V> implements RunnableFuture< V> { }
FutureTask类实现了RunnableFuture接口。

再来看RunnableFuture接口:
public interface RunnableFuture< V> extends Runnable, Future< V> {
void run();
}
RunnableFuture继承了Runnable和Future接口,而FutureTask实现了RunnableFuture接口。

首先我们通过一个类的关系图来看看这几个类之间的关系图:

FutureTask是一个可取消的异步计算,它实现了Future的基本方法,提供start、cancel操作,可以查询计算是否已经完成,并且可以获取计算的结果。结果只可以在计算完成之后获取,当计算没有完成的时候get方法会阻塞。一旦计算已经完成, 那么计算就不能再次启动或是取消。
一个FutureTask可以用来包装一个Callable或是一个Runnable对象(对应FutureTask的两个构造方法)。因为FurtureTask实现了Runnable方法,所以FutureTask可以提交(submit)给一个Excutor执行; 它同时实现了Callable, 所以也可以作为Future得到Callable的返回值。

FutureTask有两个很重要的属性分别是state和runner, FutureTask之所以支持cancel操作,也是因为这两个属性。
其中state为枚举值:
private volatile int state; // 注意volatile关键字

//在构建FutureTask时设置,同时也表示内部成员callable已成功赋值,一直到子线程完成FutureTask中的run()
private static final int NEW = 0;

//子线程在处理task时设定的中间状态,处于该状态时,说明子线程正准备设置result
private static final int COMPLETING = 1;

//当设置result结果完成后,FutureTask处于该状态,代表过程结果,该状态为最终状态final state(正确完成的最终状态)
private static final int NORMAL = 2;

//同上,只不过task执行过程出现异常,此时结果设值为exception,也是final state
private static final int EXCEPTIONAL = 3;

//也是final state, 表明task被cancel(task还没有执行就被cancel的状态)
private static final int CANCELLED = 4;

//中间状态,task运行过程中被interrupt时,设置的中间状态
private static final int INTERRUPTING = 5;

// final state, 中断完毕的最终状态
private static final int INTERRUPTED = 6;

state初始化为NEW,只有在set、setException和cancel方法中state才可以转变为终态。在任务完成期间,state的值可能为COMPLETING或INTERRUPTING。
state有四种可能的状态转换:
NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED

其他成员变量:
private Callable< V> callable; // 具体run运行时会调用其方法call(),并获得结果,结束时置为null
private Object outcome; // 返回的结果或异常,没必要为volatile,因为其是伴随state进行读写,而state是FutureTask的主导因素。
private volatile Thread runner; //具体的子线程
//Treiber stack of waiting threads
private volatile WaitNode waiters;

下面分析下Task的状态变化,也就是一个任务的生命周期:
创建一个FutureTask,首先调用构造方法。FutureTask有两个构造方法:
①直接传入一个Callable实现类
public FutureTask(Callable< V> callable){
if(Callable = = null)
throw new NullPointException();
this.callable = callable;
this.state = NEW;
}
②传入一个Runnable和一个预设结果
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
此时将state设置为初始态NEW。
这里注意第二个构造方法里,Runnable是怎样转换为Callable的,看下this.callable = Executors.callable(runnable, result); 调用Executors.callable:
public static < T> Callable< T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter< T>(task, result);
}
static final class RunnableAdapter< T> implements Callable< T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
//把传入的值直接返回
return result;
}
}
其实就是通过Callable的call方法调用Runnable的run方法,把传入的 T result 作为callable的返回结果直接返回了,所以FutureTask(Runnable runnable, V result)得到的只是预设的结果,Runnable并不会得到执行的结果。如果不需要预设的返回结果,可以这样初始化:
FutureTask<?> f = new FutureTask< Void>(runnable, null);

当创建完一个Task通常会提交给Executors来执行,当然也可以使用Thread来执行,Thread的start()方法会调用Task的run()方法。那么,看看它的源码:
public class FutureTask< V> implements RunnableFuture< V> {

public void run() {
//如果当前线程的状态不是原始状态,或者当前线程已经赋值成功,则不用重复执行该逻辑
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
//将传进来的callable对象赋值给一个临时变量
Callable< V> c = callable;
//判断传入进来的callable对象不为空并且线程状态也是新建的
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//在run方法中执行的call()方法,然后call()返回一个泛型类型的返回值 ,这种通过实现接口的方法在我们平时中是很常见的吧
result = c.call();
ran = true;
//在定义call方法的时候抛出异常,然后这里捕捉异常进行处理,因为我们在call方法中写代码难免会有异常问题的
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
//如果call方法中不跑出异常的话,则通过set()方法将结果保存起来,该set()方法其实也是Future接口定义的方法
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
//如果当前的状态不是
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

}
state刚开始值为NEW,如果不等于NEW,说明任务状态已经改变(说明它走了上面4中可能变化的一种)。如果状态是NEW,判断成员变量runner是否为null,如果runner的值为null,则把当前执行任务的线程赋值给runner, 如果成功,则返回true;如果runner不为null, 或者赋值失败,返回false,则说明已经赋过值了,不用重复操作。
call()方法就是在run()方法中执行的。
boolean ran是判断Callable中执行耗时操作是否有异常,是否成功的标识。如果异常了,result会置空,ran = false; 然后执行setException(ex);操作。

这里
!UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())
语义相当于
if (this.runner == null ){
this.runner = Thread.currentThread();
}
使用compareAndSwap能够保证原子性,保证多个线程同时提交一个futureTask时,确保该futureTask的run只被调用一次。如果想运行多次,使用runAndReset方法。

接着开始执行任务,如果要执行的任务不为空,并且state为New就执行,可以看到这里调用了Callable的call方法。如果执行成功则set结果,如果出现异常则setException。最后把runner设为null。

接着看下set方法:
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
如果现在的状态是NEW就把状态设置成COMPLETING,然后设置成NORMAL。这个执行流程的状态变化就是: NEW->COMPLETING->NORMAL。

如果异常了,result会置空,ran = false; 然后执行 setException(ex);操作,看看代码 :
protected void setException(Throwable t) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = t;
U.putOrderedInt(this, STATE, EXCEPTIONAL); // final state
finishCompletion();
}
}
第一行是把成员变量state的值由NEW变为COMPLETING,然后把异常赋值给outcome,赋值成功后,把state的值,由COMPLETING变为EXCEPTION,然后执行finishCompletion()方法,此方法是一个循环执行。
private void finishCompletion() {
for (WaitNode q; (q = waiters) != null;) {
//根据寻找内存地址的方式来修改属性在内存中的值,将该waiters对象置为null
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
for ( ; ; ) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
//回调Future接口的方法,标识程序正常的结束了,我们需要重写该方法.
done();
//特别需要注意的是需要将该接口变量置为空,防止出现因为引用问题导致内存泄漏
callable = null; // to reduce footprint,
}
当执行到该方法的时候,就标识程序正常的运行结束了,首先会将所有等待的线程全部唤醒,因为在执行FutureTask任务的时候调用get()方法是阻塞的,因为call()方法都还没有执行完成,这个时候你是获取不到任何结果的,所以会将当前调用get()方法的线程阻塞等待,直到调用finishCompletion()方法来解除线程阻塞,最后调用done()方法,这个时候我们就可以在该结束方法中执行我们想要的逻辑了;从代码中我们可以看出done()方法其实也还是运行在子线程的,所以我们不可以在done()方法中更新UI,还是需要Handler来发送消息的。
在线程全部执行结束之后,我们就可以在done()方法通过调用get()方法来获取最后执行的结果了,也就是刚刚在set()方法中看到的outcome的值。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
首先判断FutureTask的状态是否为完成状态,如果是完成状态,说明已经执行过set或setException方法,之前在调用set()方法时通过寻址(根据内存地址的偏移量)的方式修改过了state的值为NORMAL了,所以NORMAL大于COMPLETING,最后直接调用report()方法,最后直接通过return x 来返回结果。
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s==NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
可以看到,如果FutureTask的状态是NORMAL, 即正确执行了set方法,get方法直接返回处理的结果, 如果是取消状态,即执行了setException,则抛出CancellationException异常。

我们通过调用Future接口的isDone()来判断程序是否结束,可以直接根据state的状态判断是否是新创建的,该类的线程有7种不同的状态,只要状态切换成其中的一种就可以说程序结束了。
public boolean isDone() {
return state != NEW;
}
只要状态值大于CANCELLED(4),也就是用户主动调用cancel()方法,不管是主动中断线程还是其他的方式都属于取消的操作的。
public boolean isCancelled() {
return state >= CANCELLED;
}
当程序里面的FutureTask未执行完成的时候get()方法会一直阻塞调用该方法的线程,直到FutureTask里面的任务执行完才会解除阻塞。所以get()方法是一个阻塞式的去获取结果的,从上面的get()方法的代码中我们可以得出
当状态还是NEW的时候,会调用awaitDone(false ,0)方法。
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
long startTime = 0L; // Special value 0L means not yet parked
WaitNode q = null;
boolean queued = false;
for ( ; ; ) {
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
} else if (s==COMPLETING)
// We may have already promised (via isDone) that we are done, so never return empty-handed or throw InterruptedException
Thread.yield();
else if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
} else if (q == null) {
if (timed && nanos <= 0L)
return s;
q = new WaitNode();
} else if (!queued)
queued = U.compareAndSwapObject(this, WAITERS, q.next = waiters, q);
else if (timed) {

} else
LockSupport.park(this);
}
}
awaitDone方法可以看成是不断轮询查看FutureTask的状态。在get阻塞期间:
①如果执行get的线程被中断,则移除FutureTask的所有阻塞队列中的线程(waiters),并抛出中断异常;
②如果FutureTask的状态转换为完成状态(正常完成或取消),则返回完成状态;
③如果FutureTask的状态变为COMPLETING, 则说明正在set结果,此时让线程等一等;
④如果FutureTask的状态为初始态NEW,则将当前线程加入到FutureTask的阻塞线程中去;
⑤如果get方法没有设置超时时间,则阻塞当前调用get线程;如果设置了超时时间,则判断是否达到超时时间,如果到达,则移除FutureTask的所有阻塞列队中的线程,并返回此时FutureTask的状态,如果未到达时间,则在剩下的时间内继续阻塞当前线程。
该方法有个无限循环直到状态值大于COMPLETING才返回一个状态值,我们在线程未执行完成的时候调用了get()方法,可以看到首先会创建一个WaitNode对象,然后通过Unsafe类来更新成员变量waiter的值为 q,然后再次循环最后会进入LockSupport.park(this) 分支,该函数主要是获取许可阻塞当前的线程,直到程序执行结束之后,调用LockSupport.unpark(this)来释放阻塞。所以如果我们在主线程中直接调用get()方法来获取结果的话则很有可能导致ANR,直到程序结束之后才会释放阻塞的,正确的用法就是在done()方法里面调用get()来获取执行的结果的。

我们平时在使用AsyncTask的时候有一个cancel()方法来取消当前执行的任务,我们之前也说了AsyncTask的本质其实也是使用了FutureTask来实现的。其实它的cancel()方法也是调用FutureTask的取消方法的,下面看看取消的原理:
//如果返回值为true的话表示取消成功,否则为取消失败了
public boolean cancel(boolean mayInterruptIfRunning) {
//首先判断当前的状态是否是NEW,然后再通过Unsafe类去更新内存中state字段的值为cancel。
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
//如果以上的状态值设置成功的话,则判断是否设置中断运行
if (mayInterruptIfRunning) {
try {
Thread t = runner;
//直接通过调用Thread的中断方法来强制中断当前运行的线程
if (t != null)
t.interrupt();
} finally { // final state
//最后修改当前状态state的值为 INTERRUPTED 为中断
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
//最后解锁所有被阻塞的线程
finishCompletion();
}
return true;
}
第一行代码,是一个校验,如果state值为NEW, 根据mayInterruptIfRunning是否为true把state值由NEW变为INTERRUPTING或者CANCELLED,如果都成功了,则继续执行下一步,否则,终止,返回失败。比如已经执行完耗时操作,state值已经变成了COMPLETING,此时就不能取消了,直接返回false,表示失败;如果还没开始执行,或者执行了,但还没执行完,还在耗时操作之中,则根据 mayInterruptIfRunning 值,state 值由NEW变为INTERRUPTING或CANCELLED,此时,即使执行了run()方法,set(v)时也会有if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING))这个校验,此时state为INTERRUPTING或者CANCELLED,就不会继续赋值了。 继续往下看, 如果mayInterruptIfRunning为true,则会获取到耗时操作的线程,如果该线程已经执行,说明线程存在,不为空,则把它强制终止,然后会把state值由INTERRUPTING变为INTERRUPTED,表示已经终止,INTERRUPTING的意思是终止进行中。然后会执行 finishCompletion(); 方法。
我们在取消任务的时候可以设置强制中断线程运行,只要调用cancel(true) 就行了,有时候我们调用cancel(false)并不能立刻停止线程执行完成的,因为这个时候程序在run()方法中已经执行过了状态(state)值判断的话,这个时候就直接执行call()方法了,但是call()方法也没有执行完成,如果这个时候我们去取消的话, 因为我们知道取消的原理就是使用Unsafe类去修改内存中的state的值,但是这个时候设置已经来不急了。

虽然我们调用了cancel(false)方法去取消任务的,但是很多的时候还是不能马上终止任务执行,最后线程还是会继续执行的,但是到了set()方法的时候,这里会有一个状态值的判断的。之前我们已经介绍了线程并发的基石CAS,首先我们使用Unsafe类去比较state状态值是否发生了变化,如果state的值被其他的线程修改了,则不会调用done()方法了。
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {

}
}

4.FutureTask的完整源码
把FutureTask的完整代码写出来,整体思路理一下:
public class FutureTask< V> implements RunnableFuture< V> {
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
private Callable< V> callable;
private Object outcome;
private volatile Thread runner;
private volatile WaitNode waiters;

public FutureTask(Callable< V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}

public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}

public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))
return;
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 {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

public boolean isCancelled() {
return state >= CANCELLED;
}

public boolean isDone() {
return state != NEW;
}

public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}

public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}

public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}

//Returns result or throws exception for completed task.
//@param s: completed state value
@SuppressWarnings(“unchecked”)
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}

protected void done() { }

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

protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}

private void finishCompletion() {
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;

Android FutureTask相关推荐

  1. 【Android 异步操作】FutureTask 分析 ( Future 接口解析 | Runnable 接口解析 | Callable 接口解析 )

    文章目录 一.Future 接口 1.Future 接口简介 2.取消任务方法 3.Future 接口源码注释 二.Callable 接口 三.Runnable 接口 上一篇博客 [Android 异 ...

  2. 【Android 异步操作】AsyncTask 异步任务 ( FutureTask 模拟 AsyncTask 执行过程 | AsyncTask 执行过程回顾 | FutureTask 分析 )

    文章目录 一.FutureTask 使用流程 二.FutureTask 模拟 AsyncTask 执行过程 三.AsyncTask 执行过程回顾 四.FutureTask 分析 一.FutureTas ...

  3. Android中Callable、Future、FutureTask的概念以及几种线程池的使用

    学习线程池必备知识: 在开始介绍线程池之前,先来介绍下Callable和Future的概念,众所周知,Android中实现多线程的方式有两种,实现Runnable接口或者继承一个Thread,但是这两 ...

  4. 【Android】AsyncTask异步类

    一.关于AysncTask AsyncTask使得多线程编程更加简单,AsyncTask能在后台线程执行异步任务,并且在UI线程更新界面,而不再需要用户去操作Thread和Handler.AysncT ...

  5. Android之ksoap2-android详解与调用天气预报Webservice完整实例

    Google为Android平台开发Web Service客户端提供了ksoap2-android项目,在这个网址下载开发包http://code.google.com/p/ksoap2-androi ...

  6. Android Studio报错解决:droid.tools.idea.welcome.install.WizardException: SDK tools directory is missing

    早上在用Android Studio想下载最新的sdk,结果打开时报错: com.android.tools.idea.welcome.install.WizardException: SDK too ...

  7. android用什么包管理器,android – 包管理器已经死了

    我收到一个安装了大量应用程序的用户的邮件,当我的应用程序使用此代码收集活动信息时,他有问题: getPackageManager().queryIntentActivities(mAinIntent, ...

  8. android线程及线程池

    众所周知,在UI系统中进行一些耗时操作,都会导致卡顿现象,因为一次刷新在16ms,如果当次操作过了这个时间,那么用户就能感觉到明显的卡顿,甚至引起ANR . 对于这种情况,一般都是再起一个线程,进行一 ...

  9. Android 访问WebService

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010046908/article/details/50503428 首先,WebService进年 ...

最新文章

  1. 【转】Struts2 和 Spring MVC对比
  2. 如何做一个流畅的UI 组内分享记录
  3. CG CTF WEB php decode
  4. 8个主流且实用的Python开发工具推荐
  5. fiddler设置中文版本_突破安卓7.0以上版本WX小程序抓包篇
  6. ICCV 2019 | ActivityNet 挑战赛冠军方案—时序动作提名,边界匹配网络详解
  7. 【176天】黑马程序员27天视频学习笔记【Day11-上】
  8. Kubernetes通过一行shell命令给pod中的zk节点添加权限
  9. python数据分析与挖掘实战pdf_《Python数据分析与挖掘实战》PDF+完整源码
  10. SQLMAP 脱库过程(get请求)
  11. 《游戏设计、原型与开发——基于Unity与C#从构思到实现》学习笔记一
  12. Vue实现简单汇率转换器
  13. 面试题-实现数组map方法
  14. windows/dos 命令
  15. 用LangChain构建大语言模型应用
  16. iOS 【适配iPhone XR/iPhone XS Max】
  17. 3D打印开源软件Cura分析(1) 【转】
  18. HAOI 2017 游记
  19. Perl:化繁为简 (转载)
  20. 马斯克看好的赛道被中国企业率先交卷:研发投入超六成、不到3年营收超20亿,将成「人形机器人第一股」...

热门文章

  1. 身为职场人要怎么提高自信心?
  2. http常见的form表单请求方式
  3. win10连接文件服务器记住密码如何删除,win10系统删除已记住的访问共享的账户与密码的操作方法...
  4. Flask 第三篇 Flask 中的 request
  5. 用jTessBoxEditorFX训练字库
  6. intellij idea 改变文字背景色
  7. 漫画:什么是 “小镇做题家” ?
  8. vue 使用vant 上传头像并剪切上传
  9. 史上最全(全平台)docker安装方法!
  10. uniapp小程序实现弹幕功能