Handler

handler是什么?

   handler是android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息。在android的framework中Activity的生命周期中的处理函数都是系统通过handler消息处理回调的。

为什么要用handler?

    android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制就没有办法更新UI信息,就会抛出异常。android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

handler怎么用呢?

 1、利用handler.post(Runnable)方法实现在异步线程更新UI,本身这个函数是在主线程被调用

 2、 //利用handler实现定时刷新

 3、//利用Callback类实现对发来的信息的拦截

  4、//利用handler的sendMessage(Message)来从子线程发送消息给主线程

  5、//移除handler中的Runnable

demo代码:

package com.example.handler;

import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.os.Message;

public class MainActivity extends Activity implements OnClickListener{

	private TextView textView;
	private ImageView imageView;
	private Button button;
	
	private int[] image = 
		{R.drawable.img1, R.drawable.img2, R.drawable.img3};
	private int index=0;
	
	//利用handler实现定时刷新
	Handler handler = new Handler();
	MyRunnable myRunnable = new MyRunnable();
	class MyRunnable implements Runnable {

		@Override
		public void run() {  
			// TODO Auto-generated method stub
			imageView.setImageResource(image[index]);
			index++;
			index %= image.length;
			//实现定时刷新(每隔2秒刷新一次)
			handler.postDelayed(myRunnable, 2000);
		}
		
	}
	//利用Handler处理子线程发来的消息来更新UI
	Handler handler1 = new Handler() {
		
		@Override
		public void handleMessage(Message msg) {
			textView.setText("" + msg.arg1 + "-" + msg.arg2 + " " +
		              msg.obj.toString());
		}
	};
	
	class Person {
		public String name;
	    public int age;
	    @Override
	    public String toString() {
	    	// TODO Auto-generated method stub
	    	return "name=" + name + " " + "age=" + age;
	    }
	}
	
	//利用Callback类实现对发来的信息的拦截
	private Handler handler2 = new Handler(new Callback() {

		@Override
		public boolean handleMessage(Message arg0) {
			// TODO Auto-generated method stub
			Toast.makeText(getApplicationContext(), "信息拦截", 3000).show();
			//当返回值为false时不对信息进行拦截,为true时拦截Message信息,
			//handler的handleMessage就接受不到信息了
			return true;
		}
		
	}){
		//@Override
		public void handleMessage(Message msg) {
			Toast.makeText(getApplicationContext(), "信息未被拦截", 3000).show();
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		textView = (TextView) findViewById(R.id.textView1);
		imageView = (ImageView) findViewById(R.id.imageView1);
		button = (Button) findViewById(R.id.button1);
		button.setOnClickListener(this);
//      利用	handler.post(Runnable)方法实现在异步线程更新UI,本身这个函数是在主线程被调用
//		new Thread() {
//			public void run() {
//				try {
//					Thread.sleep(1000);
//					handler.post(new Runnable() {
//						public void run() {
//							textView.setText("你好。。。");
//						}
//					});
//					
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//				
//			}
//		}.start();
//      利用handler的postDelayed(Runnable,time)实现每个time时间对图片的刷新
		new Thread() {
			public void run() {
				//实现定时刷新(每隔2秒刷新一次)
				handler.postDelayed(myRunnable, 2000);
			}
		}.start();
		
		//利用handler的sendMessage(Message)来从子线程发送消息给主线程
		new Thread() {
			public void run() {
				
				Message msg = new Message();
				//msg 对象也可以利用以下语句创建
				//Message msg = handler1.obtainMessage();
				//利用msg.sndToTarget();发送该消息给对应的handler
				msg.arg1 = 8;
				msg.arg2 = 10;
				Person person = new Person();
				person.name = "tom";
				person.age = 19;
				msg.obj = person;
				handler1.sendMessage(msg);
			}
		}.start();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public void onClick(View arg0) {
		// TODO Auto-generated method stub
		//移除handler中的Runnable(本利用停止图片定时刷新)
		//handler.removeCallbacks(myRunnable);
		
		//发送一个空消息
		handler2.sendEmptyMessage(1);
	}

}




android为什么要设计只能通过Handler机制更新UI?

    最根本的目的就是解决对线程并发问题,

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

     更新界面错乱

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

      性能下降

       出于对以上目的问题的考虑,android给我们提供了一套更新UI的机制(Handler处理机制),我们只需要知道遵循这样的机制就可以了。

       根本不用去关心多线程问题,所以更新UI的操作,都是在主线程的消息队列当中去轮询处理的。

在线程中使用Handler

demo代码

package com.example.handler;

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

public class SecondActivity extends Activity {
	
	private Handler handler1  = new Handler() {
		public void handleMessage(Message msg) {
			System.out.print("UI----->" + Thread.currentThread());
		}
	};
	//实现一个与线程相关的Handler
	class MyThread extends Thread {
		public Handler handler;
		public Looper looper;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			Looper.prepare();//创建Looper对象
			looper = Looper.myLooper();
			handler = new Handler() {

			   public void handleMessage(Message msg) {
				   System.out.println("currentThread" + Thread.currentThread());
			   }
			};
			//死循环,不断取出消息队列中的消息,并交给Handler处理
			Looper.loop();
		}
	}
	
	private MyThread thread;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		TextView textView = new TextView(this);
		textView.setText("你好。。");
		setContentView(textView);
		thread = new MyThread();
		thread.start();
//		try {
//			Thread.sleep(500);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		//thread.handler.sendEmptyMessage(1);
		//handler1.sendEmptyMessage(1);
		//把handler和相应的线程绑定(这样会因为线程同步问题导致空指针异常)
		//可以通过HandlerThread来实现外部Handler和线程的绑定(详见ThreadActivity)
		Handler handler = new Handler(thread.looper) {
			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
			    System.out.println("Thread---->" + Thread.currentThread());
			}
		};
		handler.sendEmptyMessage(1);
		
	}

	
}
利用HandlerThread实现Handler通过Looper实现和子线程的关联

