2016年5月13日 星期五

Android 元件(Retrofit、GsonFormat) 如何使用第三方程式庫下載 JSON 的資料


Android 元件 Retrofit、JSON

Step1. Android Studio→先進行環境設定 加入網路權限第三方程式庫


       詳細位置:Android Manifest

         程式碼:<uses-permission android:name="android.permission.INTERNET" />


         詳細位置:如下圖

  程式碼: compile 'com.android.support:appcompat-v7:23.2.1'

                  compile 'com.squareup.retrofit2:retrofit:2.0.2'

                   compile 'com.squareup.retrofit2:converter-gson:2.0.2'
                   



Step2. Android Studio→加入第三方外掛程式
      詳細位置:設定→Plugins→搜尋"gsonformat"
      

Step3.網頁→使用Google的外掛程式,格式化JSON網頁
          詳細位置:google頁面→設定→擴充功能→搜尋"Jsonformat"
           


Step4. Android Studio→自動生成 所要對應JSON格式的類別

詳細位置:創造一個類別,名稱=Bean
                 設定 Alt + Insert→GsonFormat→複製網頁內容→貼上→勾選→完成

注意事項:只要截取一個片段,但是需要完整的包覆程式碼

常用技巧:更改變數名稱→ @SerializedNmae("原本的名子")

範例:  @SerializedName("CAT2")
            private String category;



Step5. Android Studio→創造出一個retrofit

詳細位置:產生一個介面,名稱=InterfaceName
注意事項:Call<介面的類別名稱 > 後面產生的方法名稱可以自定義。
             介面中:宣告的變數都是 public、static、final
                           方法()都是  public、static、abstrack
程式碼:  
1. public static final Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("主網址")
        .addConverterFactory(要轉碼的工廠.create())
        .build();
2. @GET("副網址") Call < Bean >  getData ();
   //使用Call這個類別 做一個方法出來,<>型別就是對應到JSON格式的類別
3. InterfaceName  apiService  = retrofit .create (InterfaceName.class);



Step6.→開始下載JSON的資料

詳細位置:→產生一個類別,名稱=DataUtil
                 →類別中建立一個靜態方法(),名稱=loadUtil()
程 式 碼:
public class DataUtil {
    public static void loadUtil () {
    Call<Bean> call =InterfaceName.apiService.getData ();
       call.enqueue(new Callback<Bean>() {
                @Override
                 public void onResponse(Call<Bean> call, Response<Bean> response) {}
 
                @Override
                public void onFailure(Call<Bean> call, Throwable t) {}
           });
                           }

Step7.詳細的介紹、使用Callback<Bean>(),要實現2個抽象的方法

詳細位置:類別 DataUtil

程 式 碼:

public class DataUtil {
public static void loadUtil () {
//apiservice  = 我們create後 打造出的retrofit,
//apiService.getData () = apiservice去讀取資料 (讀取的型別是Call<Bean>)
     Call<Bean> call =InterfaceName.apiService.getData ();
 //由call.,equene=建立一個執行緒,讓app再下載同時還能做其他事情
 //要做什麼事情? 我們就要new一個Callback(Bean)的事件
     call.enqueue(new Callback<Bean>() {
   
           @Override  //成功連線到網路後 我要做什麼?
           public void onResponse( Call<Bean> call ,  Response<Bean> response) {
                //連線到網路,但資料取得失敗
             if (!response.isSuccessful()) {
                     //建立一個日誌
                     String message = "資料取得失敗";
                     Log.d(TAG, message);
                     return; }
                     //連線到網路,成功取得資料
                 //我們要把資料取出,並由一個變數參考到這麼物件
                Bean bean = response.body();
                  Log.d(TAG, "資料取得成功");
                //做一個日誌,並且Log出 Count的訊息(由此可知)
                      
               Log.d(TAG, "count = " + bean.getResult().getCount());  }
 
           @Override  //尚未連線到網路後 我要做什麼?
           public void onFailure(Call<Bean> call, Throwable t) {
           Log.d(TAG, "沒有連線到網路");
                            }
    });
}



Step8.下載完成後,如何取得更多的資料

重點摘要:類別內 創建一個方法

詳細位置:在DataUtil這個類別內部建立一個方法,方法名稱=testlogMessage();

注意事項:避免程式碼過於肥大,所以我們新建一個方法,方便使用。

程 式 碼:

//由步驟7可得知 我們已經連線到網路,成功取得資料,  
//TaipeiAttractionBean bean = response.body();
//所以我們要把接收到資料的bean導入這方法,方便使用

public static void testlogMessage(Bean bean) {

//由JOSN格式我們可以得知每個景點都存在於陣列中
//而每個景點中都有很多getter方法來取得內部的資料(看bean的類別)                             

//我們先做一個迴圈 (i從0開始 ; getCount=取得總共幾個景點 ; i++)
for (int i = 0; i < bean.getResult().getCount(); i++) {

//取得第 i 個景點(會比較複雜 因為是一個巢狀的類別)  
TaipeiAttractionBean.ResultBean.ResultsBean attraction = bean.getResult().getAttractions().get(i);

//取得第i項景點後,我就getter它裡面的內容      
//Log 很重要的是 你要加一個 "" 讓輸出的資訊轉換成為文字
      Log.d(TAG, "----------------------" + i + "----------------------");
      Log.d(TAG, attraction.getAddress()+"");
      Log.d(TAG, attraction.getCategory()+"");
      Log.d(TAG,attraction.getMRT()+"");
      Log.d(TAG, attraction.getImagesURLs());
  }
}




Step9.如何讀取每個景點中的各項資料

詳細位置:類別 DataUtil

注意事項:類別 DataUtil 使用 testlogMessage();這個方法

程 式 碼:

public class DataUtil {
public static void loadUtil () {

    Call<Bean> call =InterfaceName.apiService.getData ();

call.enqueue(new Callback<Bean>() {
   
           @Override
           public void onResponse( Call<Bean> call ,  Response<Bean> response) {
              
             if (!response.isSuccessful()) {
                   String message = "資料取得失敗";
                   Log.d(TAG, message);
                return; }
                  //這邊很重要 我把response響應的資料 存到bean中
            TaipeiAttractionBean bean = response.body();
                 Log.d(TAG, "資料取得成功");

                 //再把bean導入這一個方法
                 testlogMessage(bean);

                                        
            Log.d(TAG, "count = " + bean.getResult().getCount());  }
 
           @Override  
           public void onFailure(Call<Bean> call, Throwable t) {
           Log.d(TAG, "沒有連線到網路");
                            }
    });
}




Step10.解析、分割資訊


詳細位置:在DataUtil這個類別內部建立一個方法,方法名稱=split(String urls);


注意事項:類別內 創建一個方法
                  避免程式碼過於肥大,所以我們新建一個方法,方便使用。
                 如果你發現 資訊是一大串的程式碼,我們可以自己寫程式解析它,並放入一個陣列管理
程 式 碼 :
// 輸入"String" 返回一個陣列<String>
public static List<String> split(String urls) {


  //封裝:如果得到的String是空值 我就return空值
  if (urls == null || urls.length() == 0) {
      return null;}


  //創造陣列=待會要放字串進去
  List<String> list = new ArrayList<>();


  //startString = 找出"http並回傳位置,找不到則回傳-1
  int startString = urls.indexOf("http");


  //先宣endString 型別為int
  int endString;
 
//迴圈開始= 如果startString有找到"http"迴圈就開始,如果找不到迴圈就結束(因為startString=-1)


  while (startString >= 0) {
      
//endString=找到下一個"http"的位置,找不到則回傳-1
endString = urls.indexOf("http", startString + 1);


//如果endString < 0,代表找不到"http"
      if (endString < 0) {
          list.add(urls.substring(startString, urls.length()));
          break;
      }


      //把字串+到陣列,substring(開始的位置,最後的位置-但不包含)
      list.add(urls.substring(startString, endString));


      //開始的位置=最後的位置
      startString = endString;
  }
 //返回一個字串
  return list;
}





Step10.讀取已剖析URL網址的資料
重點摘要:在 testlogMessage();內加入split(String urls);,並寫一個迴圈Log出剖析後的資訊


詳細位置:在DataUtil這個類別內的方法(),方法名稱=testlogMessage();


程 式 碼 :
public static void testlogMessage(Bean bean) {


//List<TaipeiAttractionBean.ResultBean.ResultsBean> test_test =bean.getResult().getAttractions();
//原本景點都存在於陣列中,而現在又要做一個陣列存在於陣列中,所以會比較複雜
//我們需要把第i項景點 裡面的陣列 給紀錄,故在欄位這邊宣告使用。
List<List<String>> imageUrlsList= new ArrayList<>();


for (int i = 0; i < bean.getResult().getCount(); i++) {


//取得第 i 個景點(會比較複雜 因為是一個巢狀的類別)  
TaipeiAttractionBean.ResultBean.ResultsBean attraction = bean.getResult().getAttractions().get(i);


      Log.d(TAG, "----------------------" + i + "----------------------");
      Log.d(TAG, attraction.getAddress()+"");
      Log.d(TAG, attraction.getCategory()+"");
      Log.d(TAG,attraction.getMRT()+"");
      Log.d(TAG, attraction.getImagesURLs());
//步驟1:剖析第i個景點中的網址,並存放在陣列中,並由list參考
      List<String> list = split(attraction.getImagesURLs());
          
//步驟2:再把list放到一個陣列中
imageUrlsList.add(list);
        


               for (int j =0; j<list.size();j++){
               //log出陣列中第j個網址,直到j++<陣列的大小
               Log.d(TAG,list.get(j).toString());
           }
 }}


Step11完整的程式碼

package com.example.andy.lab14_opendata.util;


import android.util.Log;
import com.example.andy.lab14_opendata.beans.TaipeiAttractionBean;
import com.example.andy.lab14_opendata.model.TaipeiAttractions;
import com.example.andy.lab14_opendata.myapp.MyApp;
import com.example.andy.lab14_opendata.observer.Observer;
import com.example.andy.lab14_opendata.service.TaipeiAttractionService;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;


public class DataUtil {
   public static final String TAG = "LoadOpenData";


    public static void loadTaipeiAttractions( ) {


       Call<Bean> call = TaipeiAttractionService.apiService.getData();
            //產生執行緒,並響應一個Callback的事件
            call.enqueue(new Callback<TaipeiAttractionBean>() {


                       @Override
           public void onResponse(Call<Bean> call, Response<Bean> response) {


                             if (!response.isSuccessful()) {
                   String message = "response is unSuccessful " + response.code();
                   Log.d(TAG, message);
                   return;
               }


              //下載完成 把response的資源丟入到bean
               Bean bean = response.body();
               Log.d(TAG, "response.isSuccessful()");
               Log.d(TAG, "count = " + bean.getResult().getCount());
               testlogAttraction(bean);
  }


                       @Override
           public void onFailure(Call<TaipeiAttractionBean> call, Throwable t) {
               Log.d(TAG, "沒有連線到網路");
                         }
       });
}
   public static List<String> split(String urls) {


    
       if (urls == null || urls.length() == 0) {return null;}


              List<String> list = new ArrayList<>();


               int startString = urls.indexOf("http");
               int endString;
       
        while (startString >= 0) {
                       endString = urls.indexOf("http", startString + 1);
                       if (endString < 0) {
                             list.add(urls.substring(startString, urls.length()));
               break;
           }


                     list.add(urls.substring(startString, endString));
                     startString = endString;
       }
               return list;
   }


   //輸入"陣列" 我就執行內容中的程式
   public static void testlogAttraction(Bean bean) {


       List<List<String>> imageUrlsList= new ArrayList<>();
       for (int i = 0; i < bean.getResult().getCount(); i++) {
           
           TaipeiAttractionBean.ResultBean.ResultsBean attraction = bean.getResult().getAttractions().get(i);


           List<TaipeiAttractionBean.ResultBean.ResultsBean> test_test = bean.getResult().getAttractions();
           Log.d(TAG, "----------------------" + i + "----------------------");
           Log.d(TAG, attraction.getAddress()+"");
           Log.d(TAG, attraction.getCategory()+"");
           Log.d(TAG,attraction.getMRT()+"");
           Log.d(TAG, attraction.getImagesURLs());


          
           List<String> list = split(attraction.getImagesURLs());
           imageUrlsList.add(list);


           for (int j =0; j<list.size();j++){
             
               Log.d(TAG,list.get(j).toString());
           }
       }
   }   
}

沒有留言:

張貼留言