# 主线程阻塞
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(); } }