“This is the third day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021.”
🔥 introduction
This paper uses OkHttp+Retrofit+Rxjava+Hilt to implement a network request box.
💥 Final code
iWanAndroidService.register(map)
.compose(ResponseTransformer.obtain())
.subscribe(registerData -> {
// The request succeeded
}, new ErrorConsumer() {
@Override
protected void error(ApiException e) {
// The request failed}});Copy the code
Is it particularly easy?
💥 Project Structure
🔥 OkHttp
What is 💥 OkHttp
OkHttp is a default efficient HTTP client:
- HTTP/2 support allows all requests to the same host to share a socket.
- Connection pooling reduces request latency if HTTP/2 is not available.
- Transparent GZIP reduces download size.
- Response caching completely eliminates network duplicate requests.
- After a network problem occurs, OkHttp remains unchanged and automatically recovers from the problem.
The official documentation
Simple use of + source interpretation
I won’t go into OkHttp separately.
Defect:
-
Interface configuration of network request is tedious, especially when complex request body, request header and parameters need to be configured.
-
The data parsing process requires the user to manually get responsBody for parsing, which cannot be reused;
-
Cannot accommodate automatic thread switching.
-
With nested network requests, you fall into the “callback trap.”
🔥 Retrofit
💥 What is Retrofit
Retrofit is based on OkHttp, where network requests are actually done by OkHttp, while Retrofit is primarily responsible for encapsulating interfaces.
Not only does Retrofit have OkHttp’s high performance features, it also has the following advantages:
-
Supports RESTful API design style.
-
Configure requests through annotations: request methods, request parameters, request headers, return values, and so on.
-
Can be used with various Converter to automatically parse and serialize data: Support for Gson, Jackson, Protobuff, etc. Support for RxJava is provided.
-
Request speed is fast, use very convenient and flexible.
Note: Retrofit doesn’t have network requests, so you need to set up dispensers, interceptors, etc., in OkHttpClient.
💥 Retrofit annotations
Official documentation also provides the use of various annotations.
🔥 OkHttp + Retrofit instance
💥 Add a dependency
dependencies {
implementation 'com. Squareup. Retrofit2: retrofit: 2.8.1' // Necessary dependency, retrofit
implementation 'com. Squareup. Retrofit2: converter - gson: 2.8.1' // Necessary dependencies to parse JSON characters
implementation 'com. Squareup. Okhttp3: logging - interceptor: 4.9.0' // No necessary dependency, print logs
}
Copy the code
💥 defines the request interface
public interface IWanAndroidService {
String BASE_URL = "https://www.wanandroid.com/";
@GET("banner/json")
Call<ResponseData<List<HomeBanner>>> homeBanner();
@POST("user/register")
@FormUrlEncoded
Call<ResponseData<RegisterData>> register(@FieldMap Map<String,String> map);
}
Copy the code
💥 set OkHttp + Retrofit
public class NetworkManager {
private static volatile NetworkManager instances;
private static volatile OkHttpClient okHttpClient;
private static volatile Retrofit retrofit;
public static NetworkManager getInstance(a) {
if (instances == null) {
synchronized (NetworkManager.class) {
if (instances == null) {
instances = newNetworkManager(); }}}return instances;
}
private static int TIME_OUT = 30; // The connection is disconnected due to a 30-second timeout
private OkHttpClient initClient(a) {
if (okHttpClient == null) {
synchronized (NetworkManager.class) {
if (okHttpClient == null) {
// Request log printing
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(message -> {
try {
MLog.e(URLDecoder.decode(message, "utf-8"));
} catch(UnsupportedEncodingException e) { e.printStackTrace(); MLog.e(message); }}); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);// Note 1: Create OkHttpClient
okHttpClient = new OkHttpClient.Builder()
.sslSocketFactory(newNetworkSSL(TrustManager.trustAllCert), TrustManager.trustAllCert) .connectTimeout(TIME_OUT, TimeUnit.SECONDS) .addInterceptor(loggingInterceptor) .readTimeout(TIME_OUT, TimeUnit.SECONDS) .writeTimeout(TIME_OUT, TimeUnit.SECONDS) .build(); }}}return okHttpClient;
}
public Retrofit initRetrofit(a) {
if (retrofit == null) {
synchronized (NetworkManager.class) {
if (retrofit == null) {
// Note 2: Create Retrofit
retrofit = new Retrofit.Builder()
.client(initClient())/ / optional
.baseUrl(IWanAndroidService.BASE_URL)/ / required
.addConverterFactory(GsonConverterFactory.create())// Select (data converter, parse).build(); }}}returnretrofit; }}Copy the code
-
Note 1: Create an OkHttpClient object that builds an instance of a network type, typically using the same singleton for all network requests. (If OkHttpClient uses the default, do not set this parameter.)
-
Note 2: Create a Retrofit object, which builds a carrier object for network requests. There is a lot of initialization at build time, such as setting the OkHttpClient, setting the URL of the request, adding data converters, etc.
💥 Network request
//GET
// Note 1: Dynamically get the IWanAndroidService object
IWanAndroidService service = NetworkManager.getInstance().initRetrofit().create(IWanAndroidService.class);
// Note 2: Network requests
service.homeBanner().enqueue(new Callback<ResponseData<List<HomeBanner>>>() {
@Override
public void onResponse(Call<ResponseData<List<HomeBanner>>> call, Response<ResponseData<List<HomeBanner>>> response) {
if(response.body().getData() ! =null) {
MLog.e(response.body().getData().get(0).toString());
binding.loginTvContent.setText(response.body().getData().get(0).toString()); }}@Override
public void onFailure(Call<ResponseData<List<HomeBanner>>> call, Throwable t) { MLog.e(t.getMessage()); }});//POST
Map<String, String> map = new HashMap<>();
map.put("username", account);
map.put("password", passsword);
map.put("repassword", passsword);
NetworkManager.getInstance().initRetrofit().create(IWanAndroidService.class)
.register(map).enqueue(new Callback<ResponseData<RegisterData>>() {
@Override
public void onResponse(Call<ResponseData<RegisterData>> call, Response<ResponseData<RegisterData>> response) {
if(response.body().getData() ! =null) { MLog.e(response.body().getData().toString()); binding.loginTvContent.setText(response.body().getData().toString()); }}@Override
public void onFailure(Call<ResponseData<RegisterData>> call, Throwable t) { MLog.e(t.getMessage()); }});Copy the code
The essence of Retrofit: Dynamic proxy setup for unified configuration of network requests.
💥 rendering
🔥 Rxjava
RxJava uses chained calls in the Observer mode and the Builder mode.
Observer mode:
An Observable subscribes to an Observer, notifies the corresponding Observer when it sends a message, and an Observable can Subscribe to multiple observers.
Chain call: call the corresponding method to the original object after processing the original object, so as to do the chain call.
Participants:
- Observable: The observed, or sender of a message
- An Observer of the message
- Subscriber: another representation of an observer
- Scheduler: a Scheduler that performs thread switching
RxJava is certainly good and powerful, with the following advantages:
- It has the characteristics of responsive programming.
- Asynchronous, no need to manually create a thread, and have the ability to switch threads.
- Support chain call to ensure code simplicity.
- A variety of operators, very powerful, to meet a variety of business needs.
- Exception handling is simplified.
RxJava applies to the following scenarios: Network requests, database reads and writes, file reads and writes, and scheduled tasks that need to be performed asynchronously can be performed using RxJava.
💥 Adding a dependency (New)
implementation "IO. Reactivex. Rxjava2: rxjava: 2.2.6." " // Required RXJava dependencies
implementation "IO. Reactivex. Rxjava2: rxandroid: 2.1.0." " // The rxandrroID dependency is required for thread cutting. implementation'com. Squareup. Retrofit2: adapter - rxjava2:2.5.0' // Required dependencies, used in conjunction with RxJava
Copy the code
💥 Modify request interface
public interface IWanAndroidService {
String BASE_URL = "https://www.wanandroid.com/";
//OkHttp+Retrofit
//OkHttp+Retrofit+RxJava
@GET("banner/json")
Observable<ResponseData<List<HomeBanner>>> homeBanner();
@POST("user/register")
@FormUrlEncoded
Observable<ResponseData<RegisterData>> register(@FieldMap Map<String,String> map);
}
Copy the code
💥 set OkHttp + + RxJava Retrofit
public Retrofit initRetrofitRxJava(a) {
if (retrofit == null) {
synchronized (NetworkManager.class) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.client(initClient())/ / optional
.baseUrl(IWanAndroidService.BASE_URL)/ / required
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())// Add a network request adapter
.addConverterFactory(GsonConverterFactory.create())// Select (data converter, parse).build(); }}}return retrofit;
}
Copy the code
💥 Make a network request
NetworkManager.getInstance().initRetrofitRxJava()
.create(IWanAndroidService.class)
.homeBanner()
.subscribeOn(Schedulers.io())// Switch to the IO thread
.observeOn(AndroidSchedulers.mainThread())// Switch to main thread
// Add a subscription
.subscribe(listResponseData -> {
// The request succeeded
if(listResponseData ! =null) {
MLog.e(listResponseData.getData().get(0).toString());
binding.loginTvContent.setText(listResponseData.getData().get(0).toString());
}
}, throwable -> {
// The request failed
MLog.e(throwable.getMessage());
});
Copy the code
💥 rendering
💥 Further encapsulation
Since the request is too cumbersome, let’s try to encapsulate it further.
🌀 Unified exception handling (custom ApiException)
public class ApiException extends Exception {
// Unknown error
public static final int UNKNOWN = 1000;
// Parsing error
public static final int PARSE_ERROR = 1001;
// Network error/connection error
public static final int NETWORK_ERROR = 1002;
private int code;
private String displayMessage;
public ApiException(int code, String displayMessage) {
this.code = code;
this.displayMessage = displayMessage;
}
public int getCode(a) {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDisplayMessage(a) {
return displayMessage;
}
public void setDisplayMessage(String displayMessage) {
this.displayMessage = displayMessage;
}
public static ApiException handleException(Throwable e) {
ApiException ex;
if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) {
// Parsing error
ex = new ApiException(PARSE_ERROR, e.getMessage());
return ex;
} else if (e instanceof ConnectException) {
// Network error
ex = new ApiException(NETWORK_ERROR, e.getMessage());
return ex;
} else if (e instanceof UnknownHostException || e instanceof SocketTimeoutException) {
// Connection error
ex = new ApiException(NETWORK_ERROR, e.getMessage());
return ex;
} else {
// Unknown error
ex = new ApiException(UNKNOWN, e.getMessage());
returnex; }}}Copy the code
🌀 unified exception handling (Consumer implementation<Throwable>
Interface)
public abstract class ErrorConsumer implements Consumer<Throwable> {
@Override
public void accept(@NotNull Throwable throwable) throws Exception {
// Handle exceptions
ApiException exception;
if (throwable instanceof ApiException) {
exception = (ApiException) throwable;
} else {
exception = ApiException.handleException(throwable);
}
// Call the error method
error(exception);
}
// Use the error method.
protected abstract void error(ApiException e);
}
Copy the code
🌀 Response conversion processing
public class ResponseTransformer<T> implements ObservableTransformer<ResponseData<T>, T> {
public ResponseTransformer(a) {}public static <R> ResponseTransformer<R> obtain(a){
return new ResponseTransformer<>();
}
@NotNull
@Override
public ObservableSource<T> apply(@NotNull Observable<ResponseData<T>> upstream) {
return upstream.onErrorResumeNext(new Function<Throwable, ObservableSource<? extends ResponseData<T>>>() {
@Override
public ObservableSource<? extends ResponseData<T>> apply(@NotNull Throwable throwable) throws Exception {
return Observable.error(ApiException.handleException(throwable));
}
}).flatMap(new Function<ResponseData<T>, ObservableSource<T>>() {
@Override
public ObservableSource<T> apply(@NotNull ResponseData<T> responseData) throws Exception {
// Request successful, start processing your logic
if (0==responseData.getErrorCode()) {
if (null! =responseData.getData()) {return Observable.just(responseData.getData());
} else {
If Null is returned, an exception will be generated.
// Create a data instance with no content with reflection.
returnObservable.just(responseData.getData()); }}// The request is abnormal
return Observable.error(newApiException(responseData.getErrorCode(), responseData.getErrorMsg())); } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); }}Copy the code
🌀 Is used after encapsulation
Get the data.
🔥 Hilt(Member of JetPack)
Use Hilt for dependency injection on Android. Hilt builds on Dagger and provides a standard way to incorporate Dagger dependency injection into an Android application.
Official documents are the deadliest
💥 Adding a dependency (New)
implementation 'com. Google. Dagger hilt - android: 2.40.1'
annotationProcessor 'com. Google. Dagger hilt - compiler: 2.40.1'
Copy the code
💥 Hilt Gradle plugin
🌀 build. Gradle (Project)
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com. Google. Dagger hilt - android - gradle - plugin: 2.40.1'}}Copy the code
🌀 build. Gradle (Module)
apply plugin: 'dagger.hilt.android.plugin'
Copy the code
🌀 Hilt Application
All applications that use Hilt must include an Application class annotated with @hiltAndroidApp.
Create the Application
import android.app.Application;
import dagger.hilt.android.HiltAndroidApp;
@HiltAndroidApp
public class App extends Application {}Copy the code
Set the AndroidManifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.scc.wanandroid">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".App"
.>
</application>
</manifest>
Copy the code
With all the preparation done, start building the network framework using Hilt
💥 set Retrofit OkHttp + + RxJava + Hilt
🌀 Create NetworkModule for initialization
@InstallIn(SingletonComponent.class)
@Module
public class NetworkModule {
private static int TIME_OUT = 30; // The connection is disconnected due to a 30-second timeout
@Provides
@Singleton
OkHttpClient providesOkHttpClient(a){
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
try {
MLog.e("--network--", URLDecoder.decode(message, "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
MLog.e("--network--", message); }}}); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);return new OkHttpClient.Builder()
.sslSocketFactory(new NetworkSSL(TrustManager.trustAllCert), TrustManager.trustAllCert)
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
.build();
}
@Singleton
@Provides
Retrofit providesRetrofit(OkHttpClient okHttpClient){
return newRetrofit.Builder() .client(okHttpClient) .baseUrl(IWanAndroidService.BASE_URL) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); }@Singleton
@Provides
IWanAndroidService providesWanAndroidService(Retrofit retrofit){
returnretrofit.create(IWanAndroidService.class); }}Copy the code
🌀 use
@AndroidEntryPoint
public class LoginActivity extends AppCompatActivity {
ActivityLoginBinding binding;
@Inject
IWanAndroidService iWanAndroidService;
//Retrofit+RxJava+Hilt
iWanAndroidService.homeBanner()
.compose(ResponseTransformer.obtain())
.subscribe(homeBanners -> {
// The request succeeded
if(homeBanners ! =null) {
MLog.e(homeBanners.get(0).toString());
binding.loginTvContent.setText(homeBanners.get(0).toString()); }},new ErrorConsumer() {
@Override
protected void error(ApiException e) {
// The request failedMLog.e(e.getMessage()+e.getCode()); }}); }Copy the code
🌀 rendering
🔥 summary
The project will work as soon as you get it, and of course there are areas that can be optimized.
- If the data obtained is null.
- Hiding the implementation details of network requests from the outside.
- Further details according to the actual situation, such as API module creation.