目录
[中规中矩的 Retrofit+OkHttp+ViewModel 使用介绍。](#中规中矩的 Retrofit+OkHttp+ViewModel 使用介绍。)
[为什么选择 LiveData、Retrofit 和 OkHttp?](#为什么选择 LiveData、Retrofit 和 OkHttp?)
[配置 OkHttpClient 和 Retrofit](#配置 OkHttpClient 和 Retrofit)
[创建数据模型和 API 接口](#创建数据模型和 API 接口)
[创建 Repository 类](#创建 Repository 类)
[创建 ViewModel 类](#创建 ViewModel 类)
[在 Activity 中使用 ViewModel 和 LiveData](#在 Activity 中使用 ViewModel 和 LiveData)
[Retrofit Xml能力](#Retrofit Xml能力)
[1. 添加必要的依赖项](#1. 添加必要的依赖项)
[2. 创建数据模型](#2. 创建数据模型)
[3. 创建 API 接口](#3. 创建 API 接口)
[4. 配置 Retrofit 实例](#4. 配置 Retrofit 实例)
[5. 发起请求](#5. 发起请求)
中规中矩的 Retrofit+OkHttp+ViewModel 使用介绍。
在现代 Android 开发中,使用 LiveData、Retrofit 和 OkHttp 进行网络请求和数据处理已经成为一种标准做法。这篇博文将详细介绍如何将这三者结合起来,构建一个高效、可维护的网络请求框架。
为什么选择 LiveData、Retrofit 和 OkHttp?
-
LiveData:LiveData 是一种可观察的数据持有者类,具有生命周期感知能力。它能与 Android 的生命周期组件无缝集成,确保 UI 组件只在活跃状态下更新,从而避免内存泄漏和崩溃问题。
-
Retrofit:Retrofit 是一个强大的类型安全的 HTTP 客户端,用于 Android 和 Java。它简化了网络请求的创建和处理过程,支持多种数据转换器(如 Gson、SimpleXML),并与 OkHttp 无缝集成。
-
OkHttp:OkHttp 是一个高效的 HTTP 客户端,支持连接池、缓存、重定向和失败重试等功能。它为 Retrofit 提供了底层支持,并且可以通过拦截器进行灵活的请求和响应处理。
项目依赖
首先,在你的 build.gradle
文件中添加以下依赖项:
java
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
}
配置 OkHttpClient 和 Retrofit
创建一个 ApiClient
类来配置 OkHttpClient 和 Retrofit 实例:
java
public class ApiClient {
private static final String BASE_URL = "https://api.example.com/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit == null) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
创建数据模型和 API 接口
定义一个数据模型类和一个 API 接口来描述网络请求:
java
public class User {
@SerializedName("id")
private int id;
@SerializedName("name")
private String name;
@SerializedName("email")
private String email;
// Getters and Setters
}
java
public interface ApiService {
@GET("users")
Call<List<User>> getUsers();
}
创建 Repository 类
创建一个 UserRepository
类来管理数据操作:
java
public class UserRepository {
private ApiService apiService;
public UserRepository() {
apiService = ApiClient.getClient().create(ApiService.class);
}
public LiveData<List<User>> getUsers() {
final MutableLiveData<List<User>> data = new MutableLiveData<>();
apiService.getUsers().enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.isSuccessful()) {
data.setValue(response.body());
}
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
data.setValue(null);
}
});
return data;
}
}
创建 ViewModel 类
java
public class UserViewModel extends ViewModel {
private UserRepository userRepository;
private LiveData<List<User>> users;
public UserViewModel() {
userRepository = new UserRepository();
users = userRepository.getUsers();
}
public LiveData<List<User>> getUsers() {
return users;
}
}
在 Activity 中使用 ViewModel 和 LiveData
在 Activity 中使用 ViewModel 和 LiveData 来观察数据变化:
java
public class MainActivity extends AppCompatActivity {
private UserViewModel userViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
userViewModel.getUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(List<User> users) {
// Update UI with the list of users
}
});
}
}
通过这些步骤,你可以在 Android 项目中使用 LiveData 与 Retrofit 和 OkHttp 来实现网络请求框架,并将数据绑定到 UI。这种架构设计不仅提高了代码的可读性和可维护性,还能有效地管理网络请求和数据更新。
以上就是基本使用,如果你有点追求,就是配合框架使用网络能力。
Mvvm模式使用,下面应该算是一个完整封装框架。
java
public class MainActivity extends BaseMvvmAc<AcMainBinding, HomeViewModel> {
@Override
protected int initContentView(Bundle savedInstanceState) {
return R.layout.ac_main;
}
@Override
protected int initVariableId() {
return BR.viewModel;
}
}
java
public abstract class BaseMvvmAc<V extends ViewDataBinding, VM extends BaseViewModel> extends BaseAc {
protected VM viewModel;
protected V binding;
private void initViewDataBinding(Bundle savedInstanceState) {
binding = DataBindingUtil.setContentView(this, initContentView(savedInstanceState));
if (viewModel == null) {
Class modelClass;
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[1];
} else {
//如果没有指定泛型参数,则默认使用BaseViewModel
modelClass = BaseViewModel.class;
}
viewModel = (VM) new ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()))
.get(modelClass);
}
if (initVariableId() > 0) {
binding.setVariable(initVariableId(), viewModel);
}
viewModel.uiChangeLiveData().onBackPressedEvent().observe(this, o -> {
onBackPressed();
});
}
/**
* 初始化根布局
*
* @param savedInstanceState
* @return 布局layout的id
*/
protected abstract int initContentView(Bundle savedInstanceState);
/**
* 初始化ViewModel的id
*
* @return BR的id
*/
protected abstract int initVariableId();
}
- 设置布局并获取布局实例。
- 检查viewModel是否为空,如果为空则根据泛型参数创建对应的ViewModel实例。
- 如果指定了ViewModel的BR id,则将ViewModel绑定到布局中。
- 监听ViewModel中的返回按键事件,触发onBackPressed方法。
java
public class HomeViewModel extends BaseViewModel<HomeRepository> {
public HomeViewModel(@NonNull Application application) {
super(application);
}
}
java
public class BaseViewModel<M extends BaseModel> extends AndroidViewModel implements IBaseViewModel, Consumer<Disposable> {
private UiChangeLiveData uiChangeLiveData;
private CompositeDisposable mCompositeDisposable;
protected M model;
public BaseViewModel(@NonNull Application application) {
super(application);
model = createModel();
}
private M createModel() {
try {
Type superClass = getClass().getGenericSuperclass();
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
Class<?> clazz = getRawType(type);
return (M) clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//获取当前类的父类类型。
//获取父类的第一个泛型参数类型。
//将该类型转换为具体的类类型。
//使用 newInstance 方法创建该类的实例并返回。
//如果过程中出现异常,则打印堆栈信息并返回 null。
// type不能直接实例化对象,通过type获取class的类型,然后实例化对象
private Class<?> getRawType(Type type) {
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
return (Class) rawType;
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
} else if (type instanceof TypeVariable) {
return Object.class;
} else if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
} else {
String className = type == null ? "null" : type.getClass().getName();
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but <" + type + "> is of type " + className);
}
}
protected void addDisposable(Disposable disposable) {
if (this.mCompositeDisposable == null) {
this.mCompositeDisposable = new CompositeDisposable();
}
this.mCompositeDisposable.add(disposable);
}
public UiChangeLiveData uiChangeLiveData() {
if (uiChangeLiveData == null) {
uiChangeLiveData = new UiChangeLiveData();
}
return uiChangeLiveData;
}
@Override
protected void onCleared() {
super.onCleared();
if (mCompositeDisposable != null && !mCompositeDisposable.isDisposed()) {
mCompositeDisposable.clear();
}
if (model != null) {
model.onCleared();
}
}
@Override
public void onAny(LifecycleOwner owner, Lifecycle.Event event) {
}
@Override
public void onCreate() {
}
@Override
public void onDestroy() {
}
@Override
public void onStart() {
}
@Override
public void onStop() {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void accept(Disposable disposable) throws Exception {
addDisposable(disposable);
}
public void onBackPressed() {
uiChangeLiveData.onBackPressedEvent.call();
}
public final class UiChangeLiveData extends SingleLiveEvent {
private SingleLiveEvent<Void> onBackPressedEvent;
public SingleLiveEvent<Void> onBackPressedEvent() {
return onBackPressedEvent = createLiveData(onBackPressedEvent);
}
private <T> SingleLiveEvent<T> createLiveData(SingleLiveEvent<T> liveData) {
if (liveData == null) {
liveData = new SingleLiveEvent<>();
}
return liveData;
}
}
}
java
public class HomeRepository extends BaseModel {
public void unCollect(String id, ApiCallback<Object> callback) {
ApiUtil.getArticleApi().unCollect(id).enqueue(callback);
}
}
java
/**
* Description: 不同模块BASE_URL可能不同
*/
public class ApiUtil {
public static ProjectApi getProjectApi() {
return RetrofitCreateHelper.getInstance().create(U.BASE_URL, ProjectApi.class);
}
}
java
public interface ProjectApi {
/**
* 项目分类
*
* @return
*/
@RetryCount(value = 3)
@GET("project/tree/json")
ApiCall<List<ProjectListRes>> listProjectsTab();
}
java
public class ApiCall <R> {
private final Observable<Response<ApiResponse<R>>> mEnqueueObservable;
private int mRetryCount;
private long mRetryDelay;
private long mRetryIncreaseDelay;
private Disposable mDisposable;
private Call<ApiResponse<R>> mCall;
ApiCall(Annotation[] annotations, Call<ApiResponse<R>> call) {
mCall = call;
mEnqueueObservable = RxJavaPlugins.onAssembly(new CallEnqueueObservable<>(call));
for (Annotation annotation : annotations) {
Class<? extends Annotation> clazz = annotation.annotationType();
if (clazz == RetryCount.class) {
RetryCount retryCount = (RetryCount) annotation;
mRetryCount = retryCount.value();
} else if (clazz == RetryDelay.class) {
RetryDelay retryDelay = (RetryDelay) annotation;
mRetryDelay = retryDelay.value();
} else if (clazz == RetryIncreaseDelay.class) {
RetryIncreaseDelay retryIncreaseDelay = (RetryIncreaseDelay) annotation;
mRetryIncreaseDelay = retryIncreaseDelay.value();
}
}
}
/**
* 进入请求队列
* 不绑定activity生命周期
* 建议使用传入activity的方式,以绑定生命周期
*
* @param callback 请求回调
*/
public <T extends ApiCallback<R>> void enqueue(T callback) {
enqueue(null, ProgressType.NONE, false, callback);
}
/**
* 进入请求队列
* 不绑定activity生命周期
* 建议使用传入activity的方式,以绑定生命周期
*
* @param callback 请求回调
*/
public <T extends ApiCallback<R>> void enqueue(T callback, ProgressType type) {
enqueue(null, type, false, callback);
}
/**
* 进入请求队列
* 无进度框,无toast
* 自动绑定activity生命周期
*
* @param callback 请求回调
*/
public void enqueue(Context activity, final ApiCallback<R> callback) {
enqueue(activity, ProgressType.NONE, false, callback);
}
/**
* 进入请求队列
* 带可取消进度框,有toast
* 自动绑定activity生命周期
*
* @param callback 请求回调
*/
public void enqueue2(Context activity, final ApiCallback<R> callback) {
enqueue(activity, ProgressType.CANCELABLE, true, callback);
}
/**
* 进入请求队列
*
* @param activity 界面
* @param progressType 进度框类型
* @param callback 请求回调
*/
public void enqueue(Context activity, ProgressType progressType, final ApiCallback<R> callback) {
enqueue(activity, progressType, false, callback);
}
/**
* 进入请求队列
*
* @param activity 界面
* @param toastError 是否弹错误toast
* @param callback 请求回调
*/
public void enqueue(Context activity, boolean toastError, final ApiCallback<R> callback) {
enqueue(activity, ProgressType.NONE, toastError, callback);
}
/**
* 进入请求队列
*
* @param activity 界面
* @param progressType 进度框类型
* @param toastError 是否弹错误toast
* @param callback 请求回调
*/
public void enqueue(Context activity, ProgressType progressType, final boolean toastError,
final ApiCallback<R> callback) {
Observable<Response<ApiResponse<R>>> observable;
/*if (activity instanceof RxAppCompatActivity) {
RxAppCompatActivity rxAppCompatActivity = (RxAppCompatActivity) activity;
observable = mEnqueueObservable.compose(rxAppCompatActivity.<Response<ApiResponse<R>>>bindToLifecycle());
} else {
observable = mEnqueueObservable;
}*/
observable = mEnqueueObservable;
mDisposable = observable.retryWhen(new RetryHandler<R>(mRetryCount, mRetryDelay, mRetryIncreaseDelay))
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
callback.onStart();
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Response<ApiResponse<R>>>() {
@Override
public void accept(Response<ApiResponse<R>> response) throws Exception {
ApiResponse<R> body = response.body();
if (!response.isSuccessful() || body == null) {
onError(callback, new HttpException(response), toastError);
cancel();
return;
}
callback.onSuccess(body);
cancel();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
onError(callback, throwable, toastError);
cancel();
}
});
}
/**
* Synchronously send the request and return its response.
*
* @throws IOException if a problem occurred talking to the server.
*/
public Response<ApiResponse<R>> exectue() throws IOException {
return mCall.clone().execute();
}
/**
* 处理错误
*
* @param callback 回调
* @param throwable 错误
*/
private void onError(ApiCallback<R> callback, Throwable throwable, boolean toast) {
callback.onError(throwable);
}
public void cancel() {
if (mDisposable != null) {
mDisposable.dispose();
}
}
/**
* 进度条类型
*/
public enum ProgressType {
/**
* 无进度条
*/
NONE,
/**
* 可取消进度条
*/
CANCELABLE,
/**
* 不可取消进度条
*/
UN_CANCELABLE
}
}
从使用角度,直接copy以上的代码就可以。
Retrofit Xml能力
1. 添加必要的依赖项
首先,在你的 build.gradle
文件中添加 Retrofit 和转换器的依赖项:
java
dependencies {
implementation 'com.squareup.retrofit2:converter-simplexml:2.9.0'
}
2. 创建数据模型
创建一个数据模型类,它将被转换为 XML 格式:
java
@Root(name = "restrict")
public class Restrict {
@Attribute(name = "Type")
private int type;
// Constructor
public Restrict(int type) {
this.type = type;
}
// Getter and Setter
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
3. 创建 API 接口
定义一个接口来描述你的 API 端点:
java
public interface ApiService {
@Headers({
"Content-Type: application/xml",
})
@POST("xxxx")
Call<Void> sendRestrict(@Body Restrict restrict);
}
4. 配置 Retrofit 实例
配置 Retrofit 实例并创建 API 服务:
java
public class RetrofitClient {
private static final String BASE_URL = "xxxxxxxxx";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(SimpleXmlConverterFactory.create())
.build();
}
return retrofit;
}
public static ApiService getApiService() {
return getClient().create(ApiService.class);
}
}
5. 发起请求
最后,使用创建的 ApiService
发起请求:
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApiService apiService = RetrofitClient.getApiService();
String xmlData = "";
MediaType xmlMediaType = MediaType.parse("application/xml");
RequestBody xmlBody = RequestBody.create(xmlData, xmlMediaType);
Call<Void> call = apiService.sendRestrict(xmlBody);
call.enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
Log.d("MainActivity", "Request successful");
} else {
Log.e("MainActivity", "Request failed");
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
Log.e("MainActivity", "Error: " + t.getMessage());
}
});
}
}
XML请求能力主要就两点:
1、添加内置的 simplexml 依赖,添加Retrofit转换器 .addConverterFactory(SimpleXmlConverterFactory.create())
2、使用 MediaType.parse 方法定义请求体的媒体类型为 application/xml
比较简单,但是我还是在这个功能上耗费了两个小时时间差点翻车,主要是几个同事需要马上使用。按照步骤死活无法成功,
我使用的方式是 bean转 xml 请求。
java
String xml = "<xxx></xxx>";
Serializer serializer = new Persister();
Restrict restrict = serializer.read(Restrict.class, xml);
我甚至怀疑接口是错误的,都没有怀疑这种方式有问题,用终端跑一下,成功返回数据。
然后怀疑参数问题,怀疑网络问题都没有怀疑这样的写法有什么问题。最后没办法给Okhttp添加了拦截器打印请求参数,发现 为啥是 json形式请求?说实在的至今没搞清楚为什么~
java
String xmlData = "";
MediaType xmlMediaType = MediaType.parse("application/xml");
RequestBody xmlBody = RequestBody.create(xmlData, xmlMediaType);
java
if (request.body() != null && request.body().contentType() != null) {
MediaType contentType = request.body().contentType();
if (contentType.subtype().equals("xml") || contentType.subtype().equals("json")) {
Buffer buffer = new Buffer();
request.body().writeTo(buffer);
Log.e(TAG,"Request body (" + contentType + "): " + buffer.readUtf8());
}
}