`
huihui920823
  • 浏览: 36496 次
  • 性别: Icon_minigender_1
  • 来自: 济南
文章分类
社区版块
存档分类
最新评论

Android中handler的使用及原理---学习笔记

 
阅读更多

Handler类的基本介绍以及使用:

Android中UI操作是线程不安全的操作,如果有多个线程并发操作UI组件,就会出现线程安全问题,所以Android中制定了一个规则:在Android中只允许主线程(UI线程)修改Activity中的UI组件

但是现在问题又来了,在开发中我们会需要在子线程中更新UI组件的情况,那怎么进行处理呢?其实Handler就是为了解决这种问题而生的。

Handler类的主要作用有两个:

1.在新启动的线程中发送消息

2.在主线程中获取,处理消息

Handler类中用于发送、处理消息的方法:

1. void handleMessage(Message msg):处理消息的方法。该方法通常用于被重写

2. boolean hasMessages(int what):检查消息队列中是否包含what属性为指定值得消息

3. boolean hasMeesages(int what,Object obj):检查消息队列中时候包含what属性为指定值且object属性为指定对象的消息

4. 多个重载的Message obtainMessage():获取消息

5. sendEmptyMessage(int what):发送空消息

6. boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒之后发送空消息

7. boolean sendMessage(Message msg):立即发送消息

8. boolean sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒之后发送消息

下面是一个使用Handler类更新UI组件的一个使用小例子:

package com.my.Mytimetest;

import java.util.Timer;
import java.util.TimerTask;

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

public class MainActivity extends Activity {
	private ImageView imageview;
	
	private int[] images = {R.drawable.image1,R.drawable.image2,R.drawable.image3,R.drawable.image4};
	
	private int index;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageview = (ImageView) findViewById(R.id.id_images);
        
        final Handler mHandler = new Handler(){
        	@Override
        	public void handleMessage(Message msg) {
        		if(msg.arg1==123){
        			index++;
        			imageview.setImageResource(images[index%4]);
        		}
        	}
        };
        
        //定义一个计时器,设置为延迟0ms后执行,每隔1s执行一次(这里如果设置为 timer.schedule(task,1000)表示执行一次)
        new Timer().schedule(new TimerTask(){
        	@Override
        	public void run() {
        		Message message = new Message();
        		message.arg1 = 123;
        		mHandler.sendMessage(message);
        	}
        }, 0, 1000);
        
        
    }
}



效果如图:

上边的案例,就是简单的用到了Handler类来实现一个图片轮播的效果。



Handler的实现原理:

与Handler一起工作的几个组件:

1.Message:Handler接收和处理的消息对象

2.Looper:每个线程只能拥有一个Looper.它的loop方法负责读取MessageQueue中的消息,

读到信息之后就把消息交给发送该消息的Handler进行处理。

3.MessageQueue:消息队列,它采用先进先出的方式来管理Message.

程序创建Looper对象时会在他的构造器中创建Looper对象。

Looper提供的构造器源代码如下:

private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
从源代码中可以看出:

(1). 构造方法是私有的,所以不允许我们自己创建Looper对象

(2). 程序在初始化Looper时会创建一个与之关联的MessageQueue,这个MessageQueue就负责管理消息。

4.Handler:

简而言之,Handler的作用:Handler的构造方法,其实就是在Handler中持有一个指向该Looper.mQueue对象,

当handler调用sendMessage方法时,其实就是往该mQueue中去插入一个message,然后Looper.loop()就会取出执行。


作用有两个-----发送消息和处理消息,程序中使用Handler发送消息,

被Handler发送的消息必须被送到指定的MessageQueue。

也就是说,如果希望Handler正常工作,必须在当前的线程中有一个MessageQueue,

否则消息就没有MessageQueue进行保存了。不过MessageQueue是由Looper负责管理的,

也就是说,如果希望Handler正常工作,必须在当前的线程中有一个Looper对象,

为了当前线程中有Looper对象,可以分为两种情况处理:

(1).主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,

然后就可以通过Handler来发送消息,处理消息了。

(2).在自己启动的子线程中,必须自己创建一个Looper对象,并启动它。

------创建Looper对象调用它的prepare()方法即可。

Looper类中prepare()方法的源码:

public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
prepare()方法保证每个线程最多只有一个Looper对象。

------然后调用Looper的静态loop()方法来启动它。loop()方法使用一个死循环不断的取出MessageQueue中的消息,

并将取出的消息分给该消息对应的Handler进行处理。

Looper类中loop()方法的源码:

 public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        while (true) {
            Message msg = queue.next(); // might block
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                msg.recycle();
            }
        }
    }


总结一下:

Looper,MessageQueue,Handler各自的作用如下:


Looper:每个线程只用一个Looper,负责管理MessageQueue,会不断的从MessageQueue中取出消息,

并将消息分给对应的Handler处理。

MessageQueue:由Looper负责管理,它采用先进先出的方式管理Message。

Handler: 它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。


简而言之:

Handler负责发送消息,

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

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


在线程中使用Handler的步骤如下:

1. 调用Looper的prepare()方法为当前的线程创建Looper对象,

创建Looper对象时,Looper的构造方法中会创建与之配套的MessageQueue.

2. 有了Looper对象之后,创建Handler子类的实例,重写handleMessage()方法,

该方法负责处理来自于其他线程的消息。

3. 调用Looper的loop()方法启动Looper。


下面对比一下在主线程中和在子线程中使用Handler的区别:

主线程中使用Handler计算一定范围之内的质数的例子:

package com.my.Mytimetest;

import java.util.ArrayList;
import java.util.List;

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

public class SecondActivity extends Activity implements OnClickListener {
	private EditText edt;
	private TextView textview;
	private Button stop_btn;
	private Handler mHandler;
	private List<Integer> nums = new ArrayList<Integer>();

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

		initView();
		setListener();

		mHandler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				if (msg.what == 123) {
					int num = msg.getData().getInt("num");
					//计算从2开始,到num的所有质数
					outer: for (int i = 2; i < num; i++) {
						//从i除以2开始,到i的平方根的所有数
						for (int j = 2; j < Math.sqrt(i); j++) {
							//如果可以整除,则表明这个数不是质数
							if (i != 2 && i % j == 0) {
								continue outer;
							}
						}
						nums.add(i);

					}
					
					textview.setText(nums.toString());
					
					nums.removeAll(nums);
				}
			}
		};

	}

	private void setListener() {
		stop_btn.setOnClickListener(this);

	}

	private void initView() {
		edt = (EditText) findViewById(R.id.id_edt);
		textview = (TextView) findViewById(R.id.id_textview);
		stop_btn = (Button) findViewById(R.id.id_start);
	}

	@Override
	public void onClick(View v) {
		int num = Integer.parseInt(edt.getText().toString());
		Message message = new Message();
		message.what = 123;
		Bundle bundle = new Bundle();
		bundle.putInt("num", num);
		message.setData(bundle);
		// 将消息发送给子线程中的Handler来处理
		mHandler.sendMessage(message);

	}

}


子线程中使用Handler计算一定范围之内的质数的例子:

package com.my.Mytimetest;


import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {
	private EditText edt;
	private Button stop_btn;
	private MyThread myThread;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		initView();
		setListener();
		myThread = new MyThread();
		//开启子线程
		myThread.start();
		
		
		
	}
	private void setListener() {
		stop_btn.setOnClickListener(this);
		
	}
	private void initView() {
		edt = (EditText) findViewById(R.id.id_edt);
		stop_btn = (Button) findViewById(R.id.id_start);
	}
	@Override
	public void onClick(View v) {
		int num = Integer.parseInt(edt.getText().toString());
		Message message = new Message();
		message.what = 123;
		Bundle bundle = new Bundle();
		bundle.putInt("num", num);
		message.setData(bundle);
		//将消息发送给子线程中的Handler来处理
		myThread.mHandler.sendMessage(message);
		
	}
	
	class MyThread extends Thread{
		
		public Handler mHandler;
		private List<Integer> nums = new ArrayList<Integer>();
		@Override
		public void run() {
			Looper.prepare();
			mHandler = new Handler(){
				@Override
				public void handleMessage(Message msg) {
					if(msg.what==123){
						int num = msg.getData().getInt("num");
						outer:
							for (int i = 2; i < num; i++) {
								for (int j = 2; j < Math.sqrt(i); j++) {
									if(i!=2&&i%j==0){
										continue outer;
									}
								}
								nums.add(i);
								
							}
						Toast.makeText(MainActivity.this, nums.toString(), Toast.LENGTH_LONG).show();
						nums.removeAll(nums);
					}
				}

				
			};
			Looper.loop();
		}
		
			
		}
		
	}
	
	

效果图:


HandlerThread类的使用:

介绍:

HandlerThread继承自Thread,当线程开启时,也就是它run方法运行起来后,

线程同时创建了一个含有消息队列的Looper,并对外提供自己这个Looper对象的get方法,这就是它和普通Thread唯一不同的地方。

好处:

为什么要使用HandlerThread。

1.开发中如果多次使用类似new Thread(){...}.start()

这种方式开启一个子线程,会创建多个匿名线程,使得程序运行起来越来越慢,

而HandlerThread自带Looper使他可以通过消息来多次重复使用当前线程,节省开支;

2.android系统提供的Handler类内部的Looper默认绑定的是UI线程的消息队列,

对于非UI线程又想使用消息机制,那么HandlerThread内部的Looper是最合适的,它不会干扰或阻塞UI线程。


用法:

HandlerThread既然本质是Thread,为何前面加了一个Handler?

android中Handler类本质上就是从它内部的Looper中不断取消息,

然后触发它内部的Callback接口的handleMessage方法,让用户去实现对消息的具体处理。

而HandlerThread本身自带Looper,只要它实现了Callback接口,

那么HandlerThread也可以在自己线程内处理自己线程发出的消息,

充分实现非UI线程中较低开支下的消息处理。

HandlerThread类的使用:

package com.my.Mytimetest;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;

public class FourActivity extends Activity{
	
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		HandlerThread handlerThread = new HandlerThread("handler thread");
		handlerThread.start();
		
		Handler mHandler = new Handler(handlerThread.getLooper()){
			@Override
			public void handleMessage(Message msg) {
				Log.i("tag", "当前的线程:"+Thread.currentThread());
			}
		};
		mHandler.sendEmptyMessage(1);
		
	}

}

效果图:



可以看出,打印的是子线程的信息。HandlerThread中加了同步锁,保证了线程的安全。

HandlerThread类的run()方法的源码:

    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
  

HandlerThread类中的getLooper()方法的源码:

public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }


下面介绍一下主线程与子线程之间通过Handler传递消息的方法:


主线程给子线程发送、子线程给主线程发送消息的代码:

package com.my.Mytimetest;

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

public class ThreedActivity extends Activity implements OnClickListener {

	private Button button1;
	private Button button2;
	private Handler threadHandler;
	private Message messageMain;
	private Message messageThread;

	//创建主线程的handler
	private Handler mHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			messageMain = new Message();
			Log.i("tag", "主线程中的Handler");
			//向子线程发送消息
			threadHandler.sendMessageDelayed(messageMain, 1000);
		};

	};
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.three);
		
		button1 = (Button) findViewById(R.id.id_btn1);
		button2 = (Button) findViewById(R.id.id_btn2);
		button1.setOnClickListener(ThreedActivity.this);
		button2.setOnClickListener(ThreedActivity.this);

		
		HandlerThread handlerThread = new HandlerThread("handler thread");
		handlerThread.start();
		//创建子线程的handler
		threadHandler = new Handler(handlerThread.getLooper()){
			@Override
			public void handleMessage(Message msg) {
				messageThread = new Message();
				Log.i("tag", "子线程中的Handler");
				//向主线程发送消息
				mHandler.sendMessageDelayed(messageThread, 1000);
			}
		};

	}

	
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.id_btn1:
			//主线程给子线程发送一个消息
			mHandler.sendEmptyMessage(1);
			break;

		case R.id.id_btn2:
			break;

		default:
			break;
		}

	}

}
效果图:

Android中更新UI的几种方式:

1.runOnUiThread

2.handler post

3.handler sendMessage

4.view post

代码:

package com.my.Mytimetest;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class FiveActivity extends Activity implements OnClickListener {

	private TextView textview;
	private Button changeBtn1;
	private Button changeBtn2;
	private Button changeBtn3;
	private Button changeBtn4;
	private int index;

	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			textview.setText("handler sendMessage方式更新UI");
			Log.i("tag", "handler sendMessage方式更新UI");
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.five);

		textview = (TextView) findViewById(R.id.id_textview);
		changeBtn1 = (Button) findViewById(R.id.id_change1);
		changeBtn1.setOnClickListener(this);
		changeBtn2 = (Button) findViewById(R.id.id_change2);
		changeBtn2.setOnClickListener(this);
		changeBtn3 = (Button) findViewById(R.id.id_change3);
		changeBtn3.setOnClickListener(this);
		changeBtn4 = (Button) findViewById(R.id.id_change4);
		changeBtn4.setOnClickListener(this);

		dealUI();
		
		
	}
	
	private void dealUI(){
		// 在子线程中更新UI
				new Thread() {
					@Override
					public void run() {
						try {
							Thread.sleep(2000);
							switch (index) {
							case 1:
								// 方式1
								 handler1();
								break;
							case 2:
								 //方式2:
								 handler2();
								break;
							case 3:
								 //方式3:
								updateUI();
								break;
							case 4:
								 //方式4:
								 viewUI();
								break;

							default:
								System.out.println("不会吧");
								break;
							}
							
							
							
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}.start();
	}

	/**
	 * 方式1:handler post方式
	 */
	private void handler1() {
		handler.post(new Runnable() {

			@Override
			public void run() {
				textview.setText("handler post方式更新UI");
				Log.i("tag", "handler post方式更新UI");
			}
		});
	}

	/**
	 * 方式2:handler sendMessage方式
	 */
	private void handler2() {
		handler.sendEmptyMessage(1);
		
	}

	/**
	 * 方式三:runOnUiThread方式
	 */
	private void updateUI() {
		runOnUiThread(new Runnable() {

			@Override
			public void run() {
				textview.setText("runOnUiThread方式更新UI");
				Log.i("tag", "runOnUiThread方式更新UI");
			}
		});
	}

	/**
	 * 方式四:view post方式
	 */
	private void viewUI() {
		textview.post(new Runnable() {

			@Override
			public void run() {
				textview.setText("view post方式更新UI");
				Log.i("tag", "view post方式更新UI");
			}
		});
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.id_change1:
			index=1;
			dealUI();
			break;
		case R.id.id_change2:
			index=2;
			dealUI();
			break;
		case R.id.id_change3:
			index=3;
			dealUI();
			break;
		case R.id.id_change4:
			index=4;
			dealUI();
			break;

		default:
			break;
		}
		
		
	}

}
效果图:

实际上,上边的这四种更新UI的方式都是使用到了handler机制来更新UI的,只是内部一些代码的处理不一样,看一下这四种方式的源码,我们就知道了。

源码对比:

handler post方式:

 public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
runOnUiThread方式:

  public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
viewpost方式:

 public boolean post(Runnable action) {
        Handler handler;
        if (mAttachInfo != null) {
            handler = mAttachInfo.mHandler;
        } else {
            // Assume that post will succeed later
            ViewRoot.getRunQueue().post(action);
            return true;
        }

        return handler.post(action);
    }
其实都是通过handler机制来实现的。

最后,一些常见的异常的产生原因:

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

产生的原因:

是因为在子线程中创建Handler对象的时候没为创建的Handler指定Looper,

源码解析:

<span style="color:#333333;"> public Handler() {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        </span><span style="color:#ff0000;">mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }</span><span style="color:#333333;">
        mQueue = mLooper.mQueue;
        mCallback = null;
    }
</span>
2.android.view.ViewRootImpl$CalledFromWrongThreadException:

Only the original thread that created a view hierarchy can touch its views.

产生的原因:是因为在非UI线程中更新UI

版权声明:本文为博主原创文章,未经博主允许不得转载。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics