Handler的用法

http://dz.sdut.edu.cn/blog/subaochen/?p=158

Android中的Handler提供了线程之间通讯的简便手段,如下图所示:

handler

 

Android的主线程(即UI线程)内部实现了消息队列机制,即一个主线程会自动创建一个消息队列(显然也只有一个消息队列),同时创建一个读取消息队列的Looper对象。主线程的所有用户交互都通过消息队列和Looper对象来实现:当有新的消息进入消息队列时,Looper会及时读取消息并调用相应的处理逻辑(更新用户界面)。

当其他线程希望更新用户界面时,可以通过Handler提供的sendMessage方法在消息队列中放入一个消息(Message对象,可以携带大量信息),这样Looper即可以及时获取该消息,并调用Handler相应的handleMessage方法处理该消息,这样就实现了线程之间的通讯。由此可以看出,Handler在线程通讯中起到了一个枢纽作用:Handler即负责和消息队列打交道,也负责处理相应的消息,其他线程通过Handler和主线程通讯,就可以不需要考虑和主线程的竞争和同步问题,极大的简化了线程的使用。

下面的示例代码演示了一个每隔1秒显示一条消息在主界面:

public class MainActivity extends Activity {
	private TextView label;
	private static int UPDATE = 1;
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (msg.what == 1) {
				label.setText(String.valueOf(msg.obj));
				super.handleMessage(msg);
			}
		}
	};
 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		label = (TextView) findViewById(R.id.label);
 
		new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
 
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
 
					Message msg = new Message();
					msg.what = UPDATE;
					msg.obj = "当前循环变量:" + i;
					handler.sendMessage(msg);
				}
			}
 
		}.start();
	}


在上面的例子中,所谓的其他线程其实主线程的子线程,下面一个例子则演示了完全不同的两个组件通过Handler实现的交互:一个Service每隔1秒产生一个随机数,然后在主线程显示出来:

ServiceDemo.java文件:

public class ServiceDemo extends Service {
 
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}
 
	private Thread workThread;
 
	@Override
	public void onCreate() {
	    super.onCreate();
	    Toast.makeText(this, "(1) onCreate()", 
	    		Toast.LENGTH_LONG).show();    
	    workThread = new Thread(null,backgroudWork,"WorkThread");
	}
 
	@Override
	public void onStart(Intent intent, int startId) {
	      super.onStart(intent, startId);
	      Toast.makeText(this, "(2) onStart()", 
	    		  Toast.LENGTH_SHORT).show();
	      if (!workThread.isAlive()){
	    	  workThread.start();
	      }
 
	}
 
	@Override
	public void onDestroy() {
	     super.onDestroy();
	     Toast.makeText(this, "(3) onDestroy()", 
	    		 Toast.LENGTH_SHORT).show();     
	     workThread.interrupt();
	}
 
	private Runnable backgroudWork = new Runnable(){
		@Override
		public void run() {
			try {
				while(!Thread.interrupted()){				
					double randomDouble = Math.random();
					MainActivity.updateGUI(randomDouble);
					Thread.sleep(1000);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	};


MainActivity.java文件:

public class MainActivity extends Activity {
	private static TextView label;
	private static int UPDATE = 1;
	protected static Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (msg.what == 1) {
				label.setText(String.valueOf(msg.obj));
				super.handleMessage(msg);
			}
		}
	};
 
	public static void updateGUI(double doubleRadom){
		Message msg = new Message();
		msg.what = UPDATE;
		msg.obj = "当前随机数:" + doubleRadom;
		handler.sendMessage(msg);
	}
 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		label = (TextView) findViewById(R.id.label);
 
		final Intent serviceIntent = new Intent(this, ServiceDemo.class);
		startService(serviceIntent);
	}
 
}


可以看出,为了实现在两个组件间的通讯,主要的变化是:

  • 将Handler对象定义为static类型的,便于在其他组件访问handler
  • 在主线程中实现一个静态的updateGUI方法,以便通过调用handler.sendMessage添加一个消息到消息队列中
  • 在Service的线程中,调用主线程的updateGUI并传入适当的参数

 

