今天的實作是抓取網路資料( ・_・)ノ
抓取網路資料並顯示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/
我找的資料:臺中市停車位概況-區內路外身心障礙專用停車位
製作JSON的網站
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
還要記得activity_main.xml要加入元件
以上,Android studio這邊都準備完成了(・ω・)b
接著我們要準備網站資料
相信大多數的人都像我一樣沒有自己的伺服器可以放資料,也沒有寫後端(´・_・`)
因此資料可能要想辦法自己弄
我這邊提供一個方法,可以讓你測試寫的APP
首先我的資料來源是 臺中市停車位概況-區內路外身心障礙專用停車位的公開資料
這些資料只要你上google打:台中市資料開放平臺
就可以找到很多資料
然後再資料內,找幾個有提供JSON格式的檔案
然後點進去會進到這個頁面
再來點這個
然後點這個
就可以下載囉(b^_^)b
或者你想要指定取幾筆,或者排序
可以往下拉,然後點這個
就會進到這個頁面
這邊的話可以指定筆數喔~自己試試看吧(`▽´)
接著,資料下載後,先看用什麼編輯器打開
裡面就是json格式
然後複製裡面的資料,打開這個網站
貼上去(●`・皿・)
恩...當機了==
基本上是資料量太大啦
此外因為這資料是中文索引,所以會當機
如果存放英文索引就沒問題囉
備註:少量的中文索引資料亦可
另外也實測過,中文索引不會影響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處理的用法,有機會有想到再說吧~
就先這樣囉,掰逼~
留言列表