android重新学_网络1——采用HttpURLConnection获取网络图片
随着3G、4G的普及、通信资费的降低,网络的覆盖率、网络速率比以前有很大提升,这就给我们在手机上访问互联网带来了很大方便,因此做安卓应用程序开发,很多时候我们都需要和互联网打交道。纯本地的应用越来越少。学会开发网络应用成为一名android应用开发者的基本功。
这一次,我们就一起做一个网络图片查看器的小Demo。
一 页面布局
首先,界面布局上如下图所示:一个垂直布局的线性布局,里面有显示图片的imageview和用于输入图片网络地址的edittext和点击就能够获取图片的button.
下面就是布局文件的代码:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent" 6 > 7 <ImageView android:layout_width="match_parent" 8 android:layout_height="match_parent" 9 android:layout_weight="1" 10 android:id="@+id/img_net"/> 11 <EditText 12 android:layout_width="fill_parent" 13 android:layout_height="wrap_content" 14 android:id="@+id/et_path" 15 android:hint="请输入图片的网络路径" 16 /> 17 <Button 18 android:layout_width="fill_parent" 19 android:layout_height="wrap_content" 20 android:id="@+id/btn_img" 21 android:gravity="center" 22 android:text="查看" 23 /> 24 </LinearLayout>
大家可能注意到了ImageView中的android:layout_weight="1"属性了,这个demo中是想把从网络上获取的图片最大化展示,所以宽和高都用了match_parent,但是用match_parent,其他两个控件就会被挤到屏幕外面去,所以会使用这个属性,关于这个属性的使用可以参考下面的解释:
android:weight:属性是指渲染的权重,只有指定高度或者宽度为0dp的时候,weight才会代表当前控件渲染的权重。如果指定当前的控件是fill_parent(或者是wrap_content).那weight就不代表权重了,它代表渲染的优先级。值越大代表优先级越低。这时默认的优先级都是0.
二.实现原理
布局写好了,在写java代码获取图片之前,让我们先来看看PC端的浏览器是如何获取一张图片的。通过httpwatch这款抓包工具,很简单的帮我们了解获取过程。
在我们输入网址敲击回车之后,浏览器是实际上做了一个http的get方式的网络请求,返回的resultCode是200,代表请求成功,我们还可以看一下左边具体流的数据,
蓝色线标注的是请求的路径;
黑色线代表的是接收的类型;
红色线代表的是浏览器的一些数据;
黄色线代表的是访问的主机;
而右边的内容则是服务器返回的消息
Content-Length代表的是返回数据的总长度,这里也就是图片的大小;
下面的一些乱码则是传输回来的数据。通过这个抓包的过程,我们知道从互联网上获取图片的操作无非就是http的一个get请求。我们需要指定一下,请求的路径,然后把返回回来的数据拿到。一张图片就获取到了。同样的,我们安卓客户端的代码也可以仿照这样去写。
三.代码实现
MyActivity.java
1 package com.example.getNetPic; 2 3 import android.app.Activity; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.os.Bundle; 7 import android.os.Handler; 8 import android.os.Message; 9 import android.text.TextUtils; 10 import android.view.View; 11 import android.widget.EditText; 12 import android.widget.ImageView; 13 import android.widget.Toast; 14 15 import java.io.IOException; 16 import java.net.HttpURLConnection; 17 import java.net.MalformedURLException; 18 import java.net.URI; 19 import java.net.URL; 20 import java.util.logging.LogRecord; 21 22 public class MyActivity extends Activity { 23 24 final static int MESSAGE_SHOW_IMG = 0; 25 final static int MESSAGE_RESULT_ERR = 1; 26 27 Handler handler; 28 29 @Override 30 public void onCreate(Bundle savedInstanceState) { 31 super.onCreate(savedInstanceState); 32 setContentView(R.layout.main); 33 handler = new Handler(){ 34 @Override 35 public void handleMessage(Message msg) { 36 // super.handleMessage(msg); 37 switch (msg.what){ 38 case MESSAGE_SHOW_IMG: 39 ((ImageView)findViewById(R.id.img_net)).setImageBitmap((Bitmap)msg.obj); 40 break; 41 case MESSAGE_RESULT_ERR: 42 Toast.makeText(MyActivity.this,"获取失败",Toast.LENGTH_SHORT).show(); 43 break; 44 default: 45 break; 46 } 47 } 48 }; 49 // 为了在真机上演示方便,预先设置了路径 50 ((EditText)findViewById(R.id.et_path)).setText("http://pic19.nipic.com/20120218/3096297_185027882000_2.jpg"); 51 findViewById(R.id.btn_img).setOnClickListener(new View.OnClickListener() { 52 @Override 53 public void onClick(View v) { 54 String path = ((EditText)findViewById(R.id.et_path)).getText().toString().trim(); 55 if (TextUtils.isEmpty(path)){ 56 Toast.makeText(MyActivity.this,"请输入图片的网络路径",Toast.LENGTH_SHORT).show(); 57 } else { 58 requestNetByThread(path); 59 } 60 } 61 }); 62 } 63 64 /* 65 * android4.0以后,为了提高用户体验 66 * 访问网络不能再UIThread中,需开启一个子线程 67 * */ 68 private void requestNetByThread(String path) { 69 new Thread(){ 70 @Override 71 public void run() { 72 //super.run(); 73 requestNet(path); 74 } 75 }.start(); 76 } 77 78 private void requestNet(String path) { 79 try { 80 //1.使用路径包装类URL包装路径 81 URL url = new URL(path); 82 //2.调用openConntention()方法,建立连接 83 /* 84 * 在互联网上,其实有很多访问的类型。 85 * 我们常见的有httpconnection类型,还有httpsconnection这种经过加密的类型, 86 * ftp的类型,这些类型都是基于url的。这里我们需要的是httpUrlconnection. 87 * 这种也是继承自Urlconnnution,所以这里我们进行一下强转 88 * */ 89 HttpURLConnection connection = (HttpURLConnection)url.openConnection(); 90 //3.与互联网(服务器)之间的连接搭好后,进行连接设置 91 //3.1 设置请求方式,这里我们设置成get 92 connection.setRequestMethod("GET"); 93 //3.2设置请求服务器超时的时间,单位是毫秒 94 connection.setConnectTimeout(5000); 95 //下面的设置注掉是因为这些不是必需设置的。 96 //3.3设置读取的超时时间,单位是毫秒 97 //connection.setReadTimeout(60000); 98 //3.4设置其他请求参数 99 //connection.setRequestProperty(); 100 //3.5 接收服务器返回的结果码 101 int code = connection.getResponseCode(); 102 //3.6 对结果码进行判断,200,400,500 103 if (code == 200){ 104 //请求成功,接收服务器返回的流数据 105 /* 106 * 由于是显示图片,我们就直接使用android提供给我们的 107 * BitmapFactroy工具类调用方法将输入流转化成位图bitmap 108 * */ 109 Bitmap bitmap = BitmapFactory.decodeStream(connection.getInputStream()); 110 //3.7 创建一个消息,发送给主线程 111 Message message = new Message(); 112 //3.8 设置消息的数据 113 message.obj = bitmap; 114 //3.9 设置消息的类型值 115 message.what = MESSAGE_SHOW_IMG; 116 //4.0 发送消息到handler 117 handler.sendMessage(message); 118 } else{ 119 //请求失败,同样也发送消息 120 Message message = new Message(); 121 //3.9 设置消息的类型值 122 message.what = MESSAGE_RESULT_ERR; 123 //4.0 发送消息到handler 124 handler.sendMessage(message); 125 } 126 127 } catch (Exception e) { 128 e.printStackTrace(); 129 //失败,同样也发送消息 130 Message message = new Message(); 131 //3.9 设置消息的类型值 132 message.what = MESSAGE_RESULT_ERR; 133 //4.0 发送消息到handler 134 handler.sendMessage(message); 135 } 136 } 137 }
记得,最后添加访问网络权限。
效果图:
注意:
访问网络的操作会比较耗时。谷歌为了提高用户体验,在4.0以上的系统里,谷歌做了一个判断,请求访问网络的操作是不允许在主线程里执行的。
重点代码:
URL url = new URL(path); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(5000); //connection.setReadTimeout(60000); //connection.setRequestProperty(); int code = connection.getResponseCode();
其他:
这个demo也隐含涉及了其他的知识点:
1.非UI线程使用Hnadler向UI线程发送消息
2.ANR
3.Bitmap
另外,我已经将代码上传到这里