AsyncTask源码解析

基于ResNet-50的猫狗分类训练 基于ResNet-50的猫狗分类python训练 阅读详情

AsyncTask类是Android设计的一个执行异步方法的类,我们可以利用这个类执行一些耗时的任务,然后根据结果更新UI,在执行任务时,也可以更新UI,如设置进度条的进度等等。今天,我们从源码分析这个类,学习执行异步方法的思想,这对我写异步框架应该会起到帮助。Show me the code !!

从属性开始,我分为2部分:(1)线程池的初始化。(2)普通属性。

(1)线程池的初始化
直接看代码:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
// 线程工厂,用来为线程池创建工作线程
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());
    }
};
// 任务队列,当线程池的工作线程大于核心线程CORE_POOL_SIZE,新的任务便会加入到这个任务队列中,这里定义队列大小为128
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);
// 真正执行任务的线程池
public static final Executor THREAD_POOL_EXECUTOR;
// 静态块
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,       // 核心线程数
            MAXIMUM_POOL_SIZE,    // 最大线程数
            KEEP_ALIVE_SECONDS,   // 非核心线程闲置的时间
            TimeUnit.SECONDS,     // KEEP_ALIVE_SECONDS的单位
            sPoolWorkQueue,       // 任务队列
            sThreadFactory);      // 线程工厂
    // 设置核心线程闲置时,超过KEEP_ALIVE_SECONDS也被销毁,这里是1秒
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

在静态块中,一开始便初始化了一个线程池。更多关于线程池,请参考我上一篇博文Android线程池浅析。

(2)普通属性

// 这个是加入任务的线程池,接下来会分析
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
// Handler处理消息的标志
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
// 默认执行任务的线程池,定义为SerialExecutor,待会分析
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static InternalHandler sHandler;
// 异步任务,待会分析
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
// 状态
private volatile Status mStatus = Status.PENDING;
// 原子性布尔变量,用于判断是否取消和执行的标志
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

普通属性我们简单的分析了一下,上面提到SerialExecutor线程池,我们来看看代码:

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
    public synchronized void execute(final Runnable r) {
        // 将Runnable任务加入到mTasks队列中
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        // 如果没有任务在执行,就开始执行任务
        if (mActive == null) {
            scheduleNext();
        }
    }
    protected synchronized void scheduleNext() {
        // 从mTasks队列取出任务,并加入到THREAD_POOL_EXECUTOR线程池执行任务
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

从代码可以看出,SerialExecutor是临时保存任务的,真正执行任务的是THREAD_POOL_EXECUTOR线程池。从代码还看出,AsyncTask是串行执行的,这点很重要。还涉及到队列的小知识,如果mTasks没有元素Runnable的话,调用mTasks.poll()会返回null,不会阻塞。
属性中还定义了状态,有以下3种:

 public enum Status {
    /**
     * Indicates that the task has not been executed yet.
     */
    PENDING,
    /**
     * Indicates that the task is running.
     */
    RUNNING,
    /**
     * Indicates that {@link AsyncTask#onPostExecute} has finished.
     */
    FINISHED,
}

好了,属性部分到这里已经看完了,我们看看构造函数吧:

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            ...
        }
    };
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            ...
        }
    };
}

构造函数主要初始化mFuture,里面的实现代码再分析。为什么要使用FutureTask呢?那是因为AsyncTask任务需要可取消的功能,而FutureTask正好满足需求。关于FutureTask我也写了一篇博文,请参考FutureTask源码浅析。
我们要执行AsyncTask时,要调用它的execute,代码如下:

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

@MainThread表示要在主线程执行,至于为什么,待会我一一道来。我们继续跟踪executeOnExecutor方法:

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    // 如果当前状态不是Status.PENDING,直接报错。
    // 这就表明了,一个AsyncTask只能执行一次。
    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
    mWorker.mParams = params;
    // 开始执行mFuture任务
    exec.execute(mFuture);
    return this;
}

我们先来看看mWorker。它是一个WorkerRunnable,实现Callable接口,如果你知道FutureTask,应该知道为什么要实现Callable。

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    // 定义了参数,AsyncTask就是用过这个属性,把参数传进来使用的
    Params[] mParams;
}

调用exec.execute(mFuture)便开始执行任务,上面已经分析过了,其实是先加入一个队列,然后交给THREAD_POOL_EXECUTOR线程池开启线程执行。
我们看看mFuture执行任务的代码,那么得回到初始化mFuture的代码了,也就是构造函数:

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        // 设置这个任务已经在执行了
        mTaskInvoked.set(true);
        // 设置线程优先级,处于THREAD_PRIORITY_BACKGROUND
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        // noinspection unchecked
        // 这里开始执行我们子类重写的doInBackground方法
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        // 调用postResult方法,里面主要是调用
        return postResult(result);
    }
};
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 occurred while executing doInBackground()",
                    e.getCause());
        } catch (CancellationException e) {
            postResultIfNotInvoked(null);
        }
    }
};