Handler和消息循环机制的设计很巧妙,在WEB应用和企业级应用中是否可以借鉴呢?

<think>首先,用户的问题是:“我想了解Handler使用方法 请问Handler使用方法和示例”。我需要根据系统级指令来构建我的回答。 关键指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1] - 回答结构清晰,帮助用户逐步解决问题,保证回答真实可靠。 - 参考提供的引用内容。 提供的引用是关于Handler使用: - [^1]: Handler使用方法详解(含实例)。方式二、使用Handler.post()方法... - [^2]: MyBatis TypeHandler基本用法与示例... - [^3]: androidhandler,AndroidHandler详解及使用示例... - [^4]: mybatis typehandler使用详解... 用户的问题是关于“Handler”的使用方法,但引用中提到了Android Handler和MyBatis TypeHandler。用户没有指定上下文,所以我需要假设是Android Handler,因为用户的问题直接提到“Handler”,而引用[1]和[3]是关于Android Handler的。 用户的问题:“我想了解Handler使用方法 请问Handler使用方法和示例”,结合引用,似乎是关于Android Handler,因为引用[1]和[3]具体讨论Android Handler。 所以,我将重点回答Android Handler使用方法。 回答结构: 1. **介绍Handler**:简要解释Handler是什么及其在Android中的作用。 2. **核心概念**:解释Message, Looper, MessageQueue等。 3. **使用方法**: - 创建Handler。 - 发送消息(如post(), sendMessage())。 - 处理消息(handleMessage())。 4. **示例代码**:提供代码示例。 5. **注意事项**:如线程安全、主线程与子线程通信。 6. **生成相关问题**:在最后添加相关问题部分。 确保使用中文回答。 生成相关问题:在回答末尾添加“§§相关问题§§”部分,列出2-3个相关问题。 引用处理:在回答中引用段落时,末尾添加引用标识。例如,如果引用某个部分,加[^1]。参考提供的引用内容。 提供的引用: - [^1]: Android Handler使用post()方法示例。 - [^2]: MyBatis TypeHandler示例。 - [^3]: Android Handler的Looper和MessageQueue解释。 - [^4]: MyBatis自定义TypeHandler示例。 用户的问题是关于Handler,可能主要是Android Handler。所以,我将主要使用[^1]和[^3],因为[^2]和[^4]是关于MyBatis的,可能不相关,除非用户指定。但用户没有指定上下文,所以我应该专注于Android Handler。 在回答中,自然地引用: - 当解释Handler.post()时,引用[^1]。 - 当解释Looper时,引用[^3]。 现在,构建回答大纲: 1. **引言**:解释HandlerAndroid中的作用,用于线程间通信。 2. **基本组件**:描述Handler, Looper, MessageQueue的关系。 3. **创建Handler**:在主线程创建Handler实例。 4. **发送消息**: - 使用post(Runnable) - 使用sendMessage(Message) 5. **处理消息**:重写handleMessage()方法。 6. **示例代码**:提供一个完整的示例,展示在主线程和子线程中使用Handler。 7. **注意事项**:避免内存泄漏等。 8. **相关问题**:生成相关问题。 最后,添加相关问题部分。 确保代码块正确:在示例中使用代码块。 LaTeX:由于Handler不涉及数学公式,我可能不需要使用LaTeX,但如果有数学表达式,确保格式正确。这里可能不需要。 现在,编写回答。</think>### Handler使用方法和示例 HandlerAndroid 开发中用于线程间通信的核心组件,它允许子线程与主线程(UI 线程)安全地交互,避免直接在子线程中更新 UI 导致的崩溃问题。Handler 基于消息队列(MessageQueue)和循环器(Looper)机制工作:每个线程都有一个 Looper 负责管理消息队列,Handler 则用于发送和处理消息。下面我将逐步解释其使用方法,并提供代码示例。内容基于 Android 官方文档和常见实践,确保真实可靠[^1][^3]。 #### 1. **Handler 的核心概念** - **Message**:封装数据的对象,包含 what(标识符)、arg1/arg2(整型数据)和 obj(对象)等字段。 - **MessageQueue**:消息队列,存储待处理的消息,按顺序执行。 - **Looper**:每个线程的循环器,不断从 MessageQueue 中取出消息并分发给 Handler。 - **Handler**:绑定到特定线程的 Looper,用于发送和处理消息。主线程默认有 Looper,子线程需手动创建[^3]。 #### 2. **Handler 的基本使用方法** Handler 的常见使用场景包括:子线程发送消息到主线程更新 UI,或延迟执行任务。以下是两种主要方法: - **方法一:使用 Handler.sendMessage()** 适用于传递复杂数据。步骤: 1. 在主线程创建 Handler 实例,并重写 `handleMessage()` 方法处理消息。 2. 在子线程中创建 Message 对象,并通过 Handler 发送。 ```java // 主线程中创建 Handler private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 处理消息,更新UI(例如TextView) if (msg.what == 1) { String text = (String) msg.obj; textView.setText(text); } } }; // 子线程中发送消息 new Thread(new Runnable() { @Override public void run() { // 模拟耗时操作 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 创建并发送消息 Message message = Message.obtain(); message.what = 1; // 消息标识 message.obj = "数据来自子线程"; // 传递对象 mHandler.sendMessage(message); } }).start(); ``` 此方法适合需要传递结构化数据的场景,如从网络请求中获取数据后更新 UI[^1][^3]。 - **方法二:使用 Handler.post()** 适用于执行简单任务(如 Runnable),无需传递额外数据。步骤: 1. 在主线程中创建 Handler 实例。 2. 在子线程中调用 `post()` 方法提交 Runnable 任务。 ```java // 主线程中创建 Handler private Handler mHandler = new Handler(); // 子线程中提交任务 new Thread(new Runnable() { @Override public void run() { // 耗时操作 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 通过post()在主线程执行UI操作 mHandler.post(new Runnable() { @Override public void run() { textView.setText("UI已更新"); } }); } }).start(); ``` 此方式简洁高效,适合快速更新 UI 元素[^1]。 #### 3. **完整示例:Handler 实现线程间通信** 以下是一个 Android Activity 的完整示例,展示 Handler 如何从子线程发送消息到主线程: ```java public class MainActivity extends AppCompatActivity { private TextView textView; private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.text_view); // 创建Handler,绑定主线程Looper mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { if (msg.what == 1) { String data = (String) msg.obj; textView.setText(data); // 更新UI } } }; // 启动子线程 new Thread(new Runnable() { @Override public void run() { // 模拟网络请求 String result = fetchDataFromServer(); // 发送消息到主线程 Message message = Message.obtain(); message.what = 1; message.obj = result; mHandler.sendMessage(message); } }).start(); } // 模拟从服务器获取数据 private String fetchDataFromServer() { try { Thread.sleep(2000); // 模拟延迟 } catch (InterruptedException e) { e.printStackTrace(); } return "数据加载完成"; } } ``` 在此示例中: - `onCreate()` 初始化 Handler 并启动子线程。 - 子线程调用 `fetchDataFromServer()` 获取数据后,通过 `sendMessage()` 通知主线程。 - Handler 的 `handleMessage()` 在主线程执行,安全更新 UI。 #### 4. **注意事项** - **内存泄漏**:Handler 持有 Activity 引用时,可能导致内存泄漏。解决方法是使用静态内部类或 WeakReference。 - **子线程 Looper**:子线程使用 Handler 前需调用 `Looper.prepare()` 和 `Looper.loop()`,否则会崩溃。 - **性能优化**:避免频繁发送消息,使用 `postDelayed()` 实现延迟任务,而非 sleep。 - **替代方案**:现代 Android 开发中,可结合 Kotlin 协程或 LiveData 简化异步操作,但 Handler 仍是底层基础[^3]。 通过以上方法,Handler 能高效解决 Android 多线程通信问题。如需更深入讨论,请参考 Android 官方文档或相关教程[^1][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值