今天的實作是抓取網路資料( ・_・)ノ
抓取網路資料並顯示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處理的用法,有機會有想到再說吧~
就先這樣囉,掰逼~
您好我照著您說的步驟做了 最後會跑progressdialog跑不完,查看底下是說no adapter attached skippping layout 而我開您在github的專案直接執行也會跑不出來,想請問您知道發生什麼事情了嗎?
哈囉感謝留言 會造成這個原因是因為RecyclerView的Adapter沒有被初始化所導致 你說你載了我的專案一樣無法顯示,主要是因為程式在跑Thread裡面的Try-Catch裡面掛了 所以圈圈跑不完,而導致閃退 我在猜你應該是直接用我程式中的API https://api.myjson.com/bins/15majc <-這個網頁來跑程式的 但是那個API有一定的時效,我現在也不能用了 所以如果你複製我的程式,那他會跑到catch裡面,造成介面無法顯示 因此建議你重頭操作,做一個跟我一模一樣的API吧 或者可以試試看別種連接網路資料的方法,像是可以嘗試我寫的這篇文章的方法 碼農日常-『Android studio』以okHttp第三方庫取得網路資料(POST、GET、WebSocket) ->https://thumbb13555.pixnet.net/blog/post/325387050-okhttp 這篇的API沒有時效問題,歡迎參考喔
您好謝謝您的回覆 我用的API是某遊戲的官方API logd裡面可以看得到撈出的資料 另外我有將您的github上的程式從新改寫成撈該遊戲的API 仍然出現那個錯誤QQ 我會參考您寫的另一篇文章的 謝謝!
了解,因為程式都是我自己打的並非轉載,理論上不該會出錯 應用另一篇文章中的okHttp的套件的話會更符合現在的開發需求,所以我自己是比較推薦那篇的方法 感謝你的回復,祝你開發順利
你好! 我有個問題想請問,我是按照你的方法去抓API下來,用的是公用的API,但是這個程式碼會一直跑progressdialog,請問是哪裡出了問題? 有參考你的另一篇以(okHttp第三方庫取得網路資料) 就可以順利存取JSON資料,不過卻不知道該如何跟RecycleView去做介面連結, 有去看你的另一篇RecycleView教學,也有參考這篇RecycleView的json解析,但是不知道該如何讓下面->網路程式碼,變成你教學的okHttp形式,然後去解析okHttp所GET到的json資料,把它轉換成RecycleView介面顯示出來 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;i2020-11-29 02:20 
碼農日常 2020-11-29 18:28
想移植到okHttp的話,不如你先去完成okHttp那篇的內容 然後這樣寫 JSONArray jsonArray= new JSONArray(String.valueOf(“API的回傳放這邊”)); for (int i =0;i<jsonArray.length();i++){ JSONObject jsonObject = jsonArray.getJSONObject(i); . . . 如果回傳是正確的JSON的話,應該就有想要的結果了
您好!有一些小問題想請教您,我照著您的思路去製作recyclerview,我們小組是打算做一個app的積分排行,收入各種資訊,想請問下,從網站抓下來的json格式ˊ資料,都有顯示在recyclerview(參考你的製作方式),但是唯獨email的顯示卻失效,就是在email的欄位無法顯示抓下來的eamil json資料,例如:需要顯示出 terry@gmail.com等這樣的資訊,請問這是因為email顯示需要透過不同的方式才能顯示嗎? 請問是在layout的部分作修改嗎?
嗨你好,不好意思回得比較晚(昨天我的部落格掛了好幾個小時) 如果你是用我這篇的做法的話,那首要的任務是檢查你的字串有沒有打錯(尤其是JsonObject的key字串,那裏很容易出錯) 一般來說Email大多都還是算字串格式,跟型別應該比較無關 這部分是檢查"源頭"的部分 再來就是UI的部分,這部分就是你RecyclerView item的部分了,layout寫得不熟悉的話,很容易有設定錯誤就是了 提供兩個原因供你參考囉 此外解析資料我建議使用GSON第三方資源庫做json字串解析 相關介紹在此 -> https://thumbb13555.pixnet.net/blog/post/328611247-gsonconvert 加油囉~希望你能夠成功解決問題
您好!我是樓上那位,非常感謝您的回覆!關於eamil的JSON回傳問題,我檢查過這2個地方,目前沒有看出問題所在,我在懷疑是不是網站架設失敗,導致一部分json資料沒有回傳....?可能要等負責後端的同學,架好雲端在跟他要URL了,我測試用的網站是這個「https://designer.mocky.io/manage 」 然後目前觀看了安卓農大老的文章「上下滑動排序以及側滑刪除」,試著把功能加進去之後不是很明白為什麼這段程式碼 recyclerViewAction(recyclerView, arrayList, myAdapter);括號中的 arraylist下面會有紅線,有google了文章,但是想不明白.... 請問會是什麼問題呢? 如果解決了這問題,基本上就能跟我的recyclerView做滑動跟刪除動作了? P.s 安卓農大老的文章,真的很棒!!對菜鳥的幫助超多的!!!!
是說你上一封的"email"(eamil)也拼錯了,所以你的索引該不會是拼錯的吧..如果拼錯那還真的死都收不到 R:"我在懷疑是不是網站架設失敗,導致一部分json資料沒有回傳....?" 這就不是我能幫上忙的範疇了,我無法得知你們的JSON都傳了什麼,自然也沒辦法處理 R:"recyclerViewAction(recyclerView, arrayList, myAdapter);括號中的 arraylist下面會有紅線" 這個arrayList輸入的型別是一個ArrayList<String>,也就是說你輸入的東西必須要是一個ArrayList包String型別的陣列才行 該編文章的arrayList來源處在這邊,再多參考一下吧 public class MainActivity extends AppCompatActivity { String[] A2J = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"}; ArrayList<String> arrayList = new ArrayList<>(); RecyclerView recyclerView; MyAdapter myAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //生成資料 for (int i = 0; i < A2J.length; i++) { arrayList.add(A2J[i]); } . . . } . . . } R:"如果解決了這問題,基本上就能跟我的recyclerView做滑動跟刪除動作了?" 有照著我那篇的內容寫的話,應該就會出來了 不會,希望你們也可以在我這裡找到需要的知識!