主线程阻塞、消息队列机制和图片下载

# 主线程阻塞


public class SleepActivity extends Activity {

	@Override

	protected void onCreate(Bundle savedInstanceState) {

		// TODO Auto-generated method stub

		super.onCreate(savedInstanceState);

		setContentView(R.layout.message_test);

	}

	public void click(View v){

		try {

			Thread.sleep(7000);

		} catch (InterruptedException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		}

		Toast.makeText(this, "点击了按钮", 1).show();

	}

}

* 当点下按钮时,UI停顿7s,这时其它操作是无效的。这就是主线程阻塞。我们为了防止阻塞,一般耗时操作放到其它线程中。可以参考我的线程相关博文。

# 下载网络图片

* 如果我们这样写


public class MessageTestActivity extends Activity {

	@Override

	protected void onCreate(Bundle savedInstanceState) {

		// TODO Auto-generated method stub

		super.onCreate(savedInstanceState);

		setContentView(R.layout.message_test);

	}

	public void click(View v){

		String path = "";

		try {

			URL url = new URL(path);

			HttpURLConnection conn = (HttpURLConnection) url.openConnection();

			//对连接对象初始化

			conn.setRequestMethod("GET");

			conn.setConnectTimeout(5000);

			conn.setReadTimeout(5000);

			conn.connect();

			if(conn.getResponseCode() == 200){

				InputStream in = conn.getInputStream();

				Bitmap bm = BitmapFactory.decodeStream(in);

				ImageView image = (ImageView) findViewById(R.id.image);

				image.setImageBitmap(bm);

			}

		} catch (MalformedURLException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		} catch (IOException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		}

	}

}

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    <Button 

        android:id="@+id/btn"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="下载图片"

        android:onClick="click"/>

    <ImageView 

        android:id="@+id/image"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        />

 

</LinearLayout>

* 这样在Android4.0之前是可以的,4.0之后,为防止主线程阻塞,像下载图片这样的耗时操作是不允许在主线程中运行

* 另外,上边代码还有一个问题。主线程又称UI线程,因为只有在主线程中,才能刷新UI,上边的bm是不会加载到界面的。

* 所以我们要用到伟大的:消息队列机制

# 消息队列机制

* 主线程创建时,系统会同时创建消息队列对象(MessageQueue)和消息轮询器对象(Looper)

* 轮询器的作用,就是不停的检测消息队列中是否有消息(Message)

* 消息队列一旦有消息,轮询器会把消息对象传给消息处理器(Handler),处理器会调用handleMessage方法来处理这条消息,handleMessage方法运行在主线程中,所以可以刷新ui

* 总结:只要消息队列有消息,handleMessage方法就会调用

* 子线程如果需要刷新ui,只需要往消息队列中发一条消息,触发handleMessage方法即可

* 子线程使用处理器对象的sendMessage方法发送消息

* 我们这样改就可以了:


public class MessageTestActivity extends Activity {

	Handler handler = new Handler(){

		public void handleMessage(android.os.Message msg) {

			ImageView image = (ImageView) findViewById(R.id.image);

			image.setImageBitmap((Bitmap) msg.obj);//msg.obj是Object类型,所以必须强转

		};

	};

 

	@Override

	protected void onCreate(Bundle savedInstanceState) {

		// TODO Auto-generated method stub

		super.onCreate(savedInstanceState);

		setContentView(R.layout.message_test);

	}

	public void click(View v){

		String path = "";

		try {

			URL url = new URL(path);

			HttpURLConnection conn = (HttpURLConnection) url.openConnection();

			//对连接对象初始化

			conn.setRequestMethod("GET");

			conn.setConnectTimeout(5000);

			conn.setReadTimeout(5000);

			conn.connect();

			if(conn.getResponseCode() == 200){

				InputStream in = conn.getInputStream();

				Bitmap bm = BitmapFactory.decodeStream(in);

				Message msg = new Message();

				msg.obj = bm;//Message是可以携带任何Object对象

				handler.sendMessage(msg);

			} else {

//				这个也属于主线程的操作,所以也不能放到这里

//				Toast.makeText(MessageTestActivity.this, "请求失败", 1).show();

			}

		} catch (MalformedURLException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		} catch (IOException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		}

	}

}

* 如果下载完成,就会发送消息到消息队列,轮询器Looper检查到消息就会调用handleMessage,但是如果我们有很多不同消息,如何区分?

* 一般我们会设置Message.what,让Message携带一个整型数据,做判断


public class MessageTestActivity extends Activity {

