您的位置:首页 > 理论基础 > 计算机网络

【攻克Android (41)】HttpURLConnection

2015-09-08 17:43 806 查看
[size=large]本文围绕以下三个部分展开: [/size]

[size=large]一、HttpURLConnection[/size]
[size=large]HttpURLConnection案例一:Get、Post方式访问网络[/size]
[size=large]HttpURLConnection案例二:异步加载图片[/size]


[size=large]一、HttpURLConnection[/size]

[size=medium]1. HTTP[/size]

[size=medium](1)“一次连接”:[/size]

[size=medium]HTTP通信中客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。[/size]

[size=medium](2)[/size]

[size=medium]要保持客户端程序的在线状态,需要不断向服务器发起连接请求。[/size]

[size=medium]通常的做法是即使不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端(下线),若客户端长时间无法收到服务器的回复,则认为网络已经断开。[/size]


[size=medium]2. Get和Post[/size]

[size=medium]HTTP通信中,请求网络使用最多的是 GET 和 POST 方式。[/size]

[size=medium](1)Get方式:[/size]

[size=medium]Get请求可以获取静态页面,也可以把参数放在Url字符串的后面,传递给服务器。[/size]

[size=medium]发送表单数据时:它以重写 URL 的方式,提交数据到服务器。URL 最大长度不超过 1K 。[/size]

[size=medium](2)Post方式[/size]

[size=medium]发送表单数据时:其参数不是放在URL字符串里面,而是放在Http请求数据中。[/size]

[size=medium](3)HttpURLConnection的Get和Post[/size]

[size=medium]访问不需要传递参数的网页,只需要打开一个HttpURLConnection连接,然后取得流中的数据,完成之后,关闭连接。[/size]

[size=medium]需要传递参数时:由于HttpURLConnection默认使用GET方式,所以如果要使用POST方式,则需要setRequestMethod设置,然后将我们要传递的参数内容通过writeBytes写入数据流。[/size]


[size=medium]3. URLConnection[/size]

[size=medium](1)抽象类 URLConnection是所有类的超类,它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。[/size]

[size=medium](2)通常,创建一个到 URL 的连接需要以下几个步骤:[/size]

[size=medium]1)通过在 URL 上调用 openConnection方法创建连接对象。[/size]

[size=medium]2)操作设置参数和一般请求属性。[/size]

[size=medium]3)使用connect方法建立到远程对象的实际连接。[/size]

[size=medium]4)远程对象变为可用。远程对象的头字段和内容变为可访问。[/size]


[size=medium]4. HttpURLConnection[/size]

[size=medium](1)概念[/size]

[size=medium]HttpURLConnection是Java标准类,继承自URLConnection类。[/size]

[size=medium]URLConnection与HttpURLConnection是都是抽象类,无法直接实例化对象。 其对象主要是通过URL的openConnection方法获得。[/size]

[size=medium]openConnection只是创建URLConnection或者HttpURLConnection实例,但是并不真正进行的连接操作。并且,每次openConnection都将创建一个新的实例。因此,在连接之前需要对一些属性进行操作,比如:Http超时的时间等。[/size]

[size=medium](2)开发步骤[/size]


[size=large]HttpURLConnection案例[/size]

[size=medium]一、服务器端[/size]

[size=medium]myeclipse开发好服务器端程序,然后将其打包成war包,放到tomcat下面,启动tomcat。[/size]

[align=center][/align]

[align=center][/align]

[align=center][/align]

[align=center][/align]

[align=center][/align]

[align=center][/align]


[size=medium]二、Android端[/size]

[align=center][/align]

[size=medium]1. AndroidManifest.xml。授予此 APP 访问网络的权限。[/size]

<!-- 1. 授予此 APP 访问网络的权限 -->
<user-permission android:name="android.permission.INTERNET" />



[size=medium]2. strings.xml。[/size]

<resources>
<string name="app_name">LoginWeb</string>

<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>

<string name="hint_username">用户名</string>
<string name="hint_password">密码</string>
<string name="btn_do_get">doGet</string>
<string name="btn_do_post">doPost</string>
</resources>



[size=medium]3. activity_main.xml[/size]

[align=center][/align]

<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=".MainActivity">

<EditText
android:id="@+id/txtUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_username" />

<EditText
android:id="@+id/txtPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_password"
android:inputType="textPassword" />

<Button
android:id="@+id/btnDoGet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_do_get" />

<Button
android:id="@+id/btnDoPost"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_do_post" />

</LinearLayout>



[size=medium]4. 要用到Butter Knife,因此用在build.gradle中导包。[/size]

[align=center][/align]


[size=medium]5. StreamTool。处理 云端服务器返回客户端的流(对于客户端而言,是输入流) 的工具类。[/size]

package com.android.loginweb;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

/**
* 处理 云端服务器返回客户端的流(对于客户端而言,是输入流) 的工具类
*/
public class StreamTool {
/**
* 从输入流中获取数据(可以是文本/图片)
*
* @param in 输入流
* @return
* @throws Exception
*/
public static byte[] readInputStream(InputStream in) throws Exception {
// Byte 数组输出流
ByteArrayOutputStream out = new ByteArrayOutputStream();
// 定义缓存 -- 1k
byte[] buffer = new byte[1024];
int len = 0;
// !=-1 :没有结束
while ((len = in.read(buffer)) != -1) {
// 从0开始写到len,读了多长就写多长。写到 缓存 buffer 里面。
out.write(buffer, 0, len);
}
in.close();
// 返回输出流输出的字节数组
return out.toByteArray();
}
}


[align=center][/align]


[size=medium]6. LoginService。登录的业务逻辑类。[/size]

package com.android.loginweb;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
* 登录的业务逻辑处理类
*/
public class LoginService {
/**
* doGet: 以重写 URL 的方式提交数据到服务器,URL 最大长度不超过 1K
*
* @param username
* @param password
* @return
*/
public static String doGet(String username, String password) {
try {
String urlpath = "http://192.168.1.124:8090/androidcloud/LoginServlet?username="
+ username + "&password=" + password;

/*
(1)请求云端服务器
*/
// 创建 url 路径对象
URL url = new URL(urlpath);
// 通过 url 打开网络连接
// url.openConnection() : 相当于在浏览器地址栏输入后,敲回车键访问网络。
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 设置网络超时时间:5s
conn.setConnectTimeout(5000);
// 设置请求方式为 GET
conn.setRequestMethod("GET");

/*
(2)云端服务器响应请求
*/
// 响应代码
int code = conn.getResponseCode();
// code == 200 :OK状态,表示正常响应、正常请求。
if (code == 200) {
// 从连接中获得响应的输入流
InputStream in = conn.getInputStream();
// 通过流工具类(StreamTool)把输入流数据转换为 字符串
return new String(StreamTool.readInputStream(in));
} else {
return "";
}
} catch (Exception e) {
return "";
}
}

/**
* doPost: 流的方式,浏览器直接把数据写给服务器
*
* @param username
* @param password
* @return
*/
public static String doPost(String username, String password) {
try {
// 路径后面没有附加的参数
String urlpath = "http://192.168.1.124:8090/androidcloud/LoginServlet";
// 返回的参数
String data = "username=" + username 20000 + "&password=" + password;

/*
请求云端服务器
*/
URL url = new URL(urlpath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("POST");

/*
在Web开发的时候,是自动把数据发送到服务器的。
但是,Android开发中,需要用以下几行代码写出来。
*/
// 内容类型 -- 指定是来自表单的数据
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// 内容长度
conn.setRequestProperty("Content-Length", String.valueOf(data.length()));
// 浏览器把数据写给服务器 -- 客户端向服务器输出
conn.setDoInput(true);
OutputStream out = conn.getOutputStream();
out.write(data.getBytes());

int code = conn.getResponseCode();
if (code == 200) {
InputStream in = conn.getInputStream();
return new String(StreamTool.readInputStream(in));
} else {
return "";
}
} catch (Exception e) {
return "";
}
}
}



[size=medium]7. MainActivity。写按钮事件,点击按钮提交表单到服务器。获得返回的数据后,更新UI。[/size]

package com.android.loginweb;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

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

import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;

public class MainActivity extends Activity {
@InjectView(R.id.txtUsername)
EditText txtUsername;
@InjectView(R.id.txtPassword)
EditText txtPassword;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 注册 butterknife
ButterKnife.inject(this);
}

// 按钮事件
@OnClick(R.id.btnDoGet)
public void doGetClick() {
// 注:此处,使用 new Thread(){ ... runOnUIThread() ...}.start() 处理耗时任务

// 获得 用户名和密码 这两个值ֵ
final String username = txtUsername.getText().toString();
final String password = txtPassword.getText().toString();

// 提交数据到服务器
new Thread(new Runnable() {
@Override
public void run() {
// 获得云端服务器返回的数据
final String result = LoginService.doGet(username, password);
// 非 UI 线程开启 UI 线程,然后通过 UI Thread 更新界面
runOnUiThread(new Runnable() {
@Override
public void run() {
if (TextUtils.isEmpty(result)) {
Toast.makeText(getApplicationContext(), "连接网络失败!",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), result,
Toast.LENGTH_SHORT).show();
// 登录成功,可以更新 UI 界面,也可以跳转到另外一个界面
}
}
});
}
}) {
}.start();
}

