Retrofit+OkHttp+ViewModel

目录

[中规中矩的 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)

Mvvm模式使用,下面应该算是一个完整封装框架。

[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();

}
  1. 设置布局并获取布局实例。
  2. 检查viewModel是否为空,如果为空则根据泛型参数创建对应的ViewModel实例。
  3. 如果指定了ViewModel的BR id,则将ViewModel绑定到布局中。
  4. 监听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());
            }
        }
相关推荐
云中飞鸿1 小时前
MFC中CString的Format、与XML中的XML_SETTEXT格式化注意
xml·c++·mfc
wolf犭良21 小时前
14、《SpringBoot+MyBatis集成(2)——进阶配置XML与注解的灵活运用》
xml·spring boot·mybatis
Lojarro1 天前
JavaEE基础之- xml
xml·数据库·java-ee
m0_748233882 天前
使用 Logback 的最佳实践:`logback.xml` 与 `logback-spring.xml` 的区别与用法
xml·spring·logback
zhencon-com2 天前
如何使用qt开发一个xml发票浏览器,实现按发票样式显示
xml·c++·经验分享·python·qt·学习·金融
火烧屁屁啦2 天前
【JavaEE进阶】MyBatis通过XML实现增删改查
xml·java-ee·mybatis
清风细雨_林木木2 天前
wangEditor 编辑器 Vue 2.0 + Nodejs 配置
vue.js·okhttp·编辑器
m0_748252235 天前
【mybatis】基本操作:详解Spring通过注解和XML的方式来操作mybatis
xml·spring·mybatis
veminhe7 天前
maven使用默认settings.xml配置时,Idea基于pom.xml更新依赖时报错,有些组件下载时连接超时
xml·maven·intellij-idea