热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android的AsyncTask类的解读

国庆节放假,搞了半个月都没有上班了,coding的时候一点都不在状态,本来这篇文章是在国庆节前写完的,但是因为自己的懒惰,导致延期到国庆节,哎,这种习惯真心不好呀。。。不多说了下面来进入正题之前我们解

国庆节放假,搞了半个月都没有上班了,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 sPoolWorkQueue = new LinkedBlockingQueue(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 mWorker;

private final AtomicBoolean mCancelled = new AtomicBoolean();

private final FutureTask 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 workQueue,                            ThreadFactory threadFactory,                            RejectedExecutionHandler handler)
看这个参数很容易让人以为是线程池里保持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 implements Callable {Params[] mParams;}
他是个抽象类,实现了Callable接口,这里的Param和Result是AsyncTask定义的泛型类型


第七、FutureTask类

public class FutureTask implements RunnableFuture{//.....}

在看一下RunnableFuture接口:

public interface RunnableFuture extends Runnable, Future {    /**     * 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 updatesprivate 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() {public Result call() throws Exception {mTaskInvoked.set(true);Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection uncheckedreturn postResult(doInBackground(mParams));}};mFuture = new FutureTask(mWorker) {@Overrideprotected 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(this, result));message.sendToTarget();return result;}

看一下InternalHandler类的定义:

private static class InternalHandler extends Handler {@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult result = (AsyncTaskResult) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.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(mWorker) {@Overrideprotected 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 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 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 interruptsint 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 statefinishCompletion();}}


看一下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 gcq = 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 execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);}


再看executeOnExecutor方法:

public final AsyncTask 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 mTasks = new ArrayDeque();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,写的我各种痛苦呀,以后一定要在放长假之前把事都做了。。)





















推荐阅读
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • PHP中的单例模式与静态变量的区别及使用方法
    本文介绍了PHP中的单例模式与静态变量的区别及使用方法。在PHP中,静态变量的存活周期仅仅是每次PHP的会话周期,与Java、C++不同。静态变量在PHP中的作用域仅限于当前文件内,在函数或类中可以传递变量。本文还通过示例代码解释了静态变量在函数和类中的使用方法,并说明了静态变量的生命周期与结构体的生命周期相关联。同时,本文还介绍了静态变量在类中的使用方法,并通过示例代码展示了如何在类中使用静态变量。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 栈和队列的共同处和不同处
    本文主要介绍了栈和队列的共同处和不同处。栈和队列都是由几个数据特性相同的元素组成的有限序列,也就是线性表。队列是限定仅在表的一端插入元素、在另一端删除元素的线性表,遵循先进先出的原则。栈是限定仅在表尾进行插入或删除操作的线性表,遵循后进先出的原则。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
author-avatar
上海悠u7_
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有