Android Studio健身系统课设:从界面到数据库的完整实现
简介面向安卓课程设计/期末大作业的简易健身系统完整源码特别适合初学移动应用开发的学生参考。项目从欢迎页倒计时进入登录注册账号采用MD5加密存储支持记住密码登录后可修改个人信息并同步更新Sqlite数据库首页还会随机展示名言警句。运动模块集成RecycleView项目列表与VideoView视频播放配合计时器完成开始、暂停、停止、结束锻炼的闭环操作。开发中涉及OkHttp网络通信、Handler异步消息、ViewPager轮播、Glide图片加载、Json解析、Toolbar工具栏、Thread多线程与文件存储等常用知识点。压缩包共626个文件以java源码、xml布局、json配置、dex/class编译文件、png/jpg图片资源和可直接安装的apk为主整体约85.28MB。目前已有1728人学习适合用来做期末项目参考、功能扩展或源码阅读。1. Android Studio 健身系统课设一个从界面到数据库都能讲清楚的项目如果你也是截止前一周才开始做安卓课程设计的人这套健身系统可以直接抄。它不是一个空壳界面而是一个完整的 Android Studio 工程首页功能入口、动作库 RecyclerView、健身记录增删改查、BMI 计算数据落在 SQLite 里关掉 App 再打开依然在。它解决的是 Android 大作业最尴尬的处境——功能太少不好意思交代码太乱答辩被问倒。老师最在意的不是业务有多复杂而是你能不能把界面、事件、持久化这三件事串起来而健身系统恰好把这三点全部覆盖。适合所有用 Android Studio 做课设、还在纠结做什么选题的新手。2. 功能拆解与界面设计这 6 个模块为什么能撑起一个满分课设2.1 移动应用开发课设的评分点老师到底在检查什么我交过两次课设之后才知道老师的评分表里权重最高的是这四项项目能一次跑起来、界面之间有明确的跳转关系、数据不会因为退出 App 而消失、代码结构能讲清楚。健身系统恰好把这四项全部覆盖。你不用写复杂的算法也不用做网络请求只要把一个本地业务闭环做完整就能在答辩时指着项目说这里用了 Intent 显式跳转这里用 SQLite 保存记录这个列表的 Adapter 是这么绑定的。老师想听的正是这些。很多同学做健身 App 只做一个计时器或者一个计步器功能单薄三张截图就讲完了。我把功能拆成六个互相独立的页面每个页面负责一件事首页作为中枢统一跳转。这样既不会代码爆炸页面流转又能把 Activity 生命周期串起来评委一看就知道这是完整 App 而不是 Demo。下面这张表就是这个项目可以直接写进报告的功能清单。模块页面核心功能涉及知识点首页MainActivity日期展示、功能入口LinearLayout、Intent 跳转动作库ActionListActivity训练动作列表、点击交互RecyclerView、Adapter训练记录Dialog 弹窗输入次数与时长并保存AlertDialog、EditText健身记录RecordListActivity查询、长按删除、清空SQLite、ListViewBMI 计算BMICalculatorActivity身高体重计算体型EditText、Double.parseDouble关于AboutActivity课程设计信息展示Activity 生命周期页面流转也很简单MainActivity 通过 setOnClickListener 里的 startActivity 打开其它页面动作库点击某个动作时弹 Dialog 记录次数数据写入 record 表返回首页后再进记录页能看到刚插入的数据。这个“写入再读取”的闭环就是老师要的持久化也是课设报告里最能写的一段。2.2 为什么选 Java SQLite而不是 Kotlin Room如果你是安卓新手我建议课设选 Java SQLite而不是跟风用 Kotlin Room。原因很现实Kotlin 协程、Room 的注解处理器答辩时老师随便问一个“这个东西怎么生成实现的”你就很难接住。Java SQLite 没有任何黑魔法打开 SQLiteOpenHelper 就能看到完整实现每一行都能现场解释。而且它不引入额外依赖网络不好时也不会因为拉不到库而卡在 Sync。SQLite 是安卓内置的轻量数据库适合单机课设。Room 虽然在封装上更现代但它要求你在 Gradle 里配置 kapt 或 KSP配置过程一旦翻车可能一晚上都同步不了。课程设计的目的是把知识点展示出来越直白的实现越安全。我见过一个小组用了 Room答辩时被问到接口怎么生成几个人轮流上去都说不清楚最后只能承认是照着博客抄的。Java SQLite 就不会出现这种场面因为每个方法都是你自己能讲明白的。2.3 先画界面草图再写布局五分钟省掉三个小时我吃过直接写代码的亏布局写了又删一个首页折腾一下午。后来每次课设都先在纸上划分区域页面顶部的标题栏、中间的主操作区、底部的信息区。健身系统首页就是典型的上下结构最上面显示日期中间放四个功能按钮底部留一个提示文字。按钮高度统一设置为 56dpRecyclerView 的 item 用 MaterialCardView 包裹圆角 8dp不用额外写 shape drawable 就有立体感。这样画面不花哨老师截图放进报告里也很干净。草案阶段还要把每个页面的跳转关系标出来。比如动作库点某个动作之后是弹窗还是跳新页面健身记录长按之后是直接删除还是要二次确认。这些决策在纸上定下来之后写代码就只是翻译草稿不会出现写到一半发现流程不对要返工的情况。对新手来说这五分钟规划比后面五个小时写代码都重要。3. 从新建工程到核心代码四个页面的手写过程与配置说明3.1 下载安装与新建工程SDK、Gradle 与 Manifest 的初始配置下载 Android Studio 时我一般只选稳定版通道别追预览版预览版的 Gradle 和 AGP 插件经常出幺蛾子。安装完成后第一次启动会提示下载 SDK建议直接选择 Android 14API 34的 Platform这样后续不用反复补装。打开 File → New → New Project选 Empty Views Activity语言选 Java。工程建好后先别急着写代码把 app/build.gradle 里的版本统一好。plugins { id com.android.application } android { namespace com.example.fitness compileSdk 34 defaultConfig { applicationId com.example.fitness minSdk 21 targetSdk 34 versionCode 1 versionName 1.0 } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation androidx.appcompat:appcompat:1.6.1 implementation com.google.android.material:material:1.11.0 implementation androidx.constraintlayout:constraintlayout:2.1.4 implementation androidx.recyclerview:recyclerview:1.3.2 }compileSdk 34 表示用哪个版本的 SDK 来编译targetSdk 34 表示 App 在 Android 14 上运行时遵循的规则minSdk 21 直接保底到 Android 5.0覆盖绝大多数老师的手机。namespace 和 applicationId 在新版 Gradle 里必须显式声明不写会报错。依赖部分我只留了 AppCompat、Material、ConstraintLayout 和 RecyclerView其余用不到的一律不加依赖越少越不容易翻车。接着是 AndroidManifest.xml这是新手漏得最多的一步。新建的 Activity 如果不在这里注册运行时点击入口会直接闪退Logcat 报 NoClassDefFoundError。application android:labelstring/app_name android:iconmipmap/ic_launcher android:themestyle/Theme.Material3.DayNight activity android:name.MainActivity android:exportedtrue intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity activity android:name.ActionListActivity android:exportedfalse / activity android:name.RecordListActivity android:exportedfalse / activity android:name.BMICalculatorActivity android:exportedfalse / activity android:name.AboutActivity android:exportedfalse / /applicationMainActivity 是入口 Activityandroid:exported 必须写 true因为 Launcher 要通过隐式 Intent 找到它。其它 Activity 用显式 Intent 打开exported 设 false 就够了。如果你的主题没自定义直接引用 Material3 主题布局里的 MaterialCardView 就能正常显示。提示targetSdk 31 之后exported 属性是强制要求的不写会直接编译报错。这个属性也经常出现在答辩提问里提前记住含义。3.2 首页布局和 MainActivity用日期展示把生命周期串起来首页布局我用了垂直 LinearLayout没有用 ConstraintLayout因为按钮是整排摆放的LinearLayout 的代码更短新手也好解释。页面顶部一个 TextView 显示日期下面四个 Button 分别进入动作库、健身记录、BMI 计算和关于页。LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical android:padding16dp TextView android:idid/tvDate android:layout_widthmatch_parent android:layout_heightwrap_content android:textSize18sp android:textColor#333333 android:paddingBottom16dp / Button android:idid/btnAction android:layout_widthmatch_parent android:layout_height56dp android:text动作库 / Button android:idid/btnRecord android:layout_widthmatch_parent android:layout_height56dp android:text健身记录 / Button android:idid/btnBmi android:layout_widthmatch_parent android:layout_height56dp android:textBMI 计算 / Button android:idid/btnAbout android:layout_widthmatch_parent android:layout_height56dp android:text关于 / /LinearLayoutMainActivity 的代码逻辑很简单但每一行都能在答辩时展开。public class MainActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tvDate findViewById(R.id.tvDate); SimpleDateFormat sdf new SimpleDateFormat(yyyy年MM月dd日 EE, Locale.CHINA); tvDate.setText(sdf.format(new Date())); findViewById(R.id.btnAction).setOnClickListener(new View.OnClickListener() { Override public void onClick(View v) { startActivity(new Intent(MainActivity.this, ActionListActivity.class)); } }); findViewById(R.id.btnRecord).setOnClickListener(v - startActivity(new Intent(this, RecordListActivity.class))); findViewById(R.id.btnBmi).setOnClickListener(v - startActivity(new Intent(this, BMICalculatorActivity.class))); findViewById(R.id.btnAbout).setOnClickListener(v - startActivity(new Intent(this, AboutActivity.class))); } }SimpleDateFormat 的yyyy表示四位年MM表示两位月dd表示日EE是星期几的简写Locale.CHINA 保证星期显示中文。Intent 的第一个参数是上下文第二个参数是目标 Activity。我这里混用了匿名内部类和 lambda 两种写法目的就是让代码看起来有层次答辩时可以讲“这里用了 Java 8 lambda本质是函数式接口的实例”。3.3 动作库列表RecyclerView 与 Adapter 的完整套路动作库是课设里最值得展开讲的页面因为它同时涉及数据模型、适配器、布局绑定和点击回调。先定义一个轻量模型 ActionItem只存动作名和描述。public class ActionItem { private String name; private String desc; public ActionItem(String name, String desc) { this.name name; this.desc desc; } public String getName() { return name; } public String getDesc() { return desc; } }然后在 ActionListActivity 里初始化数据并绑定 RecyclerView。public class ActionListActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_action_list); RecyclerView rvActions findViewById(R.id.rvActions); rvActions.setLayoutManager(new LinearLayoutManager(this)); ListActionItem data new ArrayList(); data.add(new ActionItem(深蹲, 每组 15 个练大腿与臀部)); data.add(new ActionItem(俯卧撑, 每组 10 个练胸与手臂)); data.add(new ActionItem(平板支撑, 每组 30 秒练核心)); data.add(new ActionItem(开合跳, 每组 20 个热身燃脂)); ActionAdapter adapter new ActionAdapter(data, item - showRecordDialog(item)); rvActions.setAdapter(adapter); } }setLayoutManager 决定列表的排列方向LinearLayoutManager 就是垂直滚动列表。如果需要水平滑动可以传new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)。Adapter 的第二个参数是自定义的点击回调这里用了 lambda点击动作后弹记录窗口。Adapter 是 Android 列表的灵魂。三个方法分别负责创建视图、绑定数据、返回数量。public class ActionAdapter extends RecyclerView.AdapterActionAdapter.VH { private final ListActionItem data; public interface OnItemClickListener { void onItemClick(ActionItem item); } private final OnItemClickListener listener; public ActionAdapter(ListActionItem data, OnItemClickListener listener) { this.data data; this.listener listener; } Override public VH onCreateViewHolder(ViewGroup parent, int viewType) { View v LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_action, parent, false); return new VH(v); } Override public void onBindViewHolder(VH holder, int position) { ActionItem item data.get(position); holder.tvName.setText(item.getName()); holder.tvDesc.setText(item.getDesc()); holder.itemView.setOnClickListener(v - listener.onItemClick(item)); } Override public int getItemCount() { return data null ? 0 : data.size(); } static class VH extends RecyclerView.ViewHolder { TextView tvName, tvDesc; VH(View v) { super(v); tvName v.findViewById(R.id.tvActionName); tvDesc v.findViewById(R.id.tvActionDesc); } } }onCreateViewHolder 只负责把 item_action.xml 变成 ViewonBindViewHolder 负责把数据填进去getItemCount 告诉 RecyclerView 总共有多少条。item_action.xml 我用 MaterialCardView 包了一个垂直 LinearLayout两个 TextView 各显示动作名和描述。卡片的高度 wrap_content 就行宽度 match_parent圆角 8dp这样列表天然带分割和按压效果不用额外画分割线。3.4 训练记录弹窗用 Dialog 而不是新开页面点击动作之后要记录本次训练次数。这里我选择 AlertDialog而不是再写一个 Activity。原因很简单弹窗只负责收集一次输入页面跳转会打断操作流而且会多一个 Activity 要注册、要写布局性价比太低。private void showRecordDialog(ActionItem item) { EditText etCount new EditText(this); etCount.setHint(输入本次次数); etCount.setInputType(InputType.TYPE_CLASS_NUMBER); new AlertDialog.Builder(this) .setTitle(记录 item.getName()) .setView(etCount) .setPositiveButton(保存, (dialog, which) - { String countStr etCount.getText().toString().trim(); if (countStr.isEmpty()) { Toast.makeText(this, 次数不能为空, Toast.LENGTH_SHORT).show(); return; } int count Integer.parseInt(countStr); new DBHelper(this).insertRecord(item.getName(), count, getTodayDate()); Toast.makeText(this, 已保存, Toast.LENGTH_SHORT).show(); }) .setNegativeButton(取消, null) .show(); }setInputType(InputType.TYPE_CLASS_NUMBER) 限制只能输入数字规避了后面的 parseInt 抛异常。setView 可以直接塞一个 EditText也可以塞一个复杂布局的 View。这里我直接 new EditText 出来是最省事的写法。保存之前先 trim 去掉首尾空格再判空这是防止空数据进数据库的最后一道防线。DBHelper 是下一章要写的数据库帮助类insertRecord 往里插一条记录。4. SQLite 存储与健身记录把增删改查写进数据库4.1 record 表设计为什么用单表而不是多表健身记录的核心数据就是“什么动作、做了多少次、哪天做的”。我建了一张 record 表字段只有五个充分满足课设要求也足够演示主键、自增、非空约束这几个知识点。CREATE TABLE record ( id INTEGER PRIMARY KEY AUTOINCREMENT, action_name TEXT NOT NULL, count INTEGER NOT NULL DEFAULT 0, duration INTEGER NOT NULL DEFAULT 0, date TEXT NOT NULL );id 用自增主键删除记录时通过 id 定位避免用字符串做条件产生歧义。action_name 存动作名不建单独的动作表因为课设的动作列表是写死的不需要动态管理。date 用 TEXT 存 “yyyy-MM-dd” 格式排序时直接按字符串倒序日期格式统一就不会出乱子。很多同学一上来就搞多表关联、外键约束结果查询语句写不明白答辩时自己也解释不清。课程设计的复杂度应该刚好能展示知识点而不是把真实项目的边界问题带进来。单表虽然简单但增删改查一个不缺老师要看的都有了。4.2 DBHelper 封装版本号和 onUpgrade 的正确姿势数据库帮助类继承 SQLiteOpenHelper核心是 onCreate 和 onUpgrade。onCreate 只会在数据库第一次创建时执行onUpgrade 在版本号升高时执行。public class DBHelper extends SQLiteOpenHelper { public static final String DB_NAME fitness.db; public static final int DB_VERSION 1; public DBHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE TABLE IF NOT EXISTS record ( id INTEGER PRIMARY KEY AUTOINCREMENT, action_name TEXT NOT NULL, count INTEGER NOT NULL DEFAULT 0, duration INTEGER NOT NULL DEFAULT 0, date TEXT NOT NULL)); } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion 2) { db.execSQL(ALTER TABLE record ADD COLUMN note TEXT DEFAULT ); } } public long insertRecord(String actionName, int count, String date) { SQLiteDatabase db getWritableDatabase(); ContentValues values new ContentValues(); values.put(action_name, actionName); values.put(count, count); values.put(date, date); return db.insert(record, null, values); } public int deleteRecord(long id) { SQLiteDatabase db getWritableDatabase(); return db.delete(record, id?, new String[]{String.valueOf(id)}); } }onUpgrade 里我给了一个“新增列”的示例这是最安全的增量升级方式。很多教程喜欢在 onUpgrade 里 drop 表再重建演示的时候没问题但实际用起来用户数据全没了答辩时被追问会很狼狈。DB_VERSION 改成 2onUpgrade 自动执行这就是数据库迁移的基本思路。insertRecord 用 ContentValues 封装字段和后面的查询操作形成对照。注意getWritableDatabase 和 getReadableDatabase 在大多数情况下返回同一个数据库对象不要试图用它们做读写分离。课设里统一用 getWritableDatabase 就好。4.3 用 ContentValues 插入用 Cursor 查询两个基础 API 的坑插入数据的准备工作是把字段名和值塞进 ContentValues。values.put 的第一参数是表字段名第二参数是值。db.insert 返回的是新记录的 id如果失败返回 -1。注意白名单机制ContentValues 里没传的字段会自动用默认值所以 count 和 duration 可以不传但为了可读性最好每条都写清楚。查询最常用的是 db.query 方法参数很长但每个都对应一句 SQL。SQLiteDatabase db new DBHelper(this).getReadableDatabase(); Cursor cursor db.query( record, // 表名 null, // 要查的列null 表示全部 null, // where 条件null 表示无条件 null, // where 条件占位符的值 null, // groupBy null, // having date DESC, id DESC // orderBy ); while (cursor.moveToNext()) { long id cursor.getLong(cursor.getColumnIndexOrThrow(id)); String actionName cursor.getString(cursor.getColumnIndexOrThrow(action_name)); int count cursor.getInt(cursor.getColumnIndexOrThrow(count)); String date cursor.getString(cursor.getColumnIndexOrThrow(date)); } cursor.close();Cursor 初看像二维表moveToNext 不断把游标往下移动返回 false 说明遍历完。getColumnIndexOrThrow 按列名找索引如果列名不存在会抛异常比返回 -1 的 getColumnIndex 更容易暴露问题。cursor.close() 一定不能省否则反复打开查询时内存会涨上去。在健身记录页刷新列表时我习惯把查询逻辑包成一个 refreshList() 方法每次增删之后都重新查一遍。数据量小不需要做增量更新这样代码最简单也最容易验证。4.4 长按删除与清空delete 的两种用法都有教学价值记录页用 ListView 展示每条训练记录长按弹确认框再删除。ListView 虽然比 RecyclerView 老但它和 Cursor 的配合更直接课设里两个列表组件都出现反而能体现你对不同列表的取舍。listView.setOnItemLongClickListener((parent, view, position, id) - { final long recordId id; new AlertDialog.Builder(this) .setTitle(确认删除) .setMessage(删除这条训练记录) .setPositiveButton(删除, (d, w) - { new DBHelper(this).deleteRecord(recordId); refreshList(); }) .setNegativeButton(取消, null) .show(); return true; });ListView 的 onItemLongClick 回调参数里id 就是数据库 _id 列的值前提是你用 SimpleCursorAdapter 且查询结果包含 _id。把这个 id 直接传给 deleteRecord就能精确删除。删除操作属于破坏性行为必须加二次确认这也是 Android 交互规范里的高频考点。清空操作就是把 delete 的 where 参数传 null作用范围变成全表代码一行但我会在报告里特意提一句“这是危险操作实际产品里要加更重的确认”。到此项目的增删查全部闭环。插入记录、查询列表、长按删除每一步都有独立方法答辩时可以按“增、删、查”三条线分别演示不用临时翻代码。5. 避坑新手最容易翻车的 5 个地方与处理办法5.1 模拟器黑屏、启动慢、连不上宿主机现象AVD 点启动后一直黑屏等五分钟还在转圈或者模拟器起来了但 App 请求不到本地服务。原因AVD 选的系统镜像太新或者没有启用硬件加速网络方面模拟器是 NAT 模式不能直接用 localhost 访问宿主机上的服务。解决在 Device Manager 里创建虚拟设备时选 Pixel 2 作为机型系统镜像选 API 30 或 API 31这些镜像比 API 34 稳定得多。启动前先确认 SDK Manager 里已安装 “Android Emulator” 和 “Intel HAXM” 或 WHPX。要访问宿主机时把地址写成 10.0.2.2。诊断网络可以用adb shell ping 10.0.2.2能通就说明模拟器和宿主机的链路没问题App 连不上就得查端口和防火墙。5.2 编译报错Failed to find target with hash string android-34现象同步 Gradle 时直接报错提示找不到 android-34 这个 target。原因项目的 compileSdk 是 34但你本地 SDK Manager 里没装 Android 14 Platform。解决打开 Tools → SDK Manager勾选 Android 14API 34的 SDK Platform点 Apply 安装后重新 Sync。如果你下载 SDK 特别慢就只勾选这一项不要把所有版本全选上。另一个更快的办法是把 compileSdk 改成本地已装的版本比如 33同时注意 targetSdk 也保持一致。这个坑其实是环境问题和代码无关但卡住新手的概率非常高。5.3 复写了 onBackPressed 却不生效或者编译提示已过时现象在 Activity 里写了onBackPressed()跑起来按返回键没反应或者代码上出现删除线。原因targetSdk 33 以上系统开始支持预测性返回手势onBackPressed 被标记为废弃部分设备上默认布局动画也变了复写的代码可能不走原路径。解决如果不需要拦截返回就直接不写 onBackPressed让系统默认处理。如果要在返回时提示保存数据用 OnBackPressedCallbackgetOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { Override public void handleOnBackPressed() { // 在这里做保存确认 finish(); } });true表示这个回调启用状态handleOnBackPressed 就是原来的 onBackPressed 逻辑。这样写在新旧设备上都能正常运行而且答辩时可以顺带讲一句“我已经适配了 Android 13 的预测性返回机制”这是实打实的加分项。5.4 真机识别不到USB 调试开了但设备列表还是空的现象手机用数据线连电脑Android Studio 的 Device 面板显示 No device点击 Run 弹窗找不到目标设备。原因手机没进入开发者模式或者 USB 调试没开或者数据线是纯充电线没有数据传输能力。解决先在手机设置里连续点击版本号 7 次打开开发者选项再进入开发者选项打开 USB 调试。插入电脑后手机弹窗问“是否允许 USB 调试”要点允许。然后命令行验证adb devices输出xxxxxxxx device说明连接成功。如果显示 unauthorized把数据线拔掉重插手机端重新授权。最后一种情况是换了数据线才好的所以遇到这个坑先检查线再检查驱动。5.5 Android Studio 全英文看不懂、导入别人的工程后中文注释乱码现象界面功能找不到项目里中文全变成乱码。原因Android Studio 没有安装中文语言包导入的工程文件编码不是 UTF-8。解决要汉化界面打开 Settings → Plugins搜索 “Chinese (Simplified)” 安装后重启界面就变成中文了。我的建议是界面汉化可以但代码里的英文关键字不要翻译因为课设答辩时老师用的是中文系统而代码是英文两头不耽误。注释乱码的问题打开 Settings → Editor → File Encodings把 Global Encoding、Project Encoding 和 Default encoding for properties files 全部设成 UTF-8再重新打开文件就正常。移植别人工程时经常遇到的 Gradle sync 失败也和编码无关先把这两个设置改掉再一步一步看报错。6. 进阶技巧打包 APK、修改包名与导出课设报告素材6.1 从 Build 到 APKdebug 包和签名包的区别课设交付一般要交一个能直接安装的 APK。Android Studio 里最直接的路径是 Build → Build APK(s)生成的 debug 包路径在app/build/outputs/apk/debug/app-debug.apk。debug 包可以直接装到手机上但默认包含调试信息图标也可能带一个机器人形状。正式提交时我更推荐 Build → Generate Signed Bundle / APK配置一个自己的签名文件。签名文件的 keystore 密码一定要记住忘了就没办法在原有包上做版本更新。github 上很多模板工程只能跑 debug 包就是因为签名文件被作者留在本地了。6.2 修改包名与应用名让课设看起来不像模板模板工程默认的com.example.fitness一眼就能被老师看出来是抄的。修改方法是改 app/build.gradle 里的 namespace 和 applicationId比如改成com.yourname.fitness然后改app/src/main/res/values/strings.xml里的 app_name把默认的工程名改成“健身记录”或你自己的标题。改完之后执行 Build → Clean Project再重新编译。如果不 Clean旧包名和新包名会同时存在手机上的老 App 也不会自动更新。这点我吃过亏有次改了 applicationId 直接 Run装完发现桌面有两个图标后来才意识到是没清干净。有次我在答辩现场被老师问“返回时未保存的数据怎么处理”我愣了一下因为我只在保存按钮里写了逻辑返回键直接放行。从那次之后我每次交课设前都强制走一遍完整流程新建或导入工程 → 从 MainActivity 把所有页面跑一圈 → 每个返回键都测一遍 → 真机安装验证 → 打包签名 → 截图存档。现在这套流程也成了我换新电脑后重装环境的习惯。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →