Android Handler

系统 1716 0

Android Handler

 

【转载】

 

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。 http://lichen.blog.51cto.com/697816/486402

 

 

       此文是关于Handler的。 Handler主要接受子线程发送的数据, 并用此数据配合主线程更新UI。 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) ,主线程为管理界面中的UI控件,进行事件分发, 比如说,你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。

 

        这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程不是线程安全的,也就是说,更新UI只能在主线程 中更新,子线程中操作是危险的。这个时候,Handler就出现了,来解决这个复杂的问题。Handler运行在主线程中(UI线程中),它与子线程可以 通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传递)Message对 象(里面包含数据),把这些消息放入主线程队列中,配合主线程进行更新UI。

 

 

      Handler android 提供的对于异步消息处理的方案。 Handler 的特点是与调用者处于同一线程,如果 Handler 里面做耗时的动作,调用者线程会阻塞。 Android UI 操作不是线程安全的,并且这些操作必须在 UI 线程中执行。  

 

      所以, Handler 经常被用来在另外的线程中更新 UI 界面。因为 UI 操作必须在 UI 线程中完成,可以通过 Handler 在别的线程中向 UI 线程发送刷新消息, UI 线程收到消息后执行相关操作。主要用到下面两个函数:

 

public final boolean sendMessage (Message msg)         发送消息

public void handleMessage(Message msg)                    处理消息

 

       Handler 对于 Message 的处理不是并发的。一个 Looper  只有处理完一条 Message 才会读取下一条,所以消息的处理是阻塞形式的。

 

        Handler一些特点:Handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用: (1): 安排消息或Runnable 在某个主线程中某个地方执行, (2)安排一个动作在不同的线程中执行。

 

       Handler中分发消息经常使用post(Runnable)和sendMessage(Message)方法。

 

       先是sendMessage的例子:

 

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:id="@+id/startButton"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="开始演示"
    android:onClick="startIt"
    />
<ProgressBar 
	android:id="@+id/bar"
	style="?android:attr/progressBarStyleHorizontal"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content" 
	android:max="500"
	android:progress="0"
	/>
<TextView  
    android:id="@+id/text"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="演示信息:"
    />
</LinearLayout>
  
 

 

Demo.java

    package com.zhouzijing.android.demo;

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

public class Demo extends Activity{
	
	private final static String TAG = "DEMO";
	
	private TextView textView;
	private Button startButton;
	private ProgressBar progressBar;
	private MyHandler myHandler;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        textView=(TextView)findViewById(R.id.text);
        startButton = (Button)findViewById(R.id.startButton);
        
		progressBar=(ProgressBar)findViewById(R.id.bar);
		progressBar.setMax(100);
		myHandler=new MyHandler();
    }
    
    /**
     * [开始演示]的按钮事件.
     * @param v
     */
    public void startIt(View v) {
    	Button btn = (Button)v;
    	//禁止演示按钮重复点击
    	btn.setClickable(false);
    	btn.setText("演示中...");
    	progressBar.setProgress(0);
    	textView.setText("演示信息:");
		new Thread(new MyThread(1)).start();
    }
    
    //在对UI进行更新时,执行时所在的线程为主UI线程 
  //继承Handler类时,必须重写handleMessage方法 
    class MyHandler extends Handler{
    	public MyHandler(){
    	}
    	public MyHandler(Looper l){
    		super(l);
    	}
    	@Override
    	public void handleMessage(Message msg) {
    		//执行接收到的通知,此时执行的顺序是按照队列进行,即先进先出 
    		Log.i(TAG,"Handler--The ThreadId is: "+Thread.currentThread().getId());
    		int what = msg.what;
    		//根据message的what属性来进行处理
    		switch(what){
    		case 100:
    			Bundle b=msg.getData();
        		String textStr1=b.getString("textStr");
        		textView.append(textStr1);//更改TextView中的值 
        		int barValue=b.getInt("barValue");
        		progressBar.setProgress(barValue);//更改进度条当中的值 
        		if(barValue==100){
        			//允许演示按钮点击
            		startButton.setText("演示结束,可以点击重新演示");
            		startButton.setClickable(true);
        		}
    			break;
    		default:
    			//其他message扔回上级处理.
    			super.handleMessage(msg);
    		}
    	}
    }
    
    //该线程将会在单独的线程中运行 
    class MyThread implements Runnable{
    	int index=1;
    	
    	public MyThread(int index) {
			this.index = index;
		}
    	
		@Override
    	public void run() {
    		while(index<11){
    			Log.i(TAG,"Thread--The ThreadId is: "+Thread.currentThread().getId());
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			int what = 100;
    			//从系统message池中取出1个message对象,并设置what属性.
    			Message msg = myHandler.obtainMessage(what);
    			Bundle b=new Bundle();
    			b.putString("textStr", "线程运行"+index+"次");
    			b.putInt("barValue", index*10);
    			index++;
    			msg.setData(b);
    			myHandler.sendMessage(msg);//通过sendMessage向Handler发送更新UI的消息 
    		}
    	}
    }
}


  

运行效果图,如下:
Android Handler

 

 

简单的说,Activity的onCreate方法里启动一个线程,在这个线程的run方法里使用一个Message对象并使用Handler的 sendMessage方法发送到队列中,最后在Activity里new一个Handler的内部类实现handMessage方法,使用这个方法把队 列中的Message对象取出来以实现异步操作。

 

