1. 程式人生 > >Android進階:網路與資料儲存—步驟1:Android網路與通訊(第1小節:網路操作)

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如何從伺服器獲取資料

  1. 例項化一個URL物件
  2. 獲取HttpURLConnection物件
  3. 設定請連線屬性
  4. 獲取響應碼,判斷連結結果碼
  5. 讀取輸入流並解析

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解析;