Android共享停车系统实战:从APK解析到定位与构建全流程
发布时间:2026/9/14 7:41:01来源:尧图网络
简介面向Android开发初学者及毕业设计学生提供一套基于Android微信小程序的共享停车位管理系统完整工程代码旨在解决城市停车难问题涵盖车位查询、预约、地图导航、账户管理等核心功能并整合移动互联网、物联网等应用场景。资源共2000个文件整体17.17MB既包含java、xml、gradle等Android工程源码也包含html、css、js等页面资源还有png、svg等界面素材以及可直接安装的apk调试包另有class、jar等编译产物。已有121人学习下载。项目实现了SQLite本地数据存储、HTTP/HTTPS网络通信、微信小程序接口对接和高德/百度地图服务调用并引入FCM或第三方推送、输入校验与HTTPS加密等安全机制也覆盖了单元测试、集成测试和性能优化等实践。开发者可导入Android Studio查看完整工程结构梳理用户登录、车位查询、预约下单、地图导航及订单管理的业务流程结合源码、XML布局、SQLite脚本与测试包快速定位关键代码适合作为毕业设计模板或移动开发实战练习。1. 一个 APK 压缩包里藏着的完整共享停车闭环先说结论别把这份《基于 Android 的共享停车位管理系统》当成一个“画了三个页面的课程作业”。解压这个 RAR 之后你会看到 resources.ap_、Secondcheweichat_ssmClient.apk、gradlew.bat 以及一批以 BaiduLocation、Command、FileTool 命名的 class 文件说明它同时覆盖了 Android 客户端、SSM 服务端接入和微信小程序入口是一个能跑通“找车位 → 预约 → 服务端记账”的最小闭环。对正在做 Android 毕业设计、或者想了解共享停车/车位预约这类 LBS 项目怎么组织代码的人来说值得把每个文件都认一遍。下文我会按“客户端 → 服务端接口 → 数据层 → 构建产物”的顺序拆最后给出一组能直接验证 APK 能否安装运行的命令。2. Android 端功能拆解Java 逻辑、XML 布局与百度地图定位模块2.1 从项目文件反推模块划分BaiduLocation、Plugs、FileTool 分别承担什么先看资源包里这几个反编译产物。BaiduLocation.class 不是业务类而是对百度地图定位 SDK 的封装负责拿到经纬度和地址BaiduLocation$MyLocationListener.class 意味着封装类里有一个内部实现的定位回调。Command.class 从命名习惯看是命令/请求入口负责把“查询车位、提交预约”这些动作转发给业务层。MyObj.class、Plugs.class、FileTool.class 分别是通用对象模型、工具集合和文件读写工具类。也就是说这个项目把功能拆成了“定位能力”“请求入口”“数据模型”三层比把所有代码堆在 Activity 里的写法好维护得多。2.2 定位模块的典型写法与参数调优定位是共享停车位 App 的地基用户打开页面第一件事就是“我在哪附近有没有空位”。完整集成百度定位需要先在开发者后台申请 AK再把 SDK 依赖进来然后在继承 Application 的类里初始化。核心启动代码按常见写法是这样public class BaiduLocation { private LocationClient mLocClient; public void startLocate(Context context) { LocationClientOption option new LocationClientOption(); option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); option.setCoorType(bd09ll); // 百度坐标系传给后端时要注意和地图坐标系一致 option.setScanSpan(3000); // 3秒回调一次位置停车场景不需要太快 option.setIsNeedAddress(true); // 需要返回省市区街道方便列表页直接展示 option.setOpenGps(true); mLocClient new LocationClient(context); mLocClient.setLocOption(option); mLocClient.registerLocationListener(new MyLocationListener()); mLocClient.start(); // 异步开始定位结果通过回调返回 } }逻辑说明Hight_Accuracy 表示 GPS、Wi-Fi、基站三源混合定位室外精度最好室内也能出结果bd09ll 是百度自己的加密坐标系如果后端地图和前端都用的百度保持一致即可不要混用 GCJ-02否则会出现“定位点偏到马路对面”的经典问题。ScanSpan 我一般设置在 3000 到 5000 毫秒既能感知用户移动又不会因为频繁回调导致电量快速下降。setIsNeedAddress(true) 会额外解析地址文字代价是每次定位网络请求变重如果列表页只显示“距离”而不显示地址可以关掉。拿到回调后常见做法是把经纬度存进一个全局对象用于刷新附近车位列表同时把地址文本直接填到页面上方的地点栏。若你的工程里没有 BaiduLocation 这个类而是引用了高德定位代码结构也基本一致只需要把 AMapLocationClientOption 里的定位模式、坐标系对应改掉。定位参数的选型可以收敛成下面这张表参数推荐值场景说明setLocationModeHight_Accuracy混合定位室外精度高室内可用setCoorTypebd09ll必须与地图 SDK、后端坐标系一致setScanSpan30005000ms感知移动同时控制电量和流量setIsNeedAddresstrue列表页需直接展示省市区街道路名时开启setOpenGpstrue依赖卫星定位地下车库场景可关闭2.3 界面层XML 布局的工作方式客户端界面由 XML 布局驱动。resources.ap_ 是资源打包器把 layout、string、drawable 编译后的资源包运行时会通过资源 ID 加载。一个典型的车位搜索页由顶部定位地址栏、中部地图或列表、底部预约按钮组成用 LinearLayout 垂直排列即可满足需求!-- 列表和按钮都用 id 声明需要在 Java 里通过 R.id 引用 -- LinearLayout android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical TextView android:idid/tv_current_address android:layout_widthmatch_parent android:layout_heightwrap_content android:padding12dp android:text正在定位... / androidx.recyclerview.widget.RecyclerView android:idid/rv_parking_list android:layout_widthmatch_parent android:layout_height0dp android:layout_weight1 / Button android:idid/btn_reserve android:layout_widthmatch_parent android:layout_heightwrap_content android:text预约车位 / /LinearLayout参数说明列表区用 layout_weight1 把剩余高度让给 RecyclerView这样键盘弹出或地址栏变化时不会挤压列表id 前缀 rv、tv、btn 是常规命名可以直接从 R.id.rv_parking_list 里引用而不用到处 findViewById。这个项目的界面逻辑不复杂难点不在布局而在“列表数据刷新”和“预约按钮状态”的联动比如车位被抢走后按钮要立即置灰这就需要在列表适配器的回调里做数据变更通知否则会出现“页面上看到有空位点预约却提示失败”的糟糕体验。3. 服务端与会话链路SSM Client、微信小程序与 HTTP 接口对接3.1 Secondcheweichat_ssmClient 里的 Client 到底指什么项目名 Secondcheweichat_ssmClient 拆开看是 second chewei车位拼音 chat ssm Client翻译过来就是“共享车位微信小程序 SSM 客户端”。这透露了架构服务端是 SSMSpring SpringMVC MyBatisJava Web 项目SSM Client 是整个 Android 工程的名字最终打出的 APK 就叫 Secondcheweichat_ssmClient.apk而 “weichat” 指向微信小程序入口。也就是说用户可能从微信小程序里看到车位再从 Android App 完成预约两条客户端共用同一套后台接口。对毕业设计而言这比只做单机版多了一个完整的前后端联调环节答辩时也更说得清“系统架构”这一栏。3.2 接口对接的一种最简实现OkHttp JSONAndroid 端对接 SSM 后台最常见的网络库是 OkHttp 配合 Retrofit。如果这个项目没有引入 Retrofit 的注解处理器直接用 OkHttp 手动组装请求也可以完成全套功能。查找附近车位的请求常见写法是这样OkHttpClient client new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .build(); Request request new Request.Builder() .url(http://192.168.1.100:8080/parking/queryNearby) .post(new FormBody.Builder() .add(lat, String.valueOf(latitude)) .add(lng, String.valueOf(longitude)) .add(radius, 2000) .build()) .build(); client.newCall(request).enqueue(new Callback() { Override public void onFailure(Call call, IOException e) { // 常见处理切换主线程 Toast 提示“网络异常” } Override public void onResponse(Call call, Response response) throws IOException { String body response.body().string(); JSONArray array new JSONObject(body).getJSONArray(data); // 手动解析后通过 Handler 或 runOnUiThread 更新适配器 } });这段代码的逻辑要点connectTimeout 和 readTimeout 都设 10 秒避免弱网环境下回调一直挂着POST 参数用 FormBody 是因为后端 SSM 控制器通常直接接收RequestParam(lat)这类表单字段经纬度用字符串拼接后端用 Double 接收。回调里不要直接操作 UI必须切回主线程否则会抛Only the original thread that created a view hierarchy can touch its views。真实项目里我一般会在 onFailure 里区分“超时”和“无网络”两种情况给用户不同的提示文案。项目里的 Command.class 很可能就是干这件事的把 queryNearby、reserve、cancelReserve 这些动作统一封装成 Command 对象再在内部持有 OkHttpClient这样 Activity 只负责传参数不关心 HTTP 细节。3.3 登录态与预约状态的同步策略共享停车位比普通查询类 App 多一个核心状态预约。预约不是只有一条“提交成功”就完了它至少经历“待支付、已锁定、已入场、已完成、已取消”几个阶段。小程序端和 Android 端共用一套接口就需要明确每个接口语义接口路径方法入参返回与状态变化/user/loginPOSTphone, code返回 token写入本地 SharedPreferences/parking/queryNearbyPOSTlat, lng, radius返回车位列表含实时 status/parking/reservePOSTlotId, userId车位 status 置为 1锁定返回订单号/parking/cancelPOSTorderId车位 status 置为 0释放资源/order/payPOSTorderId, amount订单状态置为已支付这里的核心点在于车位锁定要有时间限制比如 15 分钟内未支付自动释放。常见做法是后端用定时任务扫超时订单而不是靠客户端定时器因为用户杀掉 App 后客户端定时器就失效了。Android 端拿到“锁定成功”后本地也要把对应列表项的状态缓存为锁定避免用户重复点击同一个车位。如果你的 RAR 里没有看到后端 SSM 源码只有 Secondcheweichat_ssmClient.apk 和 resources.ap_那也没关系APK 里保存的接口路径会暴露后端路由设计可以从里面提取这套接口契约出来。2019 年之后微信小程序端不需要再单独维护一套后端直接用 wx.request 调这组接口即可登录时把 wx.login 拿到的 code 换成 openid 作为用户标识。预约状态有变化需要通知用户时可以再接极光推送这类国内推送服务课设场景下也能用前端定时轮询接口来演示。4. 数据层SQLite 本地缓存与安全校验4.1 内置数据库、表结构与 SQL 操作车位 App 虽然在网络正常时主要以服务端数据为准但本地照样要落 SQLite目的是三件事缓存在线列表、记录最近搜索历史、离线时展示上次的车位状态。设计方案里提到“用于存储车位信息、用户数据和交易记录”对应的最简表结构按常规经验可以这样给CREATE TABLE parking_lot ( id INTEGER PRIMARY KEY AUTOINCREMENT, lot_no TEXT NOT NULL, -- 车位编号如 B2-023 address TEXT NOT NULL, status INTEGER DEFAULT 0, -- 0 空闲1 锁定2 占用 price REAL DEFAULT 3.0, -- 每小时价格元 lat REAL, lng REAL, update_time INTEGER -- 毫秒时间戳用于增量更新 ); CREATE TABLE booking_order ( order_id TEXT PRIMARY KEY, lot_no TEXT NOT NULL, user_phone TEXT NOT NULL, status INTEGER DEFAULT 0, -- 0 待支付1 已支付2 已取消 amount REAL, create_time INTEGER );参数说明status 用 INTEGER 而不是 TEXT一是省空间二是可以建索引后按 status 快速过滤update_time 建议存 epochMillis后端做增量同步时只要对比这个字段就能拿到“自上次以来哪些车位状态变了”。索引方面车位表的数据量一般不会超过几万条单索引指向 status 就够用了不用过度设计。查询车位、更新状态的 SQL 通过 SQLiteOpenHelper 暴露public ListParkingLot queryNearby(double lat, double lng, int radius) { SQLiteDatabase db getReadableDatabase(); Cursor cursor db.rawQuery( SELECT * FROM parking_lot WHERE status 0 AND ABS(lat - ?) ? AND ABS(lng - ?) ? ORDER BY (lat - ?) * (lat - ?) (lng - ?) * (lng - ?), new String[]{String.valueOf(lat), String.valueOf(radius / 111000.0), String.valueOf(lng), String.valueOf(radius / 111000.0), String.valueOf(lat), String.valueOf(lat), String.valueOf(lng), String.valueOf(lng)}); // 遍历 cursor 组装对象返回 }这段查询用 ABS(lat - ?) 做了粗略的经纬度范围过滤能先排除掉大量不需要的距离计算ORDER BY 后面的平方和是简化版的距离排序不需要引入计算密集型的球面距离公式。注意 rawQuery 的参数占位符是 ? 而不是拼接字符串这是后续防注入的基础。4.2 防注入与敏感信息处理很多课设项目的通病是把 SQL 写成字符串拼接SELECT * FROM parking_lot WHERE lot_no lotNo 。一旦 lotNo 被用户输入控制就能注入出 DELETE 或 UNION 查询。正确做法是上面已有的参数化写法占位符让 SQLite 把用户输入当作字面量而不是 SQL 代码片段处理。常见误用风险正确做法字符串拼接 WHERE 条件SQL 注入rawQuery 的 ? 参数绑定密码明文存数据库库泄露即密码泄露BCrypt/SHA-256 加盐后存储网络请求用 http 明文中间人抓包改数据服务端配置 HTTPS内网调试再放开 http请求接口不带 token越权访问他人订单每次请求头加 Authorization 字段这里要特别提醒密码哈希不是简单的MessageDigest.getInstance(SHA-256)一遍就完事必须加盐。常见做法是每个用户生成随机盐值存储格式写成盐值$哈希值校验时从库里取出盐值再对输入密码做同样计算比较。这个项目如果只是课程设计可能没做这么深但你要在答辩里讲“安全设计”这块参数化 SQL HTTPS 加盐哈希这三条已经足够让评委认可。另外还有一个移动端特有的坑SQLite 文件默认放在/data/data/包名/databases/root 后可以读出来。所以交易记录这类敏感数据不要明文存库订单金额、手机号这类字段在落库前最好做一层 AES 加密密钥放在 Android Keystore 里而不是写死在 Java 代码里。见过太多同学习惯把密钥字符串写在类顶部的公共常量里反编译后一眼就能看到等于没加密。5. 从 gradlew.bat 到 APK构建、打包与产物解读5.1 项目里的构建产物清单怎么看RAR 包里的文件清单本身就是一份很好的构建课材料文件含义在构建链路中的位置gradlew.batGradle Wrapper 的 Windows 启动脚本统一构建工具版本无需手动装 Gradlejarlist.cache依赖 jar 列表缓存Gradle 解析依赖时生成用来加速下次构建resources.ap_编译后的资源包存放 layout、drawable、string 的二进制产物可被 aapt 解析MyObj.class / FileTool.class / Plugs.class编译出的字节码通用模型、工具集合、文件工具类的编译结果BaiduLocation.class百度定位封装类字节码定位 SDK 的二次封装产物Secondcheweichat_ssmClient.apk最终 APK签名打包后的安装包这里尤其注意 resources.ap_ 与 APK 的区别resources.ap_ 只是资源编译产物它不能安装APK 才是把资源、dex、签名合并后的最终安装包。能看到这些文件说明打包时保留了中间产物而 .class 文件说明我们可以直接用 javap -c 或 IDEA 反编译来看某个类的具体实现相当于附带了一套“源码阅读训练集”。5.2 一套最小构建流程拿到源码后想重新构建出 APK前提是 Android Studio 和 Android SDK 已存在。构建步骤按常规流程走# 1. 配置 SDK 路径local.properties 文件 echo sdk.dirC:/Users/name/AppData/Local/Android/Sdk local.properties # 2. Windows CMD 下执行 wrapper 脚本 gradlew.bat assembleDebug # 3. 产物路径 # app/build/outputs/apk/debug/app-debug.apk # 4. 无 AS 时命令行构建注意 JAVA_HOME set JAVA_HOMEC:/Program Files/Android/Android Studio/jbr逻辑说明local.properties 里的 sdk.dir 指向 Android SDK 目录找不到 SDK 时 Gradle 会直接报SDK location not foundgradlew.bat 会自动下载项目写死的 Gradle 版本和依赖所以第一次构建很慢是正常的。JAVA_HOME 如果指向 JDK 8 而项目要求 JDK 17会在编译阶段报Unsupported class file major version解决办法是把 JAVA_HOME 指到 Android Studio 自带的 JBR 目录。5.3 构建卡住与 SDK 版本报错的常见处理构建卡住九成发生在依赖下载阶段。第一步先看 jarlist.cache 是否已生成如果这个文件存在且很大说明依赖基本已就绪卡住的原因多是从远端仓库拉取新版本。国内网络环境下我一般会在项目的 build.gradle 里加上阿里云公共镜像仓库来加速依赖下载代码位置在根工程 build.gradle 的 repositories 里repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/central } google() mavenCentral() }注意点镜像仓库和官方仓库同时保留优先走国内镜像拉不到了再走官方能显著降低网络超时概率。另一个高频报错是tag number over 30 is not supported这是 AAPT 打包资源时某个 drawable 文件命名或格式不规范导致的资源编译失败处理方向是把图片统一转成 PNG 或 WebP并且检查文件名是否含“数字开头、中文、空格”等非法字符。SDK 版本不匹配的报错则看 app/build.gradle 里的 compileSdkVersion如果本机只装了 Android 12API 31而项目要求 API 33gradlew 会提示安装对应 platform。命令行下可以运行sdkmanager platforms;android-33来补装。sdkmanager 在 cmdline-tools/latest/bin 目录下Android Studio 安装 SDK 时通常已经包含。6. 进阶验证用 apksigner 和 adb 检查这个停车位 APK 能不能装、签名是谁6.1 校验签名与包名拿到 Secondcheweichat_ssmClient.apk 之后第一件事不是双击安装而是用构建工具链里的命令先做静态体检。签名校验用 apksigner它在 build-tools 目录下同时能确认 APK 使用了 v1 还是 v2 签名方案# 用 apksigner 查看签名指纹与签名方案 apksigner verify --print-certs Secondcheweichat_ssmClient.apk # 用 aapt 查看包名、版本号和权限声明 aapt dump badging Secondcheweichat_ssmClient.apk | grep -E package|sdkVersion|uses-permission输出里重点看三处package 行的包名是否和你 AndroidManifest 里一致sdkVersion 是否覆盖当前真机系统版本uses-permission 里是否有android.permission.ACCESS_FINE_LOCATION如果在手机设置里看到定位权限没声明后面 App 内地图定位必然失败。6.2 真机安装后需要紧盯的 5 个验证点静态检查通过后用 adb 装到 Android 11 及以上真机重点看五个点验证项命令通过标准安装是否成功adb install -r Secondcheweichat_ssmClient.apk返回 Success主界面能否拉起adb shell am start -n /.MainActivity无 ActivityNotFoundException定位权限是否正确请求adb shell pm check-permission android.permission.ACCESS_FINE_LOCATION输出 granted本地数据库是否创建adb shell run-as ls databases/出现 parking.db 或类似文件版本号是否与构建一致adb shell dumpsys package | grep -E versionNameversionCode这里额外强调 run-as 只在 debug 签名下可用如果是 release 签名需要用 adb root 或借助 Android Studio 的 Database Inspector 来观察 SQLite 表结构。验证时如果发现数据库文件没生成通常不是 SQLiteOpenHelper 的问题而是代码里还没调用过getWritableDatabase()SQLite 是懒创建的只有第一次写入才会落盘。一个更实用的技巧把 APK 签名指纹拿出来在百度地图开放平台里和申请 AK 时填的 SHA-256 比对。如果 App 能安装但地图一直黑屏、定位回调迟迟不来八成就是这个指纹不匹配。不用解压 APK直接执行keytool -printcert -jarfile Secondcheweichat_ssmClient.apk就能拿到证书指纹省去反复打包上传的麻烦。最后若 adb install 报 INSTALL_FAILED_UPDATE_INCOMPATIBLE说明手机上已有同名但签名不同的包执行adb uninstall package清掉旧包再装看到 dumpsys 里的 versionName 与工程配置一致就说明这台机器已经能正常识别这个共享停车位 App 的编译产物了。本文还有配套的精品资源点击获取
网站建设高端定制企业官网