Android中的线程状态-AsyncTask详解

在操作系统中,线程是操作系统的最小调度单位,同时线程是有限的系统资源,即线程不能无限地派生,并且创建和创建Destruction线程都会有相应的开销。
当系统中有大量线程时,系统会通过时间片轮转的方式来调度各个线程,因此线程不可能绝对并行。

如果在进程中频繁创建和销毁线程,这显然不是一个有效的方法。
正确的做法是使用线程池将一定数量的线程存放在线程池中,可以避免频繁创建和销毁线程带来的系统开销。

AsyncTask是Android封装的一个轻量级异步类(轻量级好用,代码简洁,可以在后台运行任务,然后执行,最终结果传给主线程,更新UI)

AsyncTask内部编译了两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler)。
SerialExecutor用于对任务进行排队,以便将需要执行的多个任务从工作线程有序地排列到主线程。

1AsyncTask的参数e泛型

>AsyncTask是一个抽象泛型类。

其中,三个泛型类型参数的含义如下:

参数:参数i的类型异步任务执行开始时传递;

Progress:异步任务执行过程中,返回下载进度值类型;

Result:任务异步执行后返回的结果类型;

通过这三类参数,检查AsyncTask子类各个阶段的返回类型,如果有不同的业务,就需要另外编写一个AsyncTask子类来处理加工。

2AsyncTask的Main方法

该方法会在后台任务开始运行并在主线程执行之前被调用。
用于对界面进行一些初始化操作,如显示进度条对话框等。

上述方法的调用顺序:

doInBackground()-->publishProgress()-->onProgressUpdate()-->onPostExecute()

doInBackground()-->onPostExecute(),

3AsyncTask的简单使用

这里我们模拟下载任务,并在doInBackground()方法中执行具体的下载逻辑。
在onProgressUpdate()方法中获取当前下载进度,在onPostExecute()方法中推送任务执行结果。
如果你想启动这个任务,只需要调用下面的代码即可:

4.使用AsyncTask的注意事项

①异步任务实例必须在UI线程中创建。
也就是说,AsyncTask对象必须在UI线程上创建。

②必须在UI线程上调用execute(Params...params)方法。

③不要手动调用onPreExecute()、doInBackground(Params...params)、onProgressUpdate(Progress...values)、onPostExecute(Result)。

④doInBackground(Params...params)中无法更改UI组件信息。

⑤一个任务实例只能执行一次如果第二次执行,则会抛出异常。

我们首先分析AsyncTask初始化期间调用的构造函数。

虽然这段代码看起来有点长,但实际上并没有具体执行的逻辑,只是初始化了mWorker和mFuture两个变量,并在初始化mFuture时使用mWorker作为参数。
mWorker是一个可调用对象,mFuture是一个FutureTask对象,这两个变量会暂时存储在内存中,稍后使用。
FutureTask实现了Runnable接口,关于这部分你可以阅读这篇文章。

mWorker中的call()方法执行一个耗时操作,即result=doInBackground(mParams);,然后通过postResult(result)将执行结果传递给内部Handler用于传递到主线程。
这里实例化了两个变量,并没有启动执行任务。

那么如果想要启动某个特定的任务,就需要调用该任务的execute()方法,所以现在我们看一下execute()方法的源码,如下所示:

调用了executeOnExecutor()方法,具体的执行逻辑就在这个方法中:

可以看出,先执行的是onPreExecute()方法,然后是耗时任务的具体执行在exec.execute(mFuture),并传递构造函数中实例化的mFuture。

从上面可以看出,具体是sDefaultExecutor,再追踪发现是SerialExecutor类,具体源码如下:

最后,是追踪到对SerialExecutor类的execute方法的调用。
SerialExecutor是所有实例化的AsyncTask对象所共有的静态内部类。
,然后执行下一个标头,然后执行下一个标头,此类推。

在此方法中,有两个主要步骤。

①向队列中添加一个新任务,即之前实例化的mFuture对象。