得到Message对象的方法:(1)new Message();(2)handler对象的obtainMessage();前者需要新建一个Message对象,而后者直接从系统的Message池中获取1个(不用创建对象)。建议使用后者!

 

 

 

      然后是post的例子,这里稍微说一下,直接使用new Handler().post(Runnable)这样的写法并没有新开线程。

 

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"
    >
<EditText 
	android:id="@+id/edtDownloadUrl"
	android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="http://www.zhouzijing.com/"
	/>
<Button  
    android:id="@+id/btnHandlerPostRunnable"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="开始下载"
    android:onClick="startIt"
    />
<ProgressBar 
	android:id="@+id/pBarDownload"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    style="?android:attr/progressBarStyleHorizontal"
	/>
<TextView 
	android:id="@+id/tvProgressBarPercent"
	android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
	/>
</LinearLayout>

  

 

由于下面代码要访问网络和在sdcard上写文件,因此需要在AndroidManifest.xml配置权限:

 

        <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

  
 

 

代码文件:

    package com.zhouzijing.android.demo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;

public class HandlerPostRunnable extends Activity {

	private EditText edtDownloadUrl;
	private ProgressBar pBarDownload;
	private TextView tvProgressBarPercent;
	private Button btnHandlerPostRunnable;
	private int fileSize = 0;
	private int progress = 0;
	private final String TAG = "HandlerPostRunnable";
	private Handler handler = new Handler();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.handler_post_runnable);
		//定义组件
		edtDownloadUrl = (EditText)findViewById(R.id.edtDownloadUrl);
		pBarDownload = (ProgressBar)findViewById(R.id.pBarDownload);
		tvProgressBarPercent = (TextView)findViewById(R.id.tvProgressBarPercent);
		btnHandlerPostRunnable = (Button)findViewById(R.id.btnHandlerPostRunnable);
	}

	//开始演示-按钮事件
	public void startIt(View v) {
		Button btn = (Button)v;
		btn.setText("下载中...");
		btn.setClickable(false);
		downloadFile(edtDownloadUrl.getText().toString());
	}
	/**
	 * 获取文件名.
	 * @param url
	 * @return
	 */
	private String getFileNameFromUrl(String url) {
		String fileName =  url.substring(url.lastIndexOf("/") + 1);
		return fileName;
	}

	/**
	 * 下载文件.
	 * @param url
	 */
	private void downloadFile(final String url) {
		new Thread() {
			public void run() {
				HttpClient client = new DefaultHttpClient();
				HttpGet get = new HttpGet(url);
				HttpResponse response = null;
				FileOutputStream fos = null;
				try {
					response = client.execute(get);
					HttpEntity entity = response.getEntity();
					InputStream is = entity.getContent();
					if (is != null) {
						File file = new File(
								Environment.getExternalStorageDirectory(),
								getFileNameFromUrl(url));
						fos = new FileOutputStream(file);
						fileSize = (int)entity.getContentLength();
						progress = 0;
						downloadStart();
						byte[] buf = new byte[1024];
						int ch = -1;
						while ((ch = is.read(buf)) != -1) {
							fos.write(buf, 0, ch);
							progress = progress+ch;
							downloading();
						}
					}
					fos.flush();
					fos.close();
					fos = null;
					downloadFinished();
				} catch (Exception e) {
					Log.e(TAG, e.getMessage(), e);
					downloadError(e);
				} finally {
					if (fos != null) {
						try {
							fos.close();
						} catch (IOException e) {
							Log.e(TAG, e.getMessage(), e);
						}
						fos = null;
					}
				}
			}
		}.start();
	}
	
	// 下载开始
	private void downloadStart() {
		handler.post(new Runnable() {
			public void run() {
				pBarDownload.setMax(fileSize);
				pBarDownload.setProgress(0);
			}
		});
	}
	/**
	 * 设置下载进度百分比,
	 */
	private void setDownloadPercent() {
		float percent = (float)progress/(float)fileSize*100;
		int iPercent = (int)percent;
		tvProgressBarPercent.setText(String.valueOf(iPercent)+"%");
	}
	
	// 下载中.
	private void downloading() {
		handler.post(new Runnable() {
			public void run() {
				pBarDownload.setProgress(progress);
				setDownloadPercent();
			}
		});
	}

	// 下载成功结束
	private void downloadFinished() {
		handler.post(new Runnable() {
			public void run() {
				pBarDownload.setProgress(fileSize);
				btnHandlerPostRunnable.setText("下载成功,点击再开始下载");
				btnHandlerPostRunnable.setClickable(true);
				setDownloadPercent();
				new AlertDialog.Builder(HandlerPostRunnable.this)
				.setTitle("提示").setMessage("下载成功")
				.setPositiveButton("确定", null).show();
			}
		});
	}

	// 下载失败
	private void downloadError(final Exception err) {
		handler.post(new Runnable() {
			public void run() {
				btnHandlerPostRunnable.setText("下载失败,点击再开始下载");
				btnHandlerPostRunnable.setClickable(true);
				new AlertDialog.Builder(HandlerPostRunnable.this)
						.setTitle("提示").setMessage("下载失败:"+err.getMessage())
						.setPositiveButton("确定", null).show();
			}
		});
	}
}
  
 

代码运行结果如下图:

 


Android Handler

 

 

 

 

Android Handler


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论