Android的AsyncTask类的解读
国庆节放假。搞了半个月都没有上班了,coding的时候一点都不在状态,本来这篇文章是在国庆节前写完的,可是由于自己的懒
惰,导致延期到国庆节,哎,这种习惯真心不好呀。。。不多说了以下来进入正题
之前我们解读了Handler机制,今天再来看一下AsyncTask类,由于这两个类使我们在Android进行耗时的操作的时候,不影响主线
程的情况下常常使用的两个类。我们先来看一下AsyncTask类源代码中定义的变量:
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); private static final InternalHandler sHandler = new InternalHandler(); private volatile Status mStatus = Status.PENDING; private final WorkerRunnable<Params, Result> mWorker; private final AtomicBoolean mCancelled = new AtomicBoolean(); private final FutureTask<Result> mFuture;
看到这些字段之后。我们可能发现,貌似好多类型我们都不知道。所以这里在先说AsyncTask类之前。须要做一些准备工作。介绍
一些这些类型。事实上我们还须要了解一下Java5.0加入的并发库的相关知识,參看这篇文章:
http://blog.csdn.net/jiangwei0910410003/article/details/20373497
第一、ThreadFactory类
看看他的源代码:
public interface ThreadFactory { /**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
好吧,好简单呀,就是一个接口,有一个回调方法newThread。用于创建一个Thread.
第二、BlockQueue类
是一个堵塞队列,这里就不做介绍了,參考这篇文章:
http://blog.csdn.net/jiangwei0910410003/article/details/20373497
第三、ThreadPoolExecutor类
主要是看他的构造函数的几个參数的含义,就能够了解这个类的作用了:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
看这个參数非常easy让人以为是线程池里保持corePoolSize个线程,假设不够用,就加线程入池直至maximumPoolSize大小,假设还
不够就往workQueue里加。假设workQueue也不够就用RejectedExecutionHandler来做拒绝处理。
但实际情况不是这样,具体流程例如以下:
1)当池子大小小于corePoolSize就新建线程,并处理请求
2)当池子大小等于corePoolSize。把请求放入workQueue中,池子里的空暇线程就去从workQueue中取任务并处理
3)当workQueue放不下新入的任务时,新建线程入池。并处理请求。假设池子大小撑到了maximumPoolSize就用
RejectedExecutionHandler来做拒绝处理
4)另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,假设无请求可处理就自行销毁
内部结构例如以下所看到的:
从中能够发现ThreadPoolExecutor就是依靠BlockingQueue的堵塞机制来维持线程池。当池子里的线程无事可干的时候就通过
workQueue.take()堵塞住。
第四、InternalHandler类
这个类我们在后面具体分析AsyncTask类的时候,会发现他事实上就是一个Handler。所以这里我们能够看出AsyncTask类事实上是基于
Handler+并发库技术实现的,兴许解析代码的时候会体现的更明显。
第五、Status类型
这个类型事实上是一个枚举。我们这里的关注点不是类型。而是修饰符volatile,这个修饰符的作用这里不做介绍了。请看以下一篇文
章:http://blog.csdn.net/jiangwei0910410003/article/details/20369811
第六、WorkerRunnable类
这个类是在AsyncTask类中定义的:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
他是个抽象类,实现了Callable接口,这里的Param和Result是AsyncTask定义的泛型类型
第七、FutureTask类
public class FutureTask<V> implements RunnableFuture<V>{
//.....
}
在看一下RunnableFuture接口:
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
FutureTask类事实上是实现了Future和Runnable接口,具备了这两个接口的功能。关于Future和上面的Callable能够參考以下文章:
http://blog.csdn.net/jiangwei0910410003/article/details/20373497
注意:刚才也说了AsyncTask事实上是基于Handler+并发库技术实现的。可是并发库中也是有非常多知识的。这里用到的核心技术就
是Future+Callable
以下来看看一下Future类的主要方法:
1) boolean cancel(boolean mayInterruptIfRunning):试图取消对此任务的运行。
假设任务已完毕、或已取消,或者由于某些其
他原因而无法取消。则此尝试将失败。
当调用 cancel 时,假设调用成功,而此任务尚未启动,则此任务将永不运行。假设任务已经
启动,则 mayInterruptIfRunning 參数确定是否应该以试图停止任务的方式来中断运行此任务的线程。
此方法返回后,对 isDone() 的
兴许调用将始终返回 true。假设此方法返回 true,则对 isCancelled() 的兴许调用将始终返回 true。
2)boolean isCancelled():假设在任务正常完毕前将其取消。则返回 true。
3)boolean isDone():假设任务已完毕,则返回 true。 可能由于正常终止、异常或取消而完毕。在全部这些情况中,此方法都将
返回 true。
4)V get()throws InterruptedException,ExecutionException:如有必要,等待计算完毕,然后获取其结果。
5)V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException:如有必要。最
多等待为使计算完毕所给定的时间之后,获取其结果(假设结果可用)。
第八、AtomicBoolean类
这个类,我们可能不会常常常使用到,可是我们通过它的名字会发现他是对Boolean类型加入了原子操作的功能,那么当然还有其他7种
基本类型相应的类(AtomicInteger等)
看一下他的源代码:
private static final long serialVersionUID = 4654671469794556979L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicBoolean.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value;
value值是个int类型的。这里使用int类型来模拟Boolean类型的。0就是false,1就是true.
这里有一个非常重要的类Unsafe,可是这个类,他是属于JDK的核心包中。所以要查看他的源代码的话。仅仅能从网上去搜了:
http://www.docjar.com/html/api/sun/misc/Unsafe.java.html
这个类是用于运行低级别、不安全操作的方法集合。
虽然这个类和全部的方法都是公开的(public)。可是这个类的使用仍然受限,
你无法在自己的java程序中直接使用该类,由于仅仅有授信的代码才干获得该类的实例。从上面的描写叙述,能够了解到该类是用来运行
较低级别的操作的,比方获取某个属性在内存中的位置。只是一般人非常少会有这种需求。
上面的静态代码块中的代码的功能就是用来获取AtomicBoolean实例中的value属性在内存中的位置。
这里使用了Unsafe的
objectFieldOffset方法。
这种方法是一个本地方法。 该方法用来获取一个给定的静态属性的位置。
在来看一下AtomicBoolean类的一个重要的方法:
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
/**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(boolean newValue) {
int v = newValue ? 1 : 0;
unsafe.putOrderedInt(this, valueOffset, v);
}
这里有个疑问。为什么须要获取属性在内存中的位置?在AtomicBoolean源代码中,在这样几个地方使用到了这个valueOffset值:
查找资料后,发现lazySet方法大多用在并发的数据结构中。用于低级别的优化。compareAndSet这种方法多见于并发控制中,简称
CAS(Compare And Swap),意思是假设valueOffset位置包括的值与expect值同样。则更新valueOffset位置的值为update,并返回
true。否则不更新。返回false。
这里能够举个样例来说明compareAndSet的作用,如支持并发的计数器,在进行计数的时候,首先
读取当前的值,假设值为a,对当前值 + 1得到b。可是+1操作完以后,并不能直接改动原值为b,由于在进行+1操作的过程中。可能
会有其他线程已经对原值进行了改动,所以在更新之前须要推断原值是不是等于a,假设不等于a,说明有其他线程改动了,须要重
新读取原值进行操作。假设等于a。说明在+1的操作过程中,没有其他线程来改动值,我们就能够放心的更新原值了。
AsyncTask源代码解析
全部的类型都说完之后,以下来看一下AsyncTask类的源代码
一、构造方法
首先来看一下构造方法:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
}; mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} 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);
}
}
};
}
构造方法主要初始化WorkerRunnable类和FutureTask类
在WorkerRunnable的call方法中主要调用了postResult(doInBackground(mParams)),之前看到了WorkerRunnable实现了Callable接口的。这里就实现了他的call方法,运行完这种方法之后,须要返回运行之后的结果
先看一下:doInBackground方法:
protected abstract Result doInBackground(Params... params);
是一个抽象的方法。这个须要我们自己去实现它
再来看一下postResult方法。这种方法里面我们能够看到就是将Result类型的结果值发送给InternalHandler进行处理
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
看一下InternalHandler类的定义:
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;
}
}
}
这个Handler中处理两个状态的信息:
MESSAGE_POST_RESULT:处理结果的
这里调用了finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在这种方法里面,会推断取消状态。然后运行相应的方法,由于任务结束有两个原因:
一个是被取消了:回调onCancelled方法
一个是完毕:回调onPostExecute方法
MESSAGE_POST_PROGRESS:处理过程的
这里调用onProgressUpdate方法
/**
* Runs on the UI thread after {@link #publishProgress} is invoked.
* The specified values are the values passed to {@link #publishProgress}.
*
* @param values The values indicating progress.
*
* @see #publishProgress
* @see #doInBackground
*/
@SuppressWarnings({"UnusedDeclaration"})
protected void onProgressUpdate(Progress... values) {
}
这种方法也是我们能够重写的方法
以下再看一下FutureTask类的初始化:
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} 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);
}
}
};
这里调用了一个重要的方法:postResultIfNotInvoked(get());
FutureTask的构造方法须要传递一个Callable接口对象进去。内部能够通过调用这个接口对象的call方法,获取结果,能够查看一下FutureTask类的源代码:
构造方法:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
保存一下Callable接口对象
由于也实现了Runnable接口。所以要实现run方法:
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 must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在这个run方法中我们看到会调用c.call方法获取结果。然后set(result),以后就能够通过get()方法获取到result。在看一下set方法的源代码:
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
看一下finishCompletion方法:
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, 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;
}
} done(); callable = null; // to reduce footprint
}
这里就看到调用了FutureTask类的done方法:
protected void done() { }
这是个空方法。须要子类重写这种方法。以下就看到重写这个done方法
这里FutureTask重写了done方法,在这种方法中处理(WorkerRunnable)Callable任务运行的结果的。
首先来看一下get()方法:
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return The computed result.
*
* @throws CancellationException If the computation was cancelled.
* @throws ExecutionException If the computation threw an exception.
* @throws InterruptedException If the current thread was interrupted
* while waiting.
*/
public final Result get() throws InterruptedException, ExecutionException {
return mFuture.get();
}
这里就是调用了FutureTask的取数据方法get,获取运行结果
再来看一下postResultIfNotInvoked方法:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
这种方法会推断一下,当前的Task有没有运行过,假设没有运行过,就运行postResult方法
二、execute方法
AsyncTask类的构造方法看完之后,以下来看一下他的运行方法execute
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(); mWorker.mParams = params;
exec.execute(mFuture); return this;
}
这种方法须要传递一个运行器,这个运行器的定义例如以下:
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);
}
}
}
这个运行器。我们能够看到他的execute方法,顺序运行任务。这里的THREAD_POOL_EXECUTOR就是一个线程池。在開始讲字
段定义的时候说过他。主要使用任务池中拿去Runnable去运行。这里传递进来的是我们在构造方法中初始化的FutureTask类,那么
就是在这里运行它的run方法。
在这种方法中调用onPreExecute方法:
protected void onPreExecute() {
}
这个也是一个空方法,我们能够去重写的,然后就開始使用运行器运行任务,这里须要将任务FutureTask传递进去。
这里我们能够看到,AsyncTask类的三个基本的方法:
doInBackground(...):他是一个抽象的方法。须要实现的
onPreExecute(...):是開始预备的方法,我们能够重写(可选择的)
onPostExecute(...):是运行结束之后的方法,我们能够重写(可选择的)
onProgressUpdate(...):是运行中的方法,我们能够重写(可选择)
通过上面的分析,我们能够看到仅仅有doInBackground方法是在线程池中运行的。就是在WorkerRunnable中的call方法中。其他的两
个方法都是在外部线程中运行的
onPreExecute是在AsyncTask类的execute方法中运行的。
onPostExecute是在finish方法中运行的,而finish方法又是在InternalHandler中的MESSAGE_POST_RESULT状态下运行的。
onProgressUpdate是在InternalHandler中的MESSAGE_POST_PROGRESS状态下运行的
(这里的InternalHandler採用的是默认构造方法创建的,所以他的Looper是创建AsyncTask类的线程。假设你想在子线程中创建
AsyncTask的话。会报异常的,必须将该线程Looper.prepare一下才干够)。
总结
1、AsyncTask类中有Param,Result等类型,这些是泛型类型。所以这里不要混淆就能够了
2、AsyncTask类用到的技术:Handler+FutureTask(实现了Future和Runnable接口)+Callable
3、AsyncTask类的运行周期:
1)在构造方法中,
初始化WorkerRunnable(实现了Callable接口)。实现call方法,在这种方法中回调AsyncTask类的doInBackground方法,而且返回一
个result值。
初始化FutureTask(实现了Runnable,Future接口),复写了FutureTask中的done方法,在这种方法中通过FutureTask的get方法获取
result。同一时候我们在初始化的时候须要传递一个Callable(WorkerRunnable)类型,之前看FutureTask的源代码发现,在他的run方法中,
会调用传递进来的Callable类型的call方法。并将这种方法的返回值result在调用set方法设置一下。同一时候会调用done方法。
2)在execute方法中
调用了executeOnExecutor方法。在这种方法中会调用AsyncTask类的onPreExecute方法。然后在调用ThreadPoolExecutor開始执
行FutureTask任务
3)在InternalHandler定义中
有两个状态:
一个是运行完毕了,会调用finish方法,在这种方法中会调用AsyncTask类的onPostExecute方法
一个是运行中的方法。会调用AsyncTask类的onProgressUpdate方法
这个Handler的Looper是创建AsyncTask类的线程Looper。
(PS:放了长假之后来的首篇blog,写的我各种痛苦呀,以后一定要在放长假之前把事都做了。。)
Android的AsyncTask类的解读的更多相关文章
- Android中AsyncTask的简单用法 .
在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2. 确保只 ...
- Android中异步类AsyncTask的理解
这里有两种解释的方法,各有侧重点: 第一种解释: Async Task 简介:AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦 ...
- android开发之路12(android四大组件&Fragment&AsyncTask类)
一.Activity组件1.简介:Activity组件是Android四大组件之一,通常一个Activity相当于一个用户界面,我们可以通过加载布局文件将Android提供的各种控件及自定义控件显示到 ...
- 【转】 Pro Android学习笔记(九二):AsyncTask(1):AsyncTask类
文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 在Handler的学习系列中,学习了如何h ...
- AsyncTask源码解读
AsyncTask源码解读 一.基本使用 protected void onPreExecute() protected abstract Result doInBackground(Params.. ...
- Android笔记——AsyncTask介绍
AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操 ...
- 详解Android中AsyncTask的使用
在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更 ...
- 55.Android之AsyncTask介绍 (转)
AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操 ...
- android: 使用 AsyncTask
9.2.4 使用 AsyncTask 不过为了更加方便我们在子线程中对 UI 进行操作,Android 还提供了另外一些好用的工 具,AsyncTask 就是其中之一.借助 AsyncTask, ...
随机推荐
- iOS:ABPeoplePickerNavigationController系统通讯录使用
昨天因项目需求要访问系统通讯录获取电话号码,于是乎从一无所知,开始倒腾,倒腾了一下午,总算了弄好了.写这边博客是为了记录一下,自己下一次弄的时候就别在出错了.同时,有和我一样的菜鸟能够避免走一下弯路. ...
- HDU 1695 GCD(欧拉函数+容斥原理)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695 题意:x位于区间[a, b],y位于区间[c, d],求满足GCD(x, y) = k的(x, ...
- C#读取网页
public bool getweb(string strURL,out string buf) { buf=""; try { //Uri url=new Uri(strURL, ...
- # void :;
href="#"---->top 连续点击的时候会出bug javascri中的void是一个操作符,该操作符指定要计算一个表达式但是不返回值. javascript:voi ...
- 一键搜索之Win10锁屏壁纸
前天无意发现win10的锁屏壁纸挺漂亮的,就想着能不能保存下来?网上一搜果然有资料,当时参考的连接如下 http://jingyan.baidu.com/article/fedf07375ea2513 ...
- 使用 RMI + ZooKeeper 实现远程调用框架
目录[-] 1 发布 RMI 服务1.1 定义一个 RMI 接口1.2 编写 RMI 接口的实现类1.3 通过 JNDI 发布 RMI 服务2 调用 RMI 服务3 RMI 服务的局限性4 使用 Zo ...
- MapList 自己封装的
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)/ ...
- BZOJ 1000 A+B Problem (I/O)
#include<cstdio> int main(){ int a,b; scanf("%d%d",&a,&b); printf("%d&q ...
- HDU 1240 Asteroids!
三维广搜 #include <cstdio> #include <iostream> #include <cstring> #include <queue&g ...
- Swift调用Objective C的FrameWork
很多Github的库经过很多年的发展,源码都是OC写的,,所以,用Swift调用OC的库就是开发中难免遇到的的一个问题,本文以AFNetworking为例,讲解如何跨语言调用. 第一步 创建一个空的工 ...