如果你对FutureTask熟悉的话,应该知道,FutureTask执行任务会先调用Callable的call方法。上面的代码中,mWorker会先执行call方法,然后mFuture的done方法才会调用。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;
}

从代码可以知道,postResult就是把任务执行的结果通过Handler(消息机制)传到主线程。如果对消息机制不了的,可以看我写的3篇关于消息机制的博文Android消息机制(1)- 简介。getHandler得到的是主线程的Handler,代码如下:

private static Handler getHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}

我们再来看看自定义的InternalHandler代码:

private static class InternalHandler extends Handler {
    public InternalHandler() {
        // 主线程Looper
        super(Looper.getMainLooper());
    }
    @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;
        }
    }
}

看InternalHandler重写的handleMessage方法,主要处理2个消息事件:MESSAGE_POST_RESULT和MESSAGE_POST_PROGRESS。postResult方法传过来的是MESSAGE_POST_RESULT事件,这里涉及到一个类,AsyncTaskResult,代码:

private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;
    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

其实就是一个保存数据和AsyncTask的包装类。
我们回到InternalHandler处理MESSAGE_POST_RESULT事件,调用了result.mTask.finish,从AsyncTaskResult看出,其实也就是执行AsyncTask的finish方法,代码如下:

private void finish(Result result) {
    if (isCancelled()) {
        // 如果任务取消了,调用onCancelled方法
        onCancelled(result);
    } else {
        // 如果任务没有取消,调用onPostExecute方法
        onPostExecute(result);
    }
    // 更新状态
    mStatus = Status.FINISHED;
}

finish方法代码很简单,不用多说。InternalHandler还可以处理一个事件,MESSAGE_POST_PROGRESS,作用是在执行doInBackground方法时,还能通过调用publishProgress方法更新UI。
我们先看看publishProgress方法:

@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        // 如果任务没有取消,分发消息给InternalHandler处理
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

主要是分发消息给InternalHandler处理,InternalHandler调用result.mTask.onProgressUpdate(result.mData)方法处理事件,也就是交给AsyncTask的onProgressUpdate方法处理。而onProgressUpdate是一个挂钩方法,交给子类去实现。
接下来给出子类可以重写和必须重写的方法,主要是看一下:

// 子类必须重写,执行异步的方法
@WorkerThread
protected abstract Result doInBackground(Params... params);
// 其他方法子类可以选择重写,挂钩方法
@MainThread
protected void onPreExecute() {
}
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
    onCancelled();
}    
@MainThread
protected void onCancelled() {
}

到这里,相信你对AsyncTask的机制已经很熟悉了。还有一个,AsyncTask是可取消的,我们看看取消的代码:

public final boolean cancel(boolean mayInterruptIfRunning) {
    // 设置取消标志
    mCancelled.set(true);
    // 调用FutureTask的取消方法
    return mFuture.cancel(mayInterruptIfRunning);
}
// 判断是否取消
public final boolean isCancelled() {
    return mCancelled.get();
}

FutureTask调用cancel方法传入了一个参数,通过解读FutureTask源码可知,如果参数mayInterruptIfRunning为true的话,会立刻中断线程,并设置Interrupt标志。如果为false,则会继续等待线程执行完,设置Cancel标志。
我还在想AsyncTask的cancel方法为什么没有调用onCancelled方法呢,原来我之前对中断线程有误解,正好这里更正了我对线程中断的理解。我原以为调用Thread的interrupt()方法,run方法就不会继续往下执行,直接跳出run方法,这种想法完全是错误的。其实调用Thread的interrupt()方法,如果Thread处于阻塞状态,如处于sleep或者wait,此时,Thread就会收到中断的信号,提前终止阻塞状态,同时抛出InterruptedException异常,然后继续往下执行。如果Thread并没有阻塞,调用Thread的interrupt()方法并不会起效果,run方法还是会继续执行下去。为了证明这点,我特意写了个demo,代码:

public class MainActivity extends Activity {
    // 阻塞队列
    private LinkedBlockingQueue<Integer> queue;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        queue = new LinkedBlockingQueue<>();
        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int result;
                try {
                    // 从队列取出元素
                    result = queue.take();
                } catch (InterruptedException e) {
                    // 处理Interrupted异常
                    e.printStackTrace();
                    result = -1;
                }
                Log.e(getClass().getName(), "结果为" + result);
                sayHello();
            }
        });
        thread.start();
        // 2秒后调用thread.interrupt方法
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                thread.interrupt();
            }
        }, 2000);
    }
    private void sayHello() {
        Log.e(getClass().getName(), "hello, i executed ----------- ");
    }
}