@OnClick(R.id.btnDoPost)
public void doPostClick() {
// 注:此处,使用 Async Task 处理耗时任务

LoginTask task = new LoginTask();
task.execute("");

}

private class LoginTask extends AsyncTask<String, Integer, String> {

/**
* 在异步任务的后台方法中访问云端服务器
*
* @param params
* @return
*/
@Override
protected String doInBackground(String... params) {
// 获得 用户名和密码 这两个值
String username = txtUsername.getText().toString();
String password = txtPassword.getText().toString();

// 在异步任务中访问网络
String result = LoginService.doPost(username, password);
return result;
}

/**
* 后台方法处理完后,更新 UI 界面
*
* @param result
*/
@Override
protected void onPostExecute(String result) {
if (TextUtils.isEmpty(result)) {
Toast.makeText(getApplicationContext(), "连接网络失败!",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), result,
Toast.LENGTH_SHORT).show();
// 登录成功,可以更新 UI 界面,也可以跳转到另外一个界面
}
}
}


// -------------------------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}



[size=large]HttpURLConnection案例二[/size]

[size=medium]一、服务器端[/size]

[size=medium]同案例一。[/size]


[size=medium]二、Android端[/size]

[align=center][/align]

[size=medium]1. AndroidManifest.xml。授予此 APP 访问网络的权限。[/size]

<!-- 1. 授予此 APP 访问网络的权限 -->
<user-permission android:name="android.permission.INTERNET" />



[size=medium]2. strings.xml。[/size]

<resources>
<string name="app_name">LoginWeb</string>

<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>

<string name="hint_image_url">Image URL</string>
<string name="btn_load_image">Load Image</string>
</resources>



[size=medium]3. activity_main.xml[/size]

[align=center][/align]

<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=".MainActivity">

<EditText
android:id="@+id/txtUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_image_url"
android:inputType="textUri" />

<Button
android:id="@+id/btnLoadImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_load_image" />

<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/app_name" />

</LinearLayout>



[size=medium]4. 要用到Butter Knife,因此用在build.gradle中导包。[/size]

[align=center][/align]

[size=medium]5. MainActivity。异步加载图片,显示在imageView的位置上。[/size]

package com.android.loginweb;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

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

import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;

public class MainActivity extends Activity {
@InjectView(R.id.txtUrl)
EditText txtUrl;
@InjectView(R.id.imageView)
ImageView imageView;

// (1)
private String urlpath;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 注册 butterknife
ButterKnife.inject(this);
}

/**
* (2)
*/
@OnClick(R.id.btnLoadImage)
public void loadImageClick(){
urlpath = txtUrl.getText().toString();
LoadImageTask imageTask = new LoadImageTask();
imageTask.execute(urlpath);
}

/**
* (3) 获取网络图片任务
*
*
*/
private class LoadImageTask extends AsyncTask<String, Integer, Bitmap> {
/**
* 在 doInBackground之前被调用,在ui线程执行
*/
@Override
protected void onPreExecute() {
// 设置 imageView 处的图片为空
imageView.setImageBitmap(null);
}

/**
* 在后台线程中执行的任务
*/
@Override
protected Bitmap doInBackground(String... params) {
InputStream inputStream = null;
Bitmap imgBitmap = null;
try {
URL url = new URL(urlpath);
if (url != null) {
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setConnectTimeout(2000);
connection.setDoInput(true);
// 用 get 方法取图片(post是提交数据)
connection.setRequestMethod("GET");
int code = connection.getResponseCode();
if (200 == code) {
// 输入流中有图片
inputStream = connection.getInputStream();
// 通过 BitmapFactory 把 输入流 变为一张图片
imgBitmap = BitmapFactory.decodeStream(inputStream);
}
}
} catch (Exception e) {
return null;
}
// 这里不是UI线程,故不能直接 使用 setImage(imgBitmap) 直接更新 UI 界面。
return imgBitmap;
}

/**
* 在后台线程执行完成之后,调用该方法,获取数据更新界面
* @param result
*/
@Override
protected void onPostExecute(Bitmap result) {
if (result != null) {
Toast.makeText(MainActivity.this, "成功获取图片",
Toast.LENGTH_LONG).show();
imageView.setImageBitmap(result);
} else {
Toast.makeText(MainActivity.this, "获取图片失败",
Toast.LENGTH_LONG).show();
}
}

/**
* 取消任务,在ui线程执行
*/
@Override
protected void onCancelled() {
super.onCancelled();
}
}


// -------------------------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: