Android开发之handler

先看一下执行结果,只是简单的输出

       但是handler到底是何方神圣呢,我们来看一下官方的解释:

         A Handler allows you to send and process Message and Runnable objects associated with a thread'sMessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

      大概的意思就是说,handler是用来处理线程之间的消息的。详细的解释可以看一下下面这段话,是网上看到的,讲的非常好

Handler的定义:

  * 主要接受子线程发送的数据, 并用此数据配合主线程更新UI。当应用程序启动时,

  * Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发, 比如说,

  * 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。

  * 如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,

  * 如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。

  * 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,

  * 也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。这个时候,Handler就出现了,来解决这个复杂的问题 ,

  * 由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,

  * 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象(里面包含数据),把这些消息放入主线程队列中,配合主线程进行更新UI。

  * Handler一些特点:

  * Handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),

  * 它有两个作用: (1): 安排消息或Runnable 在某个主线程中某个地方执行, (2)安排一个动作在不同的线程中执行

  * Handler中分发消息的一些方法

  * post(Runnable)

  * postAtTime(Runnable,long)

  * postDelayed(Runnable,long)

  * sendEmptyMessage(int)

  * sendMessage(Message)

  * sendMessageAtTime(Message,long)

  * sendMessageDelayed(Message,long)

  * 以上post类方法允许你排列一个Runnable对象到主线程队列中,当需要在不同于主UI线程中执行则需要配合HandlerThread进行使用:

  * HandlerThread handlerThread = new HandlerThread("myHandlerThread");

  * handlerThread.start();

  * handler = new Handler(handlerThread.getLooper());* sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

main.xml

 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">

	<Button 
		android:text="@string/start" 
		android:id="@+id/butS"
		android:layout_width="fill_parent" 
		android:layout_height="wrap_content">
	</Button>
	<Button 
		android:text="@string/end" 
		android:id="@+id/butE"
		android:layout_width="fill_parent" 
		android:layout_height="wrap_content">
	</Button>
</LinearLayout>

activity类

  1. package com.handler;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.view.View;  
  7. import android.view.View.OnClickListener;  
  8. import android.widget.Button;  
  9.   
  10. public class handlerTest extends Activity {  
  11.     /** Called when the activity is first created. */  
  12.     private Button butS,butE;  
  13.     @Override  
  14.     public void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.main);  
  17.           
  18.         butS = (Button)findViewById(R.id.butS);  
  19.         butS.setOnClickListener(new ListenerS());  
  20.         butE = (Button)findViewById(R.id.butE);  
  21.         butE.setOnClickListener(new ListenerE());  
  22.     }  
  23.     Handler handler = new Handler();  
  24.     Runnable updateThread = new Runnable(){  
  25.   
  26.         @Override  
  27.         public void run() {  
  28.             // TODO Auto-generated method stub  
  29.             System.out.println("updateThread");  
  30.             handler.postDelayed(updateThread, 3000);//3秒之后把updateThread对象加入到线程消息队列中  
  31.         }     
  32.     };  
  33.     class ListenerS implements OnClickListener{  
  34.   
  35.         @Override  
  36.         public void onClick(View arg0) {  
  37.             // TODO Auto-generated method stub  
  38.             handler.post(updateThread);//把updateThread对象加入到线程消息队列中  
  39.         }  
  40.           
  41.     }  
  42.     class ListenerE implements OnClickListener{  
  43.   
  44.         @Override  
  45.         public void onClick(View v) {  
  46.             // TODO Auto-generated method stub  
  47.             handler.removeCallbacks(updateThread);  
  48.         }     
  49.     }  

 

之前学习了handler的简单用法,如果还不知道handler是什么,可以看一下点击打开链接。这次深入一下,还是一样,先来看一下执行的效果


main.xml

 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<ProgressBar 
	android:id="@+id/bar"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    style="?android:attr/progressBarStyleHorizontal"
    android:visibility="gone"
	/>
<Button  
	android:id="@+id/btn"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="start"
    />
</LinearLayout>


 

activity类

  1. package com.handler;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.widget.Button;  
  10. import android.widget.ProgressBar;  
  11. import android.widget.Toast;  
  12.   
  13. public class progressHandler extends Activity {  
  14.     /** Called when the activity is first created. */  
  15.     private ProgressBar bar = null;  
  16.     private Button btn = null;  
  17.     @Override  
  18.     public void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.main);  
  21.           
  22.         bar = (ProgressBar)findViewById(R.id.bar);  
  23.         btn = (Button)findViewById(R.id.btn);  
  24.         btn.setOnClickListener(new MyListener());  
  25.     }  
  26.       
  27.     class MyListener implements OnClickListener{  
  28.   
  29.         @Override  
  30.         public void onClick(View arg0) {//第一步,触发button事件,把线程对象updateThread 添加到线程消息队列中  
  31.             // TODO Auto-generated method stub  
  32.             bar.setVisibility(View.VISIBLE);  
  33.             handler.post(updateThread);  
  34.         }  
  35.           
  36.     }  
  37.       
  38.     Handler handler = new Handler(){  
  39.   
  40.         @Override  
  41.         public void handleMessage(Message msg) {//第五步,处理子线程发过来的消息  
  42.             // TODO Auto-generated method stub  
  43.             super.handleMessage(msg);  
  44.             bar.setProgress(msg.arg1);  
  45.             handler.post(updateThread);//继续调用子线程  
  46.         }  
  47.           
  48.     };  
  49.     Runnable updateThread = new Runnable(){  
  50.         int i = 0;  
  51.         @Override  
  52.         public void run() {//第二步,启动线程  
  53.             // TODO Auto-generated method stub  
  54.             System.out.println("begin a thread");  
  55.             i += 10;  
  56.             Message msg = handler.obtainMessage();//第三步,从消息池中返回一个消息  
  57.             msg.arg1 = i;  
  58.             try {  
  59.                 Thread.sleep(1000);  
  60.             } catch (InterruptedException e) {  
  61.                 // TODO Auto-generated catch block  
  62.                 e.printStackTrace();  
  63.             }  
  64.             handler.sendMessage(msg);//第四步,从子线程发送消息给主线程  
  65.             if(i==100){//第五步,判断i的值(由于handler是异步处理消息,所以同时会处理子线程发送给主线程的消息)  
  66.                 handler.removeCallbacks(updateThread);  
  67.                 bar.setVisibility(View.GONE);  
  68.                 Toast.makeText(progressHandler.this"你个SB!", Toast.LENGTH_LONG).show();  
  69.             }  
  70.         }  
  71.           
  72.     };  

 

我们说handler是开启了另外一个线程,而且看代码的话确实是这样,实现了runnable接口,这在java中就是开启了一个线程,但是情况中的是这样吗?我们不妨来做个试验,如下

 

 

package com.handlerThread;

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

public class handlerThread extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Handler handler = new Handler();
        handler.post(r);
        System.out.println("activity线程ID:"+Thread.currentThread().getId());
        System.out.println("activity线程name:"+Thread.currentThread().getName());
    }
    
    Runnable r = new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
	        System.out.println("handler线程ID:"+Thread.currentThread().getId());
	        System.out.println("handler线程name:"+Thread.currentThread().getName());
	        try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
    	
    };
}

运行结果

其实,当我看到这里的时候也不敢相信,但是事实就是这样,handler没有重新开启一个线程,而是跟activity在同一个线程里,但是这种写法也就非常接近java的标准线程的写法了,难怪会误导人,如下是java的标准线程写法。

 

package com.handlerThread;

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

public class handlerThread extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
//        Handler handler = new Handler();
//        handler.post(r);
        Thread t = new Thread(r);
        t.start();
        System.out.println("activity线程ID:"+Thread.currentThread().getId());
        System.out.println("activity线程name:"+Thread.currentThread().getName());
    }
    
    Runnable r = new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
	        System.out.println("handler线程ID:"+Thread.currentThread().getId());
	        System.out.println("handler线程name:"+Thread.currentThread().getName());
	        try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
    	
    };
}

运行结果如下:

这里就才是我们这种想要的结果,两者比较我们就会发现,handler虽然实现了runnable接口,但是却并没有启动一个线程,而是直接调用run方法。那andriod为什么要这样设计呢,既然不启动新的线程,为什么还要多此一举来实现runnable接口呢

 

 

