Năm 2013, Square đã giới thiệu OkHttp một thư viện bên thứ ba ra đời để cách mạng hóa cách chúng ta xử lý các yêu cầu mạng HTTP. OkHttp không chỉ đơn giản hóa quá trình mà còn mang lại hiệu suất và độ tin cậy vượt trội. Hiểu rõ và nắm vững OkHttp sẽ trang bị cho bạn một công cụ mạnh mẽ để xây dựng các ứng dụng Android linh hoạt, phản hồi nhanh chóng và tối ưu tài nguyên.
OkHttp Android
Như tôi đã đề cập, ban đầu, Android chỉ cung cấp hai HTTP client chính: HttpURLConnection và Apache HTTP Client để gửi và nhận dữ liệu từ web. Mỗi client này yêu cầu bạn viết rất nhiều boilerplate code bên trong AsyncTask hoặc các phương thức của background thread. Hơn nữa, chúng có những hạn chế riêng khi cần hủy một yêu cầu HTTP hoặc khi thực hiện connection-pooling một kỹ thuật quan trọng để tái sử dụng các kết nối mạng nhằm giảm độ trễ và tải cho máy chủ.
OkHttp Android cung cấp một triển khai các giao diện của HttpURLConnection và Apache Client bằng cách hoạt động trực tiếp trên nền tảng java Socket mà không cần thêm bất kỳ phụ thuộc nào.
Những lợi ích nổi bật của OkHttp
OkHttp mang đến nhiều lợi ích đáng kể giúp việc quản lý yêu cầu mạng trở nên dễ dàng và hiệu quả hơn:
- Connection pooling (Quản lý nhóm kết nối)
- Gziping (Nén Gzip)
- Caching (Bộ nhớ đệm)
- Recovering from network problems (Phục hồi từ sự cố mạng)
- Redirects (Chuyển hướng)
- Retries (Thử lại)
- Support for synchronous and asynchronous calls (Hỗ trợ gọi đồng bộ và bất đồng bộ)
CSynchronous và Asynchronous calls
Khi làm việc với OkHttp, bạn có hai cách tiếp cận chính để gửi yêu cầu mạng: Synchronous (đồng bộ) và Asynchronous (bất đồng bộ).
- Synchronous calls (Gọi đồng bộ):
- Các cuộc gọi
Synchronousyêu cầu mộtwrapperAsyncTaskbao quanh chúng. Điều này có nghĩa là yêu cầu sẽ chặnmain threadcho đến khi nhận được phản hồi, tiềm ẩn nguy cơ gâyANR(Application Not Responding) nếu thời gian chờ quá lâu. - Hơn nữa, cách tiếp cận này không hỗ trợ hủy một yêu cầu đang thực thi một cách dễ dàng.
- Ngoài ra,
AsyncTasksthường gây raleak contextcủaActivity, điều này không được khuyến khích trong phát triển Android do có thể dẫn đến rò rỉ bộ nhớ.
- Các cuộc gọi
- Asynchronous Calling (Gọi bất đồng bộ):
- Gọi
Asynchronouslà cách tiếp cận được khuyến nghị. Nó thực hiện yêu cầu trên mộtbackground threadmà không chặnmain thread, đảm bảo ứng dụng luôn phản hồi. - Nó hỗ trợ hủy bỏ yêu cầu một cách tự nhiên (
native cancelling), gắn thẻ nhiều yêu cầu (tagging multiple requests), và hủy tất cả chúng chỉ bằng một lệnh gọi phương thức duy nhất (bằng cách gọicanceltrênActivity instancebên trong phương thứconPausehoặconDestroy). Điều này giúp quản lý tài nguyên hiệu quả hơn và tránh các lỗi không mong muốn.
- Gọi
Trước khi chúng ta đi sâu vào triển khai OkHttp cho Android, hãy thêm các dependency và permission cần thiết.
Thêm dependency sau vào file build.gradle của module ứng dụng:
implementation 'com.squareup.okhttp3:okhttp:3.4.1'
Thêm quyền truy cập internet vào file AndroidManifest.xml của bạn:
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
Các ví dụ code OkHttp
Bây giờ, chúng ta sẽ xem xét các ví dụ code cụ thể để minh họa cách sử dụng OkHttp cho cả yêu cầu đồng bộ và bất đồng bộ, cùng với các tác vụ phổ biến khác.
MainActivity.java cho các cuộc gọi Synchronous được đưa ra dưới đây.
package com.journaldev.okhttp;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
OkHttpClient client = new OkHttpClient();
TextView txtString;
public String url= "<https://reqres.in/api/users/2>";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtString= (TextView)findViewById(R.id.txtString);
OkHttpHandler okHttpHandler= new OkHttpHandler();
okHttpHandler.execute(url);
}
public class OkHttpHandler extends AsyncTask {
OkHttpClient client = new OkHttpClient();
@Override
protected String doInBackground(String...params) {
Request.Builder builder = new Request.Builder();
builder.url(params[0]);
Request request = builder.build();
try {
Response response = client.newCall(request).execute();
return response.body().string();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
txtString.setText(s);
}
}
}
Đối với các cuộc gọi Asynchronous, MainActivity.java nên được định nghĩa như sau:
package com.journaldev.okhttp;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
TextView txtString;
public String url= "<https://reqres.in/api/users/2>";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtString= (TextView)findViewById(R.id.txtString);
try {
run();
} catch (IOException e) {
e.printStackTrace();
}
}
void run() throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
txtString.setText(myResponse);
}
});
}
});
}
}
Chúng tôi đã sử dụng một test API từ reqres.in. Chuỗi phản hồi trả về có định dạng JSON và sẽ được in ra màn hình. Bạn có thể thử các open source API khác như Github API, Stackoverflow, v.v. để thực hành.
Ví dụ OkHttp Query Parameters
Nếu bạn cần truyền các query parameters (tham số truy vấn) trong URL của mình, bạn có thể dễ dàng thêm chúng bằng cách sử dụng lớp HttpUrl.Builder.
HttpUrl.Builder
HttpUrl.Builder urlBuilder = HttpUrl.parse("<https://httpbin.org/get>").newBuilder();
urlBuilder.addQueryParameter("website", "www.journaldev.com");
urlBuilder.addQueryParameter("tutorials", "android");
String url = urlBuilder.build().toString();
Request request = new Request.Builder()
.url(url)
.build();
Ví dụ này cho thấy cách bạn thêm các cặp key-value vào URL của mình trước khi xây dựng Request. URL trên được lấy từ https://resttesttest.com/.
Ví dụ OkHttp Android Headers
Nếu có bất kỳ authenticated query parameters nào hoặc bạn cần truyền thông tin bổ sung cho máy chủ (ví dụ: token xác thực), bạn có thể thêm chúng dưới dạng headers như sau:
Request request = new Request.Builder()
.header("Authorization", "replace this text with your token")
.url("your api url")
.build();
Phương thức header() của RequestBuilder cho phép bạn thêm các HTTP header tùy chỉnh. Điều này rất hữu ích cho các tác vụ như xác thực API (Authorization Bearer Token) hoặc cung cấp thông tin về loại nội dung (Content-Type).
Xử lý phản hồi JSON
Khi nhận được phản hồi từ máy chủ, đặc biệt là khi dữ liệu có định dạng JSON, chúng ta cần phân tích cú pháp nó để trích xuất thông tin cần thiết. Bạn có thể parse dữ liệu JSON để lấy các tham số liên quan và hiển thị chúng trong một TextView như đoạn code dưới đây:
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
JSONObject json = new JSONObject(myResponse);
// Giả sử cấu trúc JSON là {"data": {"first_name": "...", "last_name": "..."}}
txtString.setText(json.getJSONObject("data").getString("first_name")+ " "+json.getJSONObject("data").getString("last_name"));
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
Bên trong onResponse, chúng ta chuyển đổi response body thành String. Sau đó, chúng ta sử dụng JSONObject và getString() hoặc getJSONObject() để phân tích cú pháp JSON và trích xuất các giá trị mong muốn. Luôn nhớ xử lý JSONException để đảm bảo ứng dụng không gặp sự cố nếu dữ liệu JSON không đúng định dạng.
Ví dụ OkHttp Android POST
Cho đến nay, chúng ta đã xem xét cách lấy phản hồi bằng cách gọi các API (GET request). Để gửi dữ liệu lên máy chủ (thực hiện POST request), chúng ta cần xây dựng request theo cách sau:
public class MainActivity extends AppCompatActivity {
public String postUrl= "<https://reqres.in/api/users/>";
public String postBody="{\\\\n" +
" \\\\"name\\\\": \\\\"morpheus\\\\",\\\\n" +
" \\\\"job\\\\": \\\\"leader\\\\"\\\\n" +
"}";
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
postRequest(postUrl,postBody);
} catch (IOException e) {
e.printStackTrace();
}
}
void postRequest(String postUrl,String postBody) throws IOException {
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, postBody);
Request request = new Request.Builder()
.url(postUrl)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("TAG",response.body().string());
}
});
}
}
Trong đoạn code trên, chúng ta sử dụng lớp MediaType của OkHttp để định nghĩa loại dữ liệu đang được truyền đi (ở đây là JSON với application/json; charset=utf-8). RequestBody.create() tạo ra một RequestBody từ chuỗi JSON của chúng ta. Phương thức post(RequestBody body) được gọi trên RequestBuilder với RequestBody phù hợp để chỉ định đây là một POST request. Chúng ta sử dụng URL API thử nghiệm từ https://reqres.in/. Sau khi yêu cầu thành công, Log hiển thị phản hồi có dạng: {"name":"morpheus","job":"leader","id":"731","createdAt":"2017-01-03T17:26:05.158Z"}.
Kết luận
Việc nắm vững OkHttp sẽ trang bị cho bạn một kỹ năng nền tảng vững chắc để xây dựng các ứng dụng Android mạnh mẽ, có khả năng giao tiếp mượt mà với các dịch vụ web. Đây cũng là HttpClient được khuyến nghị và sử dụng bên trong thư viện Retrofit Networking một thư viện cao cấp hơn để định nghĩa API, giúp viết code gọn gàng và dễ bảo trì hơn rất nhiều. Việc hiểu OkHttp là cánh cửa mở ra việc học hỏi và làm chủ các thư viện cấp cao hơn như Retrofit.