打印的结果如下:
打印结果
从打印结果可以知道,sayHello方法还是会执行的,证明我刚才对Thread.interrupt()方法的理解是正确的。

我们再来看调用AsyncTask的cancel(true)方法(因为false不会中断任务线程),我们结合mFuture初始化代码看:

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        // 设置这个任务已经在执行了
        mTaskInvoked.set(true);
        // 设置线程优先级,处于THREAD_PRIORITY_BACKGROUND
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        // noinspection unchecked
        // 这里开始执行我们子类重写的doInBackground方法
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        // 调用postResult方法,里面主要是调用
        return postResult(result);
    }
};
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 occurred while executing doInBackground()",
                    e.getCause());
        } catch (CancellationException e) {
            postResultIfNotInvoked(null);
        }
    }
};

因为doInBackground是异步方法,一般在这里阻塞,调用FutureTask的cancel方法后,doInBackground提前结束阻塞,继续往下执行,那么postResult方法也会被执行,接着调用finish方法:

private void finish(Result result) {
    if (isCancelled()) {
        // 如果任务取消了,调用onCancelled方法
        onCancelled(result);
    } else {
        // 如果任务没有取消,调用onPostExecute方法
        onPostExecute(result);
    }
    // 更新状态
    mStatus = Status.FINISHED;
}

此时isCancelled为true,所以onCancelled会被执行。这里有学到了关于线程中断的一点小知识,太激动了!!!
到此,整个AsyncTask的原理分析的差不多了,其实我们也能利用这种思想做出我们自己异步框架。我们不是重新做轮子,我们的目标是做更好的轮子。浏览了一下源码,还有个地方你应该知道:

public static void setDefaultExecutor(Executor exec) {
    sDefaultExecutor = exec;
}

其实我们可以自定义AsyncTask执行任务的线程池和任务排序,默认是顺序执行,如果你想要任务按照你想要的顺序排序,可以自定义Executor,代码回忆一下:

...
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_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() {
            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);
        }
    }
}
...

如果你并不想要重新定义顺序,只想自定义线程池,执行方法时,调用executeOnExecutor,以下是execute和executeOnExecutor的区别:

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

AsyncTask源码到这里分析已经结束了,发现自己的好好补补线程和队列的知识。

AsyncTask源码下载地址

AsyncTask源码

参考资料

JAVA线程的interrupt

百度智能云训练图像分割模型+python调用模型并分割图像 目录 训练模型阶段 发布图像分割API 调用图像分割API 创建应用后显示以下两个代码 在pycharm中可以调用API mask解码 依赖库pycocotools安装步骤: 依赖库urllib2的安装 源码下载: 最近上课听老师介绍了一个训练图像分割模型的网站,把数据集上传之后做图像标注,然后进行训练就能得到准确率等指标。 训练模型阶段 图像分割:https://ai.baidu.com/easydl/vision/ 图像分割-->创建模型-->上传数据--&gt 阅读详情

相关推荐

EtherCAT从站协议栈---- sampleappl.c 文件解析

