android开发学习(四)——anr产生的原理&如何避免,android消息机制入门, 网络图片查看器


一、ANR

ANR: application not response 应用程序无响应

anr 产生的原因:主线程 需要做很多重要的事情,响应点击时间,更新ui
                              如果在主线程中阻塞过久的时间,应用程序就会无响应。
                              为了避免 应用程序出现anr,所有的耗时的操作都应该放在子线程里执行。


二、网络图片查看器

这里的布局文件需要提一点,关于weight,就是权重,布局优先级,权重越大,就会越后布局

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.imaginViewer.MainActivity" >

    <!-- 关于权重weight,这里设置的大,就会最后布局,不会将下面的文本框和按钮给遮住 -->
    <ImageView
        android:id="@+id/iv"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1000" />

    <EditText
        android:id="@+id/tv_path"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="请输入图片路径"
        android:singleLine="true"
        android:text="http://g.hiphotos.baidu.com/image/pic/item/962bd40735fae6cd3d79057e0db30f2443a70fdb.jpg" />

    <Button
        android:id="@+id/bt"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="click"
        android:text="查看图片" />

</LinearLayout>

(eclipse中对xml文本注释的快捷键是Ctrl+Alt+/,取消注释是 Ctrl+Alt+\

界面如图:

这里记得添加联网权限:


然后绑定按钮事件,如图所示:

代码逻辑没有错误,但是这里有一个问题,这个代码在android 2.x的系统中运行,会没有任何问题,图片也会正常显示,但是在android 4.x的系统中,会出现这个错误:


原因是在android 4.x的系统中,网络线程不允许出现在主线程上(主线程一般又叫UI线程,顾名思义主线程应该是负责UI变化的),网络线程只能有子线程完成,所以我们需要new 一个子线程,将图片显示操作放入其中(即try{}catch{}代码块放入new Thread()  {  public void run() { …… }   } 中   ),再次执行,结果logcat又回报错:

10-17 08:42:05.394: W/System.err(2623): android.view.ViewRootImpl$CalledFromWrongThreadException:
 Only the original thread that created a view hierarchy can touch its views.

错误原因是:谁创建的view对象,谁才能动他。只有主线程才能修改view对象,子线程是不能修改view的。


内部实现 更新界面的时候,做了一个检查,
更新完毕ui后,检查这个更新的操作是否在主线程的代码 执行的。
如果是 没问题
如果不是 立刻抛出一个运行时异常 view.ViewRootImpl$CalledFromWrongThreadException:

所以,这段代码操作需要放回主线程(即UI线程)去完成。

这里需要引入  “android消息机制” 。


三、android消息机制入门


那么我们需要做的就是在子线程中发送一个消息到主线程,告诉主线程可以更新UI,并把更新的内容交给主线程:

子线程:

InputStream is = conn.getInputStream();// 获取数据流
Bitmap bitmap = BitmapFactory.decodeStream(is);
// iv.setImageBitmap(bitmap);//会抛出异常,只有主线程才能修改ui,子线程不可以
// TODO:告诉主线程一个消息,帮我修改界面,内容:bitmap
Message message = new Message();
message.what = CHANGE_UI;
message.obj = bitmap;
handler.sendMessage(message);

主线程:

	// 主线程创建一个消息处理器
	private Handler handler = new Handler() {
		//在Handler中有一个handleMessage(Message msg)的方法,这里将这个方法重写一下
		public void handleMessage(android.os.Message mes) {
			if (mes.what == CHANGE_UI) {
				Bitmap bitmap = (Bitmap) mes.obj;
				iv.setImageBitmap(bitmap);
			} else if (mes.what == ERROR) {
				Toast.makeText(MainActivity.this, "图片获取失败!", 0).show();
			}
		};
	};

这样就可以正确的显示出图像。


网络图片查看器的代码如下:

package com.example.imaginViewer;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {
	protected static final int CHANGE_UI = 1;
	protected static final int ERROR = 2;
	private ImageView iv;
	private EditText et;

	// 主线程创建一个消息处理器
	private Handler handler = new Handler() {
		//在Handler中有一个handleMessage(Message msg)的方法,这里将这个方法重写一下
		public void handleMessage(android.os.Message mes) {
			if (mes.what == CHANGE_UI) {
				Bitmap bitmap = (Bitmap) mes.obj;
				iv.setImageBitmap(bitmap);
			} else if (mes.what == ERROR) {
				Toast.makeText(MainActivity.this, "图片获取失败!", 0).show();
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		iv = (ImageView) findViewById(R.id.iv);
		et = (EditText) findViewById(R.id.tv_path);
	}

	public void click(View view) {
		final String path = et.getText().toString().trim();
		if (TextUtils.isEmpty(path)) {
			Toast.makeText(this, "路径不能为空", 1).show();
		} else {
			// 主线程不能执行网络操作,网络操作必须在子线程上执行,所以创建一个子线程
			new Thread() {
				public void run() {
					// 连接服务器,get 请求获取图片
					try {
						URL url = new URL(path);
						// 根据url,发送http的请求
						HttpURLConnection conn = (HttpURLConnection) url
								.openConnection();
						// 设置请求方式
						conn.setRequestMethod("GET");
						// 设置连接超时时间
						conn.setConnectTimeout(5000);
						// 设置读取超时时间
						conn.setReadTimeout(5000);

						// 得到服务器返回的响应码
						int code = conn.getResponseCode();
						//响应码为200表示执行成功
						if (code == 200) {
							InputStream is = conn.getInputStream();// 获取数据流
							Bitmap bitmap = BitmapFactory.decodeStream(is);
							// iv.setImageBitmap(bitmap);//会抛出异常,只有主线程才能修改ui,子线程不可以
							// TODO:告诉主线程一个消息,帮我修改界面,内容:bitmap
							Message message = new Message();
							message.what = CHANGE_UI;
							message.obj = bitmap;
							handler.sendMessage(message);
						} else {
							Message message = new Message();
							message.what = ERROR;
							handler.sendMessage(message);
						}
					} catch (Exception e) {
						e.printStackTrace();
						Message message = new Message();
						message.what = ERROR;
						handler.sendMessage(message);
					}
				};
			}.start();
		}
	}
}

运行的结果:





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值