简介面向计算机专业毕业设计及Android开发学习者这份基于Android Studio的校园快递代拿跑腿App源码案例完整呈现了从需求分析到前后端联调的实现过程适合用来参考选题、搭建项目框架或进行二次开发。压缩包共52个文件以Vue页面组件和JavaScript逻辑代码为主辅以MySQL建表SQL脚本、CSS样式、JSON配置及项目说明文档整体仅540KB结构轻量紧凑。目前已有116人浏览学习具备一定参考热度。案例覆盖MVP/MVVM架构、Activity与Fragment界面组织、Retrofit/OkHttp网络请求、MySQL数据库设计、权限配置与消息推送等关键知识点并附带清晰的目录层级和数据库脚本可帮助使用者快速理解校园代取场景下的订单流转、用户认证与数据表关联是一份实践性强、易于上手的毕业设计参考资料。1. 校园快递代拿跑腿App一个能直接跑起来的Android Studio毕业设计源码校园快递代拿跑腿App是Android Studio毕业设计里的常青树选题——论技术难度不高不低但胜在模块多、链路长答辩现场演示起来非常直观。这份zip源码解决的是很实际的问题快递到驿站了但你人在教学楼发布一个代取订单跑腿员接单、取件、送到楼下双方在App内完成整个闭环不用你去写后端接口用户端、跑腿端、管理端三套界面加一份能用的SQLite数据库都给你备齐了。适合三类人选了类似题目的计算机、软件工程专业毕业生急需一份能演示、能答辩的完整工程想把框架改造成代拿外卖、代排队等其他跑腿场景的同学以及第一次接触Android Studio项目、需要一个可运行参考对象的初学者。不必担心后端基础薄弱核心链路都在App内部闭环这份源码的边界和坑我都帮你趟过了。2. 功能拆解与角色设计先看懂模块再动手改代码拿到源码先别急着导入花十分钟把角色和表结构搞清楚后面改起代码会顺很多。这个项目的本质是一个三端合一的单App应用登录后按角色走不同的主界面而不是拆成三个独立安装包。2.1 三个角色一张订单表用户端、跑腿端与管理端的边界用户端承担的动作是发布代取订单、查看自己发过的订单列表、确认跑腿员送达并完成支付动作。跑腿端承担的动作是浏览待接单列表、抢单、更新订单状态到已送达。管理端则是查看全部用户和全部订单偶尔做一下状态修正。三者的关系不是靠三个表而是靠用户表里的role字段和订单表里的publisher_id、taker_id来维系的。用户表的结构通常长这样CREATE TABLE tb_user ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password TEXT NOT NULL, role INTEGER DEFAULT 0, -- 0普通用户 1跑腿员 2管理员 phone TEXT, create_time TEXT DEFAULT (datetime(now,localtime)) );这里role字段是整套权限判断的源头。登录时查出这个值跳转到不同的Activity后续每个界面的按钮是否可见、能否操作也都要跟它比对。常见做法是把当前登录用户的role值存在SharedPreferences里每次进入Activity时读出来判断而不是重新查数据库。订单表是另一个核心我一般建议拿到源码先看这张表的建表语句因为订单状态字段直接决定整个App的交互逻辑CREATE TABLE tb_order ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, pickup_location TEXT NOT NULL, -- 取件地址比如菜鸟驿站 deliver_location TEXT NOT NULL, -- 送件地址比如12号楼 reward REAL DEFAULT 0, -- 跑腿赏金 status INTEGER DEFAULT 0, -- 0待接单 1已接单 2已送达 3已完成 4已取消 publisher_id INTEGER NOT NULL, -- 发单人id taker_id INTEGER DEFAULT 0, -- 接单人id0表示还没人接 create_time TEXT DEFAULT (datetime(now,localtime)) );如果你发现源码里的字段比这多——比如加了order_no编号、备注remark、商品类型type——那是原作者根据自己答辩报告扩展的不影响阅读。核心记住publisher_id、taker_id、status三个字段就够了改功能时九成都在动它们。2.2 订单状态机从“待接单”到“已完成”的流转跑腿App的交互本质就是一条状态链在驱动。状态机的完整流转路径是发布者创建订单后status0跑腿员抢单成功status1并写入自己的id到taker_id跑腿员把快递送到楼下后status2发布者确认收货并点击完成status3中途发布者反悔则status4。状态机里最容易翻车的就是“任何人都有可能改状态”所以源码里每个更新操作必须同时校验操作者身份和当前状态。我之前看到有些版本会犯一个经典错误——跑腿员接单后发单人还能取消订单甚至会直接删掉订单记录导致跑腿员端列表空指针崩溃。正确做法是取消操作只在status0时放行接单之后要取消必须走管理端介入。另一个要注意的细节是status2到status3之间要不要做“赏金转账”动作。很多毕设偷懒直接把确认完成等同于支付完成没有钱包表。如果你想让答辩多一点亮点可以考虑加一张tb_wallet表让发布者确认时扣款、跑腿员收到入账记录这个我在第六章会说怎么加。2.3 源码目录结构从java包到res资源的映射理解了数据模型再来看工程结构就会清晰很多。打开app/src/main/java包结构一般是按职责分层app/src/main/java/com/example/express/ ├── activity/ // 所有界面类LoginActivity、MainActivity、PublishActivity、OrderDetailActivity ├── adapter/ // RecyclerView适配器OrderAdapter、UserAdapter ├── db/ // DBHelper数据库帮助类、UserDao、OrderDao ├── model/ // 实体类User、Order ├── utils/ // MD5Utils加密工具、ToastUtils提示工具 └── base/ // BaseActivity封装了SharedPreferences读取和角色判断对应的资源目录app/src/main/res/layout/里activity_login.xml是登录页、activity_main.xml是主页容器、item_order.xml是订单列表的单行卡片。改界面样式认准layout目录改文案和配色认准values目录下的strings.xml和colors.xml。我现在拿到一份源码的习惯是先打开AndroidManifest.xml看注册了哪些Activity再看base包里的角色判断逻辑最后用SQLite可视化工具打开数据库文件过一遍表结构。这套流程走完整个项目等于已经跑了一遍脑内模拟后面改代码就不会迷路。3. 把zip变成能跑的工程Android Studio导入与Gradle配置源码到手第一件事就是让它跑起来。这个阶段新手最容易在环境配置上消耗大量时间而这本来是最不需要动脑的环节。3.1 版本匹配Gradle、JDK与SDK的三方关系Android Studio项目能编译过本质是Gradle版本、JDK版本、SDK版本三者匹配的结果。拿到源码先看三个文件gradle/wrapper/gradle-wrapper.properties里的distributionUrl决定了Gradle版本根目录build.gradle里的com.android.tools.build:gradle版本是AGP插件版本app/build.gradle里的compileSdk、minSdk、targetSdk是SDK版本。常见组合我列在下面AGP版本对应Gradle版本要求JDK版本7.0.x7.0.2JDK 117.4.x7.5JDK 118.0.x8.0JDK 17这份源码如果是前两年做的大概率停在AGP 7.x和Gradle 7.x。如果你的Android Studio是2023年之后的版本打开旧工程时IDE会提示升级AGP我建议不要急着点升级——升级到AGP 8.x之后很多旧代码的依赖写法要跟着改不熟悉的同学会陷入连环编译错误。宁可让它停留在原版本只要能sync通过就行。3.2 导入步骤从Open到Build Success第一次导入建议完整走一遍下面几步先把zip解压到纯英文路径比如D:\Projects\ExpressApp。路径里有中文的话Gradle有些任务会报诡异错误。打开Android Studio点File → Open选中刚才解压目录下含settings.gradle的那一层。等待Gradle Sync跑完期间Android Studio会去下载distributionUrl里指定的Gradle发行版。如果你的网络访问不了services.gradle.org这一步会卡很久解决办法见避坑章节。Sync完成后点菜单Build → Make Project观察底部Build窗口有没有报错。连点两次Shift弹出Search Everywhere输入AVD Manager创建一个API 30左右的模拟器。点Run按钮选模拟器安装App。导入过程里最需要注意的是如果Sync阶段报SDK组件缺失比如“SDK Build Tools xx.x.x is missing”说明你本地的SDK Manager里没装那个版本。点开SDK Tools勾选对应版本装一下就行。如果报的是Gradle本身下载失败就不是SDK的问题了。3.3 数据库初始化SDK本地数据从哪里来这个App用的是本地SQLite所有数据都存在手机内部存储里。数据库的创建和初始化在DBHelper的onCreate回调中执行——第一次安装打开App时系统会自动调用onCreate方法建表和插入种子数据。// DBHelper.java 数据库创建与预置数据 public class DBHelper extends SQLiteOpenHelper { private static final String DB_NAME express.db; private 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 tb_user (...)); // 建用户表 db.execSQL(CREATE TABLE tb_order (...)); // 建订单表 // 插入预置管理员账号和测试订单 db.execSQL(INSERT INTO tb_user (username, password, role) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 2)); db.execSQL(INSERT INTO tb_order (title, pickup_location, deliver_location, reward, status) VALUES (帮拿快递, 菜鸟驿站, 12号楼, 3.0, 0)); } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DROP TABLE IF EXISTS tb_order); db.execSQL(DROP TABLE IF EXISTS tb_user); onCreate(db); } }参数说明管理员账号的密码是e10adc3949ba59abbe56e057f20f883e这是“123456”的MD5值说明源码里做了MD5加密存储。测试订单插入了一条status0的待接单数据保证你第一次打开App时界面上是有内容可看的。这里有一个重要的调试技巧当你改了数据库表结构比如加了字段或者改了状态定义模拟器里的旧数据库不会自动重建。最省事的做法是打开App管理界面长按App图标 → 应用信息 → 存储与缓存 → 清除数据然后再重新打开App让onCreate重新建表。不然你只会遇到“android.database.sqlite.SQLiteException: no such column”这类报错。4. 核心链路实战登录鉴权、订单发布与抢单操作环境跑通之后就可以开始改代码了。这一章挑三条核心链路来讲登录后Session怎么保持、订单列表怎么刷新、抢单怎么保证并发安全。这三条改明白App的主流程你就拿捏了。4.1 登录模块Session保存与角色判断登录页的逻辑在LoginActivity里通常是这样一段代码// LoginActivity.java 登录校验 public void login(View view) { String username etUsername.getText().toString().trim(); String password etPassword.getText().toString().trim(); if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) { Toast.makeText(this, 账号和密码不能为空, Toast.LENGTH_SHORT).show(); return; } UserDao dao new UserDao(this); User user dao.queryByUsername(username); if (user ! null user.getPassword().equals(MD5Utils.md5(password))) { SharedPreferences sp getSharedPreferences(session, MODE_PRIVATE); sp.edit() .putInt(userId, user.getId()) .putInt(role, user.getRole()) .putString(username, user.getUsername()) .apply(); redirectByRole(user.getRole()); finish(); } else { Toast.makeText(this, 用户名或密码错误, Toast.LENGTH_SHORT).show(); } }逻辑说明先从输入框拿到账号密码非空校验之后调UserDao去数据库里查用户记录。查到了比对密码的MD5值比对成功就把用户信息写入SharedPreferences然后根据角色跳转到对应主页。这里有两个细节值得注意。一是密码比对用MD5Utils.md5(password)对输入做哈希后再与库中值比对这是毕设源码里最标准的做法避免明文密码直接存储和传输。二是SharedPreferences的写入用了apply()而不是commit()apply是异步写入不阻塞UI线程commit是同步等待磁盘落盘。单条数据场景两者差别不大但我见过有的同学在循环里连续commit导致页面卡顿。登录成功后的跳转方法一般是这样// LoginActivity.java 按角色跳转 private void redirectByRole(int role) { if (role 1) { startActivity(new Intent(this, RunnerMainActivity.class)); } else if (role 2) { startActivity(new Intent(this, AdminMainActivity.class)); } else { startActivity(new Intent(this, UserMainActivity.class)); } }也就是说同一个App按角色拆了三个主页。如果你想在用户端也看到全部待接单订单只需要在UserMainActivity里多加一个Fragment或者一个入口按钮跳转到待接单列表即可底层数据是同一张表。4.2 发布订单与列表刷新RecyclerView绑定数据发布订单是用户的第一个核心动作。界面上输入标题、取件地、送件地、赏金点提交后插入数据库并回到列表页。难点不在插入而在列表怎么感知数据变化并刷新。// PublishActivity.java 发布订单 public void publish(View view) { String title etTitle.getText().toString().trim(); String pickup etPickup.getText().toString().trim(); String deliver etDeliver.getText().toString().trim(); float reward Float.parseFloat(etReward.getText().toString().trim()); int userId getSharedPreferences(session, MODE_PRIVATE).getInt(userId, 0); if (TextUtils.isEmpty(title) || TextUtils.isEmpty(pickup) || TextUtils.isEmpty(deliver)) { Toast.makeText(this, 请完整填写订单信息, Toast.LENGTH_SHORT).show(); return; } Order order new Order(); order.setTitle(title); order.setPickupLocation(pickup); order.setDeliverLocation(deliver); order.setReward(reward); order.setPublisherId(userId); order.setStatus(0); OrderDao dao new OrderDao(this); long newId dao.insert(order); if (newId 0) { Toast.makeText(this, 发布成功, Toast.LENGTH_SHORT).show(); setResult(RESULT_OK); finish(); } else { Toast.makeText(this, 发布失败请稍后重试, Toast.LENGTH_SHORT).show(); } }参数说明getSharedPreferences里的session文件名需要和登录时写入的完全一致否则读不到userId发布出来的订单publisher_id会是0查不到关联用户。setResult配合startActivityForResult或者registerForActivityResult使用让列表页在返回时知道数据变化了。列表页的RecyclerView适配器是另一个重点。订单列表的Adapter通常长这样// OrderAdapter.java 订单列表适配器 public class OrderAdapter extends RecyclerView.AdapterOrderAdapter.ViewHolder { private ListOrder orderList; private OnOrderClickListener listener; public OrderAdapter(ListOrder orderList, OnOrderClickListener listener) { this.orderList orderList; this.listener listener; } Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_order, parent, false); return new ViewHolder(view); } Override public void onBindViewHolder(ViewHolder holder, int position) { Order order orderList.get(position); holder.tvTitle.setText(order.getTitle()); holder.tvPickup.setText(取件 order.getPickupLocation()); holder.tvDeliver.setText(送件 order.getDeliverLocation()); holder.tvReward.setText(¥ order.getReward()); holder.itemView.setOnClickListener(v - listener.onClick(order)); } Override public int getItemCount() { return orderList null ? 0 : orderList.size(); } class ViewHolder extends RecyclerView.ViewHolder { TextView tvTitle, tvPickup, tvDeliver, tvReward; ViewHolder(View itemView) { super(itemView); tvTitle itemView.findViewById(R.id.tv_title); tvPickup itemView.findViewById(R.id.tv_pickup); tvDeliver itemView.findViewById(R.id.tv_deliver); tvReward itemView.findViewById(R.id.tv_reward); } } }这个适配器的核心就是onBindViewHolder里把Order对象的各个字段写到View控件上同时给整张卡片设置点击回调。列表刷新时你先从数据库查出List 然后调用adapter.notifyDataSetChanged()界面就会重新渲染。我在改这类代码时的习惯是查询数据库和刷新UI分两层写数据层单独封装一个方法// OrderDao.java 查询待接单订单 public ListOrder queryOrdersByStatus(int status) { ListOrder list new ArrayList(); SQLiteDatabase db getReadableDatabase(); Cursor cursor db.query(tb_order, null, status?, new String[]{String.valueOf(status)}, null, null, create_time DESC); while (cursor.moveToNext()) { Order order new Order(); order.setId(cursor.getInt(cursor.getColumnIndexOrThrow(id))); order.setTitle(cursor.getString(cursor.getColumnIndexOrThrow(title))); order.setPickupLocation(cursor.getString(cursor.getColumnIndexOrThrow(pickup_location))); order.setDeliverLocation(cursor.getString(cursor.getColumnIndexOrThrow(deliver_location))); order.setReward(cursor.getFloat(cursor.getColumnIndexOrThrow(reward))); order.setStatus(cursor.getInt(cursor.getColumnIndexOrThrow(status))); list.add(order); } cursor.close(); return list; }这样你在Activity里只需要一行代码搞定刷新orderList dao.queryOrdersByStatus(0); adapter.notifyDataSetChanged(); 保持数据层与UI层分离后面加筛选条件也方便。4.3 接单操作与状态回写并发安全的下沉写法跑腿端最关键的动作就是抢单。场景是多个跑腿员同时盯着一个待接单订单谁先点谁成功。如果代码写得不够严谨会出现两个人同时看到这个订单、同时点击、同时提示接单成功的情况最后数据库里taker_id只保存了后写入的那个人但先写入的那个人也以为自己抢到了。这个问题的根源在于“先查询状态再更新”的操作不是原子的。查的时候status0两个线程都查到了然后各自执行更新都把status改成1各自的taker_id不同后执行的覆盖前者。解决的办法是把状态判断下沉到UPDATE语句的WHERE条件里// OrderDao.java 并发安全的接单操作 public boolean takeOrder(int orderId, int takerId) { SQLiteDatabase db getWritableDatabase(); ContentValues values new ContentValues(); values.put(status, 1); values.put(taker_id, takerId); int rows db.update(tb_order, values, id? AND status0, new String[]{String.valueOf(orderId)}); return rows 0; }参数说明db.update方法返回的是受影响的行数。因为WHERE里带了AND status0当且仅当该订单此刻仍然是待接单状态时更新才会真正生效。如果订单已经被抢走rowCount是0takeOrder返回false第二个点击的跑腿员就会看到“订单已被抢走”。把并发判断交给数据库的单条UPDATE语句天然保证了原子性这比在Java层加synchronized锁可靠得多。对应Activity里的调用代码// RunnerOrderDetailActivity.java 跑腿员点击接单 public void onTakeOrderClick(View view) { int orderId getIntent().getIntExtra(orderId, 0); int currentUserId getSharedPreferences(session, MODE_PRIVATE).getInt(userId, 0); if (currentUserId 0) { Toast.makeText(this, 登录状态失效请重新登录, Toast.LENGTH_SHORT).show(); return; } boolean ok new OrderDao(this).takeOrder(orderId, currentUserId); if (ok) { Toast.makeText(this, 接单成功, Toast.LENGTH_SHORT).show(); // 通知列表页刷新并返回 setResult(RESULT_OK); finish(); } else { new AlertDialog.Builder(this) .setTitle(提示) .setMessage(手慢了该订单已被其他跑腿员抢走) .setPositiveButton(知道了, null) .show(); } }判断currentUserId为0是为了防止直接打开详情页时未登录导致的数据异常。因为接单成功后要return掉Activity所以返回值通过startActivityForResult或新的Activity Result API传递给列表页。如果列表页是用startActivityForResult打开的详情页那么详情页finish()之前setResult(RESULT_OK)列表页的onActivityResult里收到这个结果就去重新查库刷新。这里还有一个值得注意的性能设计每次接单都查一次数据库并更新对于毕设规模完全没有问题。但如果做商用这种读多写多的场景一定要走Redis或服务端锁App直连数据库的架构撑不住并发。答辩时被问到这点可以主动说“当前实现利用了SQLite更新的原子性在单机演示场景下能保证正确大规模并发需要引入服务端和事务锁”——这就是一个很好的加分回答。5. 避坑合集导入崩溃、黑屏、订单不同步的五个典型现场这部分是血泪经验。下面五条坑我几乎在每份同类毕业设计源码里都能遇到提前知道可以省掉好几个晚上的调试时间。5.1 现象Gradle Sync卡住不动或报Could not resolve com.android.tools.build:gradle:7.4.1原因Android Studio的Gradle插件需要从Google Maven仓库下载国内网络直连经常失败或极慢。这不是源码的问题是网络环境的问题。解决在根目录build.gradle的repositories里加阿里云镜像修改后的样子是buildscript { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/public } maven { url https://maven.aliyun.com/repository/gradle-plugin } google() mavenCentral() } }改完后重新Sync。如果还是卡在下载Gradle本体直接打开gradle-wrapper.properties看distributionUrl指定的是哪个版本比如gradle-7.4.2-all.zip然后从浏览器或用其他下载工具手动下载这个zip放到用户目录.gradle/wrapper/dists/gradle-7.4.2-all/下的对应哈希目录里重新打开工程。不要解压直接让Gradle自己解压。5.2 现象App安装成功但一启动就闪退Logcat报SQLiteException: no such table: tb_order原因代码里查询的表和实际数据库里存在的表不一致。最常见的情况是你改过DBHelper里的建表语句但模拟器里已经有一个旧版本数据库文件onCreate不会重新执行。解决模拟器上卸载App重装或者在代码里临时把DB_VERSION加1触发onUpgrade。如果是在开发阶段频繁改表结构最快的办法是卸载重装不要想着做数据迁移。用adb命令一行搞定adb uninstall com.example.express adb install app/build/outputs/apk/debug/app-debug.apk另外卸载重装后之前登录写入的SharedPreferences数据也没了需要重新登录一次。如果嫌麻烦开发阶段可以把创建表的语句放到Application的onCreate里每次都检查但我个人建议还是用正规的DB_VERSION管理别给答辩埋雷。5.3 现象跑腿员点击接单提示成功但列表刷新后状态还是“待接单”原因takeOrder的UPDATE语句没有生效但界面没有感知到失败。场景往往是代码用了db.execSQL(UPDATE tb_order SET status1...)execSQL不返回受影响行数所以即使WHERE条件没匹配到任何行也不会报错代码直接往下走Toast提示成功。解决把execSQL换成db.update方法并获得返回的rowCount只有rowCount大于0才提示接单成功。这正好对应到4.3节里写的takeOrder方法。我判断一份源码质量高低就看它跟数据库交互时有没有使用update/insert/delete的返回值。用execSQL的版本十有八九存在这种“假成功”逻辑。5.4 现象真机调试点击Run后手机提示安装失败或“应用未安装”原因多数情况是签名不一致调试版签名和之前安装的发布版签名不一样。尤其是手机之前装过同一个包名的其他签名版本时Android系统会拒绝安装。另一种可能是targetSdkVersion设置高于手机操作系统版本不过这个一般不会单独导致安装失败。解决最简单粗暴的办法是卸载手机上的旧App再安装。如果是长期调试保持debug签名统一即可。真机调试还需要注意手机上开启开发者选项和USB调试后确认电脑上的驱动没毛病。Linux用户在插线后如果报insufficient permissions for device需要配置udev规则或者在Android Studio的SDK Manager里把Platform Tools更新到最新。5.5 现象订单列表刷新后界面显示的位置不对或者出现重复卡片原因RecyclerView复用机制被错误处理。常见写法是在onBindViewHolder里直接new出来的控件赋值但如果你在Activity里把List给改了忘记清除旧的List元素而是用addAll追加那列表就会出现重复卡片。还有一种情况是没有调用adapter.notifyDataSetChanged()而只调用了notifyItemInserted但插入位置和实际数据不一致导致错位。解决刷新数据时统一走如下模式// MainActivity.java 安全的列表刷新方式 private void refreshOrderList(int status) { ListOrder newList dao.queryOrdersByStatus(status); orderList.clear(); orderList.addAll(newList); adapter.notifyDataSetChanged(); }千万不要直接给orderList赋值一个新对象因为Adapter持有的引用还是旧的List对象只有clear后addAll才是在原对象上修改。这条细节点答辩时经常会问到值得记住底层原因。6. 答辩前的最后一步数据预置、录屏脚本与亮点打磨环境跑通、核心链路也没问题剩下的工作就是让答辩演示环节行云流水。这一步是最容易被忽视的但也是让老师觉得“这学生真的把系统做出来了”的关键。先做数据预置。在DBHelper的onCreate里多插几条状态不同的订单数据模拟真实使用场景// DBHelper.java 预置演示数据 db.execSQL(INSERT INTO tb_order (title, pickup_location, deliver_location, reward, status, publisher_id) VALUES (帮拿快递, 菜鸟驿站, 12号楼, 3.0, 0, 2)); db.execSQL(INSERT INTO tb_order (title, pickup_location, deliver_location, reward, status, publisher_id) VALUES (取顺丰件, 东区快递柜, 3号楼, 2.0, 0, 2)); db.execSQL(INSERT INTO tb_order (title, pickup_location, deliver_location, reward, status, publisher_id) VALUES (带杯奶茶, 西区甜品店, 图书馆, 5.0, 1, 3));参数说明最后一条状态是1表示已经被某跑腿员接单了用于演示“进行中”的界面效果。建议至少备两条待接单、一条已接单、一条已送达的数据这样演示每个角色切换时都有内容展示不至于现场临时发布等待刷新。录屏脚本建议固定下来整个演示控制在5分钟以内。第一段用普通用户账号登录演示发布一个订单附上快递到货的模拟通知第二段切到跑腿员账号演示看到新订单、抢单、改状态为已送达第三段切回用户端确认完成订单。这个顺序符合业务时序老师说到底是能听懂的。答辩时值得强调的三个技术亮点一是4.3节实现的并发安全抢单直接说“我用UPDATE带status条件的原子操作避免了超卖问题”二是MD5密码加密存储安全角度稳了一个提问三是三端合一的角色路由设计说明你对软件架构有分层意识。提前练好对这三点的话术老师追问的概率会小很多。最后的验证步骤建议强制走一遍卸载App重装确认数据库重新初始化没有报错连续快速点击两次接单按钮看第二次是否被拦截清掉后台进程重新打开App确认登录状态保持逻辑正确。这三条过一个这个demo基本就稳了。从那以后我每次拿到一份毕业设计源码不管是自己用还是帮人看都会先花十分钟把“卸载重装、连续点击、杀进程重开”这三板斧走一遍再谈改其他的。这段经验帮我避开了至少三次现场答辩翻车希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?