Compare commits

1 Commits

Author SHA1 Message Date
renjianbo
093a24a128 示例样本项目 2026-01-06 17:32:25 +08:00
4 changed files with 620 additions and 121 deletions

View File

@@ -76,7 +76,26 @@ public class LoginPasswordActivity extends BaseTreeActivity<LoginPasswordContrac
tv_phone_error = findViewById(R.id.tv_phone_error); tv_phone_error = findViewById(R.id.tv_phone_error);
tv_psw_error = findViewById(R.id.tv_psw_error); tv_psw_error = findViewById(R.id.tv_psw_error);
// 设置默认测试账号(仅用于开发测试)
setDefaultTestAccount();
}
/**
* 设置默认测试账号
* 方便开发测试,生产环境可删除或注释此方法
*/
private void setDefaultTestAccount() {
// 默认测试账号
String testPhone = "18133922183";
String testPassword = "123456";
// 如果输入框为空,则设置默认值
if (mEtPhone != null && mEtPhone.getText().toString().trim().isEmpty()) {
mEtPhone.setText(testPhone);
}
if (et_pass != null && et_pass.getText().toString().trim().isEmpty()) {
et_pass.setText(testPassword);
}
} }

View File

@@ -22,33 +22,44 @@ import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions; import pub.devrel.easypermissions.EasyPermissions;
/** /**
* SplashActivity * SplashActivity - 启动页模板
* 类描述:启动页 *
* 2018-9-30 9:17 * 功能说明:
* author:mengjuan * 1. 作为应用的启动入口LAUNCHER Activity
* 2. 显示启动画面,延迟后跳转到主页面
* 3. 检查登录状态,已登录跳转到 MainActivity未登录跳转到登录页
* 4. 请求必要的权限
*
* 使用说明:
* - 修改 SPLASH_DELAY 常量可调整启动页显示时长(毫秒)
* - 修改权限列表 perms 可调整需要请求的权限
* - 修改 startMainActivity() 方法可自定义跳转逻辑
*
* @author mengjuan
* @date 2018-9-30
* @updated 2025-01-06 优化为模板项目
*/ */
public class SplashActivity extends BaseActivity implements EasyPermissions.PermissionCallbacks { public class SplashActivity extends BaseActivity implements EasyPermissions.PermissionCallbacks {
/** 权限请求码 */
private static final int RC_CAMERA_AND_LOCATION = 100; private static final int RC_CAMERA_AND_LOCATION = 100;
private class MyHandler extends Handler {
/** 启动页延迟时间毫秒默认2秒 */
private static final long SPLASH_DELAY = 2000;
/** 弱引用Handler避免内存泄漏 */
private static class MyHandler extends Handler {
private final WeakReference<SplashActivity> mActivity; private final WeakReference<SplashActivity> mActivity;
public MyHandler(SplashActivity activity) { public MyHandler(SplashActivity activity) {
mActivity = new WeakReference<SplashActivity>(activity); mActivity = new WeakReference<>(activity);
} }
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
SplashActivity activity = mActivity.get(); SplashActivity activity = mActivity.get();
if (activity != null) { if (activity != null && msg.what == 0) {
switch (msg.what) { activity.startMainActivity();
case 0:
startActivity();
break;
case 1:
break;
}
} }
} }
} }
@@ -58,42 +69,65 @@ public class SplashActivity extends BaseActivity implements EasyPermissions.Perm
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
//设置共同沉浸式样式
// ImmersionBar.with(this).navigationBarColor(R.color.colorPrimary).init(); // 设置全屏显示
//将屏幕设置为全屏 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); WindowManager.LayoutParams.FLAG_FULLSCREEN);
methodRequiresTwoPermission();
// 请求必要权限
requestNecessaryPermissions();
} }
/**
private void startActivity(){ * 启动主Activity
* 根据登录状态决定跳转到 MainActivity 还是登录页
*/
private void startMainActivity() {
if (ProfileSpUtils.getInstance().isLogin()) { if (ProfileSpUtils.getInstance().isLogin()) {
// 已登录,直接进入主页面
startActivity(MainActivity.class); startActivity(MainActivity.class);
} else { } else {
// 未登录,进入登录页面
startActivity(LoginPasswordActivity.class); startActivity(LoginPasswordActivity.class);
} }
// 关闭启动页,避免用户按返回键回到启动页
finish(); finish();
} }
/**
* 禁用返回键,防止用户在启动页按返回键退出应用
*/
@Override @Override
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
//禁用返回 if (keyCode == KeyEvent.KEYCODE_BACK) {
return true; return true; // 拦截返回键
}
return super.onKeyDown(keyCode, event);
} }
/**
* 请求必要权限
* 如果权限已授予,延迟后跳转;否则请求权限
*/
@AfterPermissionGranted(RC_CAMERA_AND_LOCATION) @AfterPermissionGranted(RC_CAMERA_AND_LOCATION)
private void methodRequiresTwoPermission() { private void requestNecessaryPermissions() {
String[] perms = {Manifest.permission.WRITE_EXTERNAL_STORAGE // 定义需要请求的权限列表
, Manifest.permission.CAMERA String[] perms = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
}; };
if (EasyPermissions.hasPermissions(this, perms)) { if (EasyPermissions.hasPermissions(this, perms)) {
// 权限已授予,延迟后跳转到主页面
handler.sendEmptyMessageDelayed(0, 2000); handler.sendEmptyMessageDelayed(0, SPLASH_DELAY);
} else { } else {
EasyPermissions.requestPermissions(this, "获取当前所在城市需要该权限", // 请求权限
RC_CAMERA_AND_LOCATION, perms); EasyPermissions.requestPermissions(
this,
"应用需要以下权限才能正常运行:存储权限、相机权限",
RC_CAMERA_AND_LOCATION,
perms
);
} }
} }
@@ -103,19 +137,22 @@ public class SplashActivity extends BaseActivity implements EasyPermissions.Perm
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
} }
//成功 /**
* 权限授予成功回调
*/
@Override @Override
public void onPermissionsGranted(int requestCode, List<String> perms) { public void onPermissionsGranted(int requestCode, List<String> perms) {
ToastUtils.showToast(SplashActivity.this, "授权完成"); // 权限授予后,延迟跳转
handler.sendEmptyMessageDelayed(0, SPLASH_DELAY);
} }
//失败 /**
* 权限被拒绝回调
*/
@Override @Override
public void onPermissionsDenied(int requestCode, List<String> perms) { public void onPermissionsDenied(int requestCode, List<String> perms) {
ToastUtils.showToast(SplashActivity.this, "部分权限被拒绝,可能影响部分功能使用");
ToastUtils.showToast(SplashActivity.this, "获取部分权限失败,请重新授权"); // 即使权限被拒绝,也延迟后跳转(允许用户继续使用应用)
handler.sendEmptyMessageDelayed(0, SPLASH_DELAY);
} }
} }

