学习进度条
今日所花时间:一小时
今日代码量:100行
博客量:一篇
了解到的知识点: 深入学习androidStudio Android客户端如何与SpringBoot后端进行数据交互 Retrofit RecyclerView WebView
一、项目概述
本次学习实现了基于Android客户端和SpringBoot后端的政策模糊查询系统,主要功能包括:
- 通过关键字进行政策模糊查询
- 展示政策列表
- 查看政策详细信息
二、技术架构
1. 后端技术栈
- SpringBoot:快速构建RESTful API
- JPA/Hibernate:ORM框架处理数据库操作
- MySQL:数据存储
- MyBatis:SQL映射框架
2. 客户端技术栈
- Android Studio:开发环境
- Retrofit:HTTP客户端,用于与后端API交互
- RecyclerView:高效展示列表数据
- WebView:展示政策全文HTML内容
三、学习过程中的问题与解决方案
1. 跨平台通信问题
问题:Android客户端如何与SpringBoot后端进行数据交互
解决方案:
- 使用Retrofit库处理HTTP请求
- 定义统一的API接口规范
- 使用Gson进行JSON序列化和反序列化
- 确保手机和开发电脑在同一局域网下测试
2. 数据格式不一致问题
问题:后端返回的JSON数据与Android实体类不匹配
解决方案:
- 创建与后端完全对应的实体类
- 使用Gson的
setLenient()方法处理宽松的JSON解析 - 确保字段名称和类型完全一致
3. 网络请求超时问题
问题:移动网络不稳定导致请求超时
解决方案:
- 配置OkHttpClient设置合理的超时时间
- 添加重试机制
- 提供友好的错误提示
4. 大文本展示问题
问题:政策全文可能包含复杂格式,TextView无法良好展示
解决方案:
- 使用WebView加载HTML内容
- 后端存储政策内容为HTML格式
- 使用
loadDataWithBaseURL方法确保编码正确
四、涉及的核心知识点
1. 后端开发
- RESTful API设计:使用SpringBoot创建清晰的API端点
- 数据库操作:JPA与MyBatis结合使用
- 事务管理:
@Transactional注解的使用 - 跨域处理:虽然未显式配置,但需要了解CORS机制
2. Android开发
- 网络请求:Retrofit+OkHttp组合
- 异步处理:Callback机制处理网络响应
- 列表展示:RecyclerView及其适配器模式
- 页面跳转:Intent传递数据
- UI设计:XML布局与动态内容结合
3. 前后端交互
- JSON数据处理:Gson库的使用
- HTTP状态码处理:正确解析200、404、500等状态
- 错误处理:网络异常和业务异常的区别处理
五、扩展学习方向
-
安全性增强
- 添加HTTPS支持
- 实现JWT认证
- 参数校验和过滤
-
性能优化
- 后端添加缓存机制(Redis)
- Android端实现本地缓存
- 图片和PDF文件的懒加载
-
功能扩展
- 政策分类查询
- 收藏和历史记录功能
- 政策关联性分析
- 用户反馈系统
-
架构优化
- 引入MVVM架构
- 使用Dagger/Hilt依赖注入
- 实现模块化开发
六、心得体会
通过这个项目,我深刻理解了移动端与后端分离架构的优势和实现方式。主要收获包括:
- 掌握了Retrofit在Android中的实际应用,理解了如何设计良好的API接口
- 学会了处理JSON数据在不同平台间的转换和映射
- 体验了完整的开发流程,从数据库设计到前端展示
- 认识到错误处理和日志记录的重要性
- 了解了WebView在展示复杂内容时的优势
相关练习的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"
android:background="@color/cardview_light_background"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxEms="2"
android:maxLines="1"
android:text="@string/hyn"
android:textColor="#00838F"
android:textSize="18sp"
android:gravity="center"/>
<!-- <TextView-->
<!-- android:id="@+id/tv_2"-->
<!-- android:layout_width="100dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="Hello World!"-->
<!-- android:textColor="#d21015"-->
<!-- android:textSize="20sp"-->
<!-- android:maxEms="10"-->
<!-- android:maxLines="2"-->
<!-- android:ellipsize="end"-->
<!-- />-->
<EditText
android:id="@+id/et_1"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="16sp"
android:textColor="@android:color/black"
android:hint="用户名"
android:maxLines="1"
android:padding="5dp"
android:layout_marginTop="10dp"
/>
<EditText
android:id="@+id/et_2"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="16sp"
android:textColor="@android:color/black"
android:hint="密码"
android:maxLines="1"
android:padding="5dp"
android:layout_marginTop="5dp"
android:inputType="textPassword"
android:background="@drawable/bg_username"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="20dp"
>
<Button
android:id="@+id/btn_login"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="登录"
android:layout_gravity="center"
android:background="@drawable/btn_left"
/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="注册"
android:layout_marginLeft="2dp"
android:background="@drawable/btn_right"
/>
</LinearLayout>
</LinearLayout>
package com.example.demo1;
import android.content.Intent;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.example.demo1.util.ToastUtil;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity {
// 声明控件
private Button mBtnLogin;
private EditText mEtUser;
private EditText mEtPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
// 修正拼写错误
setContentView(R.layout.activity_main);
setContentView(R.layout.activity_main);
// 找到控件
mBtnLogin = findViewById(R.id.btn_login);
mEtUser = findViewById(R.id.et_1);
mEtPassword = findViewById(R.id.et_2);
// 匹配对应的用户名和密码才能进行跳转操作
mBtnLogin.setOnClickListener(this::onClick);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
private void onClick(View v) {
// 需要获取输入的用户名和密码
String username = mEtUser.getText().toString().trim();
String password = mEtPassword.getText().toString().trim();
// 检查输入是否为空
if (username.isEmpty() || password.isEmpty()) {
ToastUtil.showMsg(MainActivity.this, "用户名和密码不能为空,请输入!");
return;
}
// 弹出的内容设置
String ok = "登录成功";
String fail = "密码或者用户名有误,请重新登录";
// 假设正确的账号和密码分别是 hyn 123456
if (username.equals("hyn") && password.equals("123456")) {
// 登录成功,进行跳转
ToastUtil.showMsg(MainActivity.this, ok);
Intent intent = new Intent(MainActivity.this, SlideActivity.class);
startActivity(intent);
} else {
// 登录失败,弹出提示
ToastUtil.showMsg(MainActivity.this, fail);
}
}
}
未来可以在此基础上继续深入学习Android的高级特性,如Jetpack组件、Kotlin协程等,以及后端的微服务架构、分布式系统等知识,构建更加健壮和高效的移动应用系统。

浙公网安备 33010602011771号