Android 从网络上获取数据
1. 普通 J2EE 代码下的网络编程
2. 链接网络权限
<uses-permission android:name="android.permission.INTERNET"/>
3. ImageView : 显示图片的控件
imageView.setImageBitmap(Bitmap);
4. Bitmap : 位图
Bitmap bitmap = BitmapFactory.decodeByteArray(ByteArray, 0, ByteArray.length);
5.代码示例 : 获取网络上的一张图片,显示在 Android 手机上
1. AndroidManifest.xml -- 声明链接网络的权限
2. main.xml -- 布局
3. strings.xml -- 字符串定义
4. Service -- 获取网络上的图片的字节数组
5. Main Activity
6. 从网络上获取视频的最新资讯(从网络上获取 XML 数据)
① 新建 web project -- myvideoweb
本项目采用 struts 为视图层框架
** Service
** Action
** View
** 若访问 http://localhost:8080/myvideoweb/video/list.do,结果为:
② 新建 Android 项目
** Service
③.Main Activity
8 Android JSON
9. 获取 json 格式的数据
**服务器端 Action
** Android 客户端解析获取的 JSON 数据
Service :
** Utils
Main Activity:
10. 将数据以 GET 方式提交到网络上的 Web 应用。
** 布局[略]
** Service
** Activity
11. 将数据以 post 方式提交到网络上的 Web 应用。
用 HTTP 协议,将 Android 的数据以 POST 的方式提交到服务器保存。
1. 观察浏览器 POST 方式提交数据时的 HTTP 协议内容:
/* 以下为请求头 */
POST /videoweb/video/manage.do HTTP/1.1 (: 请求方式(POST GET) 请求的路径 所使用的 HTTP 协议版本 )
(:此处无空行 -- 浏览器所能接受的数据类型,最后面的 */* 表示任意)
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost:8080/videoweb/ (: 请求来源的 URl -- 比如,用户通过点击百度搜索结果的链接进入我的网站,我想统计,那么就可以使用这个数据)
Accept-Language: zh-cn,en-us;q=0.5 (: 浏览器所接收的语言)
Content-Type: application/x-www-form-urlencoded (: 实体数据内容类型,x-www-form-urlencoded 为表单默认数据类型,是经过 URL 编码后的数据)
Accept-Encoding: gzip, deflate (: 表示浏览器可接受的数据类型)
(:此处无空行 -- 用于表示当前使用的浏览器和版本,可以在串中查找 MSIE 6.0 来判断浏览器是否是 IE 6.0)
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; staticlogin:product=cboxf2010&act=login&info=ZmlsZW5hbWU9vfDJvbTKsNQyMDEwxaO98sbsvaKw5i5leGUmbWFjPUQ2OTUzRkRBOUUxNzQyQkNBQUM4QjQ3MDYxMTZDRjdEJnBhc3Nwb3J0PSZ2ZXJzaW9uPTIwMTAuNi4zLjYuMiZjcmFzaHR5cGU9MQ==&verify=7dda578e3701edc6e428f5b6a6d955e2)
Host: 192.168.1.136:8080 (: 来源 IP)
Content-Length: 49 (: 实体数据的内容长度)
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=13360465242B5FA4827B2B19CAB4F673 (: 如果浏览器支持 Cookie 的话,会带有这个值。JSESSIONID 为服务器会话跟踪的一种机制)
(此处一定为空行)
/* 以下为实体数据 */
method=save&name=%E6%82%A8%E5%A5%BD&timelength=23
Post方式提交, HTTP协议所必须的头字段有两个:
Content-Type: application/x-www-form-urlencoded (: 实体数据内容类型,x-www-form-urlencoded 为表单默认数据类型,是经过 URL 编码后的数据)
Content-Length: 49 (: 实体数据的内容长度)
2. 在 客户端手动 POST 提交数据
Service:
布局:
在界面输入文件的位置,点击上传
将这个文件保存在服务器上。
服务器就是一个普通的文件上传处理代码
MainActivity:
读取到界面的参数,调用 Service,传参
乱码问题: POST 方式提交数据发送乱码,就添加编码转换过滤器
Android 还集成了一个第三方开源项目: HttpClient。这个项目是 apache 组织开发的封装了 HTTP 相关操作的方法。
13. 通过 HTTP 协议实现文件上传
/* 定义分隔线 */
HTTP 的 ContentType=multipart/form-data;boundary=------------7da31c29608b2
其实体数据是由分割线分隔开的参数
在使用分隔线的时候有些需要注意:
例如定义的分隔线为:---------------------7da31c29608b2
那么,在实体数据中使用它就变成了:-----------------------7da31c29608b2
多两杠
需要特别注意
并且最后一条分割线的最后还多了两条杠 : -----------------------7da31c29608b2--
这就表示实体数据结束了
其格式具体为:
---------分隔线(回车换行/r/n)
Content-Disposition: form-data; name="name"(回车换行/r/n)
(回车换行/r/n)
实体数据(回车换行/r/n)
---------分隔线(回车换行/r/n)
例:
POST /videoweb/video/manage.do HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost:8080/videoweb/
Accept-Language: zh-cn,en-us;q=0.5
Content-Type: multipart/form-data; boundary=---------------------------7da31cf110e5e
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; staticlogin:product=cboxf2010&act=login&info=ZmlsZW5hbWU9vfDJvbTKsNQyMDEwxaO98sbsvaKw5i5leGUmbWFjPUQ2OTUzRkRBOUUxNzQyQkNBQUM4QjQ3MDYxMTZDRjdEJnBhc3Nwb3J0PSZ2ZXJzaW9uPTIwMTAuNi4zLjYuMiZjcmFzaHR5cGU9MQ==&verify=7dda578e3701edc6e428f5b6a6d955e2)
Host: 192.168.1.136:8080
Content-Length: 580
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=B175C70051A38FAFF324580E59EB4457
-----------------------------7daab2e110e5e
Content-Disposition: form-data; name="method" (注释:这里有一个回车换行 )
(注释: 这里是一个空行,必须的,加上上一行末尾的回车换行,一共是两个回车换行)
save
-----------------------------7daab2e110e5e
Content-Disposition: form-data; name="name"
aaa
-----------------------------7daab2e110e5e
Content-Disposition: form-data; name="timelength"
33
-----------------------------7daab2e110e5e (注释:以下是文件路径)
Content-Disposition: form-data; name="video"; filename="C:/Documents and Settings/Administrator/妗岄潰/鏂板缓 鏂囨湰鏂囨。.txt"
Content-Type: image/pjpeg
这是个测试上传的文本文件内容sssssss (注释:这里上传了一个文本文件,这里是文本文件的内容,本身这里应该是二进制数据的)
-----------------------------7da31cf110e5e-- (注释:实体数据结束,多出两条杠)
具体实现:
最好不再使用以前的 HttpURLConnection 了
以前的方式为:
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
OutputStream outStream = conn.getOutputStream();
outStream.write(data);
但这个类有 BUG :
因为它内部有缓存功能,它会把上传的文件放在内存里面
而一旦我们上传的文件超过了 1M 之后,就有内存溢出的危险。
就只能使用最底层的的格式来拼凑 HTTP 协议内容
上传文件时需要注意的问题:
这个文件的大小是否超出了手机的存储空间大小?
如果这个文件比较大,例如,大于1M,那么就需要一边读一边写。
接下来比较麻烦的就是组装实体数据了
【略】祥见 txt/基于Socket的HTTP协议向服务器上传文件/..
14. 客户端向服务器发送 XML 数据
* 以实体数据的方式发送给服务器,客户端
服务器端接收请求
1. 普通 J2EE 代码下的网络编程
- public class ImageTool {
- public static void main(String[] args) throws Exception {
- String path = "http://hiphotos.baidu.com/z%D6%DCy%D4%C6f%B7%E5/pic/item/6ca59b1de32de389a5866972.jpg";
- URL url = new URL(path);
- // GET 必须大写
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setRequestMethod("GET");
- // 超时 5秒
- conn.setReadTimeout(5 * 1000);
- InputStream is = conn.getInputStream();
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int len;
- while((len = is.read(buffer)) != -1 ) {
- bos.write(buffer, 0, len);
- }
- byte[] data = bos.toByteArray();
- File file = new File("zhiling.jpg");
- FileOutputStream fos = new FileOutputStream(file);
- fos.write(data);
- fos.close();
- }
- }
2. 链接网络权限
<uses-permission android:name="android.permission.INTERNET"/>
3. ImageView : 显示图片的控件
imageView.setImageBitmap(Bitmap);
4. Bitmap : 位图
Bitmap bitmap = BitmapFactory.decodeByteArray(ByteArray, 0, ByteArray.length);
5.代码示例 : 获取网络上的一张图片,显示在 Android 手机上
1. AndroidManifest.xml -- 声明链接网络的权限
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="wjh.android.image"
- android:versionCode="1"
- android:versionName="1.0">
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".ImageActivity"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- <uses-sdk android:minSdkVersion="8" />
- <!-- 访问网络权限 -->
- <uses-permission android:name="android.permission.INTERNET"/>
- </manifest>
2. main.xml -- 布局
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/path"
- />
- <EditText
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="http://hiphotos.baidu.com/z%D6%DCy%D4%C6f%B7%E5/pic/item/6ca59b1de32de389a5866972.jpg"
- android:id="@+id/path"
- />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/button"
- android:id="@+id/button"
- />
- <!-- 显示图片的控件 -->
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/image"
- />
- </LinearLayout>
3. strings.xml -- 字符串定义
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <string name="hello">Hello World, ImageActivity!</string>
- <string name="app_name">图片查看器</string>
- <string name="path">图片路径</string>
- <string name="button">查看</string>
- <string name="error">获取失败</string>
- </resources>
4. Service -- 获取网络上的图片的字节数组
- package wjh.android.image.service;
- import java.io.ByteArrayOutputStream;
- import java.io.InputStream;
- import java.net.HttpURLConnection;
- import java.net.URL;
- public class ImageService {
- public static byte[] catchImage(String path) throws Exception {
- URL url = new URL(path);
- // GET
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setRequestMethod("GET");
- // 超时 5秒
- conn.setReadTimeout(5 * 1000);
- InputStream is = conn.getInputStream();
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int len;
- while((len = is.read(buffer)) != -1 ) {
- bos.write(buffer, 0, len);
- }
- is.close();
- bos.close();
- return bos.toByteArray();
- }
- }
5. Main Activity
- public class ImageActivity extends Activity {
- private Button button;
- private EditText pathText;
- private ImageView imageView;
- private static final String TAG = "ImageActivity";
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- button = (Button) findViewById(R.id.button);
- imageView = (ImageView) findViewById(R.id.image);
- pathText = (EditText) findViewById(R.id.path);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- String path = pathText.getText().toString();
- try {
- // 获取图片数据
- byte[] imageBytes = ImageService.catchImage(path);
- Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
- imageView.setImageBitmap(bitmap);
- } catch (Exception e) {
- Log.i(TAG, e.toString());
- Toast.makeText(ImageActivity.this, R.string.error, Toast.LENGTH_LONG);
- }
- }
- });
- }
- }
6. 从网络上获取视频的最新资讯(从网络上获取 XML 数据)
① 新建 web project -- myvideoweb
本项目采用 struts 为视图层框架
** Service
- public class VideoServiceImpl implements VideoService {
- /**
- * 获取最新视频资讯
- */
- public List<VideoMessage> getLastVideoMesasages() {
- List<VideoMessage> vms = new ArrayList<VideoMessage>();
- vms.add(new VideoMessage(89, "喜羊羊与灰太狼全集", 67));
- vms.add(new VideoMessage(12, "实拍舰载直升东海救援演习", 23));
- vms.add(new VideoMessage(19, "喀麦隆VS荷兰", 90));
- return vms;
- }
** Action
- public class VideoAction extends Action {
- private VideoService service = new VideoServiceImpl();
- @Override
- public ActionForward execute(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- List<VideoMessage> vms = service.getLastVideoMesasages();
- request.setAttribute("videos", vms);
- return mapping.findForward("videoListPage");
- }
- }
** View
- <%@ page language="java" contentType="text/xml; charset=UTF-8"
- pageEncoding="UTF-8"%> <!-- 注意,最好将 XMl 声明放在第一行。否则可能解析出错。 -->
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><?xml version="1.0" encoding="UTF-8"?>
- <videos>
- <c:forEach items="${videos}" var="video">
- <video id="${video.id}">
- <title>${video.title}</title>
- <timelength>${video.timeLength}</timelength>
- </video>
- </c:forEach>
- </videos>
** 若访问 http://localhost:8080/myvideoweb/video/list.do,结果为:
- <?xml version="1.0" encoding="UTF-8" ?>
- - <videos>
- - <video id="89">
- <title>喜羊羊与灰太狼全集</title>
- <timelength>67</timelength>
- </video>
- - <video id="12">
- <title>实拍舰载直升东海救援演习</title>
- <timelength>23</timelength>
- </video>
- - <video id="19">
- <title>喀麦隆VS荷兰</title>
- <timelength>90</timelength>
- </video>
- </videos>
② 新建 Android 项目
** Service
- public class VideoService {
- /**
- * 解析从网络上获取到的 XML 文档。返回解析后的 VideoMessage 集合
- */
- public static List<VideoMessage> getLastVideos() throws Throwable {
- // 注意,这里不能写为 localhost 和 127.0.0.1,因为程序最终会在手机上运行。它们就代表手机本身的 ip 了。这里应该写成电脑的本机 IP Addr.
- String path = "http://10.4.74.20:8080/myvideoweb/video/list.do";
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setConnectTimeout(5 * 1000);
- conn.setRequestMethod("GET");
- InputStream is = conn.getInputStream();
- return parseXML(is);
- }
- /**
- * 从指定输入流中解析出 VideoMessage 集合并返回
- */
- private static List<VideoMessage> parseXML(InputStream is) throws Exception {
- List<VideoMessage> videos = null;
- VideoMessage vs = null;
- XmlPullParser pullParser = Xml.newPullParser();
- pullParser.setInput(is, "UTF-8");
- int eventType = pullParser.getEventType(); // 触发第一个事件
- while (eventType != XmlPullParser.END_DOCUMENT) { // 如果不为文档结束事件
- switch (eventType) {
- case XmlPullParser.START_DOCUMENT: // 文档开始事件
- videos = new ArrayList<VideoMessage>();
- break;
- case XmlPullParser.START_TAG: // 标签开始事件
- if ("video".equals(pullParser.getName())) {
- int id = Integer.parseInt(pullParser.getAttributeValue(0));
- vs = new VideoMessage();
- vs.setId(id);
- }
- if (vs != null) {
- if ("title".equals(pullParser.getName())) {
- vs.setTitle(pullParser.nextText());
- }
- if ("timelength".equals(pullParser.getName())) {
- vs.setTimeLength(Integer.parseInt((pullParser
- .nextText())));
- }
- }
- break;
- case XmlPullParser.END_TAG: // 标签结束事件
- if ("video".equals(pullParser.getName())) {
- videos.add(vs);
- vs = null;
- }
- break;
- }
- eventType = pullParser.next();
- }
- return videos;
- }
- }
③.Main Activity
- public class ManiActivity extends Activity {
- private static final String TAG = "ManiActivity";
- private ListView listView;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- List<VideoMessage> vms = null;
- // 调用 Service
- try {
- vms = VideoService.getLastVideos();
- } catch (Throwable e) {
- e.printStackTrace();
- Log.e(TAG, e.toString());
- Toast.makeText(this, R.string.error, Toast.LENGTH_LONG);
- }
- // 给 ListView 绑定显示数据
- listView = (ListView) findViewById(R.id.listView);
- List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
- for (VideoMessage vm : vms) {
- Map<String, Object> item = new HashMap<String, Object>();
- item.put("title", vm.getTitle());
- item.put("timelength", vm.getTimeLength());
- data.add(item);
- }
- ListAdapter adapter = new SimpleAdapter(this, data, R.layout.item,
- new String[]{"title", "timelength"}, new int[]{R.id.title, R.id.timelength});
- listView.setAdapter(adapter);
- }
- }
8 Android JSON
- public void testJson() throws Throwable {
- // JSONArray 的构造参数,是一个 JSON 数组,数组中的每个元素都是一个标准的 JSON 数据。
- JSONArray jsonArray = new JSONArray("[{name:'wjh', age:10, addr:'sc'}], [{name='java', age=15, addr='arr'}]");
- Log.i(TAG, jsonArray.getJSONObject(0).getString("age")); // print:10
- jsonArray = new JSONArray("[{name:'wjh', age:10, addr:'sc'},{name:'wjh2', age:102, addr:'sc2'}]");
- Log.i(TAG, jsonArray.getJSONObject(1).getString("name")); // print:wjh2
- }
9. 获取 json 格式的数据
**服务器端 Action
- public ActionForward execute(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- VideoForm formbean = (VideoForm)form;
- List<Video> videos = service.getLastVides();
- // 当接收到 format=json 参数时执行 if 内部操作
- if("json".equals(formbean.getFormat())){
- //[{id:32,title:"喀麦隆VS荷兰",timelength:40},{id:32,title:"喀麦隆VS荷兰",timelength:40}]
- // 组装 json 格式的数据
- StringBuilder json = new StringBuilder();
- json.append('[');
- for(Video video : videos){
- json.append('{');
- json.append("id:").append(video.getId()).append(',');
- json.append("title:/"").append(video.getTitle()).append("/",");
- json.append("timelength:").append(video.getTimelength());
- json.append('}');
- json.append(',');
- }
- json.deleteCharAt(json.length()-1);
- json.append(']');
- request.setAttribute("json", json);
- return mapping.findForward("json");
- }else{
- request.setAttribute("videos", videos);
- return mapping.findForward("video");
- }
- }
** Android 客户端解析获取的 JSON 数据
Service :
- public static List<VideoMessage> getLastVideosFromJSON() throws Throwable {
- String path = "http://10.4.74.20:8080/videoweb/video/list.do?format=json";
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setConnectTimeout(5 * 1000);
- conn.setRequestMethod("GET");
- InputStream is = conn.getInputStream();
- // 从输入流中解析出 JSON 的字符串格式数据
- byte[] bytes = ReadeStreamUtil.readStreamTobyteArr(is);
- String jsonStr = new String(bytes, "UTF-8");
- // JSON 接收的参数必须是 数组格式的 JSON 字符串数据,例如 "[{name:'XXX',age:'21'},{name:'AAA',age:'222'},{},{}]"
- JSONArray jsonArray = new JSONArray(jsonStr);
- List<VideoMessage> videos = new ArrayList<VideoMessage>();
- for(int i=0; i<jsonArray.length(); i++) {
- JSONObject jsonObject = jsonArray.getJSONObject(i);
- String title = jsonObject.getString("title");
- Integer timelength = Integer.parseInt(jsonObject.getString("timelength"));
- Integer id = Integer.parseInt(jsonObject.getString("id"));
- VideoMessage vs = new VideoMessage();
- vs.setId(id);
- vs.setTimeLength(timelength);
- vs.setTitle(title);
- videos.add(vs);
- }
- return videos;
- }
** Utils
- public class ReadeStreamUtil {
- public static byte[] readStreamTobyteArr(InputStream is) throws Throwable {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int len;
- while((len = is.read(buffer)) != -1 ) {
- bos.write(buffer, 0, len);
- }
- is.close();
- bos.close();
- return bos.toByteArray();
- }
- }
Main Activity:
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- List<VideoMessage> vms = null;
- try {
- // XML 格式
- //vms = VideoService.getLastVideos();
- // JSON 格式
- vms = VideoService.getLastVideosFromJSON();
- } catch (Throwable e) {
- e.printStackTrace();
- Log.e(TAG, e.toString());
- Toast.makeText(this, R.string.error, Toast.LENGTH_LONG);
- }
- listView = (ListView) findViewById(R.id.listView);
- List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
- for (VideoMessage vm : vms) {
- Map<String, Object> item = new HashMap<String, Object>();
- item.put("title", vm.getTitle());
- item.put("timelength", vm.getTimeLength());
- data.add(item);
- }
- ListAdapter adapter = new SimpleAdapter(this, data, R.layout.item,
- new String[]{"title", "timelength"}, new int[]{R.id.title, R.id.timelength});
- listView.setAdapter(adapter);
- }
10. 将数据以 GET 方式提交到网络上的 Web 应用。
** 布局[略]
** Service
- public class VideoService {
- /**
- * 保存视频资讯(向服务器端发送请求实现保存)
- */
- public static boolean save(String name, String timelength) throws Exception {
- String path = "http://10.4.74.20:8080/myvideoweb/video/manage.do";
- Map<String, String> params = new HashMap<String, String>();
- params.put("method", "save");
- params.put("timelength", timelength);
- params.put("name", name);
- return sendGetRequest(path, params, "UTF-8");
- }
- /**
- * 发出一个 可带参数的 GET 请求。
- * @param params 参数键值
- */
- private static boolean sendGetRequest(String path, Map<String, String> params, String encoding) throws Exception {
- StringBuilder pathWithParams = new StringBuilder(path);
- /* 组装参数 ?method=save,name=sss,timelength=20 */
- pathWithParams.append("?");
- for (Map.Entry<String, String> param : params.entrySet()) {
- pathWithParams.append(param.getKey()).append("=")
- /* 应该要对参数进行 URL 编码, 避免中文乱码问题 */
- .append(URLEncoder.encode(param.getValue(),encoding))
- .append("&");
- }
- // 去掉后缀的多余的 '&'符号
- if(pathWithParams.indexOf("&") != -1) {
- pathWithParams.deleteCharAt(pathWithParams.lastIndexOf("&"));
- }
- // 发送请求
- URL url = new URL(pathWithParams.toString());
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setConnectTimeout(5*1000);
- conn.setRequestMethod("GET");
- // 响应码为 200 表示响应成功
- if(conn.getResponseCode() == 200) {
- return true;
- }
- return false;
- }
- }
** Activity
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- timelengthText = (EditText) findViewById(R.id.timelength);
- nameText = (EditText) findViewById(R.id.name);
- button = (Button) findViewById(R.id.button);
- // 为 button 添加点击事件
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- String name = nameText.getText().toString();
- String timelength = timelengthText.getText().toString();
- try {
- // 调用 Service 方法,向服务器发送数据
- boolean status = VideoService.save(name, timelength);
- if(status) {
- Toast.makeText(MainActivity.this, R.string.success, Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(MainActivity.this, R.string.error, Toast.LENGTH_LONG).show();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- }
11. 将数据以 post 方式提交到网络上的 Web 应用。
用 HTTP 协议,将 Android 的数据以 POST 的方式提交到服务器保存。
1. 观察浏览器 POST 方式提交数据时的 HTTP 协议内容:
/* 以下为请求头 */
POST /videoweb/video/manage.do HTTP/1.1 (: 请求方式(POST GET) 请求的路径 所使用的 HTTP 协议版本 )
(:此处无空行 -- 浏览器所能接受的数据类型,最后面的 */* 表示任意)
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost:8080/videoweb/ (: 请求来源的 URl -- 比如,用户通过点击百度搜索结果的链接进入我的网站,我想统计,那么就可以使用这个数据)
Accept-Language: zh-cn,en-us;q=0.5 (: 浏览器所接收的语言)
Content-Type: application/x-www-form-urlencoded (: 实体数据内容类型,x-www-form-urlencoded 为表单默认数据类型,是经过 URL 编码后的数据)
Accept-Encoding: gzip, deflate (: 表示浏览器可接受的数据类型)
(:此处无空行 -- 用于表示当前使用的浏览器和版本,可以在串中查找 MSIE 6.0 来判断浏览器是否是 IE 6.0)
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; staticlogin:product=cboxf2010&act=login&info=ZmlsZW5hbWU9vfDJvbTKsNQyMDEwxaO98sbsvaKw5i5leGUmbWFjPUQ2OTUzRkRBOUUxNzQyQkNBQUM4QjQ3MDYxMTZDRjdEJnBhc3Nwb3J0PSZ2ZXJzaW9uPTIwMTAuNi4zLjYuMiZjcmFzaHR5cGU9MQ==&verify=7dda578e3701edc6e428f5b6a6d955e2)
Host: 192.168.1.136:8080 (: 来源 IP)
Content-Length: 49 (: 实体数据的内容长度)
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=13360465242B5FA4827B2B19CAB4F673 (: 如果浏览器支持 Cookie 的话,会带有这个值。JSESSIONID 为服务器会话跟踪的一种机制)
(此处一定为空行)
/* 以下为实体数据 */
method=save&name=%E6%82%A8%E5%A5%BD&timelength=23
Post方式提交, HTTP协议所必须的头字段有两个:
Content-Type: application/x-www-form-urlencoded (: 实体数据内容类型,x-www-form-urlencoded 为表单默认数据类型,是经过 URL 编码后的数据)
Content-Length: 49 (: 实体数据的内容长度)
2. 在 客户端手动 POST 提交数据
Service:
- /**
- * 发出一个POST请求
- * @param path 请求的路径,不带参数
- * @param params 参数
- * @return
- * @throws Exception
- */
- private static boolean sendPostRequest(String path, Map<String, String> params, String encoding) throws Exception{
- // 组装参数,其格式应该为: method=save&name=liming&timelength=78
- StringBuilder sb = new StringBuilder("");
- if(params!=null && !params.isEmpty()){
- for(Map.Entry<String, String> entry : params.entrySet()){
- sb.append(entry.getKey()).append('=')
- // 防止中文乱码问题,对参数统一进行 URL 编码
- .append(URLEncoder.encode(entry.getValue(), encoding)).append('&');
- }
- // 截取字符串后缀的多余的 "&"
- if(sb.indexOf("&") != -1) {
- sb.deleteCharAt(sb.length() - 1);
- }
- }
- byte[] data = sb.toString().getBytes();//得到实体数据的二进制表示方式
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection)url.openConnection();
- conn.setRequestMethod("POST");
- conn.setConnectTimeout(5*1000);
- //特别注意:通过 post 方式提交数据,必须要设置允许对外输出数据
- conn.setDoOutput(true);
- conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- conn.setRequestProperty("Content-Length", String.valueOf(data.length));
- OutputStream outStream = conn.getOutputStream();
- outStream.write(data);
- outStream.flush();
- outStream.close();
- // 相应码为 200 表示成功
- if(conn.getResponseCode()==200){
- return true;
- }
- return false;
布局:
在界面输入文件的位置,点击上传
将这个文件保存在服务器上。
服务器就是一个普通的文件上传处理代码
MainActivity:
读取到界面的参数,调用 Service,传参
乱码问题: POST 方式提交数据发送乱码,就添加编码转换过滤器
Android 还集成了一个第三方开源项目: HttpClient。这个项目是 apache 组织开发的封装了 HTTP 相关操作的方法。
- /**
- * 使用 HttpClient 完成数据 POST 方式提交到服务器, 不推荐使用这种方式。因为它是开源项目,里面会做很多本应用中没必要的封装,或多或少会影响效率。这对于手机平台,非常敏感。
- */
- private static boolean sendPostRequestHttpClient(String path, Map<String, String> params, String encoding) throws Exception{
- // 组装参数对象
- List<NameValuePair> paramPairs = new ArrayList<NameValuePair>();
- if(params!=null && !params.isEmpty()){
- for(Map.Entry<String, String> entry : params.entrySet()){
- BasicNameValuePair param = new BasicNameValuePair(entry.getKey(), entry.getValue());
- paramPairs.add(param);
- }
- }
- // 被 URL 编码后的实体数据对象
- UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramPairs, encoding);
- HttpPost post = new HttpPost(path);
- post.setEntity(entity);
- DefaultHttpClient client = new DefaultHttpClient();//浏览器
- // org.apache.http.HttpResponse
- HttpResponse response = client.execute(post);
- // 获得状态行 response.getStatusLine(),再获取状态码.(状态码在状态行里面 -- 也就是第一行)
- if(response.getStatusLine().getStatusCode()==200){
- return true;
- }
- return false;
- }
13. 通过 HTTP 协议实现文件上传
/* 定义分隔线 */
HTTP 的 ContentType=multipart/form-data;boundary=------------7da31c29608b2
其实体数据是由分割线分隔开的参数
在使用分隔线的时候有些需要注意:
例如定义的分隔线为:---------------------7da31c29608b2
那么,在实体数据中使用它就变成了:-----------------------7da31c29608b2
多两杠
需要特别注意
并且最后一条分割线的最后还多了两条杠 : -----------------------7da31c29608b2--
这就表示实体数据结束了
其格式具体为:
---------分隔线(回车换行/r/n)
Content-Disposition: form-data; name="name"(回车换行/r/n)
(回车换行/r/n)
实体数据(回车换行/r/n)
---------分隔线(回车换行/r/n)
例:
POST /videoweb/video/manage.do HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost:8080/videoweb/
Accept-Language: zh-cn,en-us;q=0.5
Content-Type: multipart/form-data; boundary=---------------------------7da31cf110e5e
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; staticlogin:product=cboxf2010&act=login&info=ZmlsZW5hbWU9vfDJvbTKsNQyMDEwxaO98sbsvaKw5i5leGUmbWFjPUQ2OTUzRkRBOUUxNzQyQkNBQUM4QjQ3MDYxMTZDRjdEJnBhc3Nwb3J0PSZ2ZXJzaW9uPTIwMTAuNi4zLjYuMiZjcmFzaHR5cGU9MQ==&verify=7dda578e3701edc6e428f5b6a6d955e2)
Host: 192.168.1.136:8080
Content-Length: 580
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=B175C70051A38FAFF324580E59EB4457
-----------------------------7daab2e110e5e
Content-Disposition: form-data; name="method" (注释:这里有一个回车换行 )
(注释: 这里是一个空行,必须的,加上上一行末尾的回车换行,一共是两个回车换行)
save
-----------------------------7daab2e110e5e
Content-Disposition: form-data; name="name"
aaa
-----------------------------7daab2e110e5e
Content-Disposition: form-data; name="timelength"
33
-----------------------------7daab2e110e5e (注释:以下是文件路径)
Content-Disposition: form-data; name="video"; filename="C:/Documents and Settings/Administrator/妗岄潰/鏂板缓 鏂囨湰鏂囨。.txt"
Content-Type: image/pjpeg
这是个测试上传的文本文件内容sssssss (注释:这里上传了一个文本文件,这里是文本文件的内容,本身这里应该是二进制数据的)
-----------------------------7da31cf110e5e-- (注释:实体数据结束,多出两条杠)
具体实现:
最好不再使用以前的 HttpURLConnection 了
以前的方式为:
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
OutputStream outStream = conn.getOutputStream();
outStream.write(data);
但这个类有 BUG :
因为它内部有缓存功能,它会把上传的文件放在内存里面
而一旦我们上传的文件超过了 1M 之后,就有内存溢出的危险。
就只能使用最底层的的格式来拼凑 HTTP 协议内容
上传文件时需要注意的问题:
这个文件的大小是否超出了手机的存储空间大小?
如果这个文件比较大,例如,大于1M,那么就需要一边读一边写。
接下来比较麻烦的就是组装实体数据了
【略】祥见 txt/基于Socket的HTTP协议向服务器上传文件/..
14. 客户端向服务器发送 XML 数据
* 以实体数据的方式发送给服务器,客户端
- public static void main(String[] args) throws Throwable {
- /* 请求路径 */
- String path = "http://192.168.1.10:8080/videoweb/video/manage.do?method=getXML";
- /* XML数据 */
- String xml = "<?xml version=/"1.0/" encoding=/"UTF-8/"?><persons><person id=/"23/"><name>中国</name><age>30</age>< /person></persons>";
- byte[] data = xml.getBytes("UTF-8");
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection)url.openConnection();
- /* 设置 POST 提交参数 */
- conn.setConnectTimeout(5 *1000);
- conn.setRequestMethod("POST");
- conn.setDoOutput(true);
- conn.setRequestProperty("Content-Type", "text/xml; charset=UTF-8");
- conn.setRequestProperty("Content-Length", String.valueOf(data.length));
- /* 链接到服务器的输出流 */
- OutputStream outStream = conn.getOutputStream();
- outStream.write(data);
- outStream.flush();
- outStream.close();
- System.out.println(conn.getResponseCode());
- }
服务器端接收请求
- public ActionForward getXML(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- InputStream inStream = request.getInputStream();
- byte[] data = StreamTool.readInputStream(inStream);
- String xml = new String(data, "UTF-8");
- System.out.println(xml);
- return mapping.findForward("result");
- }