这个源文件实际上包含该项目的入口函数main函数。 那么如何找到入口函数呢? 可以进入debug的simulator模式,选中run to main(), 当执行完启动文件startup_cm3.s后就进入了main函数(注意在main函数之前还进入了SystemInit函数), 如下图所示: 这个main函数主要架构如下: int main(void) { HW_Init(); /*PDI(过程数据接口)初始化,这里用的是MCI接.

Jiawei_Z的博客 3066

Android AsyncTask 源码解析

转载请标明出处:http://blog.youkuaiyun.com/lmj623565791/article/details/38614699,本文出自:【张鸿洋的博客】1、概述相信大家对AsyncTask都不陌生,对于执行耗时任务,然后更新UI是一把利器,当然也是替代Thread + Handler 的一种方式。如果你对Handler机制还不了解,请看:Android 异步消息处理机制 让你深入理解 Lo

Hongyang 3万+

【深度排查】VScode 远程 SSH 连接失败:从网络握手到服务部署的全链路诊断指南

本文详细解析了VScode远程SSH连接失败的常见原因及解决方案,从基础网络排查到SSH服务配置,再到VScode特有的问题处理。特别针对Ubuntu和TrueNAS系统提供了具体配置建议,帮助开发者快速解决'Could not establish connection'等连接问题,提升远程开发效率。

weixin_30301449的博客 446

Android AsyncTask源码解析

可以看到,这里先new出了一个SERIAL_EXECUTOR常量,然后将sDefaultExecutor的值赋值为这个常量,也就是说明,刚才在executeOnExecutor()方法中调用的execute()方法,其实也就是调用的SerialExecutor类中的execute()方法。这里我们模拟了一个下载任务,在doInBackground()方法中去执行具体的下载逻辑,在onProgressUpdate()方法中显示当前的下载进度,在onPostExecute()方法中来提示任务的执行结果。

jsjshshhshshhshw的博客 837

U2Net网络实战:从原理到代码实现(附背景去除应用)

本文深入解析了U2Net网络的原理与代码实现,重点剖析了其独特的嵌套U型结构(RSU模块)和显著图融合机制。通过实战演示,指导读者如何加载预训练模型并应用于精准的图像背景去除任务,为电商、摄影等领域提供高效的自动化抠图解决方案。

nn3456的博客 491

yolov8涨点系列之替换幽灵卷积GhostConv

yolov8涨点系列之替换幽灵卷积GhostConv

zhang1379的博客 3564

Android源码探究之AsyncTask 源码解析

AsyncTask源码使用 Api23版本,后面介绍和以前版本改动不同之处。

李剑的博客 872

【UML】-- 期末试题题库含答案(各章题目、选择题、填空题、简答题、画图题)

每章都有对应的题目和答案,选择题,填空简答,大题,答案已标红,方便大家观看

个人博客 1万+

Android开发——AsyncTask的使用以及源码解析

1.AsyncTask使用介绍 AsyncTask封装了Thread和Handler,通过AsyncTask可以很方便地在执行完后台任务后更新UI。如果不太清楚Android的Handler机制,可以查看此篇Android消息机制详解。 下面是一个使用AsyncTask的实例,利用网络下载某URL里的字符串,以模拟耗时任务。在下载过程中,会通过进度条对话框向用户展示进度。在完成任务后将字...

关于Android开发的一些技术点总结 ╮( ̄▽ ̄”)╭ 1万+

【FF-A】第四章 Concepts

SPM的责任分为两个组件:SPM调度器(SPMD)和SPM核心(SPMC)。这两个组件都可以访问整个物理地址空间,并且是受信任的计算基础的一部分。当无需区分这两个组件时,术语SPM被使用。这些组件的责任如下所示。SPMD 位于 EL3 中,以 AArch64 或 AArch32 执行状态运行。SPMC 可以与 SPMD 共存在 EL3 中,也可以位于相邻的异常级别,即 S-EL1 或 S-EL2。SPM 在启动时初始化 EL3 LSP,并在运行时促进它们与其他端点之间的通信。

baron-周贺贺-代码改变世界ctw 1195

重回童年的经典系列☀️| 【坦克大战3D版】游戏制作+解析

带你重回童年的经典系列——坦克大战3D版制作,感兴趣的可以看看呀~

努力前行,总会成为自己心中的那道光 5万+

[Android] AsyncTask源码解析

常用例子 本文依照以下常用例子展开: private class DownloadFilesTask extends AsyncTask { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i

legendmohe的技术积累 842

ximalaya 新xm-sign逆向思路

本文主要介绍目标网站新xm-sign值的逆向思路。仅代表个人理解,如有其他建议可在评论区沟通。声明仅仅记录一下自己的学习方法,不作为其他参考、更不作为商业用途。如有侵犯请联系本人删除。

weixin_66580433的博客 2017

Android AsyncTask完全解析,带你从源码的角度彻底理解

我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制。之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制,感兴趣的朋友可以参考 Android Handler、Message完全解析,带你从源码的角度彻底理解 。 不过为了更加方便我们在子线程中更新UI元素,Android从1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活地在UI线程和子线程之间自由切换。同时,它也是我们本篇文章的主角。 AsyncTas

郭霖的专栏 11万+

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

源码解析-AsyncTask源码解析

在之前的博客,我们分析了Handler的源码,在我看来Handler的源码只要理清了流程还是比较简单的。而这次要讲解的AsyncTask的源码我自认为会比Handler稍微复杂一点,我们今天一起来了解一下。 在了解AsyncTask前,你需要先了解以下内容: 1.线程池 2.AsyncTask的基本使用的结构 3.Handler的基本使用 好,我们开始进入正题,还是拿我们之前举的那个例子...

just_hu的博客 257

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

上一篇: Android线程池解析
下一篇: Java多线程 - 锁机制
JohanMan
博客等级 码龄10年 72粉丝 131原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值