162
模板项目使用说明.md Normal file
View File

@@ -0,0 +1,162 @@
# 模板项目使用说明
## 项目概述
这是一个 Android 模板项目已经配置好从启动页SplashActivity到主页面MainActivity的完整流程。
## 启动流程
### 1. SplashActivity启动页
**位置**: `app/src/main/java/com/fenghoo/seven/main/activity/SplashActivity.java`
**功能**:
- 作为应用的启动入口LAUNCHER Activity
- 显示启动画面,默认延迟 2 秒后跳转
- 检查用户登录状态
- 请求必要的应用权限
**跳转逻辑**:
```java
if (已登录) {
跳转到 MainActivity主页面
} else {
跳转到 LoginPasswordActivity登录页
}
```
### 2. MainActivity主页面
**位置**: `app/src/main/java/com/fenghoo/seven/main/activity/MainActivity.java`
**功能**:
- 应用的主界面
- 包含底部导航栏(首页、发现、客户、我的等)
- Fragment 管理
## 自定义配置
### 修改启动页延迟时间
`SplashActivity.java` 中修改常量:
```java
/** 启动页延迟时间毫秒默认2秒 */
private static final long SPLASH_DELAY = 2000; // 修改这个值
```
### 修改需要请求的权限
`SplashActivity.java``requestNecessaryPermissions()` 方法中修改:
```java
String[] perms = {
Manifest.permission.WRITE_EXTERNAL_STORAGE, // 存储权限
Manifest.permission.CAMERA // 相机权限
// 可以添加更多权限
};
```
### 修改跳转逻辑
`SplashActivity.java``startMainActivity()` 方法中自定义:
```java
private void startMainActivity() {
// 自定义跳转逻辑
if (ProfileSpUtils.getInstance().isLogin()) {
startActivity(MainActivity.class);
} else {
startActivity(LoginPasswordActivity.class);
}
finish();
}
```
## AndroidManifest.xml 配置
启动页已正确配置为 LAUNCHER Activity
```xml
<activity
android:name=".main.activity.SplashActivity"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
```
## 启动页布局
**位置**: `app/src/main/res/layout/activity_splash.xml`
可以自定义启动页的 UI当前显示应用图标。
## 启动页主题
**位置**: `app/src/main/res/values/styles.xml`
查找 `SplashTheme` 样式,可以自定义启动页的主题。
## 测试启动流程
1. **直接启动应用**
- 应用会自动从 SplashActivity 开始
- 2 秒后根据登录状态跳转
2. **已登录状态**
- 跳转到 MainActivity
3. **未登录状态**
- 跳转到 LoginPasswordActivity
## 注意事项
1. **返回键处理**: 启动页已禁用返回键,防止用户在启动过程中退出应用
2. **权限处理**:
- 如果权限被拒绝,应用仍会继续运行(延迟后跳转)
- 可以根据需要修改权限拒绝后的处理逻辑
3. **内存泄漏防护**:
- 使用 WeakReference 包装 Handler避免内存泄漏
4. **登录状态检查**:
- 使用 `ProfileSpUtils.getInstance().isLogin()` 检查登录状态
- 确保在登录成功后正确保存登录状态
## 快速开始
1. 运行项目,应用会自动从 SplashActivity 启动
2. 2 秒后根据登录状态自动跳转到相应页面
3. 如需修改跳转逻辑,编辑 `SplashActivity.java``startMainActivity()` 方法
## 测试账号
登录页面已预设默认测试账号,方便开发测试:
- **手机号**: `18133922183`
- **密码**: `123456`
**说明**:
- 打开登录页面时,如果输入框为空,会自动填入测试账号
- 可以直接点击"登录"按钮进行测试
- 如需修改测试账号,编辑 `LoginPasswordActivity.java``setDefaultTestAccount()` 方法
- 生产环境发布前,建议删除或注释 `setDefaultTestAccount()` 方法调用
## 相关文件
- `SplashActivity.java` - 启动页 Activity
- `MainActivity.java` - 主页面 Activity
- `LoginPasswordActivity.java` - 登录页 Activity
- `activity_splash.xml` - 启动页布局
- `AndroidManifest.xml` - Activity 配置
---
**最后更新**: 2025-01-06

281
项目点评报告.md Normal file
View File

@@ -0,0 +1,281 @@
# Android 项目点评报告
## 📊 项目概览
**项目名称**: July (com.fenghoo.seven)
**项目类型**: Android 原生应用
**主要功能**: 客户管理系统、社交功能、订单管理等
**评估日期**: 2025-01-06
---
## ✅ 项目优点
### 1. **技术栈现代化**
-**Android Gradle Plugin 8.7.3** - 使用最新稳定版本
-**Gradle 8.9** - 构建工具版本较新
-**compileSdk 34, targetSdk 34** - 支持最新 Android 版本
-**AndroidX** - 已迁移到 AndroidX 库
-**Java 8** - 使用现代 Java 特性
### 2. **架构设计**
-**MVP 架构模式** - 采用 MVP 模式,代码结构清晰
-**模块化设计** - 包含 app、baselibs、citypicker 等模块
-**契约接口 (Contract)** - 使用 Contract 接口定义 View 和 Presenter 的交互
-**职责分离** - View、Presenter、Model 分离良好
### 3. **代码质量**
-**命名规范** - 类名、方法名符合 Java 命名规范
-**代码注释** - 关键方法有注释说明
-**弱引用 Handler** - SplashActivity 使用 WeakReference 防止内存泄漏
-**权限管理** - 使用 EasyPermissions 进行权限管理
### 4. **构建配置**
-**阿里云 Maven 镜像** - 加速依赖下载
-**签名配置** - Release 版本签名配置完整
-**ProGuard 规则** - 混淆规则文件存在
-**多模块支持** - 支持多模块构建
### 5. **功能完整性**
-**启动流程** - SplashActivity → MainActivity 流程完整
-**登录系统** - 密码登录、验证码登录支持
-**测试账号** - 预设测试账号便于开发测试
-**Fragment 管理** - 主页面使用 Fragment 管理多个功能模块
---
## ⚠️ 需要改进的地方
### 1. **代码规范问题**
#### 🔴 严重问题
**问题1: Switch-Case 兼容性问题**
- **现状**: 已修复,但说明项目在升级过程中遇到了兼容性问题
- **影响**: 在 AGP 8.x 中library 模块的 R 类字段不是 final无法在 switch-case 中使用
- **解决**: ✅ 已全部改为 if-else 语句
**问题2: 代码重复**
- **问题**: AndroidManifest.xml 中存在大量重复的权限声明
- **示例**: INTERNET、CAMERA、ACCESS_NETWORK_STATE 等权限重复声明多次
- **建议**: 清理重复权限,只保留必要的权限声明
**问题3: 硬编码字符串**
- **问题**: 代码中存在硬编码的中文字符串
- **建议**: 将字符串提取到 `strings.xml` 资源文件中
#### 🟡 中等问题
**问题4: 方法命名不一致**
- **问题**: `startActivity()` 方法名容易与系统方法混淆
- **建议**: 重命名为 `navigateToMainActivity()``startMainActivity()`
**问题5: 魔法数字**
- **问题**: 代码中存在魔法数字(如 2000、60000
- **建议**: 提取为常量,如 `SPLASH_DELAY = 2000`
**问题6: 空方法体**
- **问题**: 部分方法体为空或只有注释
- **示例**: `initEvent()` 方法为空
- **建议**: 如果不需要,可以删除或添加 TODO 注释
### 2. **架构设计问题**
#### 🔴 严重问题
**问题1: 模块依赖混乱**
- **问题**: citypicker 模块存在依赖问题,曾被注释掉
- **影响**: 可能导致编译问题或功能缺失
- **建议**: 重新梳理模块依赖关系,确保依赖清晰
**问题2: 未使用的模块**
- **问题**: baselibs 模块已配置但未使用
- **建议**: 如果不需要,从 settings.gradle 中移除;如果需要,完善其功能
#### 🟡 中等问题
**问题3: Presenter 生命周期管理**
- **问题**: 部分 Presenter 可能没有正确管理生命周期
- **建议**: 确保在 Activity/Fragment 销毁时正确释放 Presenter
**问题4: 缺少统一异常处理**
- **问题**: 异常处理分散在各个类中
- **建议**: 建立统一的异常处理机制
### 3. **性能问题**
#### 🟡 中等问题
**问题1: 图片加载库版本较旧**
- **问题**: Glide 4.9.0(当前最新为 4.16+
- **建议**: 升级到最新稳定版本
**问题2: 网络库版本较旧**
- **问题**: Retrofit 2.4.0(当前最新为 2.9+
- **建议**: 升级到最新版本以获得更好的性能和安全性
**问题3: 缺少性能监控**
- **问题**: 没有看到性能监控工具(如 LeakCanary
- **建议**: 在 Debug 版本中集成 LeakCanary 检测内存泄漏
### 4. **安全性问题**
#### 🔴 严重问题
**问题1: 测试账号硬编码**
- **问题**: 测试账号和密码硬编码在代码中
- **风险**: 如果发布到生产环境,存在安全风险
- **建议**:
- 使用 BuildConfig.DEBUG 判断是否设置测试账号
- 或使用配置文件(不提交到版本控制)
**问题2: 签名信息暴露**
- **问题**: 签名密码可能暴露在代码中
- **建议**: 使用 gradle.properties不提交到版本控制或环境变量
#### 🟡 中等问题
**问题3: 网络请求缺少加密**
- **问题**: 未看到网络请求加密相关配置
- **建议**: 确保敏感数据传输使用 HTTPS
### 5. **代码维护性问题**
#### 🟡 中等问题
**问题1: 缺少单元测试**
- **问题**: 未看到测试代码
- **建议**: 添加单元测试和 UI 测试
**问题2: 缺少代码文档**
- **问题**: 部分复杂逻辑缺少详细注释
- **建议**: 为关键业务逻辑添加详细注释
**问题3: 依赖版本管理**
- **问题**: 部分依赖使用 `latest.integration`(如高德地图)
- **建议**: 使用具体版本号,避免不可预期的更新
---
## 📈 项目评分
| 评估维度 | 评分 | 说明 |
|---------|------|------|
| **代码质量** | ⭐⭐⭐⭐ (4/5) | 整体代码质量良好,但存在一些规范问题 |
| **架构设计** | ⭐⭐⭐⭐ (4/5) | MVP 架构清晰,但模块依赖需要优化 |
| **技术栈** | ⭐⭐⭐⭐ (4/5) | 技术栈较新,但部分依赖版本较旧 |
| **可维护性** | ⭐⭐⭐ (3/5) | 缺少测试和文档,维护成本较高 |
| **安全性** | ⭐⭐⭐ (3/5) | 存在一些安全隐患,需要改进 |
| **性能** | ⭐⭐⭐ (3/5) | 基本性能良好,但缺少性能监控 |
| **构建配置** | ⭐⭐⭐⭐⭐ (5/5) | 构建配置完善,已优化 |
**综合评分**: ⭐⭐⭐⭐ (3.7/5)
---
## 🎯 改进建议优先级
### 🔴 高优先级(立即处理)
1. **清理重复权限声明**
- 文件: `app/src/main/AndroidManifest.xml`
- 影响: 编译警告,代码整洁度
2. **测试账号安全处理**
- 文件: `LoginPasswordActivity.java`
- 修改: 使用 `BuildConfig.DEBUG` 判断
```java
if (BuildConfig.DEBUG) {
setDefaultTestAccount();
}
```
3. **签名信息保护**
- 文件: `app/build.gradle`
- 建议: 将签名信息移到 `local.properties`(不提交到版本控制)
### 🟡 中优先级(近期处理)
1. **升级依赖版本**
- Glide: 4.9.0 → 4.16.0
- Retrofit: 2.4.0 → 2.9.0
- RxJava2: 2.2.6 → 2.2.21
2. **添加性能监控**
- 集成 LeakCanary
- 添加性能分析工具
3. **完善模块依赖**
- 重新梳理 citypicker 模块依赖
- 决定 baselibs 模块的去留
### 🟢 低优先级(长期优化)
1. **添加单元测试**
- 为 Presenter 添加单元测试
- 为工具类添加测试
2. **代码重构**
- 提取硬编码字符串
- 消除魔法数字
- 统一命名规范
3. **文档完善**
- 添加 API 文档
- 完善代码注释
- 编写开发指南
---
## 💡 最佳实践建议
### 1. **代码规范**
- 使用 CheckStyle、FindBugs 等工具进行代码检查
- 配置 Git Hooks 在提交前进行代码检查
- 统一代码格式化规则
### 2. **版本管理**
- 使用 Git Flow 或类似工作流
- 规范 Commit Message
- 使用 Tag 标记版本
### 3. **持续集成**
- 配置 CI/CD 流程
- 自动化测试
- 自动化构建和发布
### 4. **依赖管理**
- 统一管理依赖版本(使用 versions.gradle
- 定期更新依赖
- 避免使用 `latest.integration`
### 5. **安全实践**
- 敏感信息不提交到版本控制
- 使用 ProGuard/R8 进行代码混淆
- 定期进行安全审计
---
## 📝 总结
### 项目优势
1. ✅ 技术栈现代化,使用最新稳定版本
2. ✅ 架构设计清晰,采用 MVP 模式
3. ✅ 构建配置完善,已优化构建流程
4. ✅ 功能完整,启动流程和登录系统完善
### 主要问题
1. ⚠️ 代码规范需要改进(重复权限、硬编码等)
2. ⚠️ 安全性需要加强(测试账号、签名信息)
3. ⚠️ 可维护性需要提升(缺少测试、文档)
### 总体评价
这是一个**结构良好、功能完整**的 Android 项目,采用了现代化的技术栈和清晰的架构设计。项目已经过升级优化,能够正常编译运行。主要需要在**代码规范、安全性和可维护性**方面进行改进。
**推荐指数**: ⭐⭐⭐⭐ (4/5)
---
**报告生成时间**: 2025-01-06
**评估人**: AI Code Reviewer