zoukankan      html  css  js  c++  java
  • Handler 使用详解

    极力推荐文章:欢迎收藏
    Android 干货分享

    阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android

    本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

    1. Handler 消息处理机制原理
    2. Handler 机制处理的4个关键对象
    3. Handler常用方法
    4. 子线程更新UI 异常处理
    5. 主线程给子线程发送消息的方法
    6. 子线程给主线程发送消息的方法
    7. 主、子 线程 互发消息方法
    8. 子线程方法中调用主线程更新UI的方法

    HandlerAndroid中用来更新UI 的一套消息处理机制。Handler 允许线程间发送MessageRunnable对象进行通信。在Android中UI修改只能通过UI Thread,子线程不能更新UI。如果子线程想更新UI,需要通过 Handler 发送消息给主线程,进而达到更新UI的目的。

    Handler 简介

    继承关系如下:

    java.lang.Object
       ↳	android.os.Handler
    

    1. Handler 消息处理机制原理

    Android 应用程序创建的时候,系统会给每一个进程提供一个Looper Looper 是一个死循环,它内部维护一个消息队列,Looper 不停的从消息队列中取Message,取到的消息就发送给handler,最后Handler 根据接收的消息去修改UI等。

    2. Handler 机制处理的4个关键对象

    1.Message

    线程之间传递的消息,可以携带一些简单的数据供子线程与主线程进行交换数据。

    2.Message Queue

    存放通过Handler 发送的 Message 的消息队列,每一个线程只有一个消息队列。

    3.Handler

    消息处理者,主要用于发送跟处理消息。

    主要功能:
    发送消息SendMessage()
    处理消息 HandleMessage()

    4.Looper

    内部包含一个死循环的MessageQueue,用于存储handler 发送的MessageLooper则是不断的从消息队列中取消,如果有消息就取出发送给Handler 处理,没有则阻塞。

    总结:

    Handler 负责发送MessageMessage QueueLooper负责从Message Queue 遍历Message ,然后直接把遍历的消息回传给Handler 自己,通过Handler 自身的handleMessage处理更新UI等操作。

    主线程、子线程间通信简单流程

    3. Handler常用方法

    1.Runnable对象

    • post(Runnable)

    使用方法举例:

    	public void BtnRunnableMethod(View view) {
    		// 1.Runnable 对象
    		RunnableHandlderMethod();
    	}
    
    	/**
    	 * Runnable 对象更新 UI 
    	 * **/
    	private Handler mRunnableHandler = new Handler();
    	public void RunnableHandlderMethod() {
    		new Thread() {
    			@Override
    			public void run() {
    				try {
    					Thread.sleep(1000);
    
    					mRunnableHandler.post(new Runnable() {
    						@Override
    						public void run() {
    							((Button) findViewById(R.id.btn_runnable))
    									.setText("Runnable");
    						}
    					});
    
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    
    			}
    		}.start();
    	}
    
    
    • postAtTime(Runnable, long)
    • postDelayed(Runnable, long)

    2. Message 对象

    • sendEmptyMessage(int)

    使用方法举例:

    	public void BtnMessageThreadMethod(View view) {
    		// 2.Message 对象
    		new MessageHandlerThreadMethod("子线程不能更新UI").start();
    	}
    	/**
    	 * Message 对象举例
    	 * ***/
    	private int mCount = 0;
    	private Handler mMessageHandler = new Handler() {
    		@Override
    		public void handleMessage(Message msg) {
    			super.handleMessage(msg);
    
    			((Button) findViewById(R.id.btn_thread)).setText("" + mCount);
    		}
    	};
    
    	class MessageHandlerThreadMethod extends Thread {
    
    		String mString;
    
    		public MessageHandlerThreadMethod(String str) {
    			mString = str;
    		}
    
    		@Override
    		public void run() {
    			for (int i = 0; i < 10; i++) {
    				try {
    					Thread.sleep(1000);
    				} catch (Exception e) {
    
    				}
    
    				mCount++;
    				mMessageHandler.sendEmptyMessage(0);
    			}
    
    		}
    	}
    
    • sendMessage(Message)

    使用方法举例:

    public void BtnMessageObjMethod(View view) {
    		HandlerMessageObjMethods();
    	}
    
    	/***
    	 * handler sendmessage 处理方法
    	 * **/
    	private Handler mHandlerMessageObj = new Handler() {
    
    		@Override
    		public void handleMessage(Message msg) {
    
    			((Button) findViewById(R.id.btn_message)).setText("arg1:"
    					+ msg.arg1 + "
    " + msg.obj);
    		}
    	};
    
    	private void HandlerMessageObjMethods() {
    		new Thread() {
    			@Override
    			public void run() {
    
    				try {
    					Thread.sleep(1000);
    					// Message message = new Message();
    					Message message = mHandlerMessageObj.obtainMessage();
    
    					message.arg1 = 100;
    
    					Person person = new Person();
    					person.name = "Lucy";
    					person.age = 12;
    
    					message.obj = person;
    					mHandlerMessageObj.sendMessage(message);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}.start();
    	}
    
    	class Person {
    
    		public int age;
    		public String name;
    
    		public String toString() {
    			return "Name=" + name + "
     Age=" + age;
    		}
    
    	}
    
    • sendMessageAtTime(Message, long),
    • sendMessageDelayed(Message, long)

    3.接收、处理Message

    • handleMessage(Message)

    使用方法举例:

    	private Handler mMessageHandler = new Handler() {
    		@Override
    		public void handleMessage(Message msg) {
    			super.handleMessage(msg);
    
    			((Button) findViewById(R.id.btn_thread)).setText("" + mCount);
    		}
    	};
    

    4. 子线程更新UI 异常处理

    子线程不能更新UI,如果在子线程中更新UI,会出现CalledFromWrongThreadException 异常。

    • CalledFromWrongThreadException

    CalledFromWrongThreadException 子线程不能更新UI

    解决方法:

    子线程通过Handler 发送消息给主线程,让主线程处理消息,进而更新UI

    5. 主线程给子线程发送消息的方法

    此例子中子线程通过Looper不断遍历主线程发送的消息,Looper 使用方法如下:

      1. 准备Looper 轮询器

    Looper.prepare();

      1. Handler 处理遍历消息

    Handler mHandler = new Handler()

      1. 遍历消息队列

    Looper.loop();

    Looper 使用方法如下:

    	// 自定义 Loop 线程 ---> 不停的处理主线程发的消息
    	class ChildLooperThread extends Thread {
    		@Override
    		public void run() {
    			// 1.准备成为loop线程
    			Looper.prepare();
    			// 2.处理消息
    			mMainHandler = new Handler() {
    				// 处理消息
    				public void handleMessage(Message msg) {
    					super.handleMessage(msg);
    					        ... ...
    						}
    					});
    				}
    			};
    			// 3.Loop循环方法
    			Looper.loop();
    		}
    
    	}
    

    主线程发送消息给子线程 的使用例子如下:

    1. 启动 子线程,并再启动后发送消息
    
    	public void BtnMainMessageMethod(View view) {
    		// 点击主线程 按钮,启动子线程,并在子线程启动后发送消息
    		Message msg = new Message();
    		msg.obj = "主线程:这是我携带的信息";
    		if (mMainHandler != null) {
    			// 2.主线程发送消息
    			mMainHandler.sendMessage(msg);
    		} else {
    			Toast.makeText(getApplicationContext(), "开启子线程轮询消息,请再次点击发送消息",
    					Toast.LENGTH_SHORT).show();
    			// 1.开启轮询线程,不断等待接收主线成消息
    			new ChildLooperThread().start();
    		}
    	}
    
    1. 子线程启动,不停的变量主线程发送的消息
    	private Handler mMainHandler;
    	String mMainMessage;
    
    	// 自定义 Loop 线程 ---> 不停的处理主线程发的消息
    	class ChildLooperThread extends Thread {
    		@Override
    		public void run() {
    			// 1.准备成为loop线程
    			Looper.prepare();
    			// 2.处理消息
    			mMainHandler = new Handler() {
    				// 处理消息
    				public void handleMessage(Message msg) {
    					super.handleMessage(msg);
    					mMainMessage = (String) msg.obj;
    					Log.i("TAG", "子线程:从主线程中接受的消息为:
    " + mMainMessage);
    					// 使用 runOnUiThread 在主线程中更新UI
    					runOnUiThread(new Runnable() {
    						@Override
    						public void run() {
    							((Button) findViewById(R.id.btn_main_message))
    									.setText(mMainMessage);
    						}
    					});
    				}
    			};
    			// 3.Loop循环方法
    			Looper.loop();
    		}
    
    	}
    

    6. 子线程给主线程发送消息的方法

    1.子线程发送消息给主线程方法

    
    	public void BtnChildMessageMethod(View view) {
    
    		new Thread() {
    			public void run() {
    				while (mCount < 100) {
    					mCount++;
    					try {
    						Thread.sleep(100);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    					/**
    					 * 利用handler 对象发送消息 Message msg=Message.obtain(); Message
    					 * msg=new Message(); 获取一个消息对象message
    					 * */
    					Message msg = Message.obtain();
    					// 消息标记
    					msg.what = 1;
    					// 传递整型值msg.obj="传递object数据"
    					msg.arg1 = mCount;
    					Log.i("TAG", "count 值=" + mCount);
    					if (mhandler != null) {
    						mhandler.sendMessage(msg);
    					}
    				}
    			}
    		}.start();
    	}
    

    2.主线程接收并处理消息的方法

    // 定义一个handler 主线程 接收子线程发来的信息
    	private Handler mhandler = new Handler() {
    		// 處理消息的方法
    		public void handleMessage(android.os.Message msg) {
    
    			switch (msg.what) {
    			case 1:
    				int value = msg.arg1;
    				Log.i("TAG", "value值=" + value);
    				((Button) findViewById(R.id.btn_child_message)).setText("当前值="
    						+ value);
    				break;
    
    			default:
    				break;
    			}
    		}
    
    	};
    

    7. 主、子 线程 互发消息方法

    主要实现主、子线程每隔1s中通信一次

    • 实现打印Log如下:

    主、子线程通信log信息

    • 实现方法如下:
    1. 启动子线程并发送给主线程消息
    	public void BtnMainChildMessageMethod(View view) {
    
    		// 创建 名称为currentThread 子线程
    		HandlerThread mChildThread = new HandlerThread("ChildThread");
    		mChildThread.start();
    		mChildHandler = new Handler(mChildThread.getLooper()) {
    			@Override
    			public void handleMessage(Message msg) {
    
    				Log.i("TAG", "主线程对我说:" + msg.obj);
    
    				// 子线程携带的消息
    				Message message = new Message();
    				message.obj = Thread.currentThread() + "我是子线程,小样,让我听你的没门";
    				// 向主线程发送消息
    				mainhandler.sendMessageDelayed(message, 1000);
    			}
    		};
    		// 主线成发送空消息,开启通信
    		mainhandler.sendEmptyMessage(1);
    	}
    

    2.主线程接收并处理子线程发送的消息

    	// 创建主线程
    	private Handler mainhandler = new Handler() {
    		@Override
    		public void handleMessage(Message msg) {
    			Log.i("TAG", "子线程对我说:" + msg.obj);
    
    			// 主线成携带的消息内容
    			Message message = new Message();
    			message.obj = Thread.currentThread() + "我是主线程:小子你得听我的。";
    
    			// 向子线程发送消息
    			mChildHandler.sendMessageDelayed(message, 1000);
    		}
    	};
    
    

    8.子线程方法中调用主线程更新UI的方法

    Activity 中 可以使用 runOnUiThread(Runnable)

    					// 使用 runOnUiThread 在主线程中更新UI
    					runOnUiThread(new Runnable() {
    						@Override
    						public void run() {
    							((Button) findViewById(R.id.btn_main_message))
    									.setText(mMainMessage);
    						}
    					});
    

    子线程使用 Handler.post(Runnable)

    
    					mRunnableHandler.post(new Runnable() {
    						@Override
    						public void run() {
    							((Button) findViewById(R.id.btn_runnable))
    									.setText("Runnable");
    						}
    					});
    

    View.post()

    							((Button) findViewById(R.id.btn_runnable)).post(new Runnable() {
    								
    								@Override
    								public void run() {
    									// TODO Auto-generated method stub
    									((Button) findViewById(R.id.btn_runnable)).setText("View.post()方法使用");
    								}
    							});
    

    Handler.sendMessage(Message)

    	public void BtnMainMessageMethod(View view) {
    		// 点击主线程 按钮,启动子线程,并在子线程启动后发送消息
    		Message msg = new Message();
    		msg.obj = "主线程:这是我携带的信息";
    		if (mMainHandler != null) {
    			// 2.主线程发送消息
    			mMainHandler.sendMessage(msg);
    			}
    }
    

    9.移除Handler 发送的消息方法

    1.移除 handler 发送的所有消息

    private Handler mChildHandler;
    mChildHandler.removeCallbacksAndMessages(null);
    

    2.移除 指定消息

    private Handler mainhandler;
    mainhandler.removeMessages(what);
    

    至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

    微信关注公众号:  程序员Android,领福利

  • 相关阅读:
    Java Character类
    JavaMath&Number类
    oracle中创建数据库实例
    Jenkins安装和配置
    crontab计划 -计划任务和日志管理
    centos修改默认字符编码为GBK(服务器端java GBK字符乱码)
    使用vsphere client 克隆虚拟机
    建索引让SQL飞起来
    oracle中对LONG列进行查询
    优化sql,返回行数少情况下,NL比hash快好多
  • 原文地址:https://www.cnblogs.com/wangjie1990/p/11310639.html
Copyright © 2011-2022 走看看