Android最佳实践之性能 - 多线程
在单独线程执行代码
參考地址:http://developer.android.com/training/multiple-threads/define-runnable.html
Runnable对象,是一个接口,里面仅仅有一个run方法。它仅仅是表示一段能够执行的代码。
说这句话,是说明它并不一定要执行在子线程中。它也能够执行在UI线程。
假设它用来执行一段代码,通常被称为一个任务(Task)。
Thread类和 Runnable类。是非常强大的基础类,它们是强大的Android基础类HandlerThread, AsyncTask和 IntentService的基础,也是ThreadPoolExecutor的基础。
这个类自己主动管理线程和任务队列。甚至能够并行多个异步任务。
定义一个Runnable
public class PhotoDecodeRunnable implements Runnable {
...
@Override
public void run() {
/*
* Code you want to run on the thread goes here
*/
...
}
...
}
实现run()方法
在设计上,Runnable对象一般设计在子线程中执行,比方new Thread(new Runnable{})中。
以下的演示样例中。一開始调用Process.setThreadPriority()方法,传入THREAD_PRIORITY_BACKGROUND。这样能够降低Runnable对象所在线程和UI线程的资源竞争。
你也应该调用Thread.currentThread()。保存一个引用,指向Runnable所在的线程。
class PhotoDecodeRunnable implements Runnable {
...
/*
* Defines the code to run for this task.
*/
@Override
public void run() {
// Moves the current Thread into the background
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
...
/*
* Stores the current Thread in the PhotoTask instance,
* so that the instance
* can interrupt the Thread.
*/
mPhotoTask.setImageDecodeThread(Thread.currentThread());
...
}
...
}
管理多线程
參考地址:http://developer.android.com/training/multiple-threads/create-threadpool.html
假设你仅仅执行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<Runnable> mDecodeWorkQueue;
...
// Instantiates the queue of Runnables as a LinkedBlockingQueue
mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
...
}
...
}
创建一个线程池
调用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
在线程池的一个线程上执行代码
參考地址:http://developer.android.com/training/multiple-threads/run-code.html#StopThread
你加入了一个task到任务队列中,当线程池中有线程空暇时,则会执行队列中的task。为节省CPU资源,也能够中断正在执行的线程。
在池里一个线程中执行代码
传一个Runnable到ThreadPoolExecutor.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线程的交互
參考地址:http://developer.android.com/training/multiple-threads/communicate-ui.html
在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从一个线程中发送的消息。
/*
* handleMessage() defines the operations to perform when
* the Handler receives a new Message to process.
*/
@Override
public void handleMessage(Message inputMessage) {
// Gets the image task from the incoming Message object.
PhotoTask photoTask = (PhotoTask) inputMessage.obj;
...
}
...
}
}
The next section shows how to tell the Handler to move data.
将数据从Task移动到UI线程
为了将数据从执行在后台线程的task中移动到UI线程,一開始我们要在task类中存数据和UI对象的引用。然后。将task对象和状态码传给被Handler实例化的对象中。然后发送一个包括task和状态码的Message给handler。
由于Handler执行在UI线程。所以它能够将数据送到UI对象中。
// A class that decodes photo files into Bitmaps
class PhotoDecodeRunnable implements Runnable {
...
PhotoDecodeRunnable(PhotoTask downloadTask) {
mPhotoTask = downloadTask;
}
...
// Gets the downloaded byte array
byte[] imageBuffer = mPhotoTask.getByteBuffer();
...
// Runs the code for this task
public void run() {
...
// Tries to decode the image buffer
returnBitmap = BitmapFactory.decodeByteArray(
imageBuffer,
0,
imageBuffer.length,
bitmapOptions
);
...
// Sets the ImageView Bitmap
mPhotoTask.setImage(returnBitmap);
// Reports a status of "completed"
mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);
...
}
...
}
...
PhotoTask中也有ImageView和Bitmap的句柄。
虽然它们在同一个对象中,也不能将Bitmap给到ImageView显示,由于它们不在UI线程。
public class PhotoTask {
...
// Gets a handle to the object that creates the thread pools
sPhotoManager = PhotoManager.getInstance();
...
public void handleDecodeState(int state) {
int outState;
// Converts the decode state to the overall state.
switch(state) {
case PhotoDecodeRunnable.DECODE_STATE_COMPLETED:
outState = PhotoManager.TASK_COMPLETE;
break;
...
}
...
// Calls the generalized state method
handleState(outState);
}
...
// Passes the state to PhotoManager
void handleState(int state) {
/*
* Passes a handle to this task and the
* current state to the class that created
* the thread pools
*/
sPhotoManager.handleState(this, state);
}
...
}
PhotoManager接收一个状态码和一个PhotoTask对象的句柄,由于状态是TASK_COMPLETE,创建一个包括状态和Task对象的Message然后发给Handler:
public class PhotoManager {
...
// Handle status messages from tasks
public void handleState(PhotoTask photoTask, int state) {
switch (state) {
...
// The task finished downloading and decoding the image
case TASK_COMPLETE:
/*
* Creates a message for the Handler
* with the state and the task object
*/
Message completeMessage =
mHandler.obtainMessage(state, photoTask);
completeMessage.sendToTarget();
break;
...
}
...
}
最后,在Handler.handleMessage()中检測Message中的状态。假设状态是TASK_COMPLETE,则表示task已完毕。PhotoTask中的ImageView须要显示当中的Bitmap对象。由于Handler.handleMessage()执行在UI线程,如今ImageView显示bitmap是同意的。
Android最佳实践之性能 - 多线程的更多相关文章
- Atitit.嵌入式web 服务器 java android最佳实践
Atitit.嵌入式web 服务器 java android最佳实践 1. Android4.4.21 2. 自己的webserver1 3. CyberHTTP for Java cybergar ...
- fir.im Weekly - 2016 年 Android 最佳实践列表
2016 年已经过去一半,你在年初制定的成长计划都实现了吗? 学海无涯,技术成长不是一簇而就的事情.本期 fir.im Weekly 推荐 王下邀月熊_Chevalier的 我的编程之路--知识管理与 ...
- Canvas 最佳实践(性能篇)
Canvas 想必前端同学们都不陌生,它是 HTML5 新增的「画布」元素,允许我们使用 JavaScript 来绘制图形.目前,所有的主流浏览器都支持 Canvas. Canvas 最常见的用途是渲 ...
- 听说你还不会用Dagger2?Dagger2 For Android最佳实践教程
前言 Dagger2是现在非常火的一个依赖注入框架,目前由Google维护,在Github上面已经有12K star了.Dagger2的入门门槛其实是比较高的,据了解,目前有很多Android工程师对 ...
- 经典的性能优化最佳实践 web性能权威指南 读书笔记
web性能权威指南 page 203 经典的性能优化最佳实践 无论什么网络,也不管所用网络协议是什么版本,所有应用都应该致力于消除或减 少不必要的网络延迟,将需要传输的数据压缩至最少.这两条标准是经典 ...
- Android最佳实践指南
Updated on 2016/1/6 修正了一些翻译段落欢迎转载,但请保留译者链接:http://www.jianshu.com/p/613d28a3c8a0 Lessons learned fro ...
- android最佳实践之设备兼容性
由于不同手机的尺寸大小,屏幕分辨率可能存在差异.在开发应用的时候,你或许遇到过这些的问题: 1, 为什么图片在另外的手机上显示的时候变小了,又或是缩小了? 2, 为什么在layout中定义好的格局在另 ...
- Android最佳实践之Material Design
Material概述及主题 学习地址:http://developer.android.com/training/material/get-started.html 使用material design ...
- 我的Android最佳实践之—— Android更新UI的两种方法:handler与runOnUiThread()
在Android开发过程中,常需要更新界面的UI.而更新UI是要主线程来更新的,即UI线程更新.如果在主线线程之外的线程中直接更新页面 显示常会报错.抛出异常:android.view.ViewRoo ...
随机推荐
- Redis(五)-数据库
Redis是一个字典结构的存储服务器,而实际上一个Redis实例提供了多个用来存储数据的字典,客户端可以将制定的书存储在哪个字典中,这与关系书库实例中可以i创建多个数据库类似,所以可以将其中的每个字典 ...
- 笔记《javascript高级程序设计》 第12章 DOM2和DOM3
DOM2 级核心:在 1 级基础上构建,为节点添加更多方法和属性 DOM2 级视图:为文档定义了基于样式信息的不同视图 DOM2 级事件:说明了如何使用事件和DOM文档交互 DOM2 级样式:如何以编 ...
- canvas 和 svg
对于开发人员而言,最直观的区别在于:1.对于画在Canvas上的部件,你需要处理重绘.而SVG则不用,你修改svg dom则系统会自动帮你重绘2.Hittest,即canvas不负责帮你侦测鼠标/触摸 ...
- oracle-3种工具使用
1:原命令行,dos 2:sqlplus,图形界面 3:isqlplus,网页版的.(假如自己机器无法安装oracle,可通过别人ip地址去使用oracle,http://ip:5560/isqlpl ...
- (转)Arcgis for JS之地图自适应调整
http://blog.csdn.net/gisshixisheng/article/details/42675897 概述:本节讲述的内容为当浏览器大小发生变化或者地图展示区域的大小发生变化时,地图 ...
- 编程领域中的 "transparent" 和 "opaque"
引言 在学习计算机的过程中,经常会接触到 “透明” 和 “非透明” 的概念. 刚开始理解 “透明” 这个概念的时候,认为 “透明” 就是程序员可以看见其中的构造,但是老师却说透明是程序员意识不到其中的 ...
- vue.js的ajax和jsonp请求
首先要声明使用ajax 在 router下边的 Index.js中 import VueResource from 'vue-resource'; Vue.use(VueResource); ajax ...
- 【剑指Offer】59、按之字形顺序打印二叉树
题目描述: 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 解题思路: 这道题仍然是二 ...
- 【剑指Offer】50、数组中重复的数字
题目描述: 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果 ...
- DOM学习之充实文档内容
HTML代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <me ...