作者微信 bishe2022

代码功能演示视频在页面下方,请先观看;如需定制开发,联系页面右侧客服
Android 之 Handler 机制(用法、错误、原理等等)

Custom Tab

Handler

跟应用程序交互的,从网络上取得图片,怎么更新我们UI上面呢?

这里就用到了Handler机制,如何不用直接从子线程给予一个图片更新到UI当中,会得到calss异常,不能在非Ui线程中直接更新UI。

标题内容:


handler是什么
handler怎么用呢
为什么要用handler
handler的原理是什么
如何实现一个线程相关的Handler
HandlerThread又是什么呢
如何在主线程给子线程发送消息呢
android 中更新UI的几种方式 
非UI线程真的不能更新UI吗
使用handler时候遇到的问题
android为什么要设计只能用过Handler机制更新UI呢



1、handler是什么

handler是android给我们提供用来更新UI的一套机制,也是一套消息处理机制,我们可以发送消息,也可以通过它处理消息

所有的activity生命周期的回调方法,都是通过Handler机制去发送消息。

fragment 的 activity的生命周期,大多数都是用AMS做处理。




2、为什么要用handler  ,  不用行不行?

是不行的。
Android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不循环这样的机制的就没有办法更新UI信息的,就会抛出异常信息(不能在一个非UI线程当中,去直接更新UI)。

3、handler怎么用呢?

来看看官网文档:国内文档(无翻墙)   国外文档(已经翻墙)


在Handler当中有两个用途:

(1)定时的去发送一个Messges 或 runnables对象

(2)可以在线程当中处理并执行一个动作



举例:

sendMessags

MainActivity----这里我举例了,一个两秒后更新内容,利用常常我们需要JSON解析,返回来的东西,更新UI

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

public class MainActivity extends Activity {


private TextView textView;

private Handler handler = new Handler(){
	public void handleMessage(android.os.Message msg){
		//从子线程中更新一个文本信息
		textView.setText(""+msg.obj);
	};
};
//常常Json解析当中拿的数据比较多,是同过这样的方法更新的

class Person{
	public  int age;
	public  String name;
	public String toString(){
		return "age"+age+"name"+name;
	}
}

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        textView  = (TextView) findViewById(R.id.textview);
        
        new Thread(){
        	public void run(){
        		try {
					Thread.sleep(2000);
					//发送Message
					Message message = handler.obtainMessage();

					
					Person pSource = new Person();
					pSource.age = 23;
					pSource.name = "yiyiyiyi";
					message.obj = pSource;
					//将数据发送出去方法一:
				//	handler.sendMessage(message);
					//方法二:
					message.sendToTarget();
					
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
        	};
        }.start();

    }
}




sendMessagsDelayed
post(Runnable)


MainActivity ------这里我举了,利用Handler中的post(Runnable)

package com.example.handler_01;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

private TextView textView;//activity_main---布局只要定义一个id

private Handler handler = new Handler();
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //找到ID
        textView = (TextView) findViewById(R.id.textview);
       //线程 
        new Thread(){
        	public void run(){
        		try {
        			//延迟两秒后更新
					Thread.sleep(2000);
					//使用Handler中的post (Runnable)方法
					handler.post(new Runnable() {
						public void run() {
							textView.setText("update thread");
						}
					});
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
        	}    	
        }.start();
    }  
}

postDelayed(Runnable,long)

MainActivity-----为了体现出这个,这里是,每2秒从线程中更换图片

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.ImageView;

public class MainActivity extends Activity {


private ImageView imageView;//定义一个图片id而已

private Handler handler = new Handler();
//建一个图片数组
private int images[] = {R.drawable.image1,R.drawable.image2,R.drawable.image3};
//创建一个数,为了了解图片在当前位置
private int index;
//定义runnable
private MyRunnable myRunnable = new MyRunnable();

class MyRunnable implements Runnable{

	@Override
	public void run() {
		index++;
		index = index%3;//让它在3张图片中不断循环
		imageView.setImageResource(images[index]);//把当前图片应用到图片当中
		//第一要求传入Runnable对象-----第二个是,时间。多久执行一次
		handler.postDelayed(myRunnable, 1500);
		
	}
	
}


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        imageView = (ImageView) findViewById(R.id.imageView1);
        //需要在主线程当中调用
        handler.postDelayed(myRunnable, 1500);
      
    }
}


4、android为什么要设计只能用过Handler机制更新UI呢

最根本的目的就是解决多线程并发问题。

   假如如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样子的问题?

更新界面错乱

   如果对更新UI的操作都进行加锁处理的话又会产生什么样子的问题?

性能下降
处于对以上目的问题的考虑,android 给我们提供了一套更新UI的Handler机制,我们只需遵循这样Handler的机制就可以了。
根本不用去关心多线程问题,所以的更新UI的操作,都是在主线程的消息队列当中去轮询处理的


----所以官方给我们提供这个是有道理的----


   

5、handler的原理是什么

一、Handler封装了消息的发送,(主要包括消息发送给谁)

Looper---消息封装的载体

1.内部包含一个消息队列就是MessageQueue,所有的Handler发送的消息都走这个消息队列。

2.Looper.Looper方法,就是一个死循环,不断的从MessageQueue取消息,如有有消息就

处理消息,没有消息就阻塞

二、MessageQueue,就是一个消息队列,可以添加消息,并处理消息

三、Handler也很简单,内部会跟Looper进行关联,也就是说在Handler内部可以找到Looper,找到了Looper也就是找到了,MessageQueue,在Handler 中发送消息,其实就是向

MessageQueue队列中发送消息


总结:handler负责发送消息,Looper负责接收Handler 发送的消息,并直接把消息回传给Handler自己,

MessageQueue就是一个存储消息的容器。




6、如何实现一个线程相关的Handler

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.widget.TextView;

public class MainActivity extends Activity {

	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {

			
			System.out.println("Ui-------"+Thread.currentThread());
		};
	};

	class MyThread extends Thread {
		public Handler handler;

		public void run() {
			Looper.prepare();
			handler = new Handler(){
				public void handleMessage(android.os.Message msg){
					System.out.println("currentTherad:"+Thread.currentThread());
				};
			};
			Looper.loop();//循环
		}
	}
	
	private MyThread thread;

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		TextView textView = new TextView(this);
		textView.setText("hello handler");
		setContentView(textView);
		
		thread = new MyThread();
		thread.start();
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		thread.handler.sendEmptyMessage(1);
		handler.sendEmptyMessage(1);
	}
}


7、HandlerThread又是什么呢

这里我是参考慕课网的:点击打开链接(下载的)




8、如何在主线程给子线程发送消息呢--主线程与子线程之间的信息交互




9、android 中更新UI的几种方式

常见的大概有四种:runOnUiThread、handler post、handler sendMessage、view post

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;

public class FiveActivity extends Activity {
	// 只是在布局布置一个 TextView
	private TextView textView;
	// 通过Handler来更新一下UI
	Handler handler = new Handler() {
		// 实现一下Handler里面的handleMessage方法
		public void handleMessage(android.os.Message msg) {
			// 第二个方法是从外边拿到数据然后返回到里面
			textView.setText("第二种方法handlerEmptyMessags");
		}
	};

	/**
	 * 第一方法 Handler post
	 */
	public void handler1() {
		handler.post(new Runnable() {

			@Override
			public void run() {
				// 这里弄个文本消息
				textView.setText("第一种方法Handler post");

			}
		});
	}

	/**
	 * 第二种方法 sendEmptyMessage
	 */
	public void handler2() {

		handler.sendEmptyMessage(1);

	}

	/**
	 * 第三种方法 runOnUiThread
	 */
	public void handler3() {
		runOnUiThread(new Runnable() {

			@Override
			public void run() {

				textView.setText("第三个方方法runOnUiThread");
			}
		});
	}

	/**
	 * 第四种方法view
	 * post----它会判断当前是不是UI线程,默认情况下,会通过Handler.post发送一个action。如果是UI线程的话执行run()方法
	 */
	public void handler4() {
		textView.post(new Runnable() {

			@Override
			public void run() {

				textView.setText("第四种方法 view post");

			}
		});
	}

	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		// 将布局,引用到这个类---因为为了方便我弄多了一个类
		setContentView(R.layout.five);
		// 找到ID
		textView = (TextView) findViewById(R.id.textview);
		// 更新UI 创建一个线程,后台处理。
		new Thread() {
			// 实现线程Thread 中的run方法
			public void run() {

				try {
					//休息两秒后再执行,因为本地网络速递较快,为了看效果。如果网络的话就去掉
					Thread.sleep(2000);

				//	handler1();
				//	handler2();
				//	handler3();
					handler4();

				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			};
		}.start();
	}

}

总结:其实这常见的四种方式来更新UI都差不多,都是同Handler来更新,只是代码上的本质而已。

10、非UI线程真的不能更新UI吗

可以的。但是不推荐这样,有些时候会进行报错

例子:举例子,是上面的9、的方法

new Thread() {
			// 实现线程Thread 中的run方法
			public void run() {

//				try {
//					//休息两秒后再执行,因为本地网络速递较快,为了看效果。如果网络的话就去掉
//					Thread.sleep(2000);
//					
					textView.setText("第几种方法");//这个时候是成功的
//如果将代码休眠2秒--注释去掉---,将报错,因为系统会认为失败,直接返回错误,不能在UI线程中更新UI

//				//	handler1();
//				//	handler2();
//				//	handler3();
//				//	handler4();
//
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}

			};
		}.start();

11、使用handler时候遇到的问题


android.view.ViewRootImpl$CalledFromWrongThreadExcetion:Only the original thread that created a view hierarchy can touch its views.

----不能在子  线程中更新Ui----()

不懂请分析源码:



Can't create handler inside thread that has not called Looper.prepare()

----原因:我们在子线程创建一个Handler当中,在程序运行的时候called 了。----

当创建Handler handler = new Handler()时候没有关联handler(也就是创建不使用),就会出现。


不懂的话看看源码,因为从源码当中了解到,从Looper.myLooper对象中拿到对象,当拿不到的时候就会出现这样的问题。


总结:所以当你创建一个Handler handler = new Handler();时,handler需要用到,不然就会出错,没有拿到Looper对象,就是等于空null的。不指定,就不给你使用Handler 机制。






转载自:http://blog.csdn.net/a873282620/article/details/53308593

Home