今天的實作是抓取網路資料( ・_・)ノ

抓取網路資料並顯示UI什麼的早就是現在寫APP中必學技能之一了

想想之前還在當菜逼八學生時,覺得從網路抓資料是件很難的事

然後畢業後,面試了幾家APP接案公司

靠北...上機題就是網路抓資料並作UI處理

金甲係哇咧ㄍㄋㄋ_(┐「ε:)_

當然,後來知道應徵公司需要哪些準備後

就盡可能地趕快把不會的補起來

最後才面上了現在待的公司(´・_・`)

好啦扯遠了(´ヘ`;)

敘述一下今天的實作功能

1.從網路撈JSON格式的資料

2.處理撈到的資料,並以RecyclerView顯示於畫面中

先上GitHub

https://github.com/thumbb13555/GetJsonFromNetExample

以及今天完成的功能

 

********************************************************

2020/06/13補充

關於網路抓取資料,後來有在寫了一篇用okHttp完成網路抓取資料的文章

這篇的方法比本篇更好用喔~建議大家可以前往參閱

->碼農日常-『Android studio』以okHttp第三方庫取得網路資料(POST、GET、WebSocket)

********************************************************

 

資料來源:台中市政府資料開放平臺

https://opendata.taichung.gov.tw/

我找的資料:臺中市停車位概況-區內路外身心障礙專用停車位

https://datacenter.taichung.gov.tw/swagger/api-docs/#/20623-05-04-2_%E8%87%BA%E4%B8%AD%E5%B8%82%E5%81%9C%E8%BB%8A%E4%BD%8D%E6%A6%82%E6%B3%81%EF%BC%8D%E5%8D%80%E5%85%A7%E8%B7%AF%E5%A4%96%E8%BA%AB%E5%BF%83%E9%9A%9C%E7%A4%99%E5%B0%88%E7%94%A8%E5%81%9C%E8%BB%8A%E4%BD%8D-Json/d3cdc433_e2d2_40d9_b8ef_0beaaaf41084

製作JSON的網站

http://myjson.com

 

OK,實作開始


首先在創建完APP專案後,先進到AndroidManifest.xml內加入網路許可權限

 

在反藍的部分加入以下

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

增加網路權限

然後先新增一個recyclerview內容(cell)用的檔案:recyclerview_item.xml

https://github.com/thumbb13555/GetJsonFromNetExample/blob/master/app/src/main/res/layout/recyclerview_item.xml

還要記得activity_main.xml要加入元件

https://github.com/thumbb13555/GetJsonFromNetExample/blob/master/app/src/main/res/layout/activity_main.xml

以上,Android studio這邊都準備完成了(・ω・)b


接著我們要準備網站資料

相信大多數的人都像我一樣沒有自己的伺服器可以放資料,也沒有寫後端(´・_・`)

因此資料可能要想辦法自己弄

我這邊提供一個方法,可以讓你測試寫的APP

首先我的資料來源是 臺中市停車位概況-區內路外身心障礙專用停車位的公開資料

這些資料只要你上google打:台中市資料開放平臺

就可以找到很多資料

然後再資料內,找幾個有提供JSON格式的檔案

然後點進去會進到這個頁面

再來點這個

然後點這個

就可以下載囉(b^_^)b

或者你想要指定取幾筆,或者排序

可以往下拉,然後點這個

就會進到這個頁面

這邊的話可以指定筆數喔~自己試試看吧(`▽´)

接著,資料下載後,先看用什麼編輯器打開

裡面就是json格式

然後複製裡面的資料,打開這個網站

http://myjson.com

貼上去(●`・皿・)

恩...當機了==

基本上是資料量太大啦

此外因為這資料是中文索引,所以會當機

如果存放英文索引就沒問題囉

備註:少量的中文索引資料亦可

另外也實測過,中文索引不會影響APP操作,但讀取可能會更花時間

此外這網站他不能存放太多筆資料(超過8筆就會刪),資料不見是很正常滴(ノ`□´)ノ⌒┻━┻

OK接著複製這串網址

