[android] 05_网络图片查看器-HttpURLConnection

Android 4.0

网络图片查看器


 

核心代码:

package cn.zengfansheng.imageview;
 
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
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 {
 
    private EditText et_url;
    private ImageView iv_image;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_image = (ImageView) this.findViewById(R.id.iv_image);
        et_url = (EditText) this.findViewById(R.id.et_url);
    }
    /**
     * 点击查看图片
     * 
     * @param view
     */
    public void viewImage(View view) {
        
        new Thread() { // 重新创建一个Thread类处理请求网络的连接
            @Override
            public void run() {
 
                // 原因: 访问网络 耗时的操作 需要一定的时间 为了避免界面卡死 无响应 4.0系统做了一个处理
                // 所有的网络访问的操作 是不可以在主线程里面执行.
                try {
                    // 1、获取用户输入的url
                    String url = et_url.getText().toString();
                    if (TextUtils.isEmpty(url)) {
                        Toast.makeText(MainActivity.this"图片url不能为空。。。", 0)
                                .show();
                        return;
                    }
 
                    // 2、将地址包url
                    URL imageUrl = new URL(url);
 
                    // 3、获取一个HttpURLConnection
                    HttpURLConnection conn = (HttpURLConnection) imageUrl
                            .openConnection();
 
                    // 4、设置请求头信息
                    conn.setRequestMethod("GET");// 设置http的请求方式 get / post 注意单词
                                                    // 大写
                    conn.setConnectTimeout(5000);// 连接两秒超时
                    conn.setReadTimeout(5000);// 读取两秒超时
                    String userAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C)";
                    conn.setRequestProperty("User-Agent", userAgent);
 
                    // 5、获取服务器返回信息
                    int code = conn.getResponseCode();
 
                    if (code == 200) { // 返回正确码
 
                        InputStream in = conn.getInputStream();// 得到服务器返回的输入流
                        Bitmap bitmap = BitmapFactory.decodeStream(in);
                        iv_image.setImageBitmap(bitmap);
 
                    } else {
                        Toast.makeText(MainActivity.this"请求失败", 0).show();
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                    Toast.makeText(MainActivity.this"获取图片失败", 0).show();
                } catch (IOException e) {
                    e.printStackTrace();
                    Toast.makeText(MainActivity.this"取图片失败", 0).show();
                }
            }
        }.start();
    }
}

结果:(但还是有问题3,)

问题1:访问网络 耗时的操作 需要一定的时间 为了避免界面卡死 无响应 4.0系统做了一个处理

// 所有的网络访问的操作 是不可以在主线程里面执行.

10-14 12:29:31.336: E/AndroidRuntime(24430): Caused by: android.os.NetworkOnMainThreadException

问题2:权限

10-14 12:39:21.748: E/AndroidRuntime(25369): java.lang.SecurityException: Permission denied (missing INTERNET permission?)

问题3:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

只有主线程才能更新UI操作

在android平台下,进行多线程编程时,经常需要在主线程之外的一个单独的线程中进行某些处理,然后更新用户界面显示。但是,

在主线线程之外的线程中直接更新页面显示的问题是:

系统会报这个异常,android.view.viewroot$calledfromwrongthreadexception: only the original thread that created a view hierarchy can touch its views.

(只有原始创建这个视图层次(view hierachy)的线程才能修改它的视图(view)。)。

也就是说必须在程序的主线程(也就是ui线程)中进行更新界面显示的工作。可以采用下面的方法之一来解决:

解决方案1:在activity.oncreate(bundle savedinstancestate)中创建一个handler类的实例, 在这个handler实例的handlemessage回调函数中调用更新界面显示的函数。例如:  

  1. public class ExampleActivity extends Activity {  
  2.     Handler h = null;  
  3.     @override  
  4.     public void onCreate(Bundle savedinstancestate){  
  5.         h = new Handler(){  
  6.             @override  
  7.             public void handleMessage(Message msg){  
  8.                 // call update gui method.  
  9.             }  
  10.         };  
  11.     }  
  12. }  

 

在其它的函数中,利用 send族或post族函数向这个h发送或邮寄消息即可。


解决方案2:利用activity.runonuithread(runnable)

把更新ui的代码创建在runnable中,然后在需要更新ui时,把这个runnable对象传给activity.runonuithread(runnable)。 这样runnable对像就能在ui程序中被调用。

 

 

问题4:10-14 16:58:13.065: E/AndroidRuntime(4876): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()  

原因:不能创建handler,在没有调用Looper.loop()之前

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        iv_image.setImageBitmap(bitmap);
    }
};
handler.sendEmptyMessage(0);

这是由于在子线程中创建Handler