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
Synchronous
yêu cầu mộtwrapper
AsyncTask
bao quanh chúng. Điều này có nghĩa là yêu cầu sẽ chặnmain thread
cho đế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,
AsyncTasks
thường gây raleak context
củ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
Asynchronous
là cách tiếp cận được khuyến nghị. Nó thực hiện yêu cầu trên mộtbackground thread
mà 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ọicancel
trênActivity instance
bên trong phương thứconPause
hoặ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
.