我们知道普通的handler类是没有实现多线程的,在android中的handler要实现多线程,可以使用HandlerThread这个类,如下所示

 

package com.handlerThread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;

public class handlerThread2 extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		System.out.println("activity线程ID:"+Thread.currentThread().getId());
		HandlerThread handlerThread = new HandlerThread("handlerThread");
		handlerThread.start();
		MyHandler handler = new MyHandler(handlerThread.getLooper());
		Message msg = handler.obtainMessage();
		msg.sendToTarget();
	}
	class MyHandler extends Handler {

		public MyHandler() {
			super();
			// TODO Auto-generated constructor stub
		}

		public MyHandler(Looper looper) {
			super(looper);
			// TODO Auto-generated constructor stub
		}

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			System.out.println("handler线程ID:"+Thread.currentThread().getId());
		}
		
	}

}

运行结果:

由此才可以说handler实现了真正意义上的多线程。

在上面的程序中我们看到有个新知识looper,那么什么是looper呢,如下是官方的解释


         大概的意思就是说,looper是用来循环获取消息的,那么整个流程就可以看作是handler往消息队列中添加消息,looper再循环获取这些消息。

前面介绍handler的时候,也用到过几种传值方式,今天来总结一下,并且重点说一下bundle方式,代码如下:

 

package com.handlerThread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;

public class handlerThread2 extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		//System.out.println("activity线程ID:"+Thread.currentThread().getId());
		HandlerThread handlerThread = new HandlerThread("handlerThread");
		handlerThread.start();
		MyHandler handler = new MyHandler(handlerThread.getLooper());
		Message msg = handler.obtainMessage();
		
		//msg.arg1 = 123;//传递整型数据
		//msg.obj = "asd";传递object类型
		
		//利用bundle对象来传值
		Bundle b = new Bundle();
		b.putInt("ID", 12);
		b.putString("name", "thinkpad");
		msg.setData(b);
		
		msg.sendToTarget();
	}
	class MyHandler extends Handler {

		public MyHandler() {
			super();
		}

		public MyHandler(Looper looper) {
			super(looper);
		}

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			//int args = msg.arg1;
			//String s = (String)msg.obj;
			
			//获取bundle对象的值
			Bundle b = msg.getData();
			int id = b.getInt("ID");
			String name = b.getString("name");
			System.out.println("id is "+id+", name is "+name);
			
			//System.out.println("handler线程ID:"+Thread.currentThread().getId());
		}
		
	}

}

运行结果

那么,bundle具体是个什么东西呢,我们来看一下官方的解释

哈哈,这个基本上是andriod文档中写的最好的一个的,短短几个单词就说的清清楚楚了,熟悉java集合框架的人应该马上就会用了,则不用去看剩下的文档了,依葫芦画瓢就好了。

 

复杂耗时的操作,常常会阻塞UI主线程,从而引起程序卡死!针对这种情况,可以通过简单的策略规避:新开一个工作线程,在工作线程中执行复杂操作,操作完毕之后,

通过Handler通知UI线程,更新状态信息!关键代码,请参阅下面:

Java代码 复制代码
  1. private void SynTask() {   
  2.     new Thread() {   
  3.         @Override  
  4.         public void run() {   
  5.            
  6.             if (IsHaveInternet()){// 联网  
  7.                 DowloadData();//下载数据  
  8.                 //通知UI  
  9.                 Message msg = new Message();   
  10.                 msg.what = mDonlandEnd;   
  11.                 handler.sendMessage(msg);   
  12.             }   
  13.         }   
  14.     }.start();   
  15. }  


UI主线程,怎么通过handler接手工作线程的信息呢:

Java代码 复制代码
  1. Handler handler = new Handler() {   
  2.     @Override  
  3.     public void handleMessage(Message msg) {   
  4.         switch (msg.what) {    
  5.         casem mDonlandEnd:   
  6.             ShowToast(GetString(R.string.DonlandEnd));   
  7.             break;   
  8.         default:   
  9.             break;   
  10.         }   
  11.     }   
  12. };  
主要用到的类有:android.os.Handler、android.os.Message及java.lang.Thread


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值