Table of contents
2. Create OkHttpClient object:
4. Send synchronization request:
5. Send an asynchronous request
1. Introduction to okhttp
OkHttp is an open source HTTP client library commonly used in Android applications to make network requests. The following are some common classes and methods of OkHttp:
1. OkHttpClient class:
OkHttpClient is the core class of the OkHttp library and is used to create and configure clients for HTTP requests. It provides various settings and configuration options such as connection timeout, read and write timeout, interceptors, etc.
Commonly used methods:
-
newBuilder()
: Create a new OkHttpClient.Builder instance for configuring and building OkHttpClient objects. -
addInterceptor(Interceptor interceptor)
: Add an Interceptor to handle requests when sending them and receiving responses. For example, you can use interceptors to authenticate, log, or transform requests. -
connectTimeout(long timeout, TimeUnit unit)
: Set the connection timeout. You can specify a time value and time unit to limit the maximum time for a connection to the server. -
readTimeout(long timeout, TimeUnit unit)
: Set the read timeout. You can specify a time value and time unit to limit the maximum time for reading data from the server. -
writeTimeout(long timeout, TimeUnit unit)
: Set the write timeout. You can specify a time value and time unit to limit the maximum time for writing data to the server. -
retryOnConnectionFailure(boolean retry)
: Set whether to retry failed connection requests. By default, OkHttpClient automatically retries requests, but retries can be disabled via this method. -
followRedirects(boolean followRedirects)
: Set whether to automatically follow redirects. By default, OkHttpClient automatically follows redirects, but automatic redirects can be disabled via this method. -
cache(Cache cache)
: Set cache. A Cache object can be passed in to cache the response data of network requests. -
dispatcher(Dispatcher dispatcher)
: Set the Dispatcher scheduler. A custom Dispatcher object can be passed in to control the number of concurrent requests and the behavior of the thread pool. -
proxy(Proxy proxy)
: Set proxy server. You can specify a Proxy object that is used to proxy requests to the target server.
2. Request class:
The Request class is used to construct HTTP requests. You can create a Request object through Request.Builder and set the URL, request method (GET, POST, etc.), request header, request body, etc.
Commonly used methods:
-
newBuilder()
: Create a new Request.Builder instance for building and configuring Request objects. -
url(String url)
: Set the requested URL address. You can pass in a string type URL address as a parameter. -
method(String method, RequestBody body)
: Set the HTTP method and request body of the request. You can specify the HTTP method of the request (such as GET, POST, PUT, etc.) and the request body RequestBody object. -
header(String name, String value)
: Add request header. You can add custom request header parameters through this method. You need to provide parameter names and parameter values. -
headers(Headers headers)
: Set request header collection. You can use this method to set multiple request headers at once, and you need to pass in the Headers object. -
cacheControl(CacheControl cacheControl)
: Set cache control policy. A CacheControl object can be passed in to control the cache behavior of the request. -
tag(Object tag)
: Set the requested label. A label of type Object can be passed in to identify and identify the request in subsequent operations. -
build()
: Construct Request object. After the configuration is complete, use this method to build the final Request object.
3. Response class:
The Response class represents an HTTP response. After sending an HTTP request, you can obtain the response information returned by the server through the Response object, including status code, response header, response body, etc.
Commonly used methods:
-
code()
: Get the status code of the HTTP response. Returns an integer value representing the status code of the HTTP response. -
message()
: Get the status message of the HTTP response. Returns a string representing the status message of the HTTP response. -
body()
: Get the response body of the HTTP response. Returns a ResponseBody object for obtaining and processing response specific data. -
headers()
: Get the response header of the HTTP response. Returns a Headers object used to obtain and process response header information. -
isSuccessful()
: Determine whether the HTTP response is successful. Returns a Boolean value indicating whether the HTTP response was successful (status code between 200-299). -
cacheResponse()
: Get cached HTTP response. If the request uses caching and there is a corresponding cached response, a Response object is returned, otherwise null is returned. -
networkResponse()
: Get the HTTP response from the network. If the request is sent over the network and there is a corresponding network response, a Response object is returned, otherwise null is returned. -
priorResponse()
: Get the previous HTTP response. Returns a Response object if the request has an associated previous HTTP response, otherwise null is returned. -
request()
: Get the request associated with this HTTP response. Returns a Request object representing the request associated with this response.
4. Call class:
The Call class represents an asynchronous HTTP request task. Create a Call object through the newCall method of OkHttpClient, and then use the enqueue method to send asynchronous requests and process the response results through the callback function.
Commonly used methods:
-
execute()
: Synchronously execute HTTP requests and return Response objects. This method blocks the current thread until the complete response result is obtained. -
enqueue(Callback callback)
: Execute HTTP request asynchronously and receive the result through callback function. This method puts the request into the request queue and executes it on the background thread. After the execution is completed, the result is returned through the Callback interface callback. -
cancel()
: Cancel HTTP request. If the HTTP request has not been completed, you can call this method to cancel the ongoing request. -
isExecuted()
: Determine whether the HTTP request has been executed. Returns a Boolean value indicating whether the HTTP request has been executed. -
isCanceled()
: Determine whether the HTTP request has been canceled. Returns a Boolean value indicating whether the HTTP request has been canceled. -
timeout()
: Get the timeout settings. Returns a Timeout object used to get and set the timeout for connections, reads, and writes. -
request()
: Get the request associated with this Call. Returns a Request object representing the HTTP request associated with this Call. -
clone()
: Create a copy of the Call object. Returns a new Call object that shares the same configuration and request information as the original Call object.
5. Interceptor interface:
The Interceptor interface can be used to intercept and process HTTP requests and responses. Custom Interceptor can be implemented to modify requests, add request headers, record logs, etc.
Commonly used methods:
-
Response intercept(Chain chain)
: Intercept and process requests and responses. This method receives a Chain object as a parameter, through which the request information can be obtained and the next interceptor executed or the actual network request initiated. -
interface Chain
: Represents a link in the interceptor chain, used to pass requests and responses and control the execution process of the interceptor. The Chain interface has the following methods:
Connection connection()
: Get the connection object of the current interceptor chain.Request request()
: Get the request object of the current interceptor chain.Response proceed(Request request)
: Execute the next interceptor and return the final response result.
6. FormBody class:
The FormBody class is a class in the OkHttp library used to build the request body in the form of a form. It provides methods to add form fields and values, and generate the final request body data.
Commonly used methods:
-
add(String name, String value)
: Add form fields and values. This method can be called multiple times to add multiple form fields and corresponding values. -
encodedName(int index)
: Get the encoded form field name. Pass in the index value and return the encoded form field name at the corresponding position. -
encodedValue(int index)
: Get the encoded form field value. Pass in the index value and return the encoded form field value at the corresponding position. -
name(int index)
: Get the unencoded form field name. Pass in the index value and return the unencoded form field name at the corresponding position. -
value(int index)
: Get the unencoded form field value. Pass in the index value and return the unencoded form field value at the corresponding position. -
size()
: Get the number of form fields. Returns an integer value representing the number of form fields. -
contentType()
: Get the media type of the request body. Returns a MediaType object, indicating the media type of the request body. The default is "application/x-www-form-urlencoded". -
contentLength()
: Get the length of the request body. Returns a long value representing the length of the request body in bytes. -
toString()
: Convert FormBody to string. Returns a string representing the contents of the FormBody.
7. MultipartBody class:
The MultipartBody class is a class used in the OkHttp library to build multipart request bodies. It allows multiple different types of data such as text, files, etc. to be included in a single request.
Commonly used methods:
-
create(MediaType type, RequestBody... parts)
: Create a MultipartBody object. You need to pass in the media type and multiple RequestBody instances. -
type()
: Get the media type of the request body. Returns a MediaType object, indicating the media type of the request body. The default is "multipart/form-data". -
boundary()
: Get the boundary string. Returns a string representing the boundary string of the request body. -
contentType()
: Get the complete media type of the request body. Returns a string representing the complete media type of the request body, including media type and boundary strings. -
parts()
: Get various parts of the request body. Returns a List<RequestBody> representing all parts of the request body. -
writeTo(BufferedSink sink)
: Write MultipartBody to the buffer. A BufferedSink object needs to be passed in to write the data in the request body. -
contentLength()
: Get the length of the request body. Returns a long value representing the length of the request body in bytes. -
addPart(Headers headers, RequestBody body)
: Add a section to the request body. You can pass in custom request headers Headers objects and corresponding RequestBody instances. -
addFormDataPart(String name, @Nullable String filename, RequestBody body)
: Add a form data section to the request body. You need to pass in the field name name, optional file name filename and RequestBody instance. -
addFormDataPart(String name, @Nullable String filename, MediaType contentType, ByteString content)
: Add a form data section to the request body. You need to pass in the field name name, optional file name filename, media type contentType and content content.
8. WebSocket class:
The WebSocket class is used for WebSocket communication. A WebSocket connection to the server can be established and communicate in both directions.
Commonly used methods:
-
request()
: Get the request associated with this WebSocket. Returns a Request object representing the HTTP request associated with this WebSocket. -
queueSize()
: Get the size of the message queue to be sent. Returns an integer value representing the number of messages in the message queue to be sent. -
send(String text)
: Send a text message. You can pass in a string type message content and send it to the server as a text message. -
send(ByteString bytes)
: Send binary messages. You can pass in a ByteString object and send it to the server as a binary message. -
close(int code, String reason)
: Close the WebSocket connection. You can pass in an integer shutdown code (such as 1000 for normal shutdown) and a string shutdown reason to send a shutdown frame to the server and close the WebSocket connection. -
cancel()
: Cancel WebSocket connection. If the WebSocket connection has not been closed, you can call this method to cancel the connection. -
send(Request request, WebSocketListener listener)
: Send a WebSocket message for the specified request. You can pass in a Request object and a WebSocketListener object, send the request to the server, and listen to the callback processing results through the WebSocketListener. -
addListener(WebSocketListener listener)
: Add WebSocket event listener. You can pass in a WebSocketListener object to monitor WebSocket connection status, message reception and other events. -
removeListener(WebSocketListener listener)
: Remove WebSocket event listener. You can pass in a WebSocketListener object to remove it from the WebSocket listener list.
2. How to use okhttp
1. Add dependencies:
Add the OkHttp dependency in the project's build.gradle file.
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
2. Create OkHttpClient object:
By new OkHttpClient()
creating an OkHttpClient instance, it is used to send HTTP requests.
OkHttpClient client = new OkHttpClient();
3. Create a Request object:
Use to Request.Builder
construct a request object and specify the URL address, request header, request body and other related information.
Request request = new Request.Builder()
.url("https://example.com/api")
.header("Content-Type", "application/json")
.post(RequestBody.create(MediaType.parse("application/json"), jsonBody))
.build();
4. Send synchronization request:
Use to client.newCall(request).execute()
send a synchronous request and obtain the Response object.
try (Response response = client.newCall(request).execute()) {
// 处理响应结果
if (response.isSuccessful()) {
String responseBody = response.body().string();
// ...
} else {
// 请求失败处理
}
} catch (IOException e) {
// IO异常处理
}
5. Send an asynchronous request
Use to client.newCall(request).enqueue(Callback)
send asynchronous requests and process the response results through Callback callbacks.
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 请求失败处理
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 处理响应结果
if (response.isSuccessful()) {
String responseBody = response.body().string();
// ...
} else {
// 请求失败处理
}
}
});
In the above example, we first created an OkHttpClient object and built a request object using Request.Builder. You can then optionally send synchronous or asynchronous requests. For synchronous requests, call execute()
the method directly and process the response result; for asynchronous request, use enqueue(Callback)
the method and process the response result through the Callback callback.
After processing the request, you need to pay attention to closing the resources of the Response object to avoid memory leaks.
3. okhttp example
The following example is the code for requesting place names from Kuou Weather Network in the first line of code written by Master Guo Lin.
Provincial json data format:
City’s json data format:
http://guolin.tech/api/china/16
County json data format:
MainActivity:
package com.example.okhttpdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.okhttpdemo.Bean.City;
import com.example.okhttpdemo.Bean.County;
import com.example.okhttpdemo.Bean.Province;
import org.litepal.LitePal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
import okhttp3.internal.Util;
public class MainActivity extends AppCompatActivity {
public static final int CURRENT_PAGE_PROVINCE = 0;
public static final int CURRENT_PAGE_CITY = 1;
public static final int CURRENT_PAGE_COUNTY = 2;
private List<Province> mProvinces;
private List<City> mCities;
private List<County> mCounties;
private TextView titleTV;
private Button backBtn;
private ListView mListView;
private List<String> mListData = new ArrayList<>();
private ArrayAdapter<String> mAdapter;
private ProgressDialog progressDialog;
private int currentPage;
private Province mCurrentProvince;
private City mCurrentCity;
private County mCurrentCounty;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LitePal.initialize(this);
initView();
}
private void initView() {
mListView = findViewById(R.id.list_view);
backBtn = findViewById(R.id.back_button);
titleTV= findViewById(R.id.title_text);
backBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (currentPage==CURRENT_PAGE_COUNTY){
queryCities();
}else if(currentPage == CURRENT_PAGE_CITY){
queryProvinces();
}
}
});
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (currentPage){
case CURRENT_PAGE_PROVINCE:
mCurrentProvince = mProvinces.get(position);
queryCities();
break;
case CURRENT_PAGE_CITY:
mCurrentCity = mCities.get(position);
queryCounty();
break;
case CURRENT_PAGE_COUNTY:
mCurrentCounty = mCounties.get(position);
Toast.makeText(MainActivity.this,"点击了"+mCurrentProvince.getProvinceName()+mCurrentCity.getCityName()+mCurrentCounty.getCountyName(),Toast.LENGTH_LONG).show();
break;
}
}
});
mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mListData);
queryProvinces();
mListView.setAdapter(mAdapter);
}
private void queryProvinces(){
titleTV.setText("中国");
backBtn.setVisibility(View.GONE);
// 从数据库中找
mProvinces = LitePal.findAll(Province.class);
if (mProvinces.size()>0){
mListData.clear();
for (Province province:mProvinces){
mListData.add(province.getProvinceName());
}
mAdapter.notifyDataSetChanged();
currentPage = CURRENT_PAGE_PROVINCE;
}else {
String url = "http://guolin.tech/api/china";
queryFromServer(url,CURRENT_PAGE_PROVINCE);
}
}
private void queryFromServer(String url, int type) {
showProgressDialog();
HttpTool.sendOkhttpRequest(url, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
closeProgressDialog();
Toast.makeText(MainActivity.this,"加载失败!!!",Toast.LENGTH_LONG).show();
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
boolean result = false;
switch (type){
case CURRENT_PAGE_PROVINCE:
result = Utility.handleProvinceResponse(response.body().string());
break;
case CURRENT_PAGE_CITY:
result = Utility.handleCitiesResponse(response.body().string(),mCurrentProvince.getId());
break;
case CURRENT_PAGE_COUNTY:
result = Utility.handleCountyResponse(response.body().string(),mCurrentCity.getId());
break;
}
if (result){
runOnUiThread(new Runnable() {
@Override
public void run() {
closeProgressDialog();
switch (type){
case CURRENT_PAGE_PROVINCE:
queryProvinces();
break;
case CURRENT_PAGE_CITY:
queryCities();
break;
case CURRENT_PAGE_COUNTY:
queryCounty();
break;
}
}
});
}
}
});
}
private void queryCounty() {
titleTV.setText(mCurrentCity.getCityName());
backBtn.setVisibility(View.VISIBLE);
// 从数据库中找
mCounties = LitePal.where("cityid=?",String.valueOf(mCurrentCity.getId())).find(County.class);
if (mCounties.size()!=0){
mListData.clear();
for (County county:mCounties){
mListData.add(county.getCountyName());
}
mAdapter.notifyDataSetChanged();
currentPage = CURRENT_PAGE_COUNTY;
}else {
int provinceCode = mCurrentProvince.getProvinceCode();
int citiCode = mCurrentCity.getCityCode();
String url = "http://guolin.tech/api/china/" + provinceCode+"/"+citiCode;
queryFromServer(url,CURRENT_PAGE_COUNTY);
}
}
private void queryCities() {
titleTV.setText(mCurrentProvince.getProvinceName());
backBtn.setVisibility(View.VISIBLE);
// 从数据库中找
mCities = LitePal.where("provinceid=?",String.valueOf(mCurrentProvince.getId())).find(City.class);
if (mCities.size()!=0){
mListData.clear();
for (City city:mCities){
mListData.add(city.getCityName());
}
mAdapter.notifyDataSetChanged();
currentPage = CURRENT_PAGE_CITY;
}else {
int provinceCode = mCurrentProvince.getProvinceCode();
String url = "http://guolin.tech/api/china/" + provinceCode;
queryFromServer(url,CURRENT_PAGE_CITY);
}
}
/**
* 显示进度对话框
*/
private void showProgressDialog() {
if (progressDialog == null) {
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("正在加载...");
progressDialog.setCanceledOnTouchOutside(false);
}
progressDialog.show();
}
/**
* 关闭进度对话框
*/
private void closeProgressDialog() {
if (progressDialog != null) {
progressDialog.dismiss();
}
}
}
Utility :
package com.example.okhttpdemo;
import android.text.TextUtils;
import com.example.okhttpdemo.Bean.City;
import com.example.okhttpdemo.Bean.County;
import com.example.okhttpdemo.Bean.Province;
import com.google.gson.Gson;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class Utility {
/**
* 解析和处理服务器返回的省级的数据
*/
public static boolean handleProvinceResponse(String response) {
if (!TextUtils.isEmpty(response)) {
try {
JSONArray allProvinces = new JSONArray(response);
for (int i = 0; i < allProvinces.length(); i++) {
JSONObject provinceObject = allProvinces.getJSONObject(i);
Province province = new Province();
province.setProvinceName(provinceObject.getString("name"));
province.setProvinceCode(provinceObject.getInt("id"));
province.save();
}
return true;
} catch (JSONException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 解析和处理服务器返回的市级的数据
*/
public static boolean handleCitiesResponse(String response,int provinceId){
if (!TextUtils.isEmpty(response)){
try {
JSONArray allCities = new JSONArray(response);
for (int i = 0; i < allCities.length() ; i++) {
JSONObject cityObject = allCities.getJSONObject(i);
City city = new City();
city.setCityName(cityObject.getString("name"));
city.setCityCode(cityObject.getInt("id"));
city.setProvinceId(provinceId);
city.save();
}
return true;
} catch (JSONException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 解析和处理服务器返回的县级的数据
*/
public static boolean handleCountyResponse(String response,int cityId){
if (!TextUtils.isEmpty(response)){
try {
JSONArray allCounties = new JSONArray(response);
for (int i = 0; i < allCounties.length() ; i++) {
JSONObject countryObject = allCounties.getJSONObject(i);
County country = new County();
country.setCountyName(countryObject.getString("name"));
country.setWeatherId(countryObject.getString("weather_id"));
country.setCityId(cityId);
country .save();
}
return true;
} catch (JSONException e) {
e.printStackTrace();
}
}
return false;
}
}
HttpTool :
package com.example.okhttpdemo;
import okhttp3.OkHttpClient;
import okhttp3.Request;
public class HttpTool {
/**
*
* @param address 接口地址URL
* @param callback
*/
public static void sendOkhttpRequest(String address,okhttp3.Callback callback){
// 1、创建OkHttpClient对象
OkHttpClient client = new OkHttpClient();
// 2、创建Request对象
Request request = new Request.Builder().url(address).build();
// 3、发送异步请求
client.newCall(request).enqueue(callback);
}
}
Province :
package com.example.okhttpdemo.Bean;
import org.litepal.crud.LitePalSupport;
public class Province extends LitePalSupport {
private int id;
private String provinceName;
private int provinceCode;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProvinceName() {
return provinceName;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
public int getProvinceCode() {
return provinceCode;
}
public void setProvinceCode(int provinceCode) {
this.provinceCode = provinceCode;
}
}
City:
package com.example.okhttpdemo.Bean;
import org.litepal.crud.LitePalSupport;
public class City extends LitePalSupport {
private int id;
private String cityName;
private int cityCode;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public int getCityCode() {
return cityCode;
}
public void setCityCode(int cityCode) {
this.cityCode = cityCode;
}
public int getProvinceId() {
return provinceId;
}
public void setProvinceId(int provinceId) {
this.provinceId = provinceId;
}
private int provinceId;
}
County :
package com.example.okhttpdemo.Bean;
import org.litepal.crud.LitePalSupport;
public class County extends LitePalSupport {
private int id;
private String countyName;
private String weatherId;
private int cityId;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountyName() {
return countyName;
}
public void setCountyName(String countyName) {
this.countyName = countyName;
}
public String getWeatherId() {
return weatherId;
}
public void setWeatherId(String weatherId) {
this.weatherId = weatherId;
}
public int getCityId() {
return cityId;
}
public void setCityId(int cityId) {
this.cityId = cityId;
}
}
activity_main:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#7283DD"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/teal_200">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/title_text"
android:layout_centerInParent="true"
android:textColor="#fff"
android:textSize="20sp"/>
<Button
android:id="@+id/back_button"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginLeft="10dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@drawable/back"
/>
</RelativeLayout>
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
litepal.xml:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value = "okhttpdemo"/>
<version value = "1"/>
<list>
<mapping class = "com.example.okhttpdemo.Bean.Province"/>
<mapping class = "com.example.okhttpdemo.Bean.City"/>
<mapping class = "com.example.okhttpdemo.Bean.County"/>
</list>
</litepal>
AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.okhttpdemo">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:networkSecurityConfig="@xml/network_security_config"
android:name = "org.litepal.LitePalApplication"
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/Theme.AppCompat.NoActionBar">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
rely:
implementation("com.squareup.okhttp3:okhttp:3.4.1")
implementation 'org.litepal.guolindev:core:3.2.3'
implementation 'com.google.code.gson:gson:2.7'