今天來寫關於"使用okHttp3抓取網路資料(API)"的方法
其實關於抓取網路資料的方法也不少,除了原生程式語言搭載的HttpURLConnection之外
幾個比較常被使用的第三方庫就是Retrofit、volley以及今天要聊的okHttp了ヾ(☆▽☆)
我在去年有曾經寫過一篇用java中的HttpURLConnection完成抓取網路資料的方法
在這邊->碼農日常-『Android studio』取得網路資料(JSON格式)並以RecyclerView顯示列表
可以去稍微看一下捧場一下(笑)
那今天的內容就稍微進階一點,差不多的功能我們改使用okHttp來完成(´・з・)
首先上今天的功能吧!
以及Github
->https://github.com/thumbb13555/OkHttpExample
喔還有,附上今天範例中的API網址
GET網址->https://jsonplaceholder.typicode.com/posts/1
POST網址->https://jsonplaceholder.typicode.com/posts
WebSocket網址->wss://echo.websocket.org
0.如何知道一個API所需要的參數?
如題,我覺得這還蠻重要的XD
當你開始一個專案時,或許你家的後端工程師會告訴你他哪些API需要哪些參數
但或許...你家的後端工程師可能會因為各種各樣的原因沒辦法很全面的告訴你(`▽´)
又或者...根本沒有這個人的存在(喂!)<-我就是這種情況....
也因此,這邊要來聊一下如何查看一個API所需要的內容
首先複製一下上面"GET"的那串API
打開google瀏覽器,把剛才的API貼上網址列後,先不要按Endter,打開開發者工具
然後點擊NetWork
接著在網址列那邊按下Enter
這時候會看到一個檔案,點擊他獲取詳細資訊
當然,實際專案不太可能這麼單純(´υ`)
如果有遇到一些奇怪的要求的話,歡迎來信跟我討論!
1.載入庫、畫介面、加入網路權限、建立介面連接
這些起手式很單純,我就直接一個一個帶過o(メ・・)=日☆
1-1 載入庫
這次要載入的庫是okHttp,其官網在這
->https://github.com/square/okhttp
以我而言,我會一次加入以下三個
implementation 'com.squareup.okhttp3:okhttp:4.7.2' implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.7.2' implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2'
第一個是okHttp的本體,這應該很單純d(>_・ )
另外兩個是可以監視API與APP之間的傳輸狀態用的,後面會介紹
1-2 畫介面
介面部分也直接貼給你就好
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button_POST" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="傳送post" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/button_GET" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/guideline4" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.9" /> <Button android:id="@+id/button_GET" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="傳送GET" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/guideline4" /> <TextView android:id="@+id/text_Respond" android:layout_width="0dp" android:layout_height="0dp" android:text="回傳" android:padding="10dp" android:textAppearance="@style/TextAppearance.AppCompat.Large" app:layout_constraintBottom_toTopOf="@+id/editText_WebSocket" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button_WebSocket" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="接收WebSocket" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/button_GET" app:layout_constraintTop_toTopOf="@+id/guideline4" /> <EditText android:id="@+id/editText_WebSocket" android:layout_width="0dp" android:layout_height="wrap_content" android:ems="10" android:hint="傳送WebSocket" android:inputType="textPersonName" app:layout_constraintBottom_toTopOf="@+id/guideline4" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
1-3 加入權限
既然是跟網路連結有關的APP,當然要加入網路權限許可摟!
請在AndroidManifest.xml內加入以下權限吧
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jetec.okhttpexample"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
1-4 建立介面連接
來到MainActivity,我們先建立基礎就好
可以先輸入以下內容就好
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btPost = findViewById(R.id.button_POST); Button btGet = findViewById(R.id.button_GET); Button btWebSocket = findViewById(R.id.button_WebSocket); /**傳送GET*/ btGet.setOnClickListener(v -> { }); /**傳送POST*/ btPost.setOnClickListener(v -> { }); /**傳送WebSocket*/ btWebSocket.setOnClickListener(v -> { }); } }
2.重點來惹(・∀・)
沒錯!!重點來惹(•ө•)♡(•ө•)♡(•ө•)♡
...冷(╯=▃=)╯︵┻━┻
好啦總之就是要寫主功能了
2-1 撰寫POST
private void sendPOST() { TextView tvRes = findViewById(R.id.text_Respond); /**建立連線*/ OkHttpClient client = new OkHttpClient().newBuilder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) .build(); /**設置傳送所需夾帶的內容*/ FormBody formBody = new FormBody.Builder() .add("userId", "1") .add("id", "1") .add("title", "Test okHttp") .build(); /**設置傳送需求*/ Request request = new Request.Builder() .url("https://jsonplaceholder.typicode.com/posts") .post(formBody) .build(); /**設置回傳*/ Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { /**如果傳送過程有發生錯誤*/ tvRes.setText(e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { /**取得回傳*/ tvRes.setText("POST回傳:\n" + response.body().string()); } }); }
重點在這↓
/**設置傳送所需夾帶的內容*/ FormBody formBody = new FormBody.Builder() .add("userId", "1") .add("id", "1") .add("title", "Test okHttp") .build(); /**設置傳送需求*/ Request request = new Request.Builder() .url("https://jsonplaceholder.typicode.com/posts") .post(formBody) .build();
FormBody是POST中攜帶資料的方法
像是如果是需要帳號密碼的POST-API
可以這樣寫
/**設置傳送所需夾帶的內容*/ FormBody formBody = new FormBody.Builder() .add("username", "123456@gmail.com") .add("password", "000000123456") .build();
這時候把副程式加入點擊事件
/**傳送POST*/ btPost.setOnClickListener(v -> { sendPOST(); });
完成後,就可以試著執行囉(・`ω´・)
2-2 撰寫GET
接下來是GET的寫法
private void sendGET() { TextView tvRes = findViewById(R.id.text_Respond); /**建立連線*/ OkHttpClient client = new OkHttpClient().newBuilder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) .build(); /**設置傳送需求*/ Request request = new Request.Builder() .url("https://jsonplaceholder.typicode.com/posts/1") // .header("Cookie","")//有Cookie需求的話則可用此發送 // .addHeader("","")//如果API有需要header的則可使用此發送 .build(); /**設置回傳*/ Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { /**如果傳送過程有發生錯誤*/ tvRes.setText(e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { /**取得回傳*/ tvRes.setText("GET回傳:\n" + response.body().string()); } }); }
這時候執行,就是以下畫面囉
2-3 撰寫WebSocket
WebSocket?瀏覽器插座??
有沒有人是第一次看到這個東西呢XD
WebSocket這東西稍微比較特別,他的網址列不是http開頭的
像這樣->wss://echo.websocket.org
而WebSocket的作用是推播變動較迅速的資料
什麼意思?(´・д・`)
像是通訊軟體中的聊天功能
像是溫度濕度之即時資料
像是遠端儀器之操作即時情況
要被即時掌握的東西,都是使用WebSocket傳送
而在Android 裡面,他會以一個監聽器的樣式出現
如下,就是它的寫法
private void sendWebSocket() { EditText edWebSocket = findViewById(R.id.editText_WebSocket); TextView tvRes = findViewById(R.id.text_Respond); /**設置傳送需求*/ Request request = new Request.Builder() .url("wss://echo.websocket.org") .build(); /**建立連線*/ OkHttpClient client = new OkHttpClient().newBuilder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) .build(); /**設置WebSocket監聽器*/ client.newWebSocket(request, new WebSocketListener() { /**回傳WebSocket已關閉時做的事情*/ @Override public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosed(webSocket, code, reason); } /**回傳WebSocket關閉時所做的事情*/ @Override public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosing(webSocket, code, reason); } /**回傳WebSocket連線失敗時的回傳*/ @Override public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { super.onFailure(webSocket, t, response); tvRes.setText("WebSocket回傳(錯誤):\n" + response + "\n" + t); } /**回傳WebSocket取得到的String回傳*/ @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { super.onMessage(webSocket, text); tvRes.setText("WebSocket回傳:\n" + text); } /**回傳WebSocket取得到的ByteArray回傳*/ @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) { super.onMessage(webSocket, bytes); } /**回傳WebSocket開始時所需做的動作*/ @Override public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { super.onOpen(webSocket, response); webSocket.send(edWebSocket.getText().toString()); // webSocket.cancel();//想斷開連線的話請加這行 } }); /**清除並關閉執行緒*/ client.dispatcher().executorService().shutdown(); }
相關的回傳我有標註註解了,請參閱一下吧:D
這個WebSocket的測試是源自這邊
->https://www.websocket.org/echo.html
這是官方給的一個測試用的API,原則上就是你打什麼他就回你什麼,請參考!
最後的效果就會是這樣囉
最後是MainActivity的全部內容
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btPost = findViewById(R.id.button_POST); Button btGet = findViewById(R.id.button_GET); Button btWebSocket = findViewById(R.id.button_WebSocket); /**傳送GET*/ btGet.setOnClickListener(v -> { sendGET(); }); /**傳送POST*/ btPost.setOnClickListener(v -> { sendPOST(); }); /**傳送WebSocket*/ btWebSocket.setOnClickListener(v -> { sendWebSocket(); }); } private void sendGET() { TextView tvRes = findViewById(R.id.text_Respond); /**建立連線*/ OkHttpClient client = new OkHttpClient().newBuilder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) .build(); /**設置傳送需求*/ Request request = new Request.Builder() .url("https://jsonplaceholder.typicode.com/posts/1") // .header("Cookie","")//有Cookie需求的話則可用此發送 // .addHeader("","")//如果API有需要header的則可使用此發送 .build(); /**設置回傳*/ Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { /**如果傳送過程有發生錯誤*/ tvRes.setText(e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { /**取得回傳*/ tvRes.setText("GET回傳:\n" + response.body().string()); } }); } private void sendPOST() { TextView tvRes = findViewById(R.id.text_Respond); /**建立連線*/ OkHttpClient client = new OkHttpClient().newBuilder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) .build(); /**設置傳送所需夾帶的內容*/ FormBody formBody = new FormBody.Builder() .add("userId", "1") .add("id", "1") .add("title", "Test okHttp") .build(); /**設置傳送需求*/ Request request = new Request.Builder() .url("https://jsonplaceholder.typicode.com/posts") .post(formBody) .build(); /**設置回傳*/ Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { /**如果傳送過程有發生錯誤*/ tvRes.setText(e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { /**取得回傳*/ tvRes.setText("POST回傳:\n" + response.body().string()); } }); } private void sendWebSocket() { EditText edWebSocket = findViewById(R.id.editText_WebSocket); TextView tvRes = findViewById(R.id.text_Respond); /**設置傳送需求*/ Request request = new Request.Builder() .url("wss://echo.websocket.org") .build(); /**建立連線*/ OkHttpClient client = new OkHttpClient().newBuilder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) .build(); /**設置WebSocket監聽器*/ client.newWebSocket(request, new WebSocketListener() { /**回傳WebSocket已關閉時做的事情*/ @Override public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosed(webSocket, code, reason); } /**回傳WebSocket關閉時所做的事情*/ @Override public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosing(webSocket, code, reason); } /**回傳WebSocket連線失敗時的回傳*/ @Override public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { super.onFailure(webSocket, t, response); tvRes.setText("WebSocket回傳(錯誤):\n" + response + "\n" + t); } /**回傳WebSocket取得到的String回傳*/ @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { super.onMessage(webSocket, text); tvRes.setText("WebSocket回傳:\n" + text); } /**回傳WebSocket取得到的ByteArray回傳*/ @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) { super.onMessage(webSocket, bytes); } /**回傳WebSocket開始時所需做的動作*/ @Override public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { super.onOpen(webSocket, response); webSocket.send(edWebSocket.getText().toString()); // webSocket.cancel();//想斷開連線的話請加這行 } }); /**清除並關閉執行緒*/ client.dispatcher().executorService().shutdown(); } }
3 補充介紹HttpLogging
以上完成之後,重新回頭介紹HttpLogging
我們把程式都寫了,但搞了半天還不知道這是幹嘛吧XD
別擔心,這時候到編輯器的LogCat輸入 “OkHttp”
就可以看到所有的連接狀態囉!
結語
網路連接API我覺得算是現在寫前端中不只必學,而且極致重要(゚д゚;)
而所有的API也為了預防中間人攻擊,以及越權獲取資料,所以才有了header、token、cookie等等需要加入的東西
經過層層握手後,才能順利取得需要的資訊
我原本也一直在思考,到底本篇要不要寫WebSocket
因為大部分的文章都只有寫POST跟GET,通常WebSocket都是另外寫的
甚至我也在想要不要用WebSocket直接寫一個聊天室〓D
後來再三考慮後,就決定變成這個樣子了ㄏ
那希望今天的文章可以幫助到大家( ・ω・)ノ
留言列表