很多初入 Android 或 Java 开发的新手对 Thread 、 Looper 、 Handler 和 Message 仍然比较迷惑,衍生的有 HandlerThread 、 java.util.concurrent 、 Task 、 AsyncTask 由于目前市面上的书籍等资料都没有谈到这些问题,今天 Android123 就这一问题做更系统性的总结。
Android 开发过程中为什么要多线程
我们创建的 Service 、 Activity 以及 Broadcast 均是一个主线程处理,这里我们可以理解为 UI 线程。但是在操作一些耗时操作时,比如 I/O 读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现 ANR 的响应提示窗口,这个时候我们可以考虑使用 Thread 线程来解决。
Android 中使用 Thread 线程会遇到哪些问题
对于从事过 J2ME 开发的程序员来说 Thread 比较简单,直接匿名创建重写 run 方法,调用 start 方法执行即可。或者从 Runnable 接口继承,但对于 Android 平台来说 UI 控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点 Google 在设计 Android 时倒是参考了下 Win32 的消息处理机制。
postInvalidate() 方法
对于线程中的刷新一个 View 为基类的界面,可以使用 postInvalidate() 方法在线程中来处理,其中还提供了一些重写方法比如 postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如 postInvalidateDelayed(long delayMilliseconds) 或 postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒,如下:
void |
|
void |
postInvalidate (int left, int top, int right, int bottom) |
void |
postInvalidateDelayed (long delayMilliseconds) |
void |
postInvalidateDelayed (long delayMilliseconds, int left, int top, int right, int bottom) |
Handler
当然推荐的方法是通过一个 Handler 来处理这些,可以在一个线程的 run 方法中调用 handler 对象的 postMessage 或 sendMessage 方法来实现, Android 程序内部维护着一个消息队列,会轮训处理这些,如果你是 Win32 程序员可以很好理解这些消息处理,不过相对于 Android 来说没有提供 PreTranslateMessage 这些干涉内部的方法。
消息的处理者, handler 负责将需要传递的信息封装成 Message ,通过调用 handler 对象的 obtainMessage() 来实现。将消息传递给 Looper ,这是通过 handler 对象的 sendMessage() 来实现的。继而由 Looper 将 Message 放入 MessageQueue 中。 当 Looper 对象看到 MessageQueue 中含有 Message ,就将其广播出去。该 handler 对象收到该消息后,调用相应的 handler 对象的 handleMessage() 方法对其进行处理。
Handler
主要接受子线程发送的数据
,
并用此数据配合主线程更新
UI.
当应用程序启动时,
Android
首先会开启一个主线程
(
也就是
UI
线程
) ,
主线程为管理界面中的
UI
控件,进行事件分发
,
比如说
,
你要是点击一个
Button ,Android
会分发事件到
Button
上,来响应你的操作。
如果此时需要一个耗时的操作,例如
:
联网读取数据,
或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,,如果你放在主线程中的话,界面会出现假死现象
,
如果
5
秒钟还没有完成的话,,会收到
Android
系统的一个错误提示
"
强制关闭
".
这个时候我们需要把这些耗时的操作,放在一个子线程中
,
因为子线程涉及到
UI
更新,,
Android
主线程是线程不安全的,也就是说,更新
UI
只能在主线程中更新,子线程中操作是危险的
.
这个时候,
Handler
就出现了
,
来解决这个复杂的问题
,
由于
Handler
运行在主线程中
(UI
线程中
),
它与子线程可以通过
Message
对象来传递数据
,
这个时候,
Handler
就承担着接受子线程传过来的
(
子线程用
sedMessage()
方法传弟
)Message
对象,
(
里面包含数据
) ,
把这些消息放入主线程队列中,配合主线程进行更新
UI
。
Handler
一些特点:
handler
可以分发
Message
对象和
Runnable
对象到主线程中
,
每个
Handler
实例
,
都会绑定到创建他的线程中
(
一般是位于主线程
),
它有两个作用
:
(1):
安排消息或
Runnable
在某个主线程中某个地方执行
, (2)
安排一个动作在不同的线程中执行
Handler
中分发消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上
post
类方法允许你排列一个
Runnable
对象到主线程队列中
,
sendMessage
类方法
,
允许你安排一个带数据的
Message
对象到队列中,等待更新
.
Handler
实例
//
子类需要继承
Hendler
类,并重写
handleMessage(Message msg)
方法
,
用于接受线程数据
//
以下为一个实例,它实现的
功能
为
:
通过线程修改界面
Button
的内容
public class MyHandlerActivity extends Activity { Button button; MyHandler myHandler; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.handlertest); button = (Button) findViewById(R.id.button); myHandler = new MyHandler(); // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据 // Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象 // (2): 让一个动作,在不同的线程中执行. // 它安排消息,用以下方法 // post(Runnable) // postAtTime(Runnable,long) // postDelayed(Runnable,long) // sendEmptyMessage(int) // sendMessage(Message); // sendMessageAtTime(Message,long) // sendMessageDelayed(Message,long) // 以上方法以 post开头的允许你处理Runnable对象 //sendMessage()允许你处理Message对象(Message里可以包含数据,) MyThread m = new MyThread(); new Thread(m).start(); } /** * 接受消息,处理消息 ,此Handler会与当前主线程一块运行 * */ class MyHandler extends Handler { public MyHandler() { } public MyHandler(Looper L) { super(L); } // 子类必须重写此方法,接受数据 @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub Log.d("MyHandler", "handleMessage......"); super.handleMessage(msg); // 此处可以更新UI Bundle b = msg.getData(); String color = b.getString("color"); MyHandlerActivity.this.button.append(color); } } class MyThread implements Runnable { public void run() { try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.d("thread.......", "mThread........"); Message msg = new Message(); Bundle b = new Bundle();// 存放数据 b.putString("color", "我的"); msg.setData(b); MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI } } }
Looper
其实 Android 中每一个 Thread 都跟着一个 Looper , Looper 可以帮助 Thread 维护一个消息队列,昨天的问题 Can't create handler inside thread 错误 一文中提到这一概念,但是 Looper 和 Handler 没有什么关系,我们从开源的代码可以看到 Android 还提供了一个 Thread 继承类 HanderThread 可以帮助我们处理,在 HandlerThread 对象中可以通过 getLooper 方法获取一个 Looper 对象控制句柄,我们可以将其这个 Looper 对象映射到一个 Handler 中去来实现一个线程同步机制, Looper 对象的执行需要初始化 Looper.prepare 方法就是昨天我们看到的问题,同时推出时还要释放资源,使用 Looper.release 方法。
Looper
是
MessageQueue
的管理者。每一个
MessageQueue
都不能脱离
Looper
而存在,
Looper
对象的创建是通过
prepare
函数来实现的。同时每一个
Looper
对象和一个线程关联。通过调用
Looper.myLooper()
可以获得当前线程的
Looper
对象
创建一个
Looper
对象时,会同时创建一个
MessageQueue
对象。除了主线程有默认的
Looper
,其他线程默认是没有
MessageQueue
对象的,所以,不能接受
Message
。如需要接受,自己定义
一个
Looper
对象
(
通过
prepare
函数
),
这样该线程就有了自己的
Looper
对象和
MessageQueue
数据结构了。
Looper
从
MessageQueue
中取出
Message
然后,交由
Handler
的
handleMessage
进行处理。处理完成后,调用
Message.recycle()
将其放入
Message Pool
中。
Message
对于 Android 中 Handler 可以传递一些内容,通过 Bundle 对象可以封装 String 、 Integer 以及 Blob 二进制对象,我们通过在线程中使用 Handler 对象的 sendEmptyMessage 或 sendMessage 方法来传递一个 Bundle 对象到 Handler 处理器。对于 Handler 类提供了重写方法 handleMessage(Message msg) 来判断,通过 msg.what 来区分每条信息。将 Bundle 解包来实现 Handler 类更新 UI 线程中的内容实现控件的刷新操作。相关的 Handler 对象有关消息发送 sendXXXX 相关方法如下,同时还有 postXXXX 相关方法,这些和 Win32 中的道理基本一致,一个为发送后直接返回,一个为处理后才返回。
Message :消息对象, Message Queue 中的存放的对象。一个 Message Queue 中包含多个 Message 。 Message 实例对象的取得,通常使用 Message 类里的静态方法 obtain(), 该方法有多个重载版本可供选择;它的创建并不一定是直接创建一个新的实例,而是先从 Message Pool( 消息池 ) 中看有没有可用的 Message 实例,存在则直接取出返回这个实例。如果 Message Pool 中没有可用的 Message 实例,则才用给定的参数创建一个 Message 对象。调用 removeMessages() 时,将 Message 从 Message Queue 中删除,同时放入到 Message Pool 中。除了上面这种方式,也可以通过 Handler 对象的 obtainMessage() 获取 一个 Message 实例。
final boolean |
sendEmptyMessage (int what) |
final boolean |
sendEmptyMessageAtTime (int what, long uptimeMillis) |
final boolean |
sendEmptyMessageDelayed (int what, long delayMillis) |
final boolean |
sendMessage ( Message msg) |
final boolean |
sendMessageAtFrontOfQueue ( Message msg) |
boolean |
sendMessageAtTime ( Message msg, long uptimeMillis) |
final boolean |
sendMessageDelayed ( Message msg, long delayMillis) |
MessageQueue
是一种
数据
结构,见名知义,就是一个消息队列,存放消息的地方。每一个线程最多只可以拥有一个
MessageQueue
数据结构。
创建一个线程的时候,并不会自动
创建其
MessageQueue
。通常使用一个
Looper
对象对该线程的
MessageQueue
进行管理。主线程创建时,会创建一个默认的
Looper
对象,而
Looper
对象的创建,将自动创建一个
Message Queue
。其他非主线程,不会自动创建
Looper
,要需要的时候,通过调用
prepare
函数来实现。
java.util.concurrent
对象分析
对于过去从事 Java 开发的程序员不会对 Concurrent 对象感到陌生吧,他是 JDK 1.5 以后新增的重要特性作为掌上设备,我们不提倡使用该类,考虑到 Android 为我们已经设计好的 Task 机制,我们这里 Android 开发网对其不做过多的赘述。
Task 以及 AsyncTask
在 Android 中还提供了一种有别于线程的处理方式,就是 Task 以及 AsyncTask ,从开源代码中可以看到是针对 Concurrent 的封装,开发人员可以方便的处理这些异步任务。 当然涉及到同步机制的方法和技巧还有很多,考虑时间和篇幅问题不再做过多的描述。
相关推荐
Android 多线程Handler/Message机制详解 Android 多线程机制是 Android 应用程序中最重要的组件之一,它允许应用程序在后台执行一些操作,而不影响用户的交互体验。在 Android 中,多线程机制是基于 Handler 和 ...
【Android_Handler详解(一)】 在Android开发中,Handler是一个至关重要的组件,它与线程、消息队列和Looper紧密关联,用于实现不同线程间的通信。本篇将深入探讨Handler的基本概念、使用方法以及其在多线程环境中的...
### Android多线程处理详解 #### 一、Android进程与线程基础 在深入探讨Android中的多线程处理之前,我们需要先理解Android的基本进程和线程模型。 **Android进程:** 当一个Android应用首次启动时,系统会为该...
本文将深入探讨Android多线程的使用,包括其原理、常见方法以及如何通过实例进行实践。 首先,我们需要理解Android主线程,也被称为UI线程,它的主要任务是处理用户界面事件,如触摸事件、绘制视图等。如果在这个...
下面是一段大家都比较熟悉的代码: 代码如下:Handler ...上述代码中的handler并没有调用线程myThread的start()方法,而是直接调用了run()方法,这也就意味着实际上并没有创建一个新的线程,只是在当前线程中调用run()
Android提供了AsyncTask、Handler、Thread和Service等机制来实现多线程。书中将介绍如何使用这些工具来避免ANR(Application Not Responding)错误。 5. 数据缓存:为了提高用户体验,网络数据的本地缓存是必要的。...
### Android多线程机制详解 #### 一、引言 Android多线程机制是Android开发中非常重要的一部分,尤其是在处理耗时任务(如网络请求、大数据处理等)时,避免阻塞UI线程,保证应用程序的流畅性和响应性。本文将详细...
### Android开发中的多线程编程技术详解 #### 一、多线程编程的重要性与挑战 在Android开发过程中,多线程编程技术对于提高应用程序的性能和用户体验至关重要。...希望本文对您理解和应用Android多线程编程有所帮助。
4. **多线程与异步处理**:了解Handler、AsyncTask等机制,实现耗时操作不在UI线程中执行。 5. **权限管理**:熟悉Android权限模型,学会正确请求和使用权限。 ### Android应用性能优化 1. **内存优化**:避免内存...
在Android开发中,多线程消息处理机制是关键的一环,尤其当涉及到UI更新和后台任务同步时。本文将深入探讨一个特殊的、不常见的Handler用法,这些用法可能在日常开发中不太常见,但对理解和优化代码逻辑具有重要意义...
此外,异步处理和多线程是优化性能的关键。Android的Handler、Looper、AsyncTask以及现代的Coroutines和Flow可以帮助开发者解决UI线程阻塞问题。 最后,Android的调试工具如Logcat、HierarchyViewer、Profiler等能...
### Android多线程详解 #### 一、进程与线程概览 在深入探讨Android多线程之前,我们首先理解进程和线程的基本概念。**进程**是正在运行的程序实例,每个进程拥有自己独立的内存空间和系统资源。而**线程**则是...
Handler作为Android开发中的核心组件之一,主要用于处理线程间的通信,特别是将子线程中的任务切换到主线程中执行,以保证UI更新的正确性和安全性。下面是Handler消息机制的详细解析: 1. **Handler的作用和要素** ...
在Android开发中,多线程处理是至关重要的,特别是在处理耗时操作如网络请求、大文件读写或复杂的计算任务时。本篇文章将深入探讨Android的多线程机制,包括如何创建新线程以及如何在不同线程间进行通信。 首先,...
这篇详细的讲解将涵盖Android应用开发的基础概念、开发环境搭建、UI设计、数据存储、网络通信、多线程处理以及性能优化等多个方面。 首先,基础概念是学习Android开发的起点。Android是一个基于Linux内核的操作系统...
### Android多线程模型与Service深入解析 #### 线程与进程概念解析 在深入探讨Android中的多线程模型之前,我们先明确一下基本概念。在计算机科学中,**进程**(Process)与**线程**(Thread)是两个核心概念。进程是...
11. **多线程和Handler**:理解Android主线程与子线程的关系,学习使用Handler、Looper和Message实现UI线程与工作线程间的通信。 12. **Fragment**:Fragment的概念和用途,如何在Activity中添加、移除和替换...