Android线程最佳实践 - 多线程(1)

管理多线程

=========

如果你只运行task(Runnable)一次,那么上一篇的内容足以;如果你要在不同的数据集中重复运行一个task,但一次也只能运行一个task,IntentService满足你的需求;为了自动将资源运用最大化、或同时运行多个task,你需要一个多线程管理对象。使用ThreadPoolExecutor,它使用闲置的线程执行队列中的task,你需要做的事就是向队列中添加任务。

一个线程池可以并行执行多个task,所以你要确保你的代码是线程安全的

定义一个线程池(Thread Pool)对象


对线程池使用静态变量

在app中,线程池可能需要单例的:

public class PhotoManager {

static {

// Creates a single static instance of PhotoManager

sInstance = new PhotoManager();

}

使用private的构造方法

将构造方法私有化,则不用 synchronized 块来闭包构造方法:

public class PhotoManager {

/**

* Constructs the work queues and thread pools used to download

* and decode images. Because the constructor is marked private,

* it’s unavailable to other classes, even in the same package.

*/

private PhotoManager() {

}

使用线程池类中的方法来执行task

在线程池类中添加一个task给任务队列:

public class PhotoManager {

// Called by the PhotoView to get a photo

static public PhotoTask startDownload(

PhotoView imageView,

boolean cacheFlag) {

// Adds a download task to the thread pool for execution

sInstance.

mDownloadThreadPool.

execute(downloadTask.getHTTPDownloadRunnable());

}

在UI线程初始化一个Handler

private PhotoManager() {

// Defines a Handler object that’s attached to the UI thread

mHandler = new Handler(Looper.getMainLooper()) {

/*

* handleMessage() defines the operations to perform when

* the Handler receives a new Message to process.

*/

@Override

public void handleMessage(Message inputMessage) {

}

}

}

确定线程池参数


初始化一个 ThreadPoolExecutor 对象,需要下面这些参数:

1、池的初始化size和最大的池size

在线程池中可以使用的线程的数量主要取决于你的设备可用CPU内核的数量:

public class PhotoManager {

/*

* Gets the number of available cores

* (not always the same as the maximum number of cores)

*/

private static int NUMBER_OF_CORES =

Runtime.getRuntime().availableProcessors();

}

这个数字可能不反映设备物理CPU内核的数量。一些设备根据系统负载已经关闭一个或多个内核的cpu,对于这些设备,availableProcessors()返回的是可用的内核数,这个数字一般小于内核总数。

2、活跃时间和时间单位

活跃时间指一个线程在关闭之前保持空闲的时间。这个时间的单位由 TimeUnit 中的常量决定。

3、任务队列

ThreadPoolExecutor 持有的任务队列里面是 Runnable

对象。初始化ThreadPoolExecutor时要传入一个实现了 BlockingQueue

接口的队列。为满足app需求,你可以选择已有的实现了这个接口的类,下面是 LinkedBlockingQueue 的例子:

public class PhotoManager {

private PhotoManager() {

// A queue of Runnables

private final BlockingQueue mDecodeWorkQueue;

// Instantiates the queue of Runnables as a LinkedBlockingQueue

mDecodeWorkQueue = new LinkedBlockingQueue();

}

}

创建一个线程池


调用ThreadPoolExecutor的构造方法ThreadPoolExecutor()来创建一个线程池:

private PhotoManager() {

// Sets the amount of time an idle thread waits before terminating

private static final int KEEP_ALIVE_TIME = 1;

// Sets the Time Unit to seconds

private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;

// Creates a thread pool manager

mDecodeThreadPool = new ThreadPoolExecutor(

NUMBER_OF_CORES, // Initial pool size

NUMBER_OF_CORES, // Max pool size

KEEP_ALIVE_TIME,

KEEP_ALIVE_TIME_UNIT,

mDecodeWorkQueue);

}

完整代码下载ThreadSample.zip

在线程池的一个线程上运行代码

==================

你添加了一个task到任务队列中,当线程池中有线程空闲时,则会执行队列中的task。为节省CPU资源,也可以中断正在执行的线程。

在池里一个线程中运行代码


传一个 RunnableThreadPoolExecutor.execute()

方法中,可开始执行一个任务。这个方法将task添加到这个线程池的工作队列中,当有空闲的线程时,就会取出队列中的任务进行执行:

public class PhotoManager {

public void handleState(PhotoTask photoTask, int state) {

switch (state) {

// The task finished downloading the image

case DOWNLOAD_COMPLETE:

// Decodes the image

mDecodeThreadPool.execute(

photoTask.getPhotoDecodeRunnable());

}

}

}

中断(Interrupt)运行代码


要结束一个task,你需要中断这个task所在的线程。为了能这样做,在创建task的时候你要保存task所在线程的句柄:

class PhotoDecodeRunnable implements Runnable {

// Defines the code to run for this task

public void run() {

/*

* Stores the current Thread in the

* object that contains PhotoDecodeRunnable

*/

mPhotoTask.setImageDecodeThread(Thread.currentThread());

}

}

调用 Thread.interrupt()

来中断一个线程。注意,Thread对象是由系统控制的,可以在app进程之外来修改它们。因此,你需要在中断它之前锁住线程中的访问,在访问的地方加synchronized代码块。例如:

public class PhotoManager {

public static void cancelAll() {

/*

* Creates an array of Runnables that’s the same size as the

* thread pool work queue

*/

Runnable[] runnableArray = new Runnable[mDecodeWorkQueue.size()];

// Populates the array with the Runnables in the queue

mDecodeWorkQueue.toArray(runnableArray);

// Stores the array length in order to iterate over the array

int len = runnableArray.length;

/*

* Iterates over the array of Runnables and interrupts each one’s Thread.

*/

synchronized (sInstance) {

// Iterates over the array of tasks

for (int runnableIndex = 0; runnableIndex < len; runnableIndex++) {

// Gets the current thread

Thread thread = runnableArray[taskArrayIndex].mThread;

// if the Thread exists, post an interrupt to it

if (null != thread) {

thread.interrupt();

}

}

}

}

}

大多数情况下,Thread.interrupt()会直接停止线程。然而,它仅仅会停止waiting的线程,而不会中断正使用CPU和网络任务的线程。为了防止拖慢或锁定系统,你应该在执行某个操作前判断是否中断了:

/*

* Before continuing, checks to see that the Thread hasn’t

* been interrupted

*/

if (Thread.interrupted()) {

return;

}

// Decodes a byte array into a Bitmap (CPU-intensive)

BitmapFactory.decodeByteArray(

imageBuffer, 0, imageBuffer.length, bitmapOptions);

完整代码下载ThreadSample.zip

UI线程的交互

===========

在Android中一般使用 Handler ,在子线程中将结果发送到UI线程,然后在UI线程操作UI。

在UI线程上定义一个Handler


Handler是Android

Framework中管理线程的一部分。一个Handler对象接收消息然后运行一些代码处理这些消息。一般,在一个新线程中创建一个Handler,你也可以在一个已有的线程中创建Handler。当在UI线程创建Handler,那么

它处理的代码也运行在UI线程。

Looper类也是Android系统管理线程的一部分,在Handler的构造方法中传入这个Looper对象,这个Handler将运行在Looper所在的线程。

private PhotoManager() {

// Defines a Handler object that’s attached to the UI thread

mHandler = new Handler(Looper.getMainLooper()) {

覆写handleMessage()方法,来处理handler从一个线程中发送的消息。

最后

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!**

往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

[外链图片转存中…(img-EkqQLZGs-1714447814588)]

[外链图片转存中…(img-hijFeVxz-1714447814589)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值