diff --git a/positions/build.properties b/positions/build.properties index cbf7f51..2822afa 100644 --- a/positions/build.properties +++ b/positions/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 01 08:48:18 GMT 2025 +#Wed Oct 01 12:10:23 GMT 2025 stageCount=4 libraryProject= baseVersion=15.0 publishVersion=15.0.3 -buildCount=56 +buildCount=58 baseBetaVersion=15.0.4 diff --git a/positions/src/main/java/cc/winboll/studio/positions/App.java b/positions/src/main/java/cc/winboll/studio/positions/App.java index 702019a..216d70e 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/App.java +++ b/positions/src/main/java/cc/winboll/studio/positions/App.java @@ -49,6 +49,7 @@ public class App extends GlobalApplication { @Override public void onCreate() { super.onCreate(); + setIsDebuging(BuildConfig.DEBUG); // 初始化 Toast 框架 ToastUtils.init(this); diff --git a/positions/src/main/java/cc/winboll/studio/positions/services/DistanceRefreshService.java b/positions/src/main/java/cc/winboll/studio/positions/services/DistanceRefreshService.java index 8b13789..0e48a34 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/services/DistanceRefreshService.java +++ b/positions/src/main/java/cc/winboll/studio/positions/services/DistanceRefreshService.java @@ -1 +1,433 @@ - +//package cc.winboll.studio.positions.services; +// +///** +// * @Author ZhanGSKen&豆包大模型 +// * @Date 2025/09/30 19:53 +// * @Describe 位置距离服务:管理数据+定时计算距离+适配Adapter(Java 7 兼容)+ GPS信号加载 +// */ +//import android.app.Service; +//import android.content.Context; +//import android.content.Intent; +//import android.content.pm.PackageManager; +//import android.location.Location; +//import android.location.LocationListener; +//import android.location.LocationManager; +//import android.os.Binder; +//import android.os.Build; +//import android.os.Bundle; +//import android.os.IBinder; +//import android.os.Looper; +//import android.widget.Toast; +//import cc.winboll.studio.libappbase.LogUtils; +//import cc.winboll.studio.positions.adapters.PositionAdapter; +//import cc.winboll.studio.positions.models.AppConfigsModel; +//import cc.winboll.studio.positions.models.PositionModel; +//import cc.winboll.studio.positions.models.PositionTaskModel; +//import cc.winboll.studio.positions.utils.NotificationUtil; +//import java.util.ArrayList; +//import java.util.HashSet; +//import java.util.Iterator; +//import java.util.Set; +//import java.util.concurrent.Executors; +//import java.util.concurrent.ScheduledExecutorService; +//import java.util.concurrent.TimeUnit; +// +///** +// * 核心职责: +// * 1. 实现 PositionAdapter.DistanceServiceInterface 接口,解耦Adapter与服务 +// * 2. 单例式管理位置/任务数据,提供安全增删改查接口 +// * 3. 后台单线程定时计算可见位置距离,主线程回调更新UI +// * 4. 内置GPS信号加载(通过LocationManager实时获取位置,解决“等待GPS信号”问题) +// * 5. 服务启动时启动前台通知(保活后台GPS功能,符合系统规范) +// * 6. 严格Java 7语法:无Lambda/Stream,显式迭代器/匿名内部类 +// */ +//public class DistanceRefreshService extends Service { +// public static final String TAG = "DistanceRefreshService"; +// +// +// // 服务状态与配置 +// private boolean isServiceRunning = false; +// +// private static final int REFRESH_INTERVAL = 3; // 距离刷新间隔(秒) +// // 前台通知相关:记录是否已启动前台服务(避免重复调用startForeground) +// private boolean isForegroundServiceStarted = false; +// +// +// +// // 服务绑定与UI回调 +// private final IBinder mBinder = new DistanceBinder(); +// +// +// +// +// /** +// * 在主线程显示Toast(避免子线程无法显示Toast的问题) +// */ +// private void showToastOnMainThread(final String message) { +// if (Looper.myLooper() == Looper.getMainLooper()) { +// Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); +// } else { +// new android.os.Handler(Looper.getMainLooper()).post(new Runnable() { +// @Override +// public void run() { +// Toast.makeText(DistanceRefreshService.this, message, Toast.LENGTH_SHORT).show(); +// } +// }); +// } +// } +// +// // ---------------------- Binder 内部类(供外部绑定服务) ---------------------- +// public class DistanceBinder extends Binder { +// /** +// * 外部绑定后获取服务实例(安全暴露服务引用) +// */ +// public DistanceRefreshService getService() { +// return DistanceRefreshService.this; +// } +// } +// +// +// +// @Override +// public void onCreate() { +// super.onCreate(); +// +// // 初始化GPS管理器(提前获取系统服务,避免启动时延迟) +// mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); +// LogUtils.d(TAG, "服务 onCreate:初始化完成,等待启动命令"); +// run(); +// } +// +// @Override +// public int onStartCommand(Intent intent, int flags, int startId) { +// run(); +// AppConfigsModel bean = AppConfigsModel.loadBean(DistanceRefreshService.this, AppConfigsModel.class); +// boolean isEnableService = (bean == null) ? false : bean.isEnableMainService(); +// // 服务启用时返回START_STICKY(被杀死后尝试重启),禁用时返回默认值 +// return isEnableService ? Service.START_STICKY : super.onStartCommand(intent, flags, startId); +// } +// +// public void run() { +// // 仅服务未运行时启动(避免重复启动) +// if (!isServiceRunning) { +// isServiceRunning = true; +// +// +// +// startDistanceRefreshTask(); // 启动定时距离计算 +// startForegroundNotification(); // 启动前台通知 +// +// LogUtils.d(TAG, "服务 onStartCommand:启动成功,刷新间隔=" + REFRESH_INTERVAL + "秒,前台通知+GPS已启动"); +// } else { +// LogUtils.w(TAG, "服务 onStartCommand:已在运行,无需重复启动(前台通知:" + (isForegroundServiceStarted ? "已启动" : "未启动") + " | GPS:" + (isGpsEnabled ? "已开启" : "未开启") + ")"); +// // 异常场景恢复:补全未启动的组件 +// if (!isForegroundServiceStarted) { +// startForegroundNotification(); +// LogUtils.d(TAG, "服务 run:前台通知未启动,已恢复"); +// } +// if (isServiceRunning && !isGpsEnabled) { +// startGpsLocation(); +// LogUtils.d(TAG, "服务 run:GPS未启动,已恢复"); +// } +// } +// } +// +// @Override +// public IBinder onBind(Intent intent) { +// return null; // 按你的业务逻辑返回,无绑定需求则保留null +// //LogUtils.d(TAG, "服务 onBind:外部绑定成功(运行状态:" + (isServiceRunning ? "是" : "否") + " | GPS状态:" + (isGpsEnabled ? "可用" : "不可用") + ")"); +// //return mBinder; // 返回Binder实例,供外部获取服务 +// } +// +// /*@Override +// public boolean onUnbind(Intent intent) { +// LogUtils.d(TAG, "服务 onUnbind:外部解绑,清理回调与可见位置"); +// // 解绑后清理资源,避免内存泄漏 +// mDistanceReceiver = null; +// mVisiblePositionIds.clear(); +// // 解绑时不停止GPS(服务仍在后台运行,需持续获取位置) +// return super.onUnbind(intent); +// }*/ +// +// @Override +// public void onDestroy() { +// super.onDestroy(); +// +// LogUtils.d(TAG, "服务 onDestroy:销毁完成,资源已释放(GPS+前台通知+线程池)"); +// } +// +// // ---------------------- 前台服务通知管理(与GPS状态联动优化) ---------------------- +// /** +// * 启动前台服务通知(调用NotificationUtils创建通知,确保仅启动一次) +// */ +// private void startForegroundNotification() { +// // 1. 校验:避免重复调用startForeground(系统不允许重复启动) +// if (isForegroundServiceStarted) { +// LogUtils.w(TAG, "startForegroundNotification:前台通知已启动,无需重复执行"); +// return; +// } +// +// try { +//// 2. 初始化通知状态文本(根据GPS初始状态动态显示,避免固定“等待”) +// String initialStatus; +// if (isGpsPermissionGranted && isGpsEnabled) { +// initialStatus = "GPS已就绪,正在获取位置(刷新间隔" + REFRESH_INTERVAL + "秒)"; +// } else if (!isGpsPermissionGranted) { +// initialStatus = "缺少定位权限,无法获取GPS位置"; +// } else { +// initialStatus = "GPS未开启,请在设置中打开"; +// } +// +// +//// 5. 标记前台服务已启动 +// isForegroundServiceStarted = true; +// LogUtils.d(TAG, "startForegroundNotification:前台服务通知启动成功,初始状态:" + initialStatus); +// +// } catch (Exception e) { +//// 捕获异常(如上下文失效、通知渠道未创建) +// isForegroundServiceStarted = false; +// LogUtils.d(TAG, "startForegroundNotification:前台通知启动失败" + e); +// } +// } +// +// +// +// /** +// +// - 主线程回调Adapter更新UI(避免跨线程操作UI异常) +// */ +// /*private void notifyDistanceUpdateToUI(final String positionId) { +// if (Looper.myLooper() == Looper.getMainLooper()) { +// if (mDistanceReceiver != null) { +// mDistanceReceiver.onDistanceUpdate(positionId); +// } +// } else { +// new android.os.Handler(Looper.getMainLooper()).post(new Runnable() { +// @Override +// public void run() { +// if (mDistanceReceiver != null) { +// mDistanceReceiver.onDistanceUpdate(positionId); +// } +// } +// }); +// } +// }*/ +// +// +// +// +// +//// ---------------------- 实现 PositionAdapter.DistanceServiceInterface 接口 ---------------------- +// +// public ArrayList getPositionList() { +// if (!isServiceRunning) { +// LogUtils.w(TAG, "getPositionList:服务未运行,返回空列表"); +// return new ArrayList(); +// } +// return new ArrayList(mPositionList); +// } +// +// +// public ArrayList getPositionTasksList() { +// if (!isServiceRunning) { +// LogUtils.w(TAG, "getPositionTasksList:服务未运行,返回空列表"); +// return new ArrayList(); +// } +// return new ArrayList(mTaskList); +// } +// +// +// +// +// /*public void setOnDistanceUpdateReceiver(PositionAdapter.OnDistanceUpdateReceiver receiver) { +// this.mDistanceReceiver = receiver; +// LogUtils.d(TAG, "setOnDistanceUpdateReceiver:回调接收器已设置(" + (receiver != null ? "有效" : "无效") + ")"); +// }*/ +// +// public void addVisibleDistanceView(String positionId) { +// if (!isServiceRunning || positionId == null) { +// LogUtils.w(TAG, "addVisibleDistanceView:服务未运行/位置ID无效,添加失败"); +// return; +// } +// if (mVisiblePositionIds.add(positionId)) { +// LogUtils.d(TAG, "addVisibleDistanceView:添加成功(位置ID=" + positionId + ",当前可见数=" + mVisiblePositionIds.size() + ")"); +//// 新增:添加可见位置后,立即更新通知(显示最新可见数量) +// if (isForegroundServiceStarted && mCurrentGpsPosition != null) { +// syncGpsStatusToNotification(); +// } +// } +// } +// +// public void removeVisibleDistanceView(String positionId) { +// if (positionId == null) { +// LogUtils.w(TAG, "removeVisibleDistanceView:位置ID为空,移除失败"); +// return; +// } +// if (mVisiblePositionIds.remove(positionId)) { +// int remainingCount = mVisiblePositionIds.size(); +// LogUtils.d(TAG, "removeVisibleDistanceView:移除成功(位置ID=" + positionId + ",当前可见数=" + remainingCount + ")"); +//// 新增:移除可见位置后,更新通知(同步数量变化) +// if (isForegroundServiceStarted && mCurrentGpsPosition != null) { +// syncGpsStatusToNotification(); +// } +// } +// } +// +// public void clearVisibleDistanceViews() { +// mVisiblePositionIds.clear(); +// LogUtils.d(TAG, "clearVisibleDistanceViews:所有可见位置已清空"); +//// 新增:清空可见位置后,更新通知(提示计算暂停) +// if (isForegroundServiceStarted) { +// updateNotificationGpsStatus("无可见位置,距离计算暂停"); +// } +// } +// +//// ---------------------- 数据管理接口(修复原有语法错误+优化逻辑) ---------------------- +// /** +// +// - 获取服务运行状态 +// */ +// public boolean isServiceRunning() { +// return isServiceRunning; +// } +// +// /** +// +// - 添加位置(修复迭代器泛型缺失问题) +// */ +// public void addPosition(PositionModel position) { +// if (!isServiceRunning || position == null || position.getPositionId() == null) { +// LogUtils.w(TAG, "addPosition:服务未运行/数据无效,添加失败"); +// return; +// }// 修复:显式声明PositionModel泛型,避免类型转换警告 +// boolean isDuplicate = false; +// Iterator posIter = mPositionList.iterator(); +// while (posIter.hasNext()) { +// PositionModel existingPos = (PositionModel)posIter.next(); +// if (position.getPositionId().equals(existingPos.getPositionId())) { +// isDuplicate = true; +// break; +// } +// }if (!isDuplicate) { +// mPositionList.add(position); +// LogUtils.d(TAG, "addPosition:添加成功(位置ID=" + position.getPositionId() + ",总数=" + mPositionList.size() + ")"); +// } else { +// LogUtils.w(TAG, "addPosition:位置ID=" + position.getPositionId() + "已存在,添加失败"); +// } +// } +// +// /** +// +// - 删除位置(修复任务删除时的类型转换错误) +// */ +// public void removePosition(String positionId) { +// if (!isServiceRunning || positionId == null) { +// LogUtils.w(TAG, "removePosition:服务未运行/位置ID无效,删除失败"); +// return; +// }// 1. 删除位置 +// boolean isRemoved = false; +// Iterator posIter = mPositionList.iterator(); +// while (posIter.hasNext()) { +// PositionModel pos = (PositionModel)posIter.next(); +// if (positionId.equals(pos.getPositionId())) { +// posIter.remove(); +// isRemoved = true; +// break; +// } +// }if (isRemoved) { +//// 修复:任务列表迭代时用PositionTaskModel泛型(原错误用PositionModel导致转换失败) +// Iterator taskIter = mTaskList.iterator(); +// while (taskIter.hasNext()) { +// PositionTaskModel task = (PositionTaskModel)taskIter.next(); +// if (positionId.equals(task.getPositionId())) { +// taskIter.remove(); +// } +// }// 3. 移除可见位置 +// mVisiblePositionIds.remove(positionId); +// LogUtils.d(TAG, "removePosition:删除成功(位置ID=" + positionId + ",剩余位置数=" + mPositionList.size() + ",剩余任务数=" + mTaskList.size() + ")"); +// } else { +// LogUtils.w(TAG, "removePosition:位置ID=" + positionId + "不存在,删除失败"); +// } +// } +// +// /** +// +// - 更新位置信息(修复代码格式+迭代器泛型) +// */ +// public void updatePosition(PositionModel updatedPosition) { +// if (!isServiceRunning || updatedPosition == null || updatedPosition.getPositionId() == null) { +// LogUtils.w(TAG, "updatePosition:服务未运行/数据无效,更新失败"); +// return; +// }boolean isUpdated = false; +// Iterator posIter = mPositionList.iterator(); +// while (posIter.hasNext()) { +// PositionModel pos = (PositionModel)posIter.next(); +// if (updatedPosition.getPositionId().equals(pos.getPositionId())) { +// pos.setMemo(updatedPosition.getMemo()); +// pos.setIsEnableRealPositionDistance(updatedPosition.isEnableRealPositionDistance()); +// if (!updatedPosition.isEnableRealPositionDistance()) { +// pos.setRealPositionDistance(-1); +// //notifyDistanceUpdateToUI(pos.getPositionId()); +// } +// isUpdated = true; +// break; +// } +// }if (isUpdated) { +// LogUtils.d(TAG, "updatePosition:更新成功(位置ID=" + updatedPosition.getPositionId() + ")"); +// } else { +// LogUtils.w(TAG, "updatePosition:位置ID=" + updatedPosition.getPositionId() + "不存在,更新失败"); +// } +// } +// +// /** +// +// - 同步任务列表(修复泛型缺失+代码格式) +// */ +// public void syncAllPositionTasks(ArrayList tasks) { +// if (!isServiceRunning || tasks == null) { +// LogUtils.w(TAG, "syncAllPositionTasks:服务未运行/任务列表为空,同步失败"); +// return; +// }// 1. 清空旧任务 +// mTaskList.clear(); +//// 2. 添加新任务(修复泛型+去重逻辑) +// Set taskIdSet = new HashSet(); +// Iterator taskIter = tasks.iterator(); +// while (taskIter.hasNext()) { +// PositionTaskModel task = (PositionTaskModel)taskIter.next(); +// if (task != null && task.getTaskId() != null && !taskIdSet.contains(task.getTaskId())) { +// taskIdSet.add(task.getTaskId()); +// mTaskList.add(task); +// } +// }LogUtils.d(TAG, "syncAllPositionTasks:同步成功(接收任务数=" + tasks.size() + ",去重后=" + mTaskList.size() + ")"); +// } +// +// +// +// +//// ---------------------- 补充:修复LocationProvider引用缺失问题(避免编译错误) ---------------------- +//// 注:原代码中onStatusChanged使用LocationProvider枚举,需补充静态导入或显式声明 +//// 此处通过内部静态类定义,解决系统API引用问题(兼容Java 7语法) +// private static class LocationProvider { +// public static final int AVAILABLE = 2; +// public static final int OUT_OF_SERVICE = 0; +// public static final int TEMPORARILY_UNAVAILABLE = 1; +// } +// +//// ---------------------- 补充:Context引用工具(避免服务销毁后Context失效) ---------------------- +// /*private Context getSafeContext() { +// // 服务未销毁时返回自身Context,已销毁时返回应用Context(避免内存泄漏) +// if (isDestroyed()) { +// return getApplicationContext(); +// } +// return this; +// }*/ +// +//// 注:isDestroyed()为API 17+方法,若需兼容更低版本,可添加版本判断 +// /*private boolean isDestroyed() { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { +// return super.isDestroyed(); +// } +// // 低版本通过状态标记间接判断(服务销毁时会置为false) +// return !isServiceRunning && !isForegroundServiceStarted; +// }*/ +//} diff --git a/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java b/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java index 1b4c6f3..c353b97 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java +++ b/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java @@ -410,6 +410,7 @@ public class MainService extends Service { * 初始化GPS位置监听器:系统GPS位置变化时触发,实时更新当前位置 */ private void initGpsLocationListener() { + LogUtils.d(TAG, "initGpsLocationListener()"); // Java 7 匿名内部类实现 LocationListener(接收GPS位置回调) mGpsLocationListener = new LocationListener() { // GPS位置发生变化时回调(核心:更新当前GPS位置)