	Handler handler = new Handler(){

		public void handleMessage(android.os.Message msg) {

			switch (msg.what) {

			case 1:

				ImageView image = (ImageView) findViewById(R.id.image);

				image.setImageBitmap((Bitmap) msg.obj);//msg.obj是Object类型,所以必须强转

				break;

			case 0:

				Toast.makeText(MessageTestActivity.this, "请求失败", 1).show();

				break;

			}

		};

	};

 

	@Override

	protected void onCreate(Bundle savedInstanceState) {

		// TODO Auto-generated method stub

		super.onCreate(savedInstanceState);

		setContentView(R.layout.message_test);

	}

	public void click(View v){

		String path = "http://f.hiphotos.baidu.com/zhidao/pic/item/1e30e924b899a90129bad66e1d950a7b0308f5df.jpg";

		try {

			URL url = new URL(path);

			HttpURLConnection conn = (HttpURLConnection) url.openConnection();

			//对连接对象初始化

			conn.setRequestMethod("GET");

			conn.setConnectTimeout(5000);

			conn.setReadTimeout(5000);

			conn.connect();

			if(conn.getResponseCode() == 200){

				InputStream in = conn.getInputStream();

				Bitmap bm = BitmapFactory.decodeStream(in);

				Message msg = new Message();

				msg.obj = bm;//Message是可以携带任何Object对象

				msg.what = 1;

				handler.sendMessage(msg);

			} else {

//				这个也属于主线程的操作,所以也不能放到这里

//				Toast.makeText(MessageTestActivity.this, "请求失败", 1).show();

				Message msg = handler.obtainMessage();

				msg.what = 0;

				handler.sendMessage(msg);

			}

		} catch (MalformedURLException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		} catch (IOException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		}

	}

}

* 上边程序还是报错,前边说了,要建新线程:

