安卓职工考勤APP开发实战:定位、围栏与防作弊实现
发布时间:2026/9/26 19:10:56来源:尧图网络
简介这是一份基于Android Studio开发的职工考勤APP完整项目源码包面向具备一定Android基础、希望将综合技能落地为真实考勤场景的移动开发学习者。项目覆盖员工信息管理、上下班考勤录入、异常记录、出勤统计、通知提醒与权限分级等核心模块代码结构完整可直接在Android Studio中打开运行也可作为课程设计或毕业设计二次开发。资源共547个文件压缩包大小约14.88MB包含XML界面布局、Java业务源码、Gradle构建脚本及APK安装包等主要类型整体工程配置清晰便于按模块分段查阅。目前已有692人学习下载。借助该包可重点掌握SQLite数据库操作、Material Design组件使用、Notification通知机制及MPAndroidChart图表集成等关键技术同时了解异常考勤统计与权限管理的完整实现思路是一份兼顾功能演示与实践参考的优质Android项目。1. 安卓职工考勤APP.7z一个压缩包背后装的是一套打卡与审批逻辑拿到「安卓职工考勤APP.7z」这类压缩包时第一件事不是急着找安装包而是先解压看结构它里面放的可能是完整的 Android Studio 工程也可能是一个已经签名的 APK 加配套的签名文件和说明文档。如果只有 APK 没有工程后续改打卡规则就得走逆向成本直接上一个台阶有工程文件才谈得上按自己公司的考勤制度去调。这个 APP 的核心价值很简单把「员工在哪儿、什么时候、用什么方式打卡」变成一条可信的记录再往后接上迟到早退、请假审批和月度报表。适合接手这类项目的安卓开发、要给外包需求做验收的负责人以及想用最少成本在企业内部跑通考勤系统的一线工程师。下面按「拆需求 → 定方案 → 跑通 → 避坑 → 交付」这条路径讲一套可以直接照做的完整做法。2. 先把打卡方式定死三种主流方案怎么选技术栈怎么定考勤 APP 的所有功能都围绕「打卡」展开打卡方式定不下来后面的围栏判断、数据表设计、防作弊全是空中楼阁。这一章先把选型问题解决掉。2.1 WiFi打卡、GPS打卡、扫码打卡适用场景与坑位对比常见的打卡方式就三种各有各的适用边界WiFi 打卡读取当前手机连接的 WiFi 的 SSID/BSSID与后台配置的「公司 WiFi」白名单比对匹配即打卡成功。适合门店、办公室这类人员位置相对固定的场景室内定位漂移影响不到它。GPS 打卡通过定位 SDK 拿到经纬度后端计算与公司坐标的距离落在围栏半径内才算有效。适合外勤、巡检、工地等人员需要移动的场景。扫码打卡扫前台或会议室张贴的二维码完成打卡本质是「在场证明」适合临时打卡点、访客或会议室签到不适合作为全员日常打卡手段。三者可以混合用。我经手的项目里最稳的组合是办公室默认 WiFi 打卡外勤员工走 GPS 打卡两种方式打出来的记录在数据库里用type字段区分报表统计时互不干扰。不要把三种方式做成「哪个先触发算哪个」否则员工在办公室连着 WiFi 同时开着定位会打出两条不同来源的记录对账时非常痛苦。2.2 技术栈选型原生 Android 和 uni-app 差在哪技术栈选择取决于一个核心问题这次交付要不要覆盖 iOS。如果领导明确说「先做安卓iOS 后面再说」我建议直接上原生 AndroidKotlin 为主。原因在于考勤 APP 绕不开定位保活、前台服务、系统权限适配这几件事原生的LocationManager和前台服务 API 最直接出问题好查。统计显示这类内部工具 APP 八成以上是纯安卓交付。如果要求一套代码同时出安卓和 iOS常见做法是选 uni-app 这类跨端框架。它自带plus.geolocation定位模块写一次逻辑双端跑界面用 Vue 语法就能写出包速度快。代价也明显后台定位策略受框架限制厂商 ROM 的保活适配要做更多兼容调底层传感器时还是得写原生插件绕一圈。所以决策准则我一般这么定纯安卓、功能以打卡和审批为主、后续可能要加人脸比对原生。双端都要、预算紧、工期短、定位精度要求不苛刻uni-app。已有现成 H5 考勤系统只想套个壳uni-app 的 WebView 方案优先但放弃复杂的后台定位需求。选型阶段最好把「防作弊」「异常申诉」这两项也纳入评估它们决定了开发量是 2 周还是 2 个月。下面这张表可以直接拿去和同事对齐对比维度原生 AndroidKotlinuni-app定位精度控制高直接调 LocationManager / FusedLocationProvider中依赖 plus.geolocation 封装后台保活与前台服务灵活可自控受框架生命周期约束双端复用只出安卓安卓 iOS 一套代码工期单人2~3 周1~2 周后期维护成本低代码可控中框架升级可能引入兼容问题选完技术栈下一步就是把最小打卡流程跑通。原生工程的可复制性最强下面以原生 Android 为例从建工程开始讲。3. 把最小打卡流程跑通工程权限、GPS定位与本地落库这一章解决的是「代码怎么写」。一个能演示的考勤 APP 最少要覆盖三件事拿到定位权限、获取经纬度、把打卡记录写进本地库。后台接口可以后面再接这三步先跑通整个 APP 的骨架就立住了。3.1 新建工程与权限声明Android 6 到 14 的权限写法差异新建 Android Studio 工程时包名建议直接命名为com.company.attendance这类和业务强相关的名字不要用com.example后面改包名会牵连签名和第三方 SDK。工程创建好后第一件事是打开AndroidManifest.xml声明权限manifest xmlns:androidhttp://schemas.android.com/apk/res/android !-- 定位权限Android 6.0 以上需要运行时申请 -- uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION / uses-permission android:nameandroid.permission.ACCESS_COARSE_LOCATION / !-- 前台服务Android 8.0 以上定位型服务必须声明 -- uses-permission android:nameandroid.permission.FOREGROUND_SERVICE / uses-permission android:nameandroid.permission.FOREGROUND_SERVICE_LOCATION / !-- 网络权限上报打卡数据和获取服务器时间 -- uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE / application android:label职工考勤 android:themestyle/Theme.Attendance !-- 前台服务组件定位保活要用 -- service android:name.service.LocationService android:foregroundServiceTypelocation / /application /manifest权限声明里有三个容易被忽略的版本差异。第一ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION在 Android 6.0API 23之后必须在运行时单独申请不能只写在 Manifest 里。第二Android 10API 29开始后台定位需要额外申请ACCESS_BACKGROUND_LOCATION但考勤打卡场景几乎不需要后台持续定位只在用户按下打卡按钮时取一次位置即可所以这个权限不申请更好申请了反而触发应用市场审核的隐私合规问题。第三Android 14API 34对前台服务类型做了强校验如果 service 声明了foregroundServiceTypelocation运行时必须真的调用startForeground并传入对应的类型否则直接崩ForegroundServiceTypeNotAllowedException。运行时权限请求的代码写法很固定用ActivityResultContracts.RequestPermission是当前最干净的方案class CheckInActivity : AppCompatActivity() { private val locationPermissionLauncher registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted - if (granted) { startLocationService() } else { Toast.makeText(this, 定位权限被拒绝无法完成打卡, Toast.LENGTH_LONG).show() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 检查是否已有权限没有则发起请求 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ! PackageManager.PERMISSION_GRANTED) { locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION) } else { startLocationService() } } private fun startLocationService() { // 启动前台定位服务内部执行一次定位并回调结果 startForegroundService(Intent(this, LocationService::class.java)) } }这段逻辑说明几个关键点。registerForActivityResult是 AndroidX Activity 1.2.0 之后推荐的权限回调方式替代了已经废弃的onRequestPermissionsResult回调一定在主线程可以安全刷新 UI。权限被拒绝后没有走「再次申请」的逻辑这是故意的——考勤 APP 在用户明确拒绝后反复弹权限框会被系统当成恶意应用正确做法是提示用户去系统设置里手动开启。前台服务启动用startForegroundService而不是startService这是 Android 8.0API 26之后的规定前者要求服务在 5 秒内调用startForeground否则抛RemoteServiceException这是新手最容易翻车的地方之一。3.2 实现GPS定位打卡获取经纬度的代码与参数设定打卡场景只需要「点下按钮时拿一个可信的位置」不需要持续监听。所以定位服务里用LocationManager.requestSingleUpdate比requestLocationUpdates更合适——只回调一次拿完即走省电省心。class LocationService : Service() { private lateinit var locationManager: LocationManager override fun onCreate() { super.onCreate() locationManager getSystemService(Context.LOCATION_SERVICE) as LocationManager startForeground( NOTIFICATION_ID, Notification.Builder(this, location_channel) .setContentTitle(考勤定位中) .setContentText(正在获取当前位置用于打卡) .setSmallIcon(android.R.drawable.ic_menu_mylocation) .build() ) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // 设置定位请求参数单次定位 val request LocationRequest.Builder(1000L) .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY) .build() if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) PackageManager.PERMISSION_GRANTED) { locationManager.getCurrentLocation( LocationManager.GPS_PROVIDER, null, mainExecutor, ::onLocationResult ) } return START_NOT_STICKY } private fun onLocationResult(location: Location) { val latitude location.latitude val longitude location.longitude val accuracy location.accuracy // 这里把经纬度回传打卡页面同时触发本地落库 saveCheckInRecord(latitude, longitude, accuracy) stopSelf() } }这里的参数设置有几个讲究。LocationRequest.Builder(1000L)的 1000L 表示最大等待时间 1 秒超过 1 秒没拿到 GPS 结果就会尝试用网络定位结果兜底。QUALITY_HIGH_ACCURACY要求 GPS、WiFi、基站三者都参与定位精度一般能到 10~30 米如果改成QUALITY_BALANCED_POWER_ACCURACY耗电会低一些但室内精度可能掉到 100 米以上打卡围栏就形同虚设。getCurrentLocation是 Android 12API 31新增的 API比老的requestSingleUpdate好在不需要手动注册和注销监听器拿不到结果时也不会挂在回调里系统会自动清理。一个重要的兜底逻辑如果getCurrentLocation超时没有回调很多实现就直接转getLastKnownLocation。这个做法只能当备用不能当主路径——lastKnownLocation可能是几个小时前的缓存位置员工在公司打卡却打出家里坐标就是它干的。我的处理是缓存位置仅在 accuracy 小于 200 米且时间戳在 5 分钟之内时才采用否则提示用户移动到开阔区域重试。3.3 考勤记录落库SQLite最小表结构与查询定位拿到之后打卡记录必须先落本地再异步上报服务器。这样即使断网打卡动作也不会丢后续补传就行。本地库用 SQLite 足够不需要引入 GreenDAO 或 Room 这种重框架单表 CRUD 用原生 API 写起来反而更直白。-- 考勤记录表 CREATE TABLE IF NOT EXISTS checkin_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, employee_id TEXT NOT NULL, -- 员工工号 checkin_date TEXT NOT NULL, -- 打卡日期格式 yyyy-MM-dd checkin_time TEXT NOT NULL, -- 打卡时间格式 yyyy-MM-dd HH:mm:ss type INTEGER NOT NULL DEFAULT 0, -- 0上班 1下班 source INTEGER NOT NULL DEFAULT 0, -- 0WiFi 1GPS 2扫码 latitude REAL, -- GPS纬度 longitude REAL, -- GPS经度 accuracy REAL, -- 定位精度单位米 address TEXT, -- 逆地理编码地址 device_id TEXT, -- 设备标识用于异常排查 status INTEGER NOT NULL DEFAULT 1, -- 1有效 0撤销 UNIQUE (employee_id, checkin_date, type) );表结构的关键设计在UNIQUE (employee_id, checkin_date, type)这条约束上。它保证了同一员工同一天只能有一条上班记录和一条下班记录前端连点两次打卡按钮、网络重试导致的重复提交都会被数据库直接挡掉。这个约束比在应用层做判断可靠得多是防重复打卡的第一道防线我从实战教训里总结出来的早期没有加这个约束测试阶段就出现过同一个人一天 17 条打卡记录的惨状。插入数据时配合INSERT OR REPLACE使用语义是「存在就替换不存在就插入」INSERT OR REPLACE INTO checkin_record (employee_id, checkin_date, checkin_time, type, source, latitude, longitude, accuracy, address, device_id, status) VALUES (EMP001, 2024-11-20, 2024-11-20 09:02:15, 0, 1, 31.2304, 121.4737, 12.5, XX路XX号, device_sn_123, 1);参数说明source字段很重要报表统计时要按来源分开核对WiFi 打卡和 GPS 打卡的围栏规则不同混在一起做分析会得出错误结论。accuracy字段也别省它是判断这次打卡是否可信的原始依据后面做围栏判定时要和公司坐标的距离一起参与运算。device_id用于发现「一台设备给五个人打卡」的作弊行为虽然不能完全杜绝但至少能给到排查线索。落库完成最小打卡闭环就成立了一半。另一半是「怎么判定这次打卡在不在公司范围内」这涉及经纬度计算和围栏设计是考勤 APP 里最容易出逻辑漏洞的地方下一章专门拆开讲。4. 打卡距离与围栏判定经纬度换算不能只靠直觉打卡页面显示「定位成功」和「判定打卡有效」之间隔着一整个计算层。很多半路出家的实现直接拿两个经纬度做绝对差值判断或者用地图 SDK 的distanceBetween却没有正确设置坐标点结果就是员工明明在公司楼下却打不上卡。这一章把计算和判定写明白。4.1 Haversine公式距离误差从哪来算两个经纬度点之间的距离工程上最常用的不是 Vincenty 算法那种高精度模型而是 Haversine 公式它把地球当成半径 6371 公里的球体来处理在公里级距离上误差可以忽略代码也好写import math def haversine_distance(lat1, lng1, lat2, lng2): # 地球半径单位公里 R 6371.0 # 角度转弧度 phi1 math.radians(lat1) phi2 math.radians(lat2) delta_phi math.radians(lat2 - lat1) delta_lambda math.radians(lng2 - lng1) # Haversine 核心公式 a math.sin(delta_phi / 2) ** 2 \ math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda / 2) ** 2 c 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) # 返回公里数乘 1000 换成米 return R * c * 1000 # 示例公司坐标 (31.2304, 121.4737)员工定位 (31.2310, 121.4740) distance haversine_distance(31.2304, 121.4737, 31.2310, 121.4740) print(f距离: {distance:.1f} 米)参数说明R 6371.0是地球平均半径如果追求再精确一点可以用 6371.0088但对考勤围栏这种几十到几百米的判定场景0.0088 公里的差别完全无感。math.radians把十进制度数转成弧度这个是新手最容易忘的忘掉的话结果会偏差几十倍。两点距离在 1 公里以内时Haversine 和 Vincenty 的差别通常在毫米级完全可以忽略只有当两点跨越大几个城市做「最近的考勤点匹配」时才会建议升级算法。有一个比公式更值得注意的坑很多在线地图 SDK 提供的是 GCJ-02 坐标系而LocationManager拿到的是 WGS-84 原始坐标两种坐标系在同一个城市偏移约 300~500 米。如果公司坐标是从地图上拾取的 GCJ-02 点员工定位是 WGS-84直接套 Haversine 公式会得出「员工在 300 米外」的结论围栏设再大都没用。正确做法是统一坐标系要么公司坐标也用官方定位 SDK 打一次点取 WGS-84要么在后端做坐标转换。我的习惯是公司坐标用「现场拿同一台测试机定位三次取平均」保证员工端和公司端坐标系完全一致。4.2 围栏半径与防作弊50米还是200米偏移阈值怎么设围栏半径的设定是考勤规则里最敏感的参数没有之一。设小了员工天天投诉打不上卡设大了打卡形同虚设。我踩过一轮坑后的经验值场所类型推荐围栏半径理由写字楼办公室1~3层室内150~200 米室内 GPS 漂移 30~80 米很常见再叠加坐标系偏移100 米以下会让部分员工无法打卡园区/工厂室外为主100~150 米室外 GPS 精度较好但大型厂区边界不规整门店/商铺80~100 米门店密集区域半径过大容易和隔壁店混打工地/外勤点位200~300 米外勤点位往往无明确门牌需要更大容错空间围栏判定逻辑本身很直白用 Haversine 算出员工定位到公司坐标的距离小于等于围栏半径则通过。但防作弊不能只靠这一个条件常见的作弊手段和对应的检测方法模拟定位Android 开发者选项里可以伪造位置。检测手段是检查location.isFromMockProviderAPI 31 之后模拟位置信息被收进Location的isMock标记读取后置为无效打卡。缓存定位拿到的是几小时前的旧位置因为 accuracy 看着正常距离也在围栏内就放行了。检测手段是检查定位时间戳超过 5 分钟直接拒绝。坐标偏移注入通过 Root 设备改定位。单靠客户端很难完全封死常见做法是连续取 3 次定位每次间隔 5 秒偏差超过 50 米就判异常让用户重试。中间还有一个伪造和真实的灰色地带必须提醒室内定位在电梯、地下停车场会出现「假漂移」员工明明在工位定位却显示公司在隔壁楼。这种情况不能简单归为作弊否则员工体验会很差。我的处理方案是连续两次定位都落在围栏外且在 500 米内才提示「定位偏差过大请确认 GPS 信号良好」给员工一个重新打卡的机会。4.3 迟到早退判定把时间规则做成状态机打卡通过围栏校验只是第一步考勤结果还取决于时间规则的判定。时间比较这块必须统一用服务器时间不能信手机本地时间道理很简单手机时间用户可以改改了就能把迟到的打卡时间伪造成准点。判定逻辑可以抽象成一个状态机每天每位员工的考勤状态从「未打卡」开始按打卡事件转移状态触发条件转移结果未打卡上班时间前完成打卡正常上班未打卡上班时间后 5 分钟内完成打卡迟到宽容期内可申诉未打卡超过宽容期才打卡迟到正常上班下班时间后完成打卡正常下班正常上班下班时间前完成打卡早退迟到/正常全天只有一次打卡当日缺卡具体实现上后端只需要把「上班截止时间」「下班开始时间」「宽容分钟数」做成可配置项每次打卡事件进来算一次状态转移结果落库到attendance_result表。这里最容易被忽略的是节假日规则不要自己维护节假日表直接对接国家的节假日接口或者让行政在后台系统里导入。自己维护一份节假日表大概率会在某个调休周末漏掉然后一整月考勤数据全错这种教训一次就够。状态机跑通后考勤核心链路就完整了定位 → 落库 → 距离判定 → 时间判定 → 生成考勤结果。到这里功能层面能演示但真实环境里有一堆比功能更难缠的问题下一章集中排雷。5. 考勤APP避坑指南定位、权限、保活与多ROM的5个坑考勤 APP 是典型的「功能简单、环境复杂」项目。代码量不大但真机上的问题各有各的玄学这里挑 5 个出现频率最高、影响面最大的坑按「现象 → 原因 → 解决」的套路记录都是可以直接抄作业的排查路径。5.1 打卡页一直转圈定位结果就是不回来现象在室内点打卡按钮页面持续显示「定位中」一分钟过去还是没有结果室外一切正常。原因LocationManager在室内收不到足够的 GPS 卫星信号而代码里只注册了GPS_PROVIDER没有启用网络定位兜底。GPS 在室内、电梯、地下车库穿透力差这是物理限制不是代码 bug。解决定位请求里同时注册GPS_PROVIDER和NETWORK_PROVIDER让系统按可用性自动选择同时设置 10~15 秒的定位超时超时后主动提示「当前环境 GPS 信号弱请靠近窗边或使用 WiFi 打卡」。不要无限制等下去考勤场景用户没有耐心也没有必要为了一个打卡等 30 秒。这个坑在第一版上线后让半个办公室的人打不上卡就是因为只认 GPS 打死不兜底。5.2 华为、小米后台运行一段时间后打卡直接失败现象APP 切到后台几个小时后再打开时定位服务已经死了打卡提示「定位服务未运行」。原因国产 ROM 的省电策略会对长时间后台运行的应用做冻结。华为的「启动管理」、小米的「神隐模式」、OPPO/vivo 的「待机优化」都会主动杀掉没有在白名单里的第三方应用。这和 APP 本身逻辑无关是厂商级的行为。解决三层配合。第一前台服务常驻并配常驻通知降低被系统清理的概率第二引导用户在 ROM 设置里把 APP 加入「不受限制」或「允许后台运行」的白名单第三打卡逻辑本身做成「按需启动」用户进入打卡页面时动态拉起定位服务而不是依赖后台长跑。第三点才是我真正推荐的方案——考勤打卡本来就应该是前台主动行为别和 ROM 的省电策略硬刚顺着系统的规则做反而最稳。5.3 同一员工同一天打出两条上班卡现象员工反馈自己只打了一次卡后台却查到两条上班记录一条 09:01一条 09:02。原因网络慢时用户手抖多点了一次按钮或者 APP 端上报请求超时后自动重试结果第一次请求其实已经到服务器了只是响应丢了。接口层面没有做幂等数据库层面没有唯一约束两条记录就都落库了。解决上两把锁。第一把锁在数据库层就是我前面讲的UNIQUE (employee_id, checkin_date, type)保证物理上不可能出现同一天同类型的两条记录第二把锁在接口层客户端生成一个request_idUUID服务端用request_id去重同一个request_id的请求只处理一次。两把锁缺一不可数据库锁防的是漏网记录接口幂等防的是重复请求。5.4 手机时间改慢 10 分钟打卡系统就判定准点现象测试阶段发现把手机系统时间调慢后迟到的人也能打出「准点卡」。原因客户端拿System.currentTimeMillis()作为打卡时间上报而后端没有校验这个时间直接把它当成打卡时间存进库。手机时间完全可控时间戳天然不可信。解决打卡时间的唯一权威来源是服务器时间。客户端只上报「打卡动作发生」服务端在收到请求那一刻用自己的时钟生成checkin_time。如果客户端需要显示打卡时间可以先通过一个时间同步接口拿到服务器时间和本地时间的差值展示时加上这个偏移但服务端写入数据库时永远只认自己的时间。这个坑在早期版本翻车过之后我再也没让任何客户端时间戳进入过考勤结果表。5.5 抓包工具看得到请求APP 里却报网络错误现象开发调试时用抓包工具能正常看到 APP 发出的请求和响应但 APP 内部一直提示网络异常日志里报 SSL 错误。原因Android 7.0API 24之后系统默认不信任用户级别的 CA 证书。抓包工具如 Charles、mitmproxy 之类安装的是用户证书APP 的OkHttp默认只信任系统证书两者对不上握手直接失败。解决调试阶段在res/xml/network_security_config.xml里显式放开用户证书信任network-security-config domain-config cleartextTrafficPermittedfalse domain includeSubdomainstrueapi.example.com/domain !-- debug 包允许信任用户证书release 包不配置此项 -- trust-anchors certificates srcsystem / certificates srcuser / /trust-anchors /domain-config /network-security-config参数说明domain节点里的api.example.com要换成自己后端接口的真实域名只对这个域名放开证书信任不要写全放行的配置否则上线后会有中间人攻击风险。更规范的做法是让build.gradle里的 debug 构建和 release 构建使用不同的network_security_config文件release 包不包含trust-anchors的 user 证书信任配置。这是工程化层面的正统解法既保证调试效率又不牺牲生产环境的安全边界。6. 从工程到7z交付包签名验证、机型适配与上线后的检查清单功能全部调试完成后最后一步是把工程或 APK 打成 7z 交付出去。打包前先做一次 clean 构建把 debug 签名替换成正式的 release 签名。用apksigner验证签名是否有效命令行是apksigner verify --print-certs app-release.apk能看到证书的 SHA-256 指纹才算签名成功。这里提醒一句签名用的 keystore 文件一定要备份并记录密码丢失之后 APP 只能换包名重新上架用户数据全部归零这种后悔药是没有的。工程文件打 7z 时要把build/、.gradle/、.idea/这类本地生成目录排除掉只保留源码、资源和 Gradle Wrapper这样对方解压后直接能编译7z a attendance_app.7z ./project \ -xr!build -xr!.gradle -xr!.idea \ -xr!local.properties -xr!*.iml参数说明-xr!表示递归排除指定目录或模式排除local.properties是因为里面含本机 SDK 路径其他人拿到会直接编译报错保留gradlew和gradle/wrapper/是为了让接手的人用固定版本构建不会因为 Gradle 版本不一致而踩坑。对 APK 的分发也要把versionCode和versionName写在变更说明里内测时靠版本号定位问题。上线后的验证我习惯固定在发版清单里一台不插 SIM 卡的真机跑 GPS 打卡一台只连 WiFi 的机器跑 WiFi 打卡再加一台安卓虚拟平台跑低版本系统兼容性检查。真机上用adb shell dumpsys location确认定位 provider 状态、adb logcat抓崩溃日志模拟器上如果访问不了内网打卡接口检查网络连接方式改成桥接模式就能通到内网。这套检查做完再考虑推给全员使用。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网