基于Android的在线学习App开发指南:从课程设计到答辩避坑
简介面向Android毕业设计与课程设计场景这份移动终端在线学习系统源码是一套功能完整、可直接运行调试的实战项目。系统后台区分管理员、教师、学生三类角色覆盖用户管理、课程管理、班级管理、题库管理、测试发布、学习数据统计及师生互动等模块Android客户端则实现登录注册、个人信息维护、自主学习、在线测试、搜索和修改密码等操作。压缩包共包含2000个文件主要以java源码、xml界面布局、json数据文件、png资源图片为主并配有jsp后台页面、sql数据库脚本、gradle构建配置及jar依赖库便于还原整个开发环境包体大小约25.36MB。目前已有291人学习下载对于正在准备Android毕业设计或课程设计的同学有直接参考价值。工程目录结构清晰按客户端、后台与数据库分层组织通过研读源码可掌握前后端数据交互、JSON解析、SQLite/MySQL建表与增删改查、多角色权限设计等关键技能也可以基于现有功能快速做二次扩展是难得的完整工程范本。1. 基于Android的移动终端在线学习别把这个毕业设计做成“播放器”当答辩老师看到你题目写着“基于Android的移动终端在线学习”他想验证的不是你会不会用Android Studio播放一段MP4而是你有没有把登录、课程列表、学习进度、在线测试这几件事串成一个完整的业务闭环。很多课程设计翻车不是因为功能太少而是因为做成了“视频播放器数据库登录”的拼盘没有一条主线。这篇笔记面向两类人第一次用Android Studio做完整项目的学生以及接手Android课程设计、想在两周内交付且能答辩的开发者。我会按技术选型、功能拆解、权限适配、打包排错一路讲下来最后给你一套能让演示不卡的落地技巧。2. 技术选型先定调Android Studio里的语言、Gradle配置与数据层怎么选2.1 从Gradle配置看项目骨架Java还是KotlinminSdk与targetSdk怎么定Android Studio新建工程时第一个选择就在“Language”那里Java还是Kotlin。如果是课程设计我的建议很直接你学过哪个就用哪个。Java写起来啰嗦但网上资料最多出错时搜得到答案Kotlin代码简洁协程处理网络请求好用但一旦遇到编译报错新手容易卡在“类型不匹配”这种看不懂的环节。我见过太多人因为听说Kotlin是趋势就硬上最后连Gradle依赖都搞不定答辩前一天又换回Java重写。选你熟的不要选“看起来高级”的。minSdk和targetSdk这两个值在build.gradle里决定了你支持什么机型、系统会按哪套规则管你的App。课程设计场景我一般配成下面这样android { compileSdk 34 defaultConfig { applicationId com.example.onlinelearn minSdk 24 // Android 7.0覆盖绝大多数旧机 targetSdk 34 // 适配最新分区存储和权限规则 versionCode 1 versionName 1.0 } }minSdk选24的意思是Android 7.0及以上都能跑。现在学校里测试用的机器多半是Android 8到1324起步不会排斥老设备又能让你少处理一大圈兼容问题。targetSdk 34会让App遵循Android 13、14的隐私规范比如通知权限、读写媒体文件的限制这些规则答辩时老师会问你按这个配置还能答得上来。2.2 数据持久化SQLite够用什么时候得上MySQL在线学习App最核心的数据是用户信息、课程表、学习进度。如果你只在Android Studio本地跑演示SQLite完全够用不需要自己搭服务器。问题是答辩老师大概率问一句“你的数据存在哪里如果换一台手机登录学习进度还在吗”这时候你要心里有数纯本地存储能交作业但“在线”二字名不副实。常见做法是把数据层拆成两层本地SQLite负责缓存和离线学习记录远程接口负责课程列表和用户认证。但不要让一个课程设计变成全栈开发除非你真有时间。我的建议是课程列表和用户登录走远程接口本地SQLite只存学习进度和测试记录。这样答辩时你能说清楚“离线也能看已下载课程”工作量也刚好卡在“够做”而不是“做不完”。2.3 网络层选型与JSON解析Retrofit还是HttpURLConnection网络请求是“在线学习”的命脉。Android原生有HttpURLConnection能用但代码丑Volley已经老了现在最常见的是Retrofit加OkHttp。Retrofit的核心价值是把接口定义和JSON解析绑在一起你只需要写一个接口类public interface ApiService { GET(api/courses) CallListCourseBean getCourseList(); }背后的原理是Retrofit用注解把“GET请求路径返回类型”打包成一个可调用的方法。你在Activity里调用时它会自动在子线程发请求回调切回主线程。这个“自动”依赖convert-gson库所以Gradle里要加implementation com.squareup.retrofit2:retrofit:2.9.0 implementation com.squareup.retrofit2:converter-gson:2.9.0参数说明2.9.0是目前最稳定的版本很多教程里的2.5.0已经不适合新版Android Studio。还要注意如果你用本地模拟器访问本机服务器URL里的主机地址不能写localhost要写10.0.2.2。这个坑几乎每个人都踩过后面避坑章节我会专门说。3. 在线学习核心功能拆解课程列表、视频播放与学习进度3.1 首页Banner和课程列表协调布局加轮播图的标准姿势一个在线学习App的首页视觉重心是顶部轮播图加下方课程卡片列表。Android里实现这个组合的常用方案是CoordinatorLayout配合AppBarLayout再在内容区放一个RecyclerView。轮播图不推荐自己用ViewPager写循环因为无限滑动、自动播放、指示器动画这三个细节会吃掉你大量时间。用现成的轮播库比如BannerViewPager几分钟就能跑起来。布局结构大致是androidx.coordinatorlayout.widget.CoordinatorLayout com.google.android.material.appbar.AppBarLayout com.google.android.material.appbar.CollapsingToolbarLayout !-- 放Banner轮播图高度约200dp -- /CollapsingToolbarLayout /com.google.android.material.appbar.AppBarLayout androidx.recyclerview.widget.RecyclerView/ /androidx.coordinatorlayout.widget.CoordinatorLayout注意Banner放在CollapsingToolbarLayout里可以实现“向上滑动时Banner逐渐折叠成标题栏”的效果这是Android课程设计里很出彩的交互点。RecyclerView的Item设计成课程卡片封面缩略图、课程名、讲师、已学人数点击跳转课程详情页。这里有一个逻辑点Banner和课程列表的数据应该都来自同一个接口只是Banner取前三条。这样代码里不需要维护两套数据源。3.2 视频播放器选型Android 9以上的“明文流量”坑视频播放是在线学习的核心。Android自带的VideoView虽然能用但进度条、全屏、倍速都要自己写一个课程设计做到一半就崩溃了。常用的做法是用ExoPlayer或GSYVideoPlayer。我建议用GSYVideoPlayer因为它是封装好的开源播放器自带全屏切换、倍速播放、进度记忆省下大量调试时间。集成时要注意Android 9API 28开始默认禁止明文HTTP流量你的课程视频如果走的是http://地址直接播放会报错。常见做法是在AndroidManifest.xml里对调试环境放开限制application android:usesCleartextTraffictrue tools:targetApi28这个属性不是给生产环境用的但课程设计答辩演示时你本地搭的服务器基本都是HTTP不开它就黑屏。有些教程让你用network_security_config.xml做更精细的配置对课程设计来说没必要。另一个坑是播放器的onPause生命周期切后台时如果不暂停回来会从头播。解决方法是记录currentPosition在onResume里用seekTo恢复。3.3 学习进度记录SharedPreferences还是Room断点续播怎么实现“在线学习”最该有的功能是记录“学到哪了”。最简单的方式是SharedPreferences但它的缺点是只能存键值对你要记录多门课程、每门课的进度、最后播放位置得建一堆key极其难维护。更专业一点是Room它是SQLite的官方封装用起来像写Java对象一样操作表。我建议直接上Room理由是答辩时这是一个亮点。定义一张进度表Entity(tableName learn_progress) public class ProgressEntity { PrimaryKey public int courseId; public int lastPosition; // 上次播放位置毫秒 public int duration; // 视频总时长 public long updateTime; // 最后学习时间 }关键逻辑在视频播放页播放器每秒钟回调一次位置你把它存入一个变量在onPause或onDestroy时写入Room。下次进入课程详情时查询这张表如果lastPosition大于0就提示“从上次位置继续播放”。这块代码不复杂但非常体现工程意识。注意Room的操作不能在主线程执行要用AsyncTask或Executors否则会崩。课程设计里用Executors.newSingleThreadExecutor()就够了不引入协程也能说清楚。4. 用户模块与Android权限登录注册、运行时权限与进度条可视化4.1 登录注册本地校验还是Token保持在线学习App必须要有用户模块否则“学习进度”不知道是谁的。课程设计最常见的实现是登录页输入用户名密码请求服务器接口校验成功后把用户ID存到SharedPreferences之后的网络请求都带上这个ID。不要存明文密码要存一个登录后返回的Token这样答辩时你可以说“没有把密码持久化在本地降低泄露风险”。注册页一般需要用户名、密码、确认密码前端先做一次非空校验和两次密码一致性校验再提交服务器。这里有个细节如果服务器接口还没写完你可以在本地用SharedPreferences模拟用户表先把流程跑通后面再替换成真实接口。这种“先跑通再替换”的思路是课程设计项目控制风险的常用办法。4.2 Android权限汇总与动态申请为什么Android 6.0以后必须运行时请求一个在线学习App会用到哪些权限读网络状态、写外部存储下载课程、录音如果做语音问答、相机头像上传。Android权限汇总里最常用的是这几个权限用途安装时还是运行时INTERNET网络请求安装时正常权限ACCESS_NETWORK_STATE判断网络是否可用安装时正常权限WRITE_EXTERNAL_STORAGE下载课程文件运行时CAMERA拍照上传头像运行时Android 6.0API 23开始危险权限必须在运行时申请光在Manifest里写是不够的。典型写法是if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ! PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE); }这个checkSelfPermission和requestPermissions是两个关键点前者查是否已授权后者弹出系统对话框。还有一点Android 10以后WRITE_EXTERNAL_STORAGE的行为又变了如果targetSdk 29以上默认启用分区存储App不能随便写公共目录。课程设计里我建议把下载的课程视频写到getExternalFilesDir()目录下这样不需要额外权限也能写答辩时还能解释“这是符合Android分区存储规范的”。4.3 自定义进度条把“已学/总学时”做成可视化组件课程设计里如果只是列表里显示一个进度数字太单薄。Android中自定义组件是很多人后面才补的加分项。做一个圆环进度条显示“已学35%”比文字直观得多。实现思路是自定义View在onDraw里画两个圆弧public class CircleProgressView extends View { private Paint backgroundPaint new Paint(Paint.ANTI_ALIAS_FLAG); private Paint progressPaint new Paint(Paint.ANTI_ALIAS_FLAG); private float progress 0f; // 0~1 Override protected void onDraw(Canvas canvas) { float cx getWidth() / 2f; float cy getHeight() / 2f; float radius getWidth() / 2f - 10f; // 底环 backgroundPaint.setColor(0xFFDDDDDD); canvas.drawCircle(cx, cy, radius, backgroundPaint); // 进度弧从12点方向开始扫过 360 * progress progressPaint.setColor(0xFF00A5FF); canvas.drawArc(cx - radius, cy - radius, cx radius, cy radius, -90, 360 * progress, false, progressPaint); } public void setProgress(float p) { this.progress p; invalidate(); } }这个自定义View的核心逻辑drawCircle画底环drawArc画进度弧。注意-90表示从12点钟方向开始这是很多新手第一次画弧线搞不清的地方。invalidate()会触发重新绘制进度更新后调用setProgress即可。在课程详情页把“已学时间/总时长”换算成这个百分比视觉上立刻专业很多。5. 避坑指南打包、资源重复、存储路径和项目移植的典型案例5.1 现象Android Studio打包签名APK后安装到手机一打开就闪退这个坑几乎每个人都有过。原因通常是混淆开启后你的JSON框架或数据库框架的实体类被重命名运行时找不到对应字段。解决方法是把实体类加入混淆白名单-keep class com.example.onlinelearn.bean.** { *; }在proguard-rules.pro里配好以后重新打包。另一个可能是你在Manifest里注册了android:debuggabletrue签名包自动忽略但你又开了混淆。课程设计里我建议直接关掉混淆buildTypes { release { minifyEnabled false shrinkResources false } }答辩演示用release包关混淆能少踩一大半坑。如果老师问你为什么不用混淆你可以回答“为了演示稳定性生产环境下会开启”。5.2 现象编译报“Android resource linking failed”或“资源重复错误”这多半是你复制的项目里有多份同名资源。比如从网上拉了一个轮播图的示例工程它的drawable里也有一个ic_launcher_background.xml和Android Studio新建工程自带的资源冲突了。解决方法是先看报错里说的资源路径去src/main/res下找到重复文件删掉其中一个。还有一个常见情况values和values-zh都有strings.xml但你新加的一个字符串只加在了默认文件里中文语言环境下找不到资源打包直接失败。排查时用Android Studio的Build Clean Project清一次再把重复资源按时间排序看一遍。5.3 现象Android模拟器上一切正常真机一打开App就提示“无法访问存储”这个问题的背后是Android 10的分区存储机制。你在模拟器里跑的是targetSdk 28以下系统放宽松了真机如果是Android 10以上targetSdk又是29以上系统强制分区存储。App读不到公共目录的课程文件。解决方法是文件路径改用/storage/emulated/0/Android/data/包名/files/也就是前面说的getExternalFilesDir()。如果网上的教程代码里硬编码了/storage/emulated/0/Download/你一眼要能认出这就是踩坑代码。写文件时统一走context.getExternalFilesDir()既避开权限又符合规范。5.4 现象换一台电脑打开Android Studio项目Gradle同步卡在下载依赖或直接报错“移植Android Studio项目”这个需求课程设计阶段几乎人人遇到。你拷贝整个项目文件夹到另一台电脑打开后Gradle版本、SDK路径不一致同步失败。常见做法是先检查gradle/wrapper/gradle-wrapper.properties里的版本再检查build.gradle里的AGP版本。如果报错是SDK location not found去File Project Structure SDK Location里手动指定本机SDK路径。但最省事的办法是只拷贝src、build.gradle和AndroidManifest.xml新建一个工程把这些文件覆盖进去。这样Gradle配置由新工程生成百分之百匹配当前Android Studio版本。别指望拷整个文件夹一次成功那是玄学。5.5 现象Retrofit请求报“CLEARTEXT communication to 10.0.2.2 not permitted”这个报错就是前面说的明文流量问题。Android 9以上默认不允许HTTP而你在模拟器里访问本地服务器用的10.0.2.2它本来就是明文地址。解决方法是Manifest里加android:usesCleartextTraffictrue或者把网络地址改成HTTPS。课程设计阶段本地没有HTTPS证书直接用usesCleartextTraffic最简单。6. 答辩前的最后一道工序用数据库调试和测试用例证明工作量6.1 用数据库调试工具展示学习进度记录答辩演示时别只展示界面把后台数据也亮出来。Android Studio自带App Inspection你在模拟器运行App时打开View Tool Windows App Inspection能看到Room数据库的表和记录。演示时先播放视频几秒然后切出去看数据库里learn_progress表多了一条记录再回到App点“继续学习”视频从上次位置接着播。这个闭环演示比说十句“我实现了学习进度”都管用。6.2 测试用例怎么写进课程设计文档很多课程设计不要求自动化测试但你要在文档里写“测试用例”章节。格式不用复杂一张表格足够用例编号操作步骤预期结果实际结果T01输入正确用户名密码登录进入首页并显示用户昵称通过T02点击课程Banner跳转对应课程详情页通过T03播放视频到30秒后退出重新进入提示“从30秒继续”通过T04断网后打开已加载课程能播放在线缓存的人内容通过写这个表格时要注意预期结果不能写“正常显示”这种话要写具体可验证的细节。T03就是针对断点续播功能的验证方式老师一眼就能看出你有测试意识。6.3 一个让演示更稳的技巧预置演示账号与本地种子数据课程设计答辩时最怕什么现场网络抽风服务器连不上首页课程列表加载不出来。我的习惯是在本地SQLite里预置一套种子数据包括三个分类、十条课程、一条进度记录。App启动时先查本地库如果为空就自动写入预置数据。这样即使服务器挂了界面也永远有内容可展示。实现方式是在onCreate里插入数据代码只有几行但对答辩稳定性是几何级提升。另一个技巧是把登录页的Demo账号做成一个显眼的按钮一键填入用户名和密码。别小看这个细节答辩现场你输入一个复杂密码手一抖就打错节奏全乱了。预置账号的Touch事件里同时做填入和登录两个动作演示流程就是打开App点一下直接进首页。这套做法我用在好几个课程设计项目里从来没有因为演示环境翻过车。最后说一个我自己的教训不要在答辩前一天晚上才打包APK。毕业设计里我上一次就因为签名配置没写好现场老师说“你直接装到这个手机上”结果闪退气氛瞬间凝固。后来我再也不依赖现场装包提前半天用备用机做一次“冷启动到完整学习流程”的实跑。但愿这些经验能帮你少熬几个夜顺利过答辩。希望帮到你。本文还有配套的精品资源点击获取