public class MessageTestActivity extends Activity {
	Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case 1:
				ImageView image = (ImageView) findViewById(R.id.image);
				image.setImageBitmap((Bitmap) msg.obj);//msg.obj是Object类型,所以必须强转
				break;
			case 0:
				Toast.makeText(MessageTestActivity.this, "请求失败", 1).show();
				break;
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.message_test);
	}
	public void click(View v){
		Thread t = new Thread(){
			public void run() {

				String path = "http://f.hiphotos.baidu.com/zhidao/pic/item/1e30e924b899a90129bad66e1d950a7b0308f5df.jpg";
				try {
					URL url = new URL(path);
					HttpURLConnection conn = (HttpURLConnection) url.openConnection();
					//对连接对象初始化
					conn.setRequestMethod("GET");
					conn.setConnectTimeout(5000);
					conn.setReadTimeout(5000);
					conn.connect();
					if(conn.getResponseCode() == 200){
						InputStream in = conn.getInputStream();
						Bitmap bm = BitmapFactory.decodeStream(in);
						Message msg = new Message();
						msg.obj = bm;//Message是可以携带任何Object对象
						msg.what = 1;
						handler.sendMessage(msg);
					} else {
						Message msg = handler.obtainMessage();
						msg.what = 0;
						handler.sendMessage(msg);
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			};
		};
		t.start();
	}
}

* 这次可以运行了,但是Handler是报警告的,我们应该把handler定义成静态变量,这样,程序任何类都可以调用,以后开发中,会发现相当方便。

* 最终的代码

public class MessageTestActivity extends Activity {
	static ImageView image;
	static MessageTestActivity mta;
	static Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case 1:
				image.setImageBitmap((Bitmap) msg.obj);//msg.obj是Object类型,所以必须强转
				break;
			case 0:
				Toast.makeText(mta, "请求失败", 1).show();
				break;
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.message_test);
		image = (ImageView) findViewById(R.id.image);
		mta = this;
	}
	public void click(View v){
		Thread t = new Thread(){
			public void run() {

				String path = "http://f.hiphotos.baidu.com/zhidao/pic/item/1e30e924b899a90129bad66e1d950a7b0308f5df.jpg";
				try {
					URL url = new URL(path);
					HttpURLConnection conn = (HttpURLConnection) url.openConnection();
					//对连接对象初始化
					conn.setRequestMethod("GET");
					conn.setConnectTimeout(5000);
					conn.setReadTimeout(5000);
					conn.connect();
					if(conn.getResponseCode() == 200){
						InputStream in = conn.getInputStream();
						Bitmap bm = BitmapFactory.decodeStream(in);
						Message msg = new Message();
						msg.obj = bm;//Message是可以携带任何Object对象
						msg.what = 1;
						handler.sendMessage(msg);
					} else {
						Message msg = handler.obtainMessage();
						msg.what = 0;
						handler.sendMessage(msg);
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			};
		};
		t.start();
	}
}

时间: 2024-08-23 11:29:05

主线程阻塞、消息队列机制和图片下载的相关文章

libjingle主线程的消息响应

trunk\talk\app\webrtc\peerconnectionproxy.h文件中定义了PeerConnection的代理类,class PeerConnection : public PeerConnectionInterface,而#define BEGIN_PROXY_MAP(c) \  class c##Proxy : public c##Interface所以BEGIN_PROXY_MAP(PeerConnection)展开即为 class PeerConnection : 

Handler详解4-epoll、looper.loop主线程阻塞

句柄与指针的区别 学习C++的人都知道句柄和指针,而且我发现很多人在句柄与指针之间直接划等号,对我们来说两者都是地址,我觉的这也造成很多人将句柄和指针划等号的直接原因. 首先说指针吧.通俗一点就是地址,他是内存的编号,通过它我们可以直接对内存进行操作,只要地址不变,我们每次操作的物理位置是绝对不变,记住这句话,这是句柄和指针的重大区别所在. 再说说句柄吧,一般是指向系统的资源的位置,可以说也是地址.但是这些资源的位置真的不变,我们都知道window支持虚拟内存的技术,同一时间内可能有些资源被换出

Android:子线程向UI主线程发送消息

在Android里,UI线程是不允许被阻塞的,因此我们要将耗时的工作放到子线程中去处理. 那么子线程耗时处理后要怎样通知UI线程呢? 我们可以在UI主线程中创建一个handler对象,然后通过重写其handleMessage(Message msg)的方法,该方法会接收到子线程中的handler对象的sendMessage((Message msg)发回来的消息.这样一发一收就完成工作: 而关于主线程向子线程发送消息的内容可以看我的上一篇博客,其中讲到了Looper类及其两个重要方法和实现原理.

ZWave 中的消息队列机制

文章主题   在我们的日常编程中,对消息队列的需求非常常见,使用一个简洁.高效的消息队列编程模型,对于代码逻辑的清晰性,对于事件处理的高效率来说,是非常重要的.这篇文章就来看看 ZWave 中是通过什么机制为我们提供了一个便捷的消息队列处理机制. 内容导航   消息队列是什么 我自己写的消息队列 ZWave 消息队列的结构 ZWave 消息队列的使用(初始化.存储消息.取出消息) 消息队列是什么   消息队列最主要特点是:存储消息,先进先出. 比如在典型的生产者-消费者编程模型中,先创建一个消息

Android主线程的消息系统(Handler\Looper)

前言: 之前的文章写的都是关于Bitmap和内存的优化技术,这一篇文章给大家谈谈Handler. Handler是Android系统中比较重要的一个知识,在Android多线程面试经常会被问到,在实际项目中的确也经常用到.当然也比较复杂,知识比较多,牵扯到的类有Thread.Looper.Message.MessageQueue. Android是支持多线程的,通常应用程序中与用户相关的UI事件都是运行在主线程中,比如点击屏幕.按钮等,为了保持主线程顺畅相应用户事件不被阻塞就需要把耗时的操作(主

Spark 消息队列机制源码学习

源码学习 spark源码注释中有下面一句话: Asynchronously passes SparkListenerEvents to registered SparkListeners 即所有spark消息SparkListenerEvents 被异步的发送给已经注册过的SparkListeners. 在SparkContext中, 首先会创建LiveListenerBus实例,这个类主要功能如下: 保存有消息队列,负责消息的缓存 保存有注册过的listener,负责消息的分发 该类的继承层次

android断点下载并显示进度,关于handle,和主线程不能联网采取子线程联网下载,和多线程下载学习

package cn.multidownload; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.ne

细说UI线程和Windows消息队列

在 Windows应用程序中,窗体是由一种称为" UI线程( User Interface Thread)"的特殊类型的线程创建的. 首先, UI线程是一种"线程",所以它具有一个线程应该具有的所有特征,比如有一个线程函数和一个线程 ID. 其次," UI线程"又是"特殊"的,这是因为 UI线程的线程函数中会创建一种特殊的对象--窗体,同时,还一并负责创建窗体上的各种控件. 窗体和控件大家都很熟悉了,这些对象具有接收用户操作的

Handler消息传递机制(四)子线程接收主线程发送的消息

package com.example.looper; 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.widg