一、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();
}
}
}
运行的结果: