• Android简易音乐重构MVVM Java版-LiveData+用户登录+http模块(十)


    关于

      本篇内容主要是http模块(retrofit+okhttp3)重构,以及登录页面的重构。

    效果图

    在这里插入图片描述

      app启动图标在上篇放出的百度网盘链接里面有对应图片。

    添加http模块

      结构图如下:
    在这里插入图片描述

    添加 ApiService

    import androidx.lifecycle.LiveData;
    import com.tobery.livedata.call.livedatalib.ApiResponse;
    import com.tobery.personalmusic.entity.Login_Bean;
    import com.tobery.personalmusic.entity.MainRecommendListBean;
    import com.tobery.personalmusic.entity.SearchHotDetail_Bean;
    import com.tobery.personalmusic.entity.banner_bean;
    import io.reactivex.Observable;
    import retrofit2.http.GET;
    import retrofit2.http.POST;
    import retrofit2.http.Query;
    
    public interface  ApiService {
        @GET("banner")
        LiveData<ApiResponse<banner_bean>> getBanner(@Query("type") int type);
        @GET("login/cellphone")
        Observable<Login_Bean> login(@Query("phone") String phone, @Query("password") String password);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    添加RetrofitUtils请求网络api

    public class RetrofitUtils {
        /**
         * 单例模式
         */
        public static ApiService apiService;
        public static ApiService getmApiUrl(){
            if (apiService == null){
                synchronized (RetrofitUtils.class){
                    if (apiService == null){
                        apiService = new RetrofitUtils().getRetrofit();
                    }
                }
            }
            return apiService;
        }
    
        private ApiService getRetrofit() {
            //初始化Retrofit
            ApiService apiService = initRetrofit(initOkHttp()).create(ApiService.class);
            return apiService;
        }
    
        private OkHttpClient initOkHttp() {
            //ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(MyApplication.getContext()));
            return new OkHttpClient().newBuilder()
                    .readTimeout(Constant.DEFAULT_TIME, TimeUnit.SECONDS) //设置读取超时时间
                    .connectTimeout(Constant.DEFAULT_TIME,TimeUnit.SECONDS) //设置请求超时时间
                    .writeTimeout(Constant.DEFAULT_TIME,TimeUnit.SECONDS) //设置写入超时时间
                    .addInterceptor(new LogInterceptor())  //添加打印拦截器
                    .retryOnConnectionFailure(true) //设置出错进行重新连接
                    //.cookieJar(cookieJar)
                    .build();
        }
    
        /**
         * 初始化Retrofit
         */
        @NonNull
        private Retrofit initRetrofit(OkHttpClient client){
            return new Retrofit.Builder()
                    .client(client)
                    .baseUrl(Constant.BaseUrl)
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addCallAdapterFactory(LiveDataCallAdapterFactory.create())//livedataFactory
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
    
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    添加RXHelper用于线程切换

    public class RXHelper {
        public static <T> ObservableTransformer<T, T> observableIO2Main(final Context context) {
            return upstream -> {
                Observable<T> observable = upstream.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
                return composeContext(context, observable);
            };
        }
    
        public static <T> ObservableTransformer<T, T> observableIO2Main(final RxFragment fragment) {
            return upstream -> upstream.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread()).compose(fragment.<T>bindToLifecycle());
        }
    
        public static <T> FlowableTransformer<T, T> flowableIO2Main() {
            return upstream -> upstream
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread());
        }
    
        private static <T> ObservableSource<T> composeContext(Context context, Observable<T> observable) {
            if(context instanceof RxActivity) {
                return observable.compose(((RxActivity) context).bindUntilEvent(ActivityEvent.DESTROY));
            } else if(context instanceof RxFragmentActivity){
                return observable.compose(((RxFragmentActivity) context).bindUntilEvent(ActivityEvent.DESTROY));
            }else if(context instanceof RxAppCompatActivity){
                return observable.compose(((RxAppCompatActivity) context).bindUntilEvent(ActivityEvent.DESTROY));
            }else {
                return observable;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    添加RxExceptionUtil网络异常处理类

    public class RxExceptionUtil {
        public static String exceptionHandler(Throwable e){
            String errMsg = "未知错误";
            if (e instanceof UnknownHostException){
                errMsg = "网络不可用";
            }else if (e instanceof SocketTimeoutException){
                errMsg = "请求网络超时";
            }else if (e instanceof HttpException) {
                HttpException httpException = (HttpException) e;
                errMsg = convertStatusCode(httpException);
            }else if (e instanceof ParseException ||  e instanceof JSONException){
                errMsg = "数据解析错误";
            }
            return errMsg;
        }
        private static String convertStatusCode(HttpException httpException) {
            String msg;
            if (httpException.code() >= 500 && httpException.code() < 600) {
                msg = "服务器处理请求出错";
            } else if (httpException.code() >= 400 && httpException.code() < 500) {
                msg = "服务器无法处理请求";
            } else if (httpException.code() >= 300 && httpException.code() < 400) {
                msg = "请求被重定向到其他页面";
            } else {
                msg = httpException.message();
            }
            return msg;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    拦截器LogInterceptor打印请求和返回日志

    public class LogInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Log.d("拦截器---->","request:"+request.toString());
            long t1 = System.nanoTime();
            Response response = chain.proceed(chain.request());
            long t2 = System.nanoTime();
            Log.d("拦截器---->",String.format(Locale.getDefault(), "Received response for %s in %.1fms%n%s",
                    response.request().url(), (t2 - t1) / 1e6d, response.headers()));
            MediaType mediaType = response.body().contentType();
            String content = response.body().string();
            Log.d("拦截器---->","response body:"+content);
            return response.newBuilder()
                    .body(ResponseBody.create(mediaType,content))
                    .build();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    添加GeneratedAppGlideModule

    @GlideModule
    public class GeneratedAppGlideModule extends AppGlideModule {
        @Override
        public boolean isManifestParsingEnabled() {
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    定义LoginUi绑定用户账号密码

    public class LoginUi implements Serializable {
    
        public ObservableField<String> userName;
        public ObservableField<String> password;
    
        public LoginUi(ObservableField<String> userName, ObservableField<String> password) {
            this.password = password;
            this.userName = userName;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    新增LoginViewModel

    public class LoginViewModel extends ViewModel {
        private SavedStateHandle state;
    
        public LoginUi ui;
    
    
        public LoginViewModel(SavedStateHandle savedStateHandle) {
            this.state = savedStateHandle;
            ui = state.get(KEY_LOGIN_UI) == null?new LoginUi(new ObservableField<>(""),new ObservableField<>("")):state.get(KEY_LOGIN_UI);
        }
    
        public Observable<Login_Bean> login() {
            return RetrofitUtils.getmApiUrl().login(ui.userName.get(),ui.password.get());
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    新增页面login页面

      新增activity_login.xml

    <?xml version="1.0" encoding="utf-8"?>
    <layout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">
    
        <data>
            <variable
                name="vm"
                type="com.tobery.personalmusic.ui.login.LoginViewModel" />
        </data>
        <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".ui.login.LoginActivity">
    
            <include
                android:id="@+id/title"
                layout="@layout/ui_common_title" />
    
    
            <TextView
                android:id="@+id/im_User"
                android:layout_width="@dimen/dp_25"
                android:layout_height="@dimen/dp_25"
                android:layout_marginStart="@dimen/dp_30"
                android:layout_marginTop="@dimen/dp_30"
                android:background="@drawable/ic_login_user"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/title"
                />
    
            <EditText
                android:id="@+id/edit_user"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/dp_35"
                android:layout_marginEnd="@dimen/dp_30"
                android:background="@null"
                android:hint="@string/input_phone"
                android:inputType="phone"
                android:maxLength="20"
                android:maxLines="1"
                android:text="@={vm.ui.userName}"
                android:textColor="@color/tv_black"
                android:textColorHint="@color/tv_hint"
                android:textSize="@dimen/sp_15"
                app:layout_constraintStart_toEndOf="@id/im_User"
                app:layout_constraintStart_toStartOf="@id/im_User"
                app:layout_constraintBottom_toBottomOf="@id/im_User"
                />
    
            <View
                android:id="@+id/view_user"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_1"
                android:layout_marginLeft="@dimen/dp_30"
                android:layout_marginRight="@dimen/dp_30"
                android:layout_marginTop="@dimen/dp_12"
                android:background="@color/color_border"
                app:layout_constraintTop_toBottomOf="@id/im_User"
                />
    
            <TextView
                android:id="@+id/im_password"
                android:layout_width="@dimen/dp_25"
                android:layout_height="@dimen/dp_25"
                android:layout_marginStart="@dimen/dp_30"
                android:layout_marginTop="@dimen/dp_30"
                android:background="@drawable/ic_login_password"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/view_user"
                />
    
            <EditText
                android:id="@+id/edit_password"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/dp_35"
                android:layout_marginEnd="@dimen/dp_30"
                android:background="@null"
                android:hint="@string/input_pwd"
                android:maxLength="20"
                android:maxLines="1"
                android:text="@={vm.ui.password}"
                android:textColor="@color/tv_black"
                android:textColorHint="@color/tv_hint"
                android:textSize="@dimen/sp_15"
                app:layout_constraintStart_toStartOf="@id/im_User"
                app:layout_constraintTop_toTopOf="@id/im_password"
                app:layout_constraintBottom_toBottomOf="@id/im_password"
                />
    
            <View
                android:id="@+id/view_password"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_1"
                android:layout_marginLeft="@dimen/dp_30"
                android:layout_marginRight="@dimen/dp_30"
                android:layout_marginTop="@dimen/dp_12"
                android:background="@color/color_border"
                app:layout_constraintTop_toBottomOf="@id/im_password"
                />
    
            <Button
                android:id="@+id/btn_login"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_45"
                android:layout_marginStart="@dimen/dp_60"
                android:layout_marginTop="@dimen/dp_30"
                android:layout_marginEnd="@dimen/dp_60"
                android:background="@drawable/shape_btn_login_confirm"
                android:text="@string/login"
                android:textColor="@color/tv_white"
                app:layout_constraintTop_toBottomOf="@id/view_password"
                />
    
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    </layout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    添加通用标题栏

      新建ui_common_title.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:background="@color/colorPrimary"
        android:layout_height="@dimen/dp_45">
    
        <TextView
            android:id="@+id/iv_back"
            android:layout_width="@dimen/dp_30"
            android:layout_height="@dimen/dp_30"
            android:background="@drawable/ic_common_back"
            android:layout_marginStart="@dimen/dp_15"
            android:layout_marginTop="@dimen/dp_5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            />
    
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_centerInParent="true"
            android:layout_centerHorizontal="true"
            android:layout_marginStart="@dimen/dp_15"
            android:textColor="@color/tv_black"
            android:textSize="@dimen/sp_18"
            android:typeface="monospace"
            android:visibility="gone"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
           />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    添加登录按钮背景

      新增shape_btn_login_confirm.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
        <corners android:radius="@dimen/dp_40"/>
        <solid android:color="@color/colorPrimary"/>
    </shape>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    修改LoginActivity.java

    public class LoginActivity extends BaseActivity {
    
        private ActivityLoginBinding binding;
    
        private LoginViewModel viewModel;
    
        private AlertDialog dialog;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
            binding.setLifecycleOwner(this);
            viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
            binding.setVm(viewModel);
            initView();
        }
    
        private void initView() {
            binding.title.ivBack.setOnClickListener(view -> finish());
            binding.btnLogin.setOnClickListener(view -> {
                if (ClickUtil.enableClick()){
                    if (Objects.requireNonNull(viewModel.ui.userName.get()).isEmpty() || viewModel.ui.password.get().isEmpty()){
                        ToastUtils.show(R.string.toast_login);
                        return;
                    }
                    dialog = AlertDialogUtil.Companion.createLoading(this,"登录中");
                    viewModel.login()
                            .compose(RXHelper.observableIO2Main(this))
                            .subscribe(new Observer<Login_Bean>() {
                                @Override
                                public void onSubscribe(Disposable d) {
    
                                }
    
                                @Override
                                public void onNext(Login_Bean login_bean) {
                                    Log.e("数据",login_bean.toString());
                                    AlertDialogUtil.Companion.closeDialog(dialog);
                                    if (login_bean.getCode() == 200){
                                        SharePreferencesUtil.getInstance(LoginActivity.this).saveUserInfo(login_bean,viewModel.ui.userName.get());
                                        Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                                        startActivity(intent);
                                        finish();
                                    }else if (login_bean.getCode() == 502){
                                        ToastUtils.show(R.string.msg_password_error);
                                    }else {
                                        ToastUtils.show(R.string.msg_user_error);
                                    }
    
                                }
    
                                @Override
                                public void onError(Throwable e) {
                                    AlertDialogUtil.Companion.closeDialog(dialog);
                                    onFailure(e, RxExceptionUtil.exceptionHandler(e));
                                }
    
                                @Override
                                public void onComplete() {}
                            });
    
    
                }
            });
        }
    
        private void onFailure(Throwable e, String exceptionHandler) {
            ToastUtils.show(exceptionHandler);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (dialog !=null){
                dialog.dismiss();
                dialog = null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
  • 相关阅读:
    音视频的基本概念
    rv1126-rv1109-sfc-分区表获取流程分析
    【js奇妙说】如何跟非计算机从业者解释,为什么浮点数计算0.1+0.2不等于0.3?
    PHP 中 16 个技巧使用方法
    uiautomation函数讲解(中)
    使用JS简单实现一下apply、call和bind方法
    复旦、人大等发布大五人格+MBTI测试 角色扮演AI特质还原率达82.8%
    window平台C#实现软件升级功能(控制台)
    【HALCON】如何实现hw窗口自适应相机拍照成像的大小
    【c++】 跟webrtc学周期性任务:tgcalls 5 网络超时检查
  • 原文地址:https://blog.csdn.net/Tobey_r1/article/details/125416981