②调用scheduleNext()方法,调用THREAD_POOL_EXECUTOR执行队列顶部的任务。

它实际上是一个线程池,开启一定数量的核心线程和工作线程。
然后调用线程池的execute()方法。
执行具体的耗时任务,即启动时构造函数中mWorker上的call()方法的内容。
首先执行doInBackground()方法,然后执行postResult()方法我们看一下该方法的具体内容:

该方法向AsyncTask中的Handler对象发送消息。
er对象源码:

在InternalHandler中,如果接收到的消息是MESSAGE_POST_RESULT,即执行了doInBackground()方法并给出了结果,则调用finish()方法。

如果任务被取消,则再次调用onCancelled()方法,否则再次调用onPostExecute()方法。

如果收到的消息是MESSAGE_POST_PROGRESS,则再次调用onProgressUpdate()方法更新进度。

至此,源码从任务执行的开始到结束就分析完了。

串行与并行AsyncTask

从上面的源码分析可以看出,AsyncTask的执行效果默认是串行的,因为SerialExecutor类是用来维护保证队列的。
如果想要并行执行任务,可以直接传递SerialExecutor类,使用executeOnExecutor()来执行任务。

4不使用的后果正确的AsyncTask

1.)生命周期

AsyncTask不与任何组件的生命周期相连,因此在Activity/中。
或Fragment创建并运行AsyncTask时,最好在Activity/Fragment的onDestory()中调用cancel(boolean)<;/p>

2.)内存泄漏

3.)结果丢失

滚动屏幕或者Activity被后台系统销毁都会导致Activity被重新创建第一个AsyncTask(非静态内部类)将保存对前一个活动的引用。

我是一名从事开发七年的Android工程师,很多人私下问我,2019年如何学习进阶Android,有什么方法吗?

是的,年初我花了一个多月的时间整理了学习资料,希望可以帮助到那些想进阶Android开发但不知道如何进阶的朋友。
【包括高级UI、性能优化、架构师课程、NDK、Kotlin、混合开发(ReactNative+Weex)、Flutter等架构技术资料】希望能帮助你面试前复习,找到好工作,同时省钱钱每个人都花时间在互联网上寻找信息来学习。

Java|自定义线程池的七大参数详解

欢迎来到程序员棚分享站,今天我们来讨论七种Java自定义线程池的详细讲解。
首先,核心线程数(CorePoolSize)始终是线程池中的线程数,即使空闲也不会消失,除非设置允许核心线程过期。
接下来,最大线程数(MaximumPoolSize)是指线程池中允许的最大线程数。
当作业队列已满且创建的线程数小于最大线程数时,线程池将创建新的线程来执行作业。
Keep-AliveTime是指非主线程保持活动状态的时间量。
当线程池中的线程数量超过主线程数量时,超出的线程如果在一定时间内没有被使用,就会被删除。
时间单位(TimeUnit)与空闲线程的生存时间结合使用来指定生存时间,例如秒、分钟等。
工作队列(WorkQueue)用于存储要执行的任务。
当所有主线程都忙时,新进入的作业会被放入作业队列中等待执行。
ThreadFactory用于创建新线程。
通过自定义线程工厂,可以为创建的线程设置一些属性,比如线程名、线程组、优先级等。
拒绝方法(RejectedExecutionHandler)用于处理当线程池和队列已满时如何执行新提交的任务。
Java提供了几种内置的拒绝策略,例如AbortPolicy、CallerRunsPolicy、DiscardPolicy和DiscardOldestPolicy。
以是Java自定义线程池的七个参数,它们共同决定了线程池的行为。
现在,我们来创建一个自定义线程池,示例代码如下:根据上述参数创建自定义线程池的示例代码如下:您可以通过控制这七个参数来管理线程池。
Java具有灵活性,提高代码效率和性能。
在编程实践中正确设置线程池参数可以避免资源浪费,提高程序响应速度。