就可以在網頁上看到你的資料囉!(`・ω・´)b


好回到APP

首先先建立一個副程式

private void catchData(){
   .
   .
   .
}

然後再onCreate呼叫他

以及先寫好陣列的全域變數

public class MainActivity extends AppCompatActivity {
    String TAG = MainActivity.class.getSimpleName()+"My";
    ArrayList<HashMap<String,String>> arrayList = new ArrayList<>();


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

        }
      private void catchData(){

          .
          .
          .

         }

    }

接著要處理網路接收資料的部分

要特別注意的是:因為網路接收資料是所謂的耗時操作

因此在程式碼內必須寫入一些耗時執行緒的操作

像是繼承Thread啊,或是Handler,或者是AsyneTask都可以(*-ω-)

我自己是習慣用Thread搭配runOnUIThread做使用

於是程式變成這樣(catchData內)

        String catchData = "https://api.myjson.com/bins/15majc";
       
        new Thread(()->{
            try {
                URL url = new URL(catchData);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                InputStream is = connection.getInputStream();
                BufferedReader in = new BufferedReader(new InputStreamReader(is));
                String line = in.readLine();
                StringBuffer json = new StringBuffer();
                while (line != null) {
                    json.append(line);
                    line = in.readLine();
                }
              Log.d(TAG,""+json);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }

        }).start();

紅色的部分是網路連接部分

此外在coding中打完第二行會遇到底線紅色(゚ー゚;

這時候只要加入try的例外處理就可以了

主要是因為程式很貼心地提示你,萬一連不到或無法取得資料

程式才不會崩潰(・ωー)~☆

此外在HttpURLConnection connection = (HttpURLConnection) url.openConnection();

的方法內,也有跳出連線的方法

connection.setReadTimeout(15000);

connection.setConnectTimeout(15000);

可以使用(´・з・)

甚至也可以指定使用POST格式或者GET格式

connection.setRequestMethod("POST");

但是這個是些資料到後端才會用到的,暫時不多提了

最後,在連線後方一個Log,就可以取得讀過來的資料囉(棕色部分)

資料取得像這樣

OK這時候要來觀察索引

一般來說json通常是

{索引:“資料”,索引:“資料”,索引:“資料”,...}

這樣的規則處理

因此我在這個陣列解出來的所有大致是這樣

String RptNo = jsonObject.getString("RptNo");
String RptName = jsonObject.getString("RptName");
String StatCourseNo = jsonObject.getString("StatCourseNo");
String StatCourseName = jsonObject.getString("StatCourseName");
String DataDate = jsonObject.getString("DataDate");
String PlaceNo = jsonObject.getString("PlaceNo");
String PlaceName = jsonObject.getString("PlaceName");
String PeriodNo = jsonObject.getString("PeriodNo");
String PeriodName = jsonObject.getString("PeriodName");
String Complex1 = jsonObject.getString("Complex1");
String ComplexName = jsonObject.getString("ComplexName");
String Complex2 = jsonObject.getString("Complex2");
String Complex2Name = jsonObject.getString("Complex2Name");
String Complex3 = jsonObject.getString("Complex3");
String Complex3Name = jsonObject.getString("Complex3Name");
String Complex4 = jsonObject.getString("Complex4");
String Complex4Name = jsonObject.getString("Complex4Name");
String Complex5 = jsonObject.getString("Complex5");
String Complex5Name = jsonObject.getString("Complex5Name");
String DeriveNo = jsonObject.getString("DeriveNo");
String DeriveExplain = jsonObject.getString("DeriveExplain");
String FValue = jsonObject.getString("FValue");
String SValue = jsonObject.getString("SValue");
String RptDeptNo = jsonObject.getString("RptDeptNo");
String RptDeptName = jsonObject.getString("RptDeptName");
String CreateTime = jsonObject.getString("CreateTime");
String ModifyTime = jsonObject.getString("ModifyTime");

解到眼睛脫窗...(⌐▨_▨)

為了取得所有資料,必須要解JSON陣列

於是寫個for迴圈就好

像這樣

JSONArray jsonArray= new JSONArray(String.valueOf(json));
for (int i =0;i<jsonArray.length();i++){
     JSONObject jsonObject = jsonArray.getJSONObject(i);
     String RptNo = jsonObject.getString("RptNo");
     String RptName = jsonObject.getString("RptName");
     String StatCourseNo = jsonObject.getString("StatCourseNo");
     String StatCourseName = jsonObject.getString("StatCourseName");
     String DataDate = jsonObject.getString("DataDate");
     String PlaceNo = jsonObject.getString("PlaceNo");
     String PlaceName = jsonObject.getString("PlaceName");
     String PeriodNo = jsonObject.getString("PeriodNo");
     String PeriodName = jsonObject.getString("PeriodName");
     String Complex1 = jsonObject.getString("Complex1");
     String ComplexName = jsonObject.getString("ComplexName");
     String Complex2 = jsonObject.getString("Complex2");
     String Complex2Name = jsonObject.getString("Complex2Name");
     String Complex3 = jsonObject.getString("Complex3");
     String Complex3Name = jsonObject.getString("Complex3Name");
     String Complex4 = jsonObject.getString("Complex4");
     String Complex4Name = jsonObject.getString("Complex4Name");
     String Complex5 = jsonObject.getString("Complex5");
     String Complex5Name = jsonObject.getString("Complex5Name");
     String DeriveNo = jsonObject.getString("DeriveNo");
     String DeriveExplain = jsonObject.getString("DeriveExplain");
     String FValue = jsonObject.getString("FValue");
     String SValue = jsonObject.getString("SValue");
     String RptDeptNo = jsonObject.getString("RptDeptNo");
     String RptDeptName = jsonObject.getString("RptDeptName");
     String CreateTime = jsonObject.getString("CreateTime");
     String ModifyTime = jsonObject.getString("ModifyTime");

     HashMap<String,String> hashMap = new HashMap<>();
     hashMap.put("PlaceName",PlaceName);
     hashMap.put("DataDate",DataDate);
     hashMap.put("Car",ComplexName);
     hashMap.put("Type",Complex2Name);
     hashMap.put("Price",Complex3Name);

     arrayList.add(hashMap);
    }

紅色部分記得要將字串轉JSONArray才能用喔

紫色部分是再創建成自己需要的array陣列以供後續顯示

再來一樣把這些東西丟進RecyclerView就可以了

不知道RecyclerView怎麼用可以參考這篇(´υ`)