demo代码:

package com.example.handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.widget.Button;
import android.widget.TextView;
public class ThreeActivity extends Activity {

	/**
	 * 利用HandlerThread实现Handler通过Looper实现
	 * 和子线程的关联
	 */
	private HandlerThread thread;
	private Handler handler;
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		TextView textView = new TextView(this);
		textView.setText("你好。。");
		setContentView(textView);
		thread = new HandlerThread("handler thread");
		thread.start();
		handler = new Handler(thread.getLooper()) {
			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				System.out.println("Thread--->" + Thread.currentThread());
			}
		};
		handler.sendEmptyMessage(1);
	}
}

子线程和主线程互相发送消息

demo代码

package com.example.handler;

import android.app.Activity;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class CommThreadActivity extends Activity implements OnClickListener {

	private Button button1;
	private Button button2;
	//定义一个子线程的Handler
	private Handler threadHandler;
	//创建主线程的Handler
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			System.out.println(msg.arg1);
			Message message = new Message();
			message.arg1 = 2;
			//向子线程发送消息
			threadHandler.sendMessageDelayed(message, 1000);
			
		}
	};
	@Override
	protected void onCreate(android.os.Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	    setContentView(R.layout.main);
		button1 = (Button) findViewById(R.id.button1);
		button2 = (Button) findViewById(R.id.button2);
		button1.setOnClickListener(this);
		button2.setOnClickListener(this);
		//利用HandlerThread创建子线程
		HandlerThread thread = new HandlerThread("thread handler");
		thread.start();
		//子线程的Handler
		threadHandler = new Handler(thread.getLooper()) {
			@Override
			public void handleMessage(Message msg) {
				System.out.println(msg.arg1);
				Message message = new Message();
				message.arg1 = 1;
				//向主线程发送消息
				handler.sendMessageDelayed(message, 1000);
			};
		};
	}
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch(v.getId()) {
		case R.id.button1:
			//向主线程发送一个消息
			handler.sendEmptyMessage(1);
			break;
		case R.id.button2:
			//移除一个消息
			handler.removeMessages(1);
			break;
		}
	}
}

子线程中更新UI的几种方式

1、利用Handler的post方法

2、利用Handler的sendMessage方法

3、利用Activity的runOnUiThread方法

4、利用View的post方法

demo 代码:

package com.example.handler;

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

public class UpdateUIActivity extends Activity {

	private TextView textView;
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			textView.setText("你好。。");
		};
	};
	
	//利用handler的post()函数更新UI
	public void update1() {
		handler.post(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				textView.setText("你好。。");
			}
			
		});
	}
	//利用handler的sengMessage更新UI
	public void update2() {
	
		handler.sendEmptyMessage(1);
	}
	
	//Activity自己提供的在子线程中更新UI
	public void update3() {
		runOnUiThread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				textView.setText("你好。。");
			}
			
		});
	}
	//利用View提供的子线程更新UI的方法
	public void update4() {
		textView.post(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				textView.setText("你好。。");
			}
		});
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.updateui);
		textView = (TextView) findViewById(R.id.textView1);
		new Thread() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//更新UI
				update1();
			}
		}.start();
		
	};
}

子线程中也可以更新UI,不过得在 ViewRootImpl对象初始化完成之后。ViewRootImpl对象初始化是在Activity的onResume方法中,当一个线程在onCreate方法中创建后马上更新UI则系统不会报出错误,若ViewRootImpl对象已经被初始化了在去更新UI则会抛出android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.异常。


Handler常出现的异常有

1、android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.ViewRootImp对象初始化后在子线程中更新UI。

2、在子线程中handler传入Looper对象时(线程同步导致的)

你可能感兴趣的:(Handler)