├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── hqumath
│ │ └── androidmvvm
│ │ ├── adapter
│ │ ├── MyFragmentPagerAdapter.java
│ │ └── MyRecyclerAdapters.java
│ │ ├── app
│ │ ├── App.java
│ │ ├── AppExecutors.java
│ │ ├── AppManager.java
│ │ ├── Constant.java
│ │ └── CrashHandler.java
│ │ ├── base
│ │ ├── BaseActivity.java
│ │ ├── BaseFragment.java
│ │ ├── BaseModel.java
│ │ ├── BaseRecyclerAdapter.java
│ │ ├── BaseRecyclerViewHolder.java
│ │ └── BaseViewModel.java
│ │ ├── bean
│ │ ├── BaseResultEntity.java
│ │ ├── CommitEntity.java
│ │ ├── ReposEntity.java
│ │ └── UserInfoEntity.java
│ │ ├── net
│ │ ├── ApiService.java
│ │ ├── CreateRequestBodyUtil.java
│ │ ├── HandlerException.java
│ │ ├── HttpListener.java
│ │ ├── LogInterceptor.java
│ │ ├── RetrofitClient.java
│ │ ├── TokenException.java
│ │ ├── download
│ │ │ ├── DownloadInterceptor.java
│ │ │ ├── DownloadListener.java
│ │ │ └── DownloadResponseBody.java
│ │ └── upload
│ │ │ ├── ProgressRequestBody.java
│ │ │ └── UploadProgressListener.java
│ │ ├── repository
│ │ ├── AppDatabase.java
│ │ ├── MyModel.java
│ │ └── dao
│ │ │ └── UserInfoDao.java
│ │ ├── service
│ │ └── UpdateService.java
│ │ ├── ui
│ │ ├── fileupdown
│ │ │ ├── FileUpDownActivity.java
│ │ │ └── FileUpDownViewModel.java
│ │ ├── follow
│ │ │ ├── FollowersFragment.java
│ │ │ ├── FollowersViewModel.java
│ │ │ ├── FollowersViewModelOld.java
│ │ │ ├── ProfileDetailActivity.java
│ │ │ └── ProfileDetailViewModel.java
│ │ ├── login
│ │ │ ├── LoginActivity.java
│ │ │ └── LoginViewModel.java
│ │ ├── main
│ │ │ ├── AboutFragment.java
│ │ │ ├── MainActivity.java
│ │ │ └── SettingsFragment.java
│ │ └── repos
│ │ │ ├── MyReposFragment.java
│ │ │ ├── ReposDetailActivity.java
│ │ │ ├── ReposDetailViewModel.java
│ │ │ ├── ReposFragment.java
│ │ │ ├── ReposViewModel.java
│ │ │ └── StarredFragment.java
│ │ ├── utils
│ │ ├── ByteUtil.java
│ │ ├── CommonUtil.java
│ │ ├── Density.java
│ │ ├── DeviceUtil.java
│ │ ├── DialogUtil.java
│ │ ├── FileUtil.java
│ │ ├── LogUtil.java
│ │ ├── PermissionUtil.java
│ │ ├── SPUtil.java
│ │ ├── SSLSocketClient.java
│ │ ├── SignatureUtil.java
│ │ ├── StringUtil.java
│ │ └── ZxingUtil.java
│ │ └── widget
│ │ ├── DownloadingDialog.java
│ │ └── DownloadingProgressBar.java
│ └── res
│ ├── anim
│ ├── in_from_left.xml
│ ├── in_from_right.xml
│ ├── out_to_left.xml
│ ├── out_to_right.xml
│ ├── push_bottom_in.xml
│ └── push_bottom_out.xml
│ ├── drawable
│ ├── bg_button_login.xml
│ ├── bottom_navigation_item_selector.xml
│ ├── dialog_common_bg.xml
│ ├── ic_code.xml
│ ├── ic_custom.png
│ ├── ic_empty.xml
│ ├── ic_github.xml
│ ├── ic_group_secondary.xml
│ ├── ic_issues.xml
│ ├── ic_link_secondary.xml
│ ├── ic_logout.xml
│ ├── ic_mail.xml
│ ├── ic_mail_secondary.xml
│ ├── ic_menu_about.xml
│ ├── ic_menu_person.xml
│ ├── ic_menu_repo.xml
│ ├── ic_menu_settings.xml
│ ├── ic_menu_star.xml
│ ├── ic_menu_trace.xml
│ └── ic_style.png
│ ├── layout
│ ├── activity_fileupdown.xml
│ ├── activity_login.xml
│ ├── activity_main.xml
│ ├── activity_profile_detail.xml
│ ├── activity_repos_detail.xml
│ ├── dialog_common.xml
│ ├── dialog_downloading.xml
│ ├── fragment_about.xml
│ ├── fragment_followers.xml
│ ├── fragment_repos.xml
│ ├── fragment_settings.xml
│ ├── fragment_swipe_list.xml
│ ├── recycler_item_commits.xml
│ ├── recycler_item_followers.xml
│ ├── recycler_item_repos.xml
│ ├── recycler_layout_empty.xml
│ └── recycler_layout_empty_databinding.xml
│ ├── menu
│ ├── main_bottom_navigation.xml
│ └── menu_navigation.xml
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values
│ ├── anim.xml
│ ├── attrs.xml
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ └── file_paths.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── img
├── architecture.png
├── fc.png
└── paging.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /.gradle
3 | /build
4 | /captures
5 | /local.properties
6 | /*.iml
7 | /*.rar
8 | /.DS_Store
9 | /*.apk
10 | /.externalNativeBuild
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidMVVM
2 | Android MVVM是一款基于MVVM框架,以Jetpack组件DataBinding+LiveData+ViewModel为基础,整合Retrofit+RxJava网络模块的快速开发框架。
3 |
4 | ## 框架流程
5 | 
6 | 
7 |
8 | ## 框架特点
9 | - **Jetpack组件**
10 |
11 | 1. ViewBinding & DataBinding
12 | 2. Lifecycles
13 | 3. LiveData
14 | 4. Navigation
15 | 5. Paging
16 | 6. Room
17 | 7. ViewModel
18 |
19 | - **流行框架**
20 |
21 | 1. [retrofit](https://github.com/square/retrofit)+[okhttp](https://github.com/square/okhttp)+[rxJava](https://github.com/ReactiveX/RxJava)负责网络请求
22 | 2. [gson](https://github.com/google/gson)负责解析json数据
23 | 3. [glide](https://github.com/bumptech/glide)负责加载图片;
24 |
25 | - **基类封装**
26 |
27 | 1. BaseActivity
28 | 2. BaseFragment
29 | 3. BaseViewModel
30 |
31 | - **全局操作**
32 |
33 | 1. 全局的Activity堆栈式管理
34 | 2. LoggingInterceptor全局拦截网络请求日志
35 | 3. 全局的异常捕获,程序发生异常时不会崩溃,返回上个界面。
36 | 4. 使用androidx
37 | 5. 不使用kotlin
38 |
39 | - **Room组件**
40 |
41 | 1. 实现了Network only 和 Network & database 两种模式
42 |
43 | FollowersFragment 使用 Room 持久化存储列表数据,Network => DB => LiveData => RecyclerView
44 |
45 | 
46 |
47 | ## 界面
48 |
49 | 1. 登录界面(使用任意账户登录)
50 | 2. 我的仓库列表
51 | 3. 我的star仓库列表
52 | 4. 我的following列表
53 | 5. 仓库详情
54 | 6. 用户详情
55 |
56 | ## 注意
57 |
58 | 1. 接口使用GitHub API v3,单IP限制每小时60次requests
59 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /*.apk
3 | /*.iml
4 | /release
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | namespace 'com.hqumath.androidmvvm' //影响R类生成
7 | compileSdk 31
8 |
9 | defaultConfig {
10 | applicationId "com.hqumath.androidmvvm" //影响AndroidManifest中package
11 | minSdk 21
12 | //noinspection ExpiredTargetSdkVersion
13 | targetSdk 31
14 | versionCode 20211020
15 | versionName "2.0"
16 | }
17 | buildFeatures {
18 | viewBinding true
19 | dataBinding true
20 | }
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility JavaVersion.VERSION_1_8
29 | targetCompatibility JavaVersion.VERSION_1_8
30 | }
31 | //配置自定义打包名称
32 | applicationVariants.all { variant ->
33 | variant.outputs.all {
34 | def fileName
35 | if (variant.buildType.name.equals('release')) {
36 | fileName = "MVVMDemo_${variant.mergedFlavor.versionName}_${variant.mergedFlavor.versionCode}.apk"
37 | } else if (variant.buildType.name.equals('debug')) {
38 | fileName = "MVVMDemo_${variant.mergedFlavor.versionName}_debug_${variant.mergedFlavor.versionCode}.apk"
39 | }
40 | outputFileName = fileName
41 | }
42 | }
43 | }
44 |
45 | dependencies {
46 | implementation fileTree(dir: 'libs', include: ['*.jar'])
47 | implementation 'androidx.appcompat:appcompat:1.2.0'
48 | implementation 'androidx.recyclerview:recyclerview:1.1.0'
49 | implementation 'androidx.cardview:cardview:1.0.0'
50 | implementation 'com.google.android.material:material:1.2.0'
51 | //lifecycle
52 | def lifecycle_version = "2.4.0-rc01"
53 | implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
54 | implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
55 | implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
56 | //room
57 | def room_version = "2.3.0"
58 | implementation "androidx.room:room-runtime:$room_version"
59 | annotationProcessor "androidx.room:room-compiler:$room_version"
60 | //implementation "androidx.room:room-paging:2.4.0-beta01"
61 | //paging 2=>3 升级,大刀阔斧很多API改变了,稳定后再用吧,暂不处理显示大数据集(本地数据库和网络)的问题
62 | //def paging_version = "3.0.1"
63 | //implementation "androidx.paging:paging-runtime:$paging_version"
64 | //implementation "androidx.paging:paging-compose:1.0.0-alpha14"
65 |
66 | //rxjava
67 | implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
68 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
69 | //network
70 | implementation 'com.squareup.okhttp3:okhttp:3.12.1'
71 | implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'
72 | implementation 'com.squareup.retrofit2:retrofit:2.5.0'
73 | implementation 'com.squareup.retrofit2:converter-gson:2.4.0'//数据解析器
74 | implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'//网络请求适配器
75 | // MultiDex的依赖
76 | //implementation 'org.robolectric:shadows-multidex:3.4-rc2'
77 | //下拉刷新
78 | implementation 'com.scwang.smart:refresh-layout-kernel:2.0.3' //核心必须依赖
79 | implementation 'com.scwang.smart:refresh-header-classics:2.0.3' //经典刷新头
80 | //权限获取
81 | implementation 'com.yanzhenjie:permission:2.0.3'
82 | //picture
83 | implementation 'com.github.bumptech.glide:glide:4.12.0'
84 |
85 | //内存泄漏
86 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
87 |
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
39 |
43 |
47 |
51 |
52 |
55 |
56 |
57 |
62 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/adapter/MyFragmentPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.hqumath.androidmvvm.adapter;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 | import androidx.fragment.app.Fragment;
6 | import androidx.fragment.app.FragmentManager;
7 | import androidx.fragment.app.FragmentPagerAdapter;
8 |
9 | import com.hqumath.androidmvvm.base.BaseFragment;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * ****************************************************************
15 | * 文件名称: MyFragmentPagerAdapter
16 | * 作 者: Created by gyd
17 | * 创建时间: 2019/9/2 15:15
18 | * 文件描述:
19 | * 注意事项:
20 | * 版权声明:
21 | * ****************************************************************
22 | */
23 | public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
24 |
25 | private List fragmentList;
26 | private List titles;
27 |
28 | public MyFragmentPagerAdapter(FragmentManager fm) {
29 | super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
30 | }
31 |
32 | public void setData(List fragmentList, List titles) {
33 | this.fragmentList = fragmentList;
34 | this.titles = titles;
35 | }
36 |
37 |
38 | @NonNull
39 | @Override
40 | public Fragment getItem(int position) {
41 | return fragmentList.get(position);
42 | }
43 |
44 | @Override
45 | public int getCount() {
46 | return fragmentList.size();
47 | }
48 |
49 | @Nullable
50 | @Override
51 | public CharSequence getPageTitle(int position) {
52 | return titles == null ? "" : titles.get(position);
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/adapter/MyRecyclerAdapters.java:
--------------------------------------------------------------------------------
1 | package com.hqumath.androidmvvm.adapter;
2 |
3 | import android.content.Context;
4 | import android.text.TextUtils;
5 | import android.widget.ImageView;
6 |
7 | import com.bumptech.glide.Glide;
8 | import com.hqumath.androidmvvm.R;
9 | import com.hqumath.androidmvvm.base.BaseRecyclerAdapter;
10 | import com.hqumath.androidmvvm.base.BaseRecyclerViewHolder;
11 | import com.hqumath.androidmvvm.bean.CommitEntity;
12 | import com.hqumath.androidmvvm.bean.ReposEntity;
13 | import com.hqumath.androidmvvm.bean.UserInfoEntity;
14 | import com.hqumath.androidmvvm.utils.CommonUtil;
15 |
16 | import java.text.ParseException;
17 | import java.text.SimpleDateFormat;
18 | import java.util.Date;
19 | import java.util.List;
20 |
21 | public class MyRecyclerAdapters {
22 |
23 | //我的跟随
24 | public static class FollowRecyclerAdapter extends BaseRecyclerAdapter {
25 | public FollowRecyclerAdapter(Context context, List mData) {
26 | super(context, mData, R.layout.recycler_item_followers);
27 | }
28 |
29 | public void setData(List list) {
30 | this.mData = list;
31 | }
32 |
33 | @Override
34 | public void convert(BaseRecyclerViewHolder holder, int position) {
35 | UserInfoEntity data = mData.get(position);
36 | holder.setText(R.id.tv_name, data.getLogin());
37 | ImageView ivHead = holder.getView(R.id.iv_head);
38 | if (!TextUtils.isEmpty(data.getAvatar_url())) {
39 | Glide.with(CommonUtil.getContext())
40 | .load(data.getAvatar_url())
41 | .circleCrop()
42 | .into(ivHead);
43 | }
44 | }
45 | }
46 |
47 | //我的仓库
48 | public static class ReposRecyclerAdapter extends BaseRecyclerAdapter {
49 | public ReposRecyclerAdapter(Context context, List mData) {
50 | super(context, mData, R.layout.recycler_item_repos);
51 | }
52 |
53 | @Override
54 | public void convert(BaseRecyclerViewHolder holder, int position) {
55 | ReposEntity data = mData.get(position);
56 | holder.setText(R.id.tv_name, data.getName());
57 | holder.setText(R.id.tv_description, data.getDescription());
58 | holder.setText(R.id.tv_author, data.getOwner().getLogin());
59 | }
60 | }
61 |
62 | //提交记录
63 | public static class CommitsRecyclerAdapter extends BaseRecyclerAdapter {
64 | public CommitsRecyclerAdapter(Context context, List mData) {
65 | super(context, mData, R.layout.recycler_item_commits);
66 | }
67 |
68 | @Override
69 | public void convert(BaseRecyclerViewHolder holder, int position) {
70 | CommitEntity data = mData.get(position);
71 | holder.setText(R.id.tv_name, data.getCommit().getCommitter().getName());
72 | holder.setText(R.id.tv_message, data.getCommit().getMessage());
73 | holder.setText(R.id.tv_sha, data.getSha());
74 | //时间格式化
75 | String date = data.getCommit().getCommitter().getDate();//2011-12-29T04:45:11Z
76 | date = date.replace("Z", " UTC");//UTC是世界标准时间
77 | SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss Z");
78 | SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
79 | try {
80 | Date date1 = format1.parse(date);
81 | String date2 = format2.format(date1);
82 | holder.setText(R.id.tv_time, date2);
83 | } catch (ParseException e) {
84 | e.printStackTrace();
85 | }
86 | }
87 | }
88 | }
89 |
90 |
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/app/App.java:
--------------------------------------------------------------------------------
1 | package com.hqumath.androidmvvm.app;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.os.Bundle;
6 |
7 | import androidx.annotation.NonNull;
8 |
9 | import com.hqumath.androidmvvm.utils.CommonUtil;
10 | import com.hqumath.androidmvvm.utils.Density;
11 |
12 | /**
13 | * ****************************************************************
14 | * 文件名称: AppApplication
15 | * 作 者: Created by gyd
16 | * 创建时间: 2019/5/31 17:04
17 | * 文件描述:
18 | * 注意事项:
19 | * 版权声明:
20 | * ****************************************************************
21 | */
22 | public class App extends Application {
23 | //获取全局上下文 CommonUtil.getContext();
24 | //private static Application sInstance;
25 |
26 | @Override
27 | public void onCreate() {
28 | super.onCreate();
29 | setApplication(this);
30 | //初始化工具类
31 | CommonUtil.init(this);
32 | //屏幕适配方案,根据ui图修改,屏幕最小宽度375dp
33 | Density.setDensity(this, 375f);
34 |
35 | //异常捕获后重启,umeng等可能无法统计到异常信息
36 | //CrashHandler myCrashHandler =CrashHandler.getInstance();
37 | //myCrashHandler.init(this);
38 | }
39 |
40 | public static synchronized void setApplication(@NonNull Application application) {
41 | //sInstance = application;
42 | //注册监听每个activity的生命周期,便于堆栈式管理
43 | application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
44 |
45 | @Override
46 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
47 | AppManager.getInstance().addActivity(activity);
48 | }
49 |
50 | @Override
51 | public void onActivityStarted(Activity activity) {
52 | }
53 |
54 | @Override
55 | public void onActivityResumed(Activity activity) {
56 | }
57 |
58 | @Override
59 | public void onActivityPaused(Activity activity) {
60 | }
61 |
62 | @Override
63 | public void onActivityStopped(Activity activity) {
64 | }
65 |
66 | @Override
67 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
68 | }
69 |
70 | @Override
71 | public void onActivityDestroyed(Activity activity) {
72 | AppManager.getInstance().removeActivity(activity);
73 | }
74 | });
75 | }
76 |
77 | /**
78 | * 获得当前app运行的Application
79 | */
80 | /*public static Application getInstance() {
81 | if (sInstance == null) {
82 | throw new NullPointerException("please inherit BaseApplication or call setApplication.");
83 | }
84 | return sInstance;
85 | }*/
86 |
87 | /*@Override
88 | protected void attachBaseContext(Context base) {
89 | super.attachBaseContext(base);
90 | MultiDex.install(this);//方法数超过65k
91 | }*/
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/app/AppExecutors.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.hqumath.androidmvvm.app;
18 |
19 | import android.os.Handler;
20 | import android.os.Looper;
21 | import androidx.annotation.NonNull;
22 |
23 | import java.util.concurrent.Executor;
24 | import java.util.concurrent.ExecutorService;
25 | import java.util.concurrent.Executors;
26 | import java.util.concurrent.ScheduledExecutorService;
27 |
28 | /**
29 | * 全局线程池
30 | * Global executor pools for the whole application.
31 | * 对任务进行分组,io操作和网络请求可同时执行
32 | * 用法:AppExecutors.getInstance().driveWorkThread().execute(() -> {});
33 | */
34 | public class AppExecutors {
35 |
36 | //双重校验锁
37 | /*private volatile static AppExecutors sInstance;
38 |
39 | public static AppExecutors getInstance() {
40 | if (sInstance == null) {
41 | synchronized (AppExecutors.class) {
42 | if (sInstance == null) {
43 | sInstance = new AppExecutors();
44 | }
45 | }
46 | }
47 | return sInstance;
48 | }*/
49 |
50 | //静态内部类
51 | private static class AppExecutorsHolder {
52 | private static final AppExecutors instance = new AppExecutors();
53 | }
54 |
55 | public static AppExecutors getInstance() {
56 | return AppExecutorsHolder.instance;
57 | }
58 |
59 | private MainThreadExecutor mainThread;//ui线程操作
60 | private ExecutorService workThread;//工作线程池,执行普通任务。例如:网络通讯和多媒体操作
61 | private ScheduledExecutorService scheduledWork;//循环线程池,执行普通循环任务。
62 |
63 | public MainThreadExecutor mainThread() {
64 | if (mainThread == null) {
65 | mainThread = new MainThreadExecutor();
66 | }
67 | return mainThread;
68 | }
69 |
70 | public static class MainThreadExecutor implements Executor {
71 | private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
72 |
73 | @Override
74 | public void execute(@NonNull Runnable command) {
75 | mainThreadHandler.post(command);
76 | }
77 |
78 | public void postDelayed(Runnable command, int delayMillis) {
79 | mainThreadHandler.postDelayed(command, delayMillis);
80 | }
81 | }
82 |
83 | public ExecutorService workThread() {
84 | if (workThread == null || workThread.isShutdown())
85 | workThread = Executors.newFixedThreadPool(8);//骁龙888八个CPU核心
86 | return workThread;
87 | }
88 |
89 | public ScheduledExecutorService scheduledWork() {
90 | if (scheduledWork == null || scheduledWork.isShutdown())
91 | scheduledWork = Executors.newScheduledThreadPool(4);
92 | return scheduledWork;
93 | }
94 |
95 | public void shutdownWorkThread() {
96 | if (workThread != null) {
97 | workThread.shutdown();
98 | workThread = null;
99 | }
100 | }
101 |
102 | public void shutdownScheduledWork() {
103 | if (scheduledWork != null) {
104 | scheduledWork.shutdown();
105 | scheduledWork = null;
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/app/AppManager.java:
--------------------------------------------------------------------------------
1 | package com.hqumath.androidmvvm.app;
2 |
3 | import android.app.Activity;
4 |
5 | import java.util.Stack;
6 |
7 | /**
8 | * 栈管理
9 | * 添加、删除当前activity、删除指定的activity、清空栈、求栈大小
10 | */
11 | public class AppManager {
12 |
13 | private static Stack activityStack = new Stack();
14 |
15 | private static AppManager appManager = null;
16 |
17 | private AppManager() {
18 |
19 | }
20 |
21 | public static AppManager getInstance() {
22 | if (appManager == null) {
23 | synchronized (AppManager.class) {
24 | if (appManager == null) {
25 | appManager = new AppManager();
26 | }
27 | }
28 | }
29 | return appManager;
30 | }
31 |
32 | /**
33 | * 添加一个activity
34 | *
35 | * @param activity
36 | */
37 | public void addActivity(Activity activity) {
38 | activityStack.add(activity);
39 | }
40 |
41 |
42 | /**
43 | * 删除指定activity
44 | *
45 | * @param activity
46 | */
47 | public void removeActivity(Activity activity) {
48 | for (int i = activityStack.size() - 1; i >= 0; i--) {
49 | if (activityStack.get(i).getClass() == activity.getClass()) {
50 | activity.finish();
51 | activityStack.remove(activity);
52 | break;
53 | }
54 | }
55 | }
56 |
57 | /**
58 | * 删除指定activity
59 | *
60 | * @param clazz
61 | */
62 | public void removeActivity(Class> clazz) {
63 | Activity act = null;
64 | for (int i = activityStack.size() - 1; i >= 0; i--) {
65 | act = activityStack.get(i);
66 | if (act.getClass() == clazz) {
67 | act.finish();
68 | activityStack.remove(act);
69 | break;
70 | }
71 | }
72 | }
73 |
74 | /**
75 | * 删除当前activity
76 | */
77 | public void removeCurrent() {
78 | Activity curActivity = getCurrent();
79 | if (curActivity != null) {
80 | curActivity.finish();
81 | activityStack.remove(curActivity);
82 | }
83 | }
84 |
85 | /**
86 | * 删除当前activity
87 | */
88 | public Activity getCurrent() {
89 | if (!activityStack.empty()) {
90 | return activityStack.lastElement();
91 | }
92 | return null;
93 | }
94 |
95 | /**
96 | * 清空栈
97 | */
98 | public void clear() {
99 | while (activityStack != null && !activityStack.empty()) {
100 | Activity curActivity = getCurrent();
101 | if (curActivity == null) {
102 | break;
103 | }
104 | curActivity.finish();
105 | activityStack.remove(curActivity);
106 | }
107 | }
108 |
109 | /**
110 | * 求栈大小
111 | *
112 | * @return
113 | */
114 | public int getSize() {
115 | return activityStack.size();
116 | }
117 |
118 | /**
119 | * 保存的Activity是否为空
120 | * 在Application里判断程序是否在后台运行
121 | */
122 | public static boolean isStackEmpty() {
123 | return activityStack == null || activityStack.size() <= 0;
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/app/Constant.java:
--------------------------------------------------------------------------------
1 | package com.hqumath.androidmvvm.app;
2 |
3 | import com.hqumath.androidmvvm.utils.SPUtil;
4 |
5 | import java.util.HashMap;
6 |
7 | /**
8 | * ****************************************************************
9 | * 文件名称: AppNetConfig
10 | * 作 者: Created by gyd
11 | * 创建时间: 2019/1/22 14:30
12 | * 文件描述: 网络地址
13 | * 注意事项:
14 | * 版权声明:
15 | * ****************************************************************
16 | */
17 | public class Constant {
18 | public static String baseUrl = "https://api.github.com/"; //API服务器
19 | public static String downloadHost = "http://cps.yingyonghui.com/"; //下载线路
20 |
21 | //请求通用参数
22 | public static HashMap getBaseMap() {
23 | String token = SPUtil.getInstance().getString(TOKEN);
24 | HashMap map = new HashMap<>();
25 | map.put("token", token);
26 | return map;
27 | }
28 |
29 | //SP Key
30 | public static final String USER_NAME = "USER_NAME";//用户名
31 | public static final String TOKEN = "TOKEN";
32 | public static final String APK_URL = "APK_URL";//apk下载地址
33 | public static final String APK_NAME = "APK_NAME";//apk文件名称
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.hqumath.androidmvvm.base;
2 |
3 | import android.app.ProgressDialog;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | import androidx.appcompat.app.AppCompatActivity;
8 |
9 | /**
10 | * ****************************************************************
11 | * 文件名称: BaseActivity
12 | * 作 者: Created by gyd
13 | * 创建时间: 2019/7/2 14:56
14 | * 文件描述:
15 | * 注意事项:
16 | * 版权声明:
17 | * ****************************************************************
18 | */
19 | public abstract class BaseActivity extends AppCompatActivity {
20 | protected BaseActivity mContext;
21 | private ProgressDialog mProgressDialog;
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | mContext = this;
27 | setContentView(initContentView(savedInstanceState));
28 | //事件监听
29 | initListener();
30 | //初始化数据
31 | initData();
32 | //页面事件监听的方法,一般用于ViewModel层转到View层的事件注册
33 | initViewObservable();
34 | }
35 |
36 | protected abstract View initContentView(Bundle savedInstanceState);
37 |
38 | protected abstract void initListener();
39 |
40 | protected abstract void initData();
41 |
42 | protected void initViewObservable() {
43 | }
44 |
45 | protected void showProgressDialog(String content) {
46 | if (mProgressDialog == null) {
47 | mProgressDialog = new ProgressDialog(mContext);
48 | mProgressDialog.setCancelable(true);
49 | }
50 | mProgressDialog.setMessage(content);
51 | mProgressDialog.show();
52 | }
53 |
54 | protected void dismissProgressDialog() {
55 | if (mProgressDialog != null) {
56 | mProgressDialog.dismiss();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/base/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.hqumath.androidmvvm.base;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.os.Bundle;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import androidx.fragment.app.Fragment;
13 |
14 | /**
15 | * ****************************************************************
16 | * 文件名称: BaseFragment
17 | * 作 者: Created by gyd
18 | * 创建时间: 2019/1/21 15:12
19 | * 文件描述:
20 | * 注意事项:
21 | * 版权声明:
22 | * ****************************************************************
23 | */
24 | public abstract class BaseFragment extends Fragment {
25 | protected Activity mContext;
26 |
27 | @Override
28 | public void onAttach(@NonNull Context context) {
29 | super.onAttach(context);
30 | mContext = (Activity)context;
31 | }
32 |
33 | @Override
34 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,@Nullable Bundle savedInstanceState) {
35 | View rootView = initContentView(inflater, container, savedInstanceState);
36 | //事件监听
37 | initListener();
38 | //初始化数据
39 | initData();
40 | //页面事件监听的方法,一般用于ViewModel层转到View层的事件注册
41 | initViewObservable();
42 | return rootView;
43 | }
44 |
45 | protected abstract View initContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
46 |
47 | protected abstract void initListener();
48 |
49 | protected abstract void initData();
50 |
51 | protected void initViewObservable() {
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hqumath/androidmvvm/base/BaseModel.java:
--------------------------------------------------------------------------------
1 | package com.hqumath.androidmvvm.base;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.hqumath.androidmvvm.net.HandlerException;
6 | import com.hqumath.androidmvvm.net.HttpListener;
7 | import com.hqumath.androidmvvm.utils.FileUtil;
8 |
9 | import java.io.File;
10 |
11 | import io.reactivex.Observable;
12 | import io.reactivex.Observer;
13 | import io.reactivex.disposables.CompositeDisposable;
14 | import io.reactivex.disposables.Disposable;
15 | import io.reactivex.functions.Function;
16 | import io.reactivex.schedulers.Schedulers;
17 | import okhttp3.ResponseBody;
18 |
19 | /**
20 | * ****************************************************************
21 | * 文件名称: BaseModel
22 | * 作 者: Created by gyd
23 | * 创建时间: 2019/1/21 15:12
24 | * 文件描述: 防止MVP内存泄漏
25 | * 注意事项:
26 | * 版权声明:
27 | * ****************************************************************
28 | */
29 | public class BaseModel {
30 | protected CompositeDisposable compositeDisposable = new CompositeDisposable();//管理订阅事件,用于主动取消网络请求
31 |
32 | //网络请求
33 | protected void sendRequest(Observable observable, HttpListener listener) {
34 | observable.subscribeOn(Schedulers.io())
35 | //.observeOn(AndroidSchedulers.mainThread()) 在工作线程处理
36 | .subscribe(new Observer