標(biāo)注:本文為個(gè)人學(xué)習(xí)使用,僅做自己學(xué)習(xí)參考使用贸弥,請(qǐng)勿轉(zhuǎn)載和轉(zhuǎn)發(fā)
2018-09-05: 初稿小压,最近學(xué)習(xí)進(jìn)度好慢,參考博主coder-pig
0. 引言
- 前面兩節(jié)主要是一些概念性的東西羽资,Http的協(xié)議以及協(xié)議頭等,有時(shí)間還是需要深入研究一下
- 本節(jié)學(xué)習(xí)的是Http的請(qǐng)求方式之一:HttpURLConnection遵班,除了這種還有一種HttpClient屠升,下一節(jié)來描述
- HttpURLConnection請(qǐng)求復(fù)雜,使用起來比較麻煩狭郑,在4.4版本中HttpURLConnection已經(jīng)被替換稱OkHttp了腹暖!但在實(shí)際開發(fā)中并不會(huì)用HttpURLConnection和HttpClient,因?yàn)閯e人已經(jīng)封裝好的第三方網(wǎng)絡(luò)請(qǐng)求框架翰萨,諸如Volley脏答、android-async-http,loopj等,因?yàn)榫W(wǎng)絡(luò)操作涉及到異步以及多線程以蕴,自己動(dòng)手實(shí)現(xiàn)的話糙麦,比較麻煩,所以實(shí)際開發(fā)的過程還是直接使用的第三方丛肮,但是第三方畢竟也是在這些的基礎(chǔ)上架構(gòu)起來的赡磅,所以還是得從頭來。
1. HttpURLConnection介紹
- 一種多用途宝与、輕量級(jí)的HTTP客戶端焚廊,使用它來進(jìn)行Http操作可以適用于大多數(shù)的應(yīng)用程序,雖然HttpURLConnection的API提供比較簡(jiǎn)單习劫,但是同時(shí)這個(gè)也使我們可以更加容易的去使用和擴(kuò)展它咆瘟,集成至URLConnection,抽象類诽里,無法直接實(shí)例化對(duì)象袒餐。通過調(diào)用openCollection()方法獲得對(duì)象實(shí)例,默認(rèn)是帶gzip壓縮的谤狡;
2. HttpURLConnection的使用步驟
- 使用HttpURLConnection的步驟如下:
創(chuàng)建一個(gè)URL對(duì)象:
URL url = new URL(http://www.baidu.com);
調(diào)用URL對(duì)象的openConnection()來獲取HttpURLConnect對(duì)象實(shí)例:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
設(shè)置Http請(qǐng)求使用的方法灸眼,GET活著POST,活著其他請(qǐng)求方式比如:PUT
conn.setRequestMethod("GET");
設(shè)置連接超時(shí)墓懂,讀取超時(shí)的毫秒書焰宣,以及服務(wù)器希望得到的一些消息頭
conn.setConnectTimeout(6 * 1000);
conn.setReadTimeout(6 * 1000)
調(diào)用getInputStream()方法獲得服務(wù)器返回的輸入流,然后輸入流進(jìn)行讀取了
InputStream in = conn.getInputStream();
最后調(diào)用disconnect()方法將Http連接關(guān)掉
conn.disconnect();
備注
- 除了上面這些外捕仔,有時(shí)后還需要對(duì)相應(yīng)碼進(jìn)行判斷匕积,比如200,
if (conn.getResponseCode() != 200)
然后進(jìn)行一些處理榜跌, - 還有有時(shí)我們并不需要傳遞什么參數(shù)闪唆,而是直接去訪問一個(gè)界面,就可以直接用
final InputStream in = new URL("url").openStream();
然后直接讀流钓葫,不過這個(gè)方法適合于直接訪問頁(yè)面的情況悄蕾,底層實(shí)現(xiàn)其實(shí)也是return openConnection.getInputStream(), 而且我們還不能設(shè)置一些請(qǐng)求頭的東西,看情況定瓤逼。
3. HttpURLConnection使用示例
- 這里我們主要針對(duì)GET和POST請(qǐng)求寫兩種不同的示例笼吟,我們可以
conn.getInputStream()
獲取到的是一個(gè)流库物,所以我們需要寫一個(gè)類霸旗,將流轉(zhuǎn)化為二進(jìn)制數(shù)組,定義為工具類如下:
StreamTool.java
public class StreamTool {
//從流中讀取數(shù)據(jù)
public static byte[] read(InputStream inStream) throws Exception{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len = inStream.read(buffer)) != -1)
{
outStream.write(buffer,0,len);
}
inStream.close();
return outStream.toByteArray();
}
}
3.1 HttpURLConnection發(fā)送GET請(qǐng)求代碼示例
運(yùn)行效果圖:
核心部分代碼:
布局:activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/txtMenu"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#4EA9E9"
android:clickable="true"
android:gravity="center"
android:text="長(zhǎng)按我戚揭,加載菜單"
android:textSize="20sp" />
<ImageView
android:id="@+id/imgPic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<ScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextView
android:id="@+id/txtshow"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</ScrollView>
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
獲取數(shù)據(jù)類:GetData.java
public class GetData {
// 定義一個(gè)獲取網(wǎng)絡(luò)圖片數(shù)據(jù)的方法:
public static byte[] getImage(String path) throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 設(shè)置連接超時(shí)為5秒
conn.setConnectTimeout(5000);
// 設(shè)置請(qǐng)求類型為Get類型
conn.setRequestMethod("GET");
// 判斷請(qǐng)求Url是否成功
if (conn.getResponseCode() != 200) {
throw new RuntimeException("請(qǐng)求url失敗");
}
InputStream inStream = conn.getInputStream();
byte[] bt = StreamTool.read(inStream);
inStream.close();
return bt;
}
// 獲取網(wǎng)頁(yè)的html源代碼
public static String getHtml(String path) throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (conn.getResponseCode() == 200) {
InputStream in = conn.getInputStream();
byte[] data = StreamTool.read(in);
String html = new String(data, "UTF-8");
return html;
}
return null;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private TextView txtMenu, txtshow;
private ImageView imgPic;
private WebView webView;
private ScrollView scroll;
private Bitmap bitmap;
private String detail = "";
private boolean flag = false;
private final static String PIC_URL = "http://ww2.sinaimg.cn/large/7a8aed7bgw1evshgr5z3oj20hs0qo0vq.jpg";
private final static String HTML_URL = "http://www.baidu.com";
// 用于刷新界面
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0x001:
hideAllWidget();
imgPic.setVisibility(View.VISIBLE);
imgPic.setImageBitmap(bitmap);
Toast.makeText(MainActivity.this, "圖片加載完畢", Toast.LENGTH_SHORT).show();
break;
case 0x002:
hideAllWidget();
scroll.setVisibility(View.VISIBLE);
txtshow.setText(detail);
Toast.makeText(MainActivity.this, "HTML代碼加載完畢", Toast.LENGTH_SHORT).show();
break;
case 0x003:
hideAllWidget();
webView.setVisibility(View.VISIBLE);
webView.loadDataWithBaseURL("", detail, "text/html", "UTF-8", "");
Toast.makeText(MainActivity.this, "網(wǎng)頁(yè)加載完畢", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
;
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setViews();
}
private void setViews() {
txtMenu = (TextView) findViewById(R.id.txtMenu);
txtshow = (TextView) findViewById(R.id.txtshow);
imgPic = (ImageView) findViewById(R.id.imgPic);
webView = (WebView) findViewById(R.id.webView);
scroll = (ScrollView) findViewById(R.id.scroll);
registerForContextMenu(txtMenu);
}
// 定義一個(gè)隱藏所有控件的方法:
private void hideAllWidget() {
imgPic.setVisibility(View.GONE);
scroll.setVisibility(View.GONE);
webView.setVisibility(View.GONE);
}
@Override
// 重寫上下文菜單的創(chuàng)建方法
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflator = new MenuInflater(this);
inflator.inflate(R.menu.menus, menu);
super.onCreateContextMenu(menu, v, menuInfo);
}
// 上下文菜單被點(diǎn)擊是觸發(fā)該方法
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.one:
new Thread() {
public void run() {
try {
byte[] data = GetData.getImage(PIC_URL);
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0x001);
}
;
}.start();
break;
case R.id.two:
new Thread() {
public void run() {
try {
detail = GetData.getHtml(HTML_URL);
} catch (Exception e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0x002);
};
}.start();
break;
case R.id.three:
if (detail.equals("")) {
Toast.makeText(MainActivity.this, "先請(qǐng)求HTML先嘛~", Toast.LENGTH_SHORT).show();
} else {
handler.sendEmptyMessage(0x003);
}
break;
}
return true;
}
}
最后別忘了加上聯(lián)網(wǎng)權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
注意事項(xiàng):
- 用handler的原因就不用講了吧~ 另外我們加載html代碼的使用的是webView的loadDataWithBaseURL而非LoadData诱告, 如果用LoadData又要去糾結(jié)中文亂碼的問題,so…用loadDataWithBaseURL就可以不用糾結(jié)那么多了 另外有些頁(yè)面可能需要我們提交一些參數(shù)民晒,比如賬號(hào)密碼:我們只需把對(duì)應(yīng)參數(shù)拼接到url尾部即可精居,比如: http://192.168.191.1:8080/ComentServer/LoginServlet?passwd=123&name=Jack 然后服務(wù)端getParamater("passwd")這樣就可以獲得相應(yīng)的參數(shù)了锄禽,我們請(qǐng)求時(shí)這些東西都會(huì)看得清清楚楚 ,所以說GET方式并不安全靴姿!另外還有一點(diǎn)要注意的就是Android從4.0開始就不允許在非UI線程中進(jìn)行UI操作!
3.2 HttpURLConnection發(fā)送POST請(qǐng)求代碼示例
- 有GET自然有POST沃但,通過openConnection獲取到的HttpURLConnection默認(rèn)是進(jìn)行Get請(qǐng)求的,所以我們使用POST提交數(shù)據(jù)佛吓,應(yīng)提前設(shè)置好相關(guān)的參數(shù):conn.setRequestMethod("POST");
- 設(shè)置
conn.setDoOutput(true);
和conn.setDoInput(true);
允許輸入宵晚、輸出。 - 設(shè)置
conn.setUseCaches(false);
POST方法不能緩存维雇,要手動(dòng)設(shè)置為false淤刃,具體實(shí)現(xiàn)看代碼:
運(yùn)行效果圖:
核心代碼:
PostUtils.java
public class PostUtils {
public static String LOGIN_URL = "http://172.16.2.54:8080/HttpTest/ServletForPost";
public static String LoginByPost(String number,String passwd)
{
String msg = "";
try{
HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection();
//設(shè)置請(qǐng)求方式,請(qǐng)求超時(shí)信息
conn.setRequestMethod("POST");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
//設(shè)置運(yùn)行輸入,輸出:
conn.setDoOutput(true);
conn.setDoInput(true);
//Post方式不能緩存,需手動(dòng)設(shè)置為false
conn.setUseCaches(false);
//我們請(qǐng)求的數(shù)據(jù):
String data = "passwd="+ URLEncoder.encode(passwd, "UTF-8")+
"&number="+ URLEncoder.encode(number, "UTF-8");
//這里可以寫一些請(qǐng)求頭的東東...
//獲取輸出流
OutputStream out = conn.getOutputStream();
out.write(data.getBytes());
out.flush();
if (conn.getResponseCode() == 200) {
// 獲取響應(yīng)的輸入流對(duì)象
InputStream is = conn.getInputStream();
// 創(chuàng)建字節(jié)輸出流對(duì)象
ByteArrayOutputStream message = new ByteArrayOutputStream();
// 定義讀取的長(zhǎng)度
int len = 0;
// 定義緩沖區(qū)
byte buffer[] = new byte[1024];
// 按照緩沖區(qū)的大小,循環(huán)讀取
while ((len = is.read(buffer)) != -1) {
// 根據(jù)讀取的長(zhǎng)度寫入到os對(duì)象中
message.write(buffer, 0, len);
}
// 釋放資源
is.close();
message.close();
// 返回字符串
msg = new String(message.toByteArray());
return msg;
}
}catch(Exception e){e.printStackTrace();}
return msg;
}
}
4. Cookie問題的處理
- 首先要理解兩個(gè)概念:Session和Cookie吱型,Cookie只是Session機(jī)制的一種常用形式逸贾,我們也可以使用其他方式來作為客戶端的一個(gè)唯一表示,這個(gè)由服務(wù)器決定津滞,唯一能夠證明一個(gè)客戶端的標(biāo)識(shí)就好铝侵!
- 除了這種方式外,我們還可以使用URL重寫方法來實(shí)現(xiàn)据沈,所以不能說Session就是Cookie
-
下面有一個(gè)例子可以理解Cookie:原博主通過賬號(hào)登陸學(xué)校的教務(wù)系統(tǒng)哟沫,然后訪問課表信息成功,然后如果你是用的Chrome锌介,按F12進(jìn)入來法模式:來到Resources界面就可以看到我們的Cookies
點(diǎn)擊后我們可以看到里面保存的內(nèi)容嗜诀,由名稱;值孔祸;cookie所在的域(domain)隆敢; cookie所在的目錄(path)Asp.net默認(rèn)為/即根目錄;過期時(shí)間崔慧;Cookie大蟹餍:
我們可以看到請(qǐng)求頭中有一個(gè)Cookie的字段:
恩呢,現(xiàn)在我們把Cookie清掉(或者等幾分鐘)惶室,然后再訪問下述鏈接:
這時(shí)候温自,頁(yè)面竟然自動(dòng)跳回登陸頁(yè)面了!當(dāng)然一些其他的網(wǎng)站可能會(huì)彈出一個(gè)對(duì)話框說 "登陸超時(shí)"之類的東西皇钞!
小結(jié)
- Http請(qǐng)求登陸的一個(gè)簡(jiǎn)單的流程悼泌,一般是登陸的時(shí)候,服務(wù)器通過Set-Cookie相應(yīng)頭夹界,返回一個(gè)Cookie馆里,瀏覽器默認(rèn)保存這個(gè)Cookie,后續(xù)訪問相關(guān)頁(yè)面的時(shí)候會(huì)帶上這個(gè)Cookie,通過Cookie請(qǐng)求頭來完成訪問鸠踪,如果沒Cookie活著Cookie過期丙者,就是提示用戶沒登陸,登陸超時(shí)营密,訪問需要登陸之類的信息械媒!
- 而我們使用HttpClient和HttpURLConnection其實(shí)也就是模擬這一個(gè)流程,登陸后拿到cookie拿著它去發(fā)送請(qǐng)求:關(guān)鍵代碼如下:
獲得Cookie:
conn.getHeaderField("Set-Cookie");
請(qǐng)求時(shí)帶上Cookie:
conn.setRequestProperty("Cookie", cookie);
- 另外评汰,除了這種設(shè)置請(qǐng)求頭的方式外滥沫,還可以用另一種折衷的方式:
URL重寫:
就是在原先請(qǐng)求連接的基礎(chǔ)上,加上一個(gè)…&sessionid=xxxxx的參數(shù)键俱,然后由服務(wù)器來解析兰绣、判斷!GET可以這么寫编振,而Post的寫法如下:
5. 使用HttpURLConnection發(fā)送的PUT請(qǐng)求
- Put請(qǐng)求對(duì)于很多朋友來說可能有點(diǎn)陌生缀辩,畢竟平時(shí)接觸的不多,這個(gè)是需要在POST的基礎(chǔ)上改一些東西就可以使用了踪央!而HttpClient也給我們提供了一個(gè)HttpPut的API臀玄,原博主實(shí)現(xiàn)的代碼如下
public static String LoginByPut(Context mContext, String mobile, String password, int from,
String devid,String version_name, int remember_me) {
String resp = "";
try {
HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection();
conn.setRequestMethod("PUT");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
String data = "mobile=" + mobile + "&password=" + password + "&from=" + from + "&devid=" + "devid"
+ "&version_name=" + "version_name" + "&remember_me=" + remember_me;
;
// 獲取輸出流:
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
writer.write(data);
writer.flush();
writer.close();
// 獲取相應(yīng)流對(duì)象:
InputStream in = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null)
response.append(line);
SPUtils.put(mContext, "session", conn.getHeaderField("Set-Cookie"));
// 資源釋放:
in.close();
// 返回字符串
Log.e("HEHE", response.toString());
return response.toString();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}