http://thumbb13555.pixnet.net/blog/post/311803031

那部分先省略,先來執行

這時候你會發現,為何畫面總是慢個一兩秒才出現(*-ω-)

原因是...因為網路抓資料是耗時工作啊.....

所以"耗時"是很正常滴(・ω・`)………..

一般我們都會在宣告Thread前面加入轉圈圈

ProgressDialog dialog = ProgressDialog.show(this,"讀取中"
                ,"請稍候",true);

然後結束加入dialog.dismiss();

讓使用者去等,這樣就不會讓使用者覺得奇怪囉

OK網路部分整體是這樣

public class MainActivity extends AppCompatActivity {
    String TAG = MainActivity.class.getSimpleName()+"My";
    ArrayList<HashMap<String,String>> arrayList = new ArrayList<>();


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


    }

    private void catchData(){
        String catchData = "https://api.myjson.com/bins/15majc";
        ProgressDialog dialog = ProgressDialog.show(this,"讀取中"
                ,"請稍候",true);
        new Thread(()->{
            try {
                URL url = new URL(catchData);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                InputStream is = connection.getInputStream();
                BufferedReader in = new BufferedReader(new InputStreamReader(is));
                String line = in.readLine();
                StringBuffer json = new StringBuffer();
                while (line != null) {
                    json.append(line);
                    line = in.readLine();
                }

                JSONArray jsonArray= new JSONArray(String.valueOf(json));
                for (int i =0;i<jsonArray.length();i++){
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    String RptNo = jsonObject.getString("RptNo");
                    String RptName = jsonObject.getString("RptName");
                    String StatCourseNo = jsonObject.getString("StatCourseNo");
                    String StatCourseName = jsonObject.getString("StatCourseName");
                    String DataDate = jsonObject.getString("DataDate");
                    String PlaceNo = jsonObject.getString("PlaceNo");
                    String PlaceName = jsonObject.getString("PlaceName");
                    String PeriodNo = jsonObject.getString("PeriodNo");
                    String PeriodName = jsonObject.getString("PeriodName");
                    String Complex1 = jsonObject.getString("Complex1");
                    String ComplexName = jsonObject.getString("ComplexName");
                    String Complex2 = jsonObject.getString("Complex2");
                    String Complex2Name = jsonObject.getString("Complex2Name");
                    String Complex3 = jsonObject.getString("Complex3");
                    String Complex3Name = jsonObject.getString("Complex3Name");
                    String Complex4 = jsonObject.getString("Complex4");
                    String Complex4Name = jsonObject.getString("Complex4Name");
                    String Complex5 = jsonObject.getString("Complex5");
                    String Complex5Name = jsonObject.getString("Complex5Name");
                    String DeriveNo = jsonObject.getString("DeriveNo");
                    String DeriveExplain = jsonObject.getString("DeriveExplain");
                    String FValue = jsonObject.getString("FValue");
                    String SValue = jsonObject.getString("SValue");
                    String RptDeptNo = jsonObject.getString("RptDeptNo");
                    String RptDeptName = jsonObject.getString("RptDeptName");
                    String CreateTime = jsonObject.getString("CreateTime");
                    String ModifyTime = jsonObject.getString("ModifyTime");

                    HashMap<String,String> hashMap = new HashMap<>();
                    hashMap.put("PlaceName",PlaceName);
                    hashMap.put("DataDate",DataDate);
                    hashMap.put("Car",ComplexName);
                    hashMap.put("Type",Complex2Name);
                    hashMap.put("Price",Complex3Name);

                    arrayList.add(hashMap);
                }
                Log.d(TAG, "catchData: "+arrayList);

                runOnUiThread(()->{
                    dialog.dismiss();
                    RecyclerView recyclerView;
                    MyAdapter myAdapter;
                    recyclerView = findViewById(R.id.recyclerView);
                    recyclerView.setLayoutManager(new LinearLayoutManager(this));
                    recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
                    myAdapter = new MyAdapter();
                    recyclerView.setAdapter(myAdapter);

                });
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }

        }).start();
    }

   private class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
        .
        .
        .
   }
}

而RecyclerView的Adapter部分則是這樣

public class MainActivity extends AppCompatActivity {
    String TAG = MainActivity.class.getSimpleName()+"My";
    ArrayList<HashMap<String,String>> arrayList = new ArrayList<>();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        catchData();
//        testChineseIndex(); 測試中文索引


    }
    private void catchData(){
        .
        .
        .
    }

    private class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
        public class ViewHolder extends RecyclerView.ViewHolder {

            TextView tvPos,tvType,tvPrice,tvCar,tvDateTime;
            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                tvPos = itemView.findViewById(R.id.textView_pos);
                tvType = itemView.findViewById(R.id.textView_type);
                tvPrice = itemView.findViewById(R.id.textView_price);
                tvCar = itemView.findViewById(R.id.textView_car);
                tvDateTime = itemView.findViewById(R.id.textView_time);
            }
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.recyclerview_item,parent,false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            holder.tvPos.setText(arrayList.get(position).get("PlaceName"));
            holder.tvType.setText("類型:"+arrayList.get(position).get("Type"));
            holder.tvPrice.setText("收費與否:"+arrayList.get(position).get("Price"));
            holder.tvCar.setText("停放種類:"+arrayList.get(position).get("Car"));
            holder.tvDateTime.setText("新增資料時間:"+arrayList.get(position).get("DataDate"));

        }

        @Override
        public int getItemCount() {
            return arrayList.size();
        }
    }
}

結論

這次實作的寫法算是最基礎最簡單的寫法

真的在專案上時,還得考慮沒有連線的處理,連線多久的處理等等

一般我都會再開一個類別,讓類別繼承AsyncTask<Void, Void, String>非同步執行

去處理這堆東西

關於AsyncTask處理的用法,有機會有想到再說吧~

就先這樣囉,掰逼~

 

TK2

 
arrow
arrow
    創作者介紹
    創作者 碼農日常 的頭像
    碼農日常

    碼農日常大小事

    碼農日常 發表在 痞客邦 留言(5) 人氣()