Android進階:網路與資料儲存—步驟1:Android網路與通訊(第1小節:網路操作)
網路操作
課程介紹:
- 掌握Android中操作網路的方式,和相關的許可權設定,以及資料解析。
一、網路的基礎知識
1.1客戶端與服務端
- 什麼是客戶端(Client)?
享受服務的每一個使用者
- 什麼是服務端(Server)?
為客戶端提供資源資料
1.2Http協議(超文字傳輸協議)
1.什麼是Http
HTTP 協議即超文字傳送協議(Hypertext Transfer Protocol )
是 Web 聯網的基 礎,也是手機聯網常用的協議之一,HTTP 協議是建立在 TCP 協議之上的一種應用。
- 2.Http與TCP的區別
- 3.Http的特點及工作原理
HTTP 連線最顯著的特點是客戶端傳送的每次請求都需要伺服器回送響應,在請求結束後,會主動釋放連線。
從建立連線到關閉連線的過程稱為“一次連線”。
HTTP 提供了封裝或者顯示資料的具體形式。Socket 提供了網路通訊的能力
- 4.客戶端與服務端的通訊
- 5.客戶端瀏覽器解析HTML的內容
1.3 URL解析
1.一個URL是有哪些部分組成
- 協議、域名、埠、虛擬目錄、引數.....
二、網路請求
概要:
- 1.從伺服器獲取資料
- 2.GET請求
- 3.POST請求
- 4.GET VS POST
2.1如何從伺服器獲取資料
- 例項化一個URL物件
- 獲取HttpURLConnection物件
- 設定請連線屬性
- 獲取響應碼,判斷連結結果碼
- 讀取輸入流並解析
2.2GET請求 VS POST 請求
GET:獲取資料
相當於重新整理朋友圈,返回新的朋友圈訊息給你看(也可以理解去超市(相當於伺服器)買東西(我要什麼..),返回了筆和書本)
POST:提交資料
相當於釋出一條朋友圈,釋出後展示在朋友圈中(相當於把自己的東西打包後,傳送給伺服器,伺服器更新資料展示出來)
區別:一個直接寫在url上面,一個用一個盒子做起來
傳輸角度上:GET是明文的,POST是隱藏的,所以POST更安全
本質上:GET安全的、獲取資料伺服器的資料不會修改,POST提交資料,相當於寫入操作伺服器會被修改
冪等性:指同樣的一次操作,進行一次和多次的操作對系統的資源產生的影響是一樣的,GET是冪等性的,POST提交資料,會對伺服器修改。
可快取:GET獲取資料是可以快取的
2.3程式碼演示GET請求
2.4網路操作可能遇到的注意事項
1.如果直接在主執行緒裡面訪問網路操作(因為網路是一個很耗時的操作),它會被卡死報錯
android.os.NetworkOnMainThreadException
舉個例子:假如在家裡面做飯,煤氣灶上只有一個鍋煮飯,而煮飯時間很長,
鍋被卡死了,你不能鍋煮其他的東西;那麼就要開另外一個鍋(執行緒)
解決方式:另開一個執行緒把網路訪問放在這個執行緒裡執行
new Thread(new Runnable(){
網路請求操作
}).start();
2.網路操作在新開的執行緒下執行,依舊會報錯
Caused by: android.system.ErrnoException: android_getaddrinfo failed: EACCES (Permission denied)
解決方式:因為沒有在AndroidManifest.xml中宣告網路許可權
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
3.完成以上兩點再執行任然會報錯
Only the original thread that created a view hierarchy can touch its views.
因為更新UI不能再子執行緒裡進行,只能在主執行緒更新UI,而網路操作又得在子執行緒裡面進行,在主執行緒操作網路就會閃退
要實現子執行緒更新主執行緒的有什麼辦法呢?Hanlder
或者使用 runOnUiThread();或者mTextView.post(); 這兩個方法都能夠是更新UI操作轉到主執行緒進行
方法1:
runOnUiThread(new Runnable(){
@Override
public void run(){
mTextView.setText(result);//更新UI的操作
}
});
方法2:
mTextView.post(new Runnable(){
@Override
public void run(){
mTextView.setText(result);//更新UI的操作
}
});
GET完整程式碼
try {
//首先拿到URL物件
URL url = new URL("http://www.imooc.com/api/teacher?type=2&page=1");
//開啟url物件的連結,獲得connection連結
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//設定超時時間30s
connection.setConnectTimeout(30 * 1000);
//設定請求方法型別 GET請求獲取資料
connection.setRequestMethod("POST");
//設定請求的屬性
//我希望拿到內容的格式是json
connection.setRequestProperty("Content-Type", "application/json");
//我希望拿到的字符集是UTF-8
connection.setRequestProperty("Charset", "UTF-8");
//我們希望接受到的字符集是UTF-8
connection.setRequestProperty("Accept-Charset", "UTF-8");
//發起連線
connection.connect();
//連線開始後
//獲取資訊
//獲取請求碼
int responseCode = connection.getResponseCode();
//拿到訊息
String repsonseMessage = connection.getResponseMessage();
//如果請求成功
if (responseCode == HttpURLConnection.HTTP_OK) {//如果拿到請求碼為200(HTTP_OK)
//拿到輸入流
InputStream inputStream=connection.getInputStream();
//將將流轉換成字串
result = streamToString(inputStream);
//更新UI只能在主執行緒操作
//通過使用runOnUiThread()方法將更新UI操作放到主執行緒進行
runOnUiThread(new Runnable() {
@Override
public void run() {
//將資料轉化成UTF-8的格式
result=decode(result);
//將拿到的資料展示出來(更新UI)
mTextView.setText(result);
}
});
}else{//如果請求失敗
//TODO:error fail
Log.e(TAG,"run:error code"+responseCode+",message:"+repsonseMessage);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
2.5POST請求
與GET不一樣,它不是直接把引數放在URL上,而是把資料打包好
//首先拿到URL物件type=2&page=1
URL url = new URL("http://www.imooc.com/api/teacher");
使用POST還要多加一些設定
//*使用POST還有設定執行的輸入輸出
connection.setDoOutput(true);
connection.setDoInput(true);
//POST不能使用快取
connection.setUseCaches(false);
POST請求的資料打包
//*POST請求資料打包
String data = "username=" + getEncodeValue("abc") + "&number=" + getEncodeValue("123456");
//輸出流寫入資料
OutputStream outputStream = connection.getOutputStream();
//將我的打包好的資料寫入
outputStream.write(data.getBytes());
outputStream.flush();//如果沒滿,要呼叫flush()方法:重新整理釋放緩衝區的資源
outputStream.close();//用完關閉,防止記憶體洩漏
POST完整程式碼:
try {
//首先拿到URL物件type=2&page=1
URL url = new URL("http://www.imooc.com/api/teacher");
//開啟url物件的連結,獲得connection連結
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//設定超時時間30s
connection.setConnectTimeout(30 * 1000);
//設定請求方法型別 GET請求獲取資料
connection.setRequestMethod("POST");
//設定請求的屬性
//我希望拿到內容的格式是json
connection.setRequestProperty("Content-Type", "application/json");
//我希望拿到的字符集是UTF-8
connection.setRequestProperty("Charset", "UTF-8");
//我們希望接受到的字符集是UTF-8
connection.setRequestProperty("Accept-Charset", "UTF-8");
//*使用POST還有設定執行的輸入輸出
connection.setDoOutput(true);
connection.setDoInput(true);
//POST不能使用快取
connection.setUseCaches(false);
//發起連線
connection.connect();
//*POST請求資料打包
String data = "username=" + getEncodeValue("abc") + "&number=" + getEncodeValue("123456");
//輸出流寫入資料
OutputStream outputStream = connection.getOutputStream();
//將我的打包好的資料寫入
outputStream.write(data.getBytes());
outputStream.flush();//如果沒滿,要呼叫flush()方法:重新整理釋放緩衝區的資源
outputStream.close();//用完關閉,防止記憶體洩漏
//連線開始後
//獲取資訊
//獲取請求碼
int responseCode = connection.getResponseCode();
//拿到訊息
String repsonseMessage = connection.getResponseMessage();
//如果請求成功
if (responseCode == HttpURLConnection.HTTP_OK) {//如果拿到請求碼為200(HTTP_OK)
//拿到輸入流
InputStream inputStream=connection.getInputStream();
//將將流轉換成字串
result = streamToString(inputStream);
//更新UI只能在主執行緒操作
//通過使用runOnUiThread()方法將更新UI操作放到主執行緒進行
runOnUiThread(new Runnable() {
@Override
public void run() {
//將資料轉化成UTF-8的格式
result=decode(result);
//將拿到的資料展示出來(更新UI)
mTextView.setText(result);
}
});
}else{//如果請求失敗
//TODO:error fail
Log.e(TAG,"run:error code"+responseCode+",message:"+repsonseMessage);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
三、資料解析
Json資料解析
Json 全稱 JavaScript Object Natation ,用來描述資料結構,它是基於純文字的資料格式,是一種輕量級的資料交換格式。廣泛應用於 服務端 與 客戶端 的資料互動。
-
格式
Json 以 key-value的形式儲存資料;
Key的取值 為
String
型別;Value的取值 為
String
,boolean
,Number
,陣列
,Object
,null
;Json 串 以
{
開始, 以}
結尾;Json 串中
陣列
是 以[
開始, 以]
結尾;Json 串中
Object
是 以{
開始, 以}
結尾;
其中status:1 表示是int型的;“ ”表示是字串型的;true or false表示是boolean型別的
data:[ ]是一個數組
data數組裡面又有很多物件,物件裡面又有屬性
首先建一個完全對映這個json格式的類
/**
* 完全對映json
*/
public class LessonResult {
//status:1表示整形,如果是"",表示String,如果是true or false表示boolean
private int mStatus;
//data:[]是一個數組,數組裡面又包含很多物件,lesson是對應很多物件
private List<Lesson> mLessons;
public int getStatus() {
return mStatus;
}
public void setStatus(int status) {
mStatus = status;
}
public List<Lesson> getLessons() {
return mLessons;
}
public void setLessons(List<Lesson> lessons) {
mLessons = lessons;
}
@Override
public String toString() {
return "LessonResult{" +
"mStatus=" + mStatus +
", mLessons=" + mLessons +
'}';
}
//data數組裡面的物件類
public static class Lesson {
private int mID;//獲取陣列data裡面的id
private int mLearnerNumber;//獲取陣列data裡面的LearnerNumber
private String mName;//
private String mSmallPictureUrl;
private String mBigPictureUrl;
private String mDescription;
public int getmID() {
return mID;
}
public void setmID(int mID) {
this.mID = mID;
}
public int getmLearnerNumber() {
return mLearnerNumber;
}
public void setmLearnerNumber(int mLearnerNumber) {
this.mLearnerNumber = mLearnerNumber;
}
public String getmName() {
return mName;
}
public void setmName(String mName) {
this.mName = mName;
}
public String getmSmallPictureUrl() {
return mSmallPictureUrl;
}
public void setmSmallPictureUrl(String mSmallPictureUrl) {
this.mSmallPictureUrl = mSmallPictureUrl;
}
public String getmBigPictureUrl() {
return mBigPictureUrl;
}
public void setmBigPictureUrl(String mBigPictureUrl) {
this.mBigPictureUrl = mBigPictureUrl;
}
public String getmDescription() {
return mDescription;
}
public void setmDescription(String mDescription) {
this.mDescription = mDescription;
}
@Override
public String toString() {
return "Lesson{" +
"mID=" + mID +
", mLearnerNumber=" + mLearnerNumber +
", mName='" + mName + '\'' +
", mSmallPictureUrl='" + mSmallPictureUrl + '\'' +
", mBigPictureUrl='" + mBigPictureUrl + '\'' +
", mDescription='" + mDescription + '\'' +
'}';
}
}
}
通過程式碼一步一步獲取json的資料,再返回到上面的類,通過toString方式顯示出來
快速生成getter和setter方法,生成toString方法 MAC快捷鍵command+N;
try {
//新建一個jsonObject物件,獲取我們GET請求得到的result(json格式)
JSONObject jsonObject = new JSONObject(result);
//讀取json物件裡面的status
int status = jsonObject.getInt("status");
LessonResult lessonResult = new LessonResult();
lessonResult.setStatus(status);//將讀到的status給物件賦值
//讀取json裡面的陣列,名稱是data
JSONArray lessons = jsonObject.getJSONArray("data");
//建立一個List每一次都把Item裡面的資料新增進去
List<LessonResult.Lesson> lessonList = new ArrayList<>();
//因為data數組裡面還有很多物件,所以要迴圈取出
if (lessons != null && lessons.length() > 0) {
for (int index = 0; index < lessons.length(); index++) {
//拿到data裡面的每一個item
JSONObject lesson = (JSONObject) lessons.get(index);
//拿到每一個item裡面的物件的變數
int id = lesson.getInt("id");
int learner = lesson.getInt("learner");
String name = lesson.getString("name");
String smallPic = lesson.getString("picSmall");
String bigPic = lesson.getString("picBig");
String description = lesson.getString("description");
//把所有資料拿到後給物件賦值
LessonResult.Lesson lessonItem = new LessonResult.Lesson();
lessonItem.setmID(id);
lessonItem.setmName(name);
lessonItem.setmSmallPictureUrl(smallPic);
lessonItem.setmBigPictureUrl(bigPic);
lessonItem.setmDescription(description);
lessonItem.setmLearnerNumber(learner);
lessonList.add(lessonItem);//每一次都把資料新增到List裡面
}
//迴圈結束把lessonList設定到Lessons裡面
lessonResult.setLessons(lessonList);
}
mTextView.setText(lessonResult.toString());
} catch (JSONException e) {
e.printStackTrace();
}
使用Gson解析資料,谷歌的json解析;