在单独线程执行代码

參考地址:http://developer.android.com/training/multiple-threads/define-runnable.html

Runnable对象,是一个接口,里面仅仅有一个run方法。它仅仅是表示一段能够执行的代码。

说这句话,是说明它并不一定要执行在子线程中。它也能够执行在UI线程

假设它用来执行一段代码,通常被称为一个任务(Task)。

Thread类和 Runnable类。是非常强大的基础类,它们是强大的Android基础类HandlerThread, AsyncTaskIntentService的基础,也是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资源,也能够中断正在执行的线程。

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

传一个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线程的交互

參考地址: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最佳实践之性能 - 多线程的更多相关文章

  1. Atitit.嵌入式web 服务器 java android最佳实践

    Atitit.嵌入式web 服务器 java android最佳实践 1. Android4.4.21 2. 自己的webserver1 3. CyberHTTP for Java  cybergar ...

  2. fir.im Weekly - 2016 年 Android 最佳实践列表

    2016 年已经过去一半,你在年初制定的成长计划都实现了吗? 学海无涯,技术成长不是一簇而就的事情.本期 fir.im Weekly 推荐 王下邀月熊_Chevalier的 我的编程之路--知识管理与 ...

  3. Canvas 最佳实践(性能篇)

    Canvas 想必前端同学们都不陌生,它是 HTML5 新增的「画布」元素,允许我们使用 JavaScript 来绘制图形.目前,所有的主流浏览器都支持 Canvas. Canvas 最常见的用途是渲 ...

  4. 听说你还不会用Dagger2?Dagger2 For Android最佳实践教程

    前言 Dagger2是现在非常火的一个依赖注入框架,目前由Google维护,在Github上面已经有12K star了.Dagger2的入门门槛其实是比较高的,据了解,目前有很多Android工程师对 ...

  5. 经典的性能优化最佳实践 web性能权威指南 读书笔记

    web性能权威指南 page 203 经典的性能优化最佳实践 无论什么网络,也不管所用网络协议是什么版本,所有应用都应该致力于消除或减 少不必要的网络延迟,将需要传输的数据压缩至最少.这两条标准是经典 ...

  6. Android最佳实践指南

    Updated on 2016/1/6 修正了一些翻译段落欢迎转载,但请保留译者链接:http://www.jianshu.com/p/613d28a3c8a0 Lessons learned fro ...

  7. android最佳实践之设备兼容性

    由于不同手机的尺寸大小,屏幕分辨率可能存在差异.在开发应用的时候,你或许遇到过这些的问题: 1, 为什么图片在另外的手机上显示的时候变小了,又或是缩小了? 2, 为什么在layout中定义好的格局在另 ...

  8. Android最佳实践之Material Design

    Material概述及主题 学习地址:http://developer.android.com/training/material/get-started.html 使用material design ...

  9. 我的Android最佳实践之—— Android更新UI的两种方法:handler与runOnUiThread()

    在Android开发过程中,常需要更新界面的UI.而更新UI是要主线程来更新的,即UI线程更新.如果在主线线程之外的线程中直接更新页面 显示常会报错.抛出异常:android.view.ViewRoo ...

随机推荐

  1. LMS、NLMS最优步长理论分析与Speex回声消除可能的改进想法

    一.回声消除算法模型 先来分析下自适应回声消除的主要组成部分,大体上可以把回声消除模型分为两个部分 横向滤波器结构 滤波器系数自适应与步长控制 横向滤波器用脉冲响应w(n)[有的地方也称为回声路径]与 ...

  2. C# 取两位小数

    double s=0.55555;result=s.ToString("#0.00");//点后面几个0就保留几位 如果要四舍五入的话,用这个double dbdata = 0.5 ...

  3. Mysql数据库系列

    详情点击 MySQL基础 Mysql表操作 Mysql插入 更新 删除 查询操作 Mysql创建用户和授权 基本的Mysql语句 Mysql库的操作 Mysql表的操作 Mysql数据类型(一) My ...

  4. java 实现yaml 数据转json与map

    首先引入snakeyaml-1.16.jar的包. 直接上代码: package com.ming.yaml; import java.util.Map; import org.yaml.snakey ...

  5. C#三种创建对象方法所需时间比较。。。。。

    C#创建对象的三种方法  new().Activator.Assembly,接下来通过代码直接来看看运行的速度.... 首先,先看看三种创建对象实例的方法: //new(); public stati ...

  6. 【java基础】(6)内部类

    内部类不是很好理解,但说白了其实也就是一个类中还包含着另外一个类 如同一个人是由大脑.肢体.器官等身体结果组成,而内部类相当于其中的某个器官之一,例如心脏:它也有自己的属性和行为(血液.跳动) 显然, ...

  7. JavaScript特效之图片特效放大,缩小,旋转

    效果图如下: 效果代码如下: <!doctype html> <html lang="en"> <head> <meta charset= ...

  8. 5、scala数组转换

    1.使用yield和函数式编程转换数组 2.算法案例:移除第一个负数之后的所有负数 1.使用yield和函数式编程转换数组 使用yield进行数组转换 结合if守卫,仅转换需要转换的元素 使用函数式编 ...

  9. java Web(3)

    Servlet 是运行在Web服务器或应用服务器上的Java程序 在Web上创建动态内容的有效而强大的解决方案 由容器来管理生命周期与Web服务器交互 由Sun规范了其功能 Servlet部署: 一个 ...

  10. css单双行样式

    #random_box li:nth-child(odd) {//双行 background: #fff5c4; } #random_box li:nth-child(even) {//单行 back ...