From bf52f86b3df14132dff72b442f78091b973c6a1d Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 5 Nov 2025 16:50:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BF=90=E5=8A=A8=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=A3=80=E6=B5=8B=E6=A8=A1=E5=9D=97=EF=BC=8C=E8=BF=90?= =?UTF-8?q?=E5=8A=A8=E7=8A=B6=E6=80=81=E6=A3=80=E6=B5=8B=E9=98=88=E5=80=BC?= =?UTF-8?q?=E6=A0=87=E5=87=86=E6=9C=AA=E6=B5=8B=E5=AE=9A=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- positions/build.properties | 4 +- .../positions/services/MainService.java | 441 ++++++++---------- .../positions/utils/LocalMotionDetector.java | 168 +++++++ 3 files changed, 368 insertions(+), 245 deletions(-) create mode 100644 positions/src/main/java/cc/winboll/studio/positions/utils/LocalMotionDetector.java diff --git a/positions/build.properties b/positions/build.properties index f7130a2..83554ea 100644 --- a/positions/build.properties +++ b/positions/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Nov 04 12:54:59 GMT 2025 +#Wed Nov 05 08:47:36 GMT 2025 stageCount=18 libraryProject= baseVersion=15.0 publishVersion=15.0.17 -buildCount=20 +buildCount=29 baseBetaVersion=15.0.18 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 e1c7293..c68884f 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 @@ -3,13 +3,12 @@ package cc.winboll.studio.positions.services; /** * @Author ZhanGSKen * @Date 2024/07/19 14:30:57 - * @Describe 应用主要服务组件类(适配运动状态GPS控制) + * @Describe 应用主要服务组件类(适配运动状态GPS控制+静止10秒切换) */ import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.location.Location; @@ -25,7 +24,6 @@ import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.positions.models.PositionModel; import cc.winboll.studio.positions.models.PositionTaskModel; -import cc.winboll.studio.positions.receivers.MotionStatusReceiver; import cc.winboll.studio.positions.utils.AppConfigsUtil; import cc.winboll.studio.positions.utils.DistanceCalculatorUtil; import cc.winboll.studio.positions.utils.NotificationUtil; @@ -38,83 +36,125 @@ import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import cc.winboll.studio.positions.utils.LocalMotionDetector; -public class MainService extends Service { +public class MainService extends Service implements LocalMotionDetector.MotionStatusCallback { public static final String TAG = "MainService"; public static final String EXTRA_IS_SETTING_TO_ENABLE = "EXTRA_IS_SETTING_TO_ENABLE"; - // ---------------------- 定时器相关变量 ---------------------- - // 静止时GPS定时任务变量(Java 7 显式声明) - private ScheduledExecutorService gpsStaticTimer; // 静止时GPS定时获取线程池 - private volatile boolean isGpsListening = false; // GPS是否处于持续监听状态 - private static final long GPS_STATIC_DURATION = 3; // 静止时GPS单次获取超时时间(秒) + // ---------------------- 核心:LocalMotionDetector 实例(唯一运动状态检测工具) ---------------------- + private LocalMotionDetector mMotionDetector; // 本地运动状态检测器 + private static final long GPS_STATIC_INTERVAL = 30; // 静止时GPS间隔(30秒) - // GPS监听接口(Java 7 标准接口定义) + // ---------------------- 定时器相关变量 ---------------------- + private ScheduledExecutorService gpsStaticTimer; // 静止时GPS定时任务 + private volatile boolean isGpsListening = false; // GPS是否持续监听 + private static final long GPS_STATIC_DURATION = 3; // 单次GPS获取超时(秒) + + // ---------------------- 静止延迟切换配置(核心功能) ---------------------- + private static final long STATIC_DELAY_MS = 10 * 1000; // 静止10秒后切换 + private Handler staticDelayHandler; // 延迟执行切换逻辑的Handler + private volatile boolean isDelaySwitching = false; // 标记是否正在等待延迟切换 + + // GPS监听接口 public interface GpsUpdateListener { void onGpsPositionUpdated(PositionModel currentGpsPos); void onGpsStatusChanged(String status); } - // 任务更新监听接口(Java 7 风格) + // 任务更新监听接口 public interface TaskUpdateListener { void onTaskUpdated(); } - // 监听管理(弱引用+线程安全集合,适配Java 7) + // 监听管理 private final Set> mGpsListeners = new HashSet>(); private final Set> mTaskListeners = new HashSet>(); - private final Object mListenerLock = new Object(); // 监听操作锁 + private final Object mListenerLock = new Object(); - // 原有核心变量(Java 7 显式初始化) - private LocalBinder mLocalBinder; // 持有 LocalBinder 实例 + // 原有核心变量 + private LocalBinder mLocalBinder; private LocationManager mLocationManager; private LocationListener mGpsLocationListener; - private static final long GPS_UPDATE_INTERVAL = 2000; // GPS更新间隔:2秒 - private static final float GPS_UPDATE_DISTANCE = 1; // GPS更新距离阈值:1米 - private boolean isGpsEnabled = false; // GPS是否启用标记 - private boolean isGpsPermissionGranted = false; // 定位权限是否授予标记 + private static final long GPS_UPDATE_INTERVAL = 2000; // 实时GPS更新间隔:2秒 + private static final float GPS_UPDATE_DISTANCE = 1; // 实时GPS更新距离:1米 + private boolean isGpsEnabled = false; + private boolean isGpsPermissionGranted = false; - // 数据存储集合(Java 7 基础集合) - private final ArrayList mPositionList = new ArrayList(); // 位置数据列表 - private final ArrayList mAllTasks = new ArrayList();// 任务数据列表 - private static PositionModel _mCurrentGpsPosition; // 当前GPS定位数据 + // 数据存储集合 + private final ArrayList mPositionList = new ArrayList(); + private final ArrayList mAllTasks = new ArrayList(); + private static PositionModel _mCurrentGpsPosition; - // 服务相关变量(Java 7 显式声明) + // 服务相关变量 MyServiceConnection mMyServiceConnection; - volatile static boolean _mIsServiceRunning; // 服务运行状态(volatile保证可见性) + volatile static boolean _mIsServiceRunning; AppConfigsUtil mAppConfigsUtil; - private ScheduledExecutorService distanceExecutor = Executors.newSingleThreadScheduledExecutor(); // 距离计算线程池 - private final Set mVisiblePositionIds = new HashSet(); // 可见位置ID集合 + private ScheduledExecutorService distanceExecutor = Executors.newSingleThreadScheduledExecutor(); + private final Set mVisiblePositionIds = new HashSet(); - // 单例+应用上下文(Java 7 静态变量) + // 单例+应用上下文 private static volatile MainService sInstance; private static Context sAppContext; - // 核心:运动状态Receiver(动态注册/注销,Java 7 显式声明) - private MotionStatusReceiver mMotionStatusReceiver; - private IntentFilter mMotionStatusFilter; + + // ========================================================================= + // 核心:运动状态回调(仅通过LocalMotionDetector触发,低运动归类为静止) + // ========================================================================= + @Override + public void onMotionStatusChanged(boolean isWalking, String statusDesc) { + LogUtils.d(TAG, "运动状态回调:" + statusDesc + "(isWalking=" + isWalking + ")"); + updateNotificationGpsStatus("当前状态:" + statusDesc); + + // 仅行走状态切实时GPS,非行走(静止+低运动)统一按静止处理 + if (isWalking) { + // 行走状态:立即切回实时GPS(取消未执行的延迟任务) + if (staticDelayHandler != null) { + staticDelayHandler.removeCallbacksAndMessages(null); // 清空延迟任务 + isDelaySwitching = false; // 重置标记 + } + stopGpsStaticTimer(); // 停止定时GPS + startGpsLocation(); // 启动实时GPS + LogUtils.d(TAG, "行走状态:已切换为实时GPS(2秒/1米)"); + + } else { + // 非行走状态:延迟10秒切换为定时GPS + if (isGpsListening && !isDelaySwitching) { // 仅实时GPS运行且无延迟任务时触发 + isDelaySwitching = true; + staticDelayHandler.postDelayed(new Runnable() { + @Override + public void run() { + stopGpsLocation(); // 停止实时GPS + startGpsStaticTimer(GPS_STATIC_INTERVAL); // 启动30秒定时GPS + LogUtils.d(TAG, "静止/低运动10秒:已切换为30秒定时GPS"); + isDelaySwitching = false; // 重置标记 + } + }, STATIC_DELAY_MS); + LogUtils.d(TAG, "检测到静止/低运动:将在10秒后切换为定时GPS(若持续非行走)"); + } + LogUtils.d(TAG, "静止/低运动状态:等待延迟切换GPS"); + } + } // ========================================================================= - // 静止GPS定时任务方法(Java 7 语法) + // 静止GPS定时任务方法(适配Java7) // ========================================================================= - // 启动静止时GPS定时获取 - public void startGpsStaticTimer(long interval) { - // 先销毁旧定时器(避免重复创建) + // 启动静止时GPS定时获取(间隔:秒) + public void startGpsStaticTimer(long intervalSeconds) { stopGpsStaticTimer(); - // 创建单线程定时器 gpsStaticTimer = Executors.newSingleThreadScheduledExecutor(); gpsStaticTimer.scheduleAtFixedRate(new Runnable() { @Override public void run() { LogUtils.d(TAG, "静止时GPS定时任务触发:开始单次GPS获取"); - startSingleGpsRetrieve(); // 单次GPS获取 + startSingleGpsRetrieve(); } - }, 0, interval, TimeUnit.MINUTES); // 立即执行第一次,之后按间隔执行 + }, 0, intervalSeconds, TimeUnit.SECONDS); - LogUtils.d(TAG, "静止时GPS定时任务已启动(间隔:" + interval + "分钟)"); + LogUtils.d(TAG, "静止时GPS定时任务已启动(间隔:" + intervalSeconds + "秒)"); } // 停止静止时GPS定时任务 @@ -135,7 +175,7 @@ public class MainService extends Service { } } - // 单次GPS获取(获取后关闭监听,Java 7 匿名内部类) + // 单次GPS获取(获取后关闭监听) private void startSingleGpsRetrieve() { if (!checkGpsReady()) { LogUtils.w(TAG, "单次GPS获取:GPS未就绪,跳过"); @@ -143,13 +183,11 @@ public class MainService extends Service { } try { - // 注册临时GPS监听(仅获取一次位置) mLocationManager.requestSingleUpdate( LocationManager.GPS_PROVIDER, new LocationListener() { @Override public void onLocationChanged(Location location) { - // 获取到位置:同步数据+关闭监听 if (location != null) { PositionModel gpsPos = new PositionModel(); gpsPos.setLatitude(location.getLatitude()); @@ -159,7 +197,6 @@ public class MainService extends Service { syncCurrentGpsPosition(gpsPos); LogUtils.d(TAG, "单次GPS获取成功:纬度=" + location.getLatitude()); } - // 移除监听(避免内存泄漏) mLocationManager.removeUpdates(this); } @@ -171,24 +208,23 @@ public class MainService extends Service { @Override public void onProviderDisabled(String provider) { - mLocationManager.removeUpdates(this); // 禁用时移除监听 + mLocationManager.removeUpdates(this); } }, Looper.getMainLooper() ); - // 超时处理:指定时间内未获取则强制关闭 + // 超时处理 new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { - // 移除所有临时监听 mLocationManager.removeUpdates(new LocationListener() { @Override public void onLocationChanged(Location location) {} @Override public void onStatusChanged(String provider, int status, Bundle extras) {} @Override public void onProviderEnabled(String provider) {} @Override public void onProviderDisabled(String provider) {} }); - LogUtils.d(TAG, "单次GPS获取超时(" + GPS_STATIC_DURATION + "秒),已强制关闭监听"); + LogUtils.d(TAG, "单次GPS获取超时(" + GPS_STATIC_DURATION + "秒)"); } }, GPS_STATIC_DURATION * 1000); @@ -201,7 +237,7 @@ public class MainService extends Service { // ========================================================================= - // 任务操作核心接口(Java 7 实现) + // 任务操作核心接口 // ========================================================================= public void addTask(PositionTaskModel newTask) { if (newTask == null || TextUtils.isEmpty(newTask.getPositionId())) { @@ -209,7 +245,6 @@ public class MainService extends Service { return; } - // 任务去重(Java 7 迭代器遍历) boolean isDuplicate = false; Iterator taskIter = mAllTasks.iterator(); while (taskIter.hasNext()) { @@ -236,7 +271,6 @@ public class MainService extends Service { return posTasks; } - // 筛选任务(Java 7 迭代器遍历) Iterator taskIter = mAllTasks.iterator(); while (taskIter.hasNext()) { PositionTaskModel task = taskIter.next(); @@ -280,7 +314,6 @@ public class MainService extends Service { return; } - // 迭代器删除(Java 7 安全方式) Iterator taskIter = mAllTasks.iterator(); while (taskIter.hasNext()) { PositionTaskModel task = taskIter.next(); @@ -326,7 +359,6 @@ public class MainService extends Service { while (iter.hasNext()) { final WeakReference ref = iter.next(); if (ref.get() != null) { - // 主线程判断+切换(Java 7 匿名Runnable) if (Looper.myLooper() == Looper.getMainLooper()) { ref.get().onTaskUpdated(); } else { @@ -338,7 +370,7 @@ public class MainService extends Service { }); } } else { - iter.remove(); // 清理已回收弱引用 + iter.remove(); } } } @@ -346,7 +378,7 @@ public class MainService extends Service { // ========================================================================= - // 服务基础方法(核心调整:动态注册/注销MotionStatusReceiver) + // 服务基础方法(初始化LocalMotionDetector,删除Receiver相关逻辑) // ========================================================================= public static synchronized MainService getInstance(Context context) { if (sInstance == null) { @@ -364,7 +396,7 @@ public class MainService extends Service { @Override public IBinder onBind(Intent intent) { - return mLocalBinder; // 返回LocalBinder暴露服务 + return mLocalBinder; } @Override @@ -374,55 +406,45 @@ public class MainService extends Service { sInstance = this; sAppContext = getApplicationContext(); - // 初始化LocalBinder mLocalBinder = new LocalBinder(this); - _mIsServiceRunning = false; mAppConfigsUtil = AppConfigsUtil.getInstance(this); - // 初始化服务连接 if (mMyServiceConnection == null) { mMyServiceConnection = new MyServiceConnection(); } - // 初始化GPS监听(仅初始化,不启动) - //initGpsLocationListener(); + // 初始化:LocalMotionDetector+延迟切换Handler(Java7兼容) + mMotionDetector = LocalMotionDetector.getInstance(); + staticDelayHandler = new Handler(Looper.getMainLooper()); if (mAppConfigsUtil.isEnableMainService(true)) { - run(); // 启动服务核心逻辑 + run(); } } - // 服务核心运行方法(调整:启动服务时仅注册Receiver,不启动GPS) + // 服务核心运行方法(删除Receiver注册逻辑) public void run() { if (mAppConfigsUtil.isEnableMainService(true)) { if (!_mIsServiceRunning) { _mIsServiceRunning = true; - wakeupAndBindAssistant(); // 唤醒并绑定辅助服务 + wakeupAndBindAssistant(); // 启动前台服务 String initialStatus = "[ Positions ] is in Service."; - NotificationUtil.createForegroundServiceNotification(this, initialStatus); + NotificationUtil.createForegroundServiceNotification(this, initialStatus); startForeground(NotificationUtil.FOREGROUND_SERVICE_NOTIFICATION_ID, NotificationUtil.createForegroundServiceNotification(this, initialStatus)); - // 初始化LocationManager(仅初始化,不启动GPS) + // 初始化LocationManager mLocationManager = (LocationManager) sInstance.getApplicationContext().getSystemService(Context.LOCATION_SERVICE); // 加载本地数据 PositionModel.loadBeanList(MainService.this, mPositionList, PositionModel.class); PositionTaskModel.loadBeanList(MainService.this, mAllTasks, PositionTaskModel.class); - // 初始化运动状态Receiver(动态注册准备) - initMotionStatusReceiver(); - - // 动态注册MotionStatusReceiver(核心:交给Receiver控制GPS) - registerMotionStatusReceiver(); - - Intent triggerIntent = new Intent(MotionStatusReceiver.ACTION_MOTION_STATUS_RECEIVER); - triggerIntent.putExtra(MotionStatusReceiver.EXTRA_SENSORS_ENABLE, true); - sendBroadcast(triggerIntent); - LogUtils.d(TAG, "已发送自主触发广播,唤醒MotionStatusReceiver"); + // 核心:启动LocalMotionDetector监测(唯一运动状态来源) + mMotionDetector.startDetection(this, this); // 提示与日志 ToastUtils.show(initialStatus); @@ -431,45 +453,6 @@ public class MainService extends Service { } } - // 初始化运动状态Receiver和IntentFilter - private void initMotionStatusReceiver() { - LogUtils.d(TAG, "initMotionStatusReceiver()"); - mMotionStatusReceiver = new MotionStatusReceiver(); - mMotionStatusFilter = new IntentFilter(); - mMotionStatusFilter.addAction(MotionStatusReceiver.ACTION_MOTION_STATUS_RECEIVER); - } - - // 动态注册MotionStatusReceiver - private void registerMotionStatusReceiver() { - LogUtils.d(TAG, "registerMotionStatusReceiver()"); - if (mMotionStatusReceiver != null && mMotionStatusFilter != null) { - try { - registerReceiver(mMotionStatusReceiver, mMotionStatusFilter); - LogUtils.d(TAG, "MotionStatusReceiver 动态注册成功"); - } catch (Exception e) { - LogUtils.e(TAG, "MotionStatusReceiver 注册失败:" + e.getMessage()); - } - } - } - - // - // 动态注销MotionStatusReceiver - // - private void unregisterMotionStatusReceiver() { - if (mMotionStatusReceiver != null) { - try { - unregisterReceiver(mMotionStatusReceiver); - mMotionStatusReceiver.forceCleanResources(); // 新增:强制清理传感器 - LogUtils.d(TAG, "MotionStatusReceiver 注销并强制清理传感器"); - } catch (Exception e) { - LogUtils.e(TAG, "MotionStatusReceiver 注销失败:" + e.getMessage()); - } finally { - mMotionStatusReceiver = null; - mMotionStatusFilter = null; - } - } - } - public boolean isServiceRunning() { return _mIsServiceRunning; } @@ -479,37 +462,44 @@ public class MainService extends Service { super.onDestroy(); sInstance = null; - // 核心清理:1.注销MotionStatusReceiver 2.关闭GPS 3.停止定时任务 - unregisterMotionStatusReceiver(); // 注销Receiver,避免内存泄漏 - stopGpsLocation(); // 强制关闭GPS监听 - stopGpsStaticTimer(); // 停止静止时GPS定时任务 + // 核心:停止LocalMotionDetector(避免传感器内存泄漏) + if (mMotionDetector != null) { + mMotionDetector.stopDetection(); + LogUtils.d(TAG, "LocalMotionDetector 已停止检测"); + } - // 其他资源清理 + // 清理延迟Handler任务(避免内存泄漏) + if (staticDelayHandler != null) { + staticDelayHandler.removeCallbacksAndMessages(null); + staticDelayHandler = null; + } + + // 原有清理逻辑(已删除Receiver注销代码) + stopGpsLocation(); + stopGpsStaticTimer(); clearAllData(); stopForeground(true); - // 清理所有监听者 synchronized (mListenerLock) { mGpsListeners.clear(); mTaskListeners.clear(); } - // 销毁线程池 if (distanceExecutor != null && !distanceExecutor.isShutdown()) { distanceExecutor.shutdown(); } - // 重置状态变量 _mIsServiceRunning = false; isGpsEnabled = false; isGpsListening = false; + isDelaySwitching = false; mLocationManager = null; LogUtils.d(TAG, "MainService 销毁完成,所有资源已清理"); } // ========================================================================= - // 位置操作方法(Java 7 语法) + // 位置操作方法 // ========================================================================= public ArrayList getPositionList() { return mPositionList; @@ -519,7 +509,6 @@ public class MainService extends Service { return _mCurrentGpsPosition; } - // 对外提供:判断GPS是否处于持续监听状态(给MotionStatusReceiver调用) public boolean isGpsListening() { return isGpsListening; } @@ -529,7 +518,6 @@ public class MainService extends Service { LogUtils.w(TAG, "removePosition:参数无效"); return; } - // 迭代器遍历删除 Iterator iter = mPositionList.iterator(); while (iter.hasNext()) { PositionModel pos = iter.next(); @@ -546,7 +534,6 @@ public class MainService extends Service { LogUtils.w(TAG, "updatePosition:参数无效"); return; } - // 基础for循环查找并更新 for (int i = 0; i < mPositionList.size(); i++) { PositionModel oldPos = mPositionList.get(i); if (updatedPos.getPositionId().equals(oldPos.getPositionId())) { @@ -573,7 +560,6 @@ public class MainService extends Service { LogUtils.w(TAG, "addPosition:位置为空"); return; } - // 位置去重(增强for循环) boolean isDuplicate = false; for (PositionModel pos : mPositionList) { if (newPos.getPositionId().equals(pos.getPositionId())) { @@ -613,7 +599,6 @@ public class MainService extends Service { LogUtils.d(TAG, "syncCurrentGpsPosition:成功(纬度=" + position.getLatitude() + ",经度=" + position.getLongitude() + ")"); notifyAllGpsListeners(position); - // 服务运行中同步通知栏状态 if (_mIsServiceRunning) { syncGpsStatusToNotification(); } @@ -623,14 +608,12 @@ public class MainService extends Service { if (!_mIsServiceRunning || _mCurrentGpsPosition == null) { return; } - // 格式化通知内容 final String gpsStatus = String.format( "GPS位置:北纬%.4f° 东经%.4f° | 可见位置:%d个", _mCurrentGpsPosition.getLatitude(), _mCurrentGpsPosition.getLongitude(), mVisiblePositionIds.size() ); - // 主线程切换 if (Looper.myLooper() == Looper.getMainLooper()) { NotificationUtil.updateForegroundServiceStatus(this, gpsStatus); } else { @@ -645,7 +628,7 @@ public class MainService extends Service { // ========================================================================= - // 服务生命周期+辅助服务相关(Java 7 语法) + // 服务生命周期+辅助服务相关 // ========================================================================= @Override public int onStartCommand(Intent intent, int flags, int startId) { @@ -653,24 +636,18 @@ public class MainService extends Service { if (intent != null) { isSettingToEnable = intent.getBooleanExtra(EXTRA_IS_SETTING_TO_ENABLE, false); if (isSettingToEnable) { - run(); // 重启服务核心逻辑(重新注册Receiver) + run(); } } - - // 根据是否自启动返回对应启动模式 return isSettingToEnable ? Service.START_STICKY : super.onStartCommand(intent, flags, startId); } - // 服务连接内部类 private class MyServiceConnection implements ServiceConnection { @Override - public void onServiceConnected(ComponentName name, IBinder service) { - // 空实现(如需绑定辅助服务可补充逻辑) - } + public void onServiceConnected(ComponentName name, IBinder service) {} @Override public void onServiceDisconnected(ComponentName name) { - // 辅助服务断开时重新绑定 if (mAppConfigsUtil.isEnableMainService(true)) { wakeupAndBindAssistant(); } @@ -678,9 +655,7 @@ public class MainService extends Service { } void wakeupAndBindAssistant() { - // 检查辅助服务是否存活 if (!ServiceUtil.isServiceAlive(getApplicationContext(), AssistantService.class.getName())) { - // 启动+绑定辅助服务 startService(new Intent(MainService.this, AssistantService.class)); bindService(new Intent(MainService.this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT); } @@ -688,28 +663,25 @@ public class MainService extends Service { // ========================================================================= - // GPS相关核心方法(Java 7 语法,仅保留Receiver调用的启动/停止逻辑) + // GPS相关核心方法 // ========================================================================= public MainService() { distanceExecutor = Executors.newSingleThreadScheduledExecutor(); initGpsLocationListener(); } - // 初始化GPS监听(仅创建Listener,不注册) private void initGpsLocationListener() { LogUtils.d(TAG, "initGpsLocationListener"); mGpsLocationListener = new LocationListener() { @Override public void onLocationChanged(Location location) { if (location != null) { - // 封装GPS位置 PositionModel gpsPos = new PositionModel(); gpsPos.setLatitude(location.getLatitude()); gpsPos.setLongitude(location.getLongitude()); gpsPos.setPositionId("CURRENT_GPS_POS"); gpsPos.setMemo("实时GPS位置"); - // 同步位置+校验任务 syncCurrentGpsPosition(gpsPos); DistanceCalculatorUtil.getInstance(MainService.this).checkAllTaskTriggerCondition(gpsPos); LogUtils.d(TAG, "GPS位置更新:纬度=" + location.getLatitude() + ",经度=" + location.getLongitude()); @@ -720,7 +692,6 @@ public class MainService extends Service { public void onStatusChanged(String provider, int status, Bundle extras) { if (provider.equals(LocationManager.GPS_PROVIDER)) { String statusDesc = ""; - // Java 7 基础switch switch (status) { case LocationProvider.AVAILABLE: statusDesc = "GPS状态:已就绪(可用)"; @@ -745,8 +716,7 @@ public class MainService extends Service { String statusDesc = "GPS已开启(用户手动打开)"; LogUtils.d(TAG, statusDesc); notifyAllGpsStatusListeners(statusDesc); - updateNotificationGpsStatus("GPS已开启,等待Receiver控制启动..."); - // 不主动启动GPS,由Receiver根据运动状态控制 + updateNotificationGpsStatus("GPS已开启,等待运动状态触发..."); } } @@ -766,20 +736,15 @@ public class MainService extends Service { }; } - // GPS就绪检查(给Receiver调用的GPS操作做前置判断) private boolean checkGpsReady() { - // 检查定位权限 isGpsPermissionGranted = checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; - // 初始化LocationManager if (mLocationManager == null) { mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); } - // 检查GPS是否启用 isGpsEnabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); - // 权限未授予处理 if (!isGpsPermissionGranted) { String tip = "GPS准备失败:缺少精确定位权限"; LogUtils.e(TAG, tip); @@ -788,7 +753,6 @@ public class MainService extends Service { ToastUtils.show("请授予定位权限,否则无法获取GPS位置"); return false; } - // GPS未启用处理 if (!isGpsEnabled) { String tip = "GPS准备失败:系统GPS未开启"; LogUtils.e(TAG, tip); @@ -802,14 +766,13 @@ public class MainService extends Service { return true; } - // 启动GPS持续监听(仅Receiver可调用) + // 启动GPS持续监听(运动状态回调调用) public void startGpsLocation() { if (!checkGpsReady() || isGpsListening) { return; } try { - // 注册GPS位置更新 mLocationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, GPS_UPDATE_INTERVAL, @@ -818,10 +781,9 @@ public class MainService extends Service { Looper.getMainLooper() ); - // 标记GPS进入持续监听状态 isGpsListening = true; - // 获取最后已知GPS位置 + // 获取最后已知位置 Location lastKnownLocation = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (lastKnownLocation != null) { PositionModel lastGpsPos = new PositionModel(); @@ -853,9 +815,8 @@ public class MainService extends Service { } } - // 停止GPS持续监听(仅Receiver可调用) + // 停止GPS持续监听(运动状态回调调用) public void stopGpsLocation() { - // 校验参数:避免空指针+权限未授予时调用 if (mLocationManager != null && mGpsLocationListener != null && isGpsPermissionGranted) { try { mLocationManager.removeUpdates(mGpsLocationListener); @@ -878,7 +839,6 @@ public class MainService extends Service { return; } - // 格式化通知内容 final String triggerContent = String.format( "任务触发:%s\n位置:%s\n当前距离:%.1f米(条件:%s%d米)", task.getTaskDescription(), @@ -888,10 +848,8 @@ public class MainService extends Service { task.getDiscussDistance() ); - // 更新前台通知 updateNotificationGpsStatus(triggerContent); - // 显示Toast(主线程安全) if (Looper.myLooper() == Looper.getMainLooper()) { ToastUtils.show(triggerContent); NotificationUtil.show(MainService.this, task.getTaskId(), task.getPositionId(), task.getTaskDescription()); @@ -910,7 +868,6 @@ public class MainService extends Service { // 更新前台通知的GPS状态 void updateNotificationGpsStatus(final String statusText) { if (_mIsServiceRunning) { - // 主线程判断+切换(Java7匿名内部类) if (Looper.myLooper() == Looper.getMainLooper()) { NotificationUtil.updateForegroundServiceStatus(this, statusText); } else { @@ -926,7 +883,7 @@ public class MainService extends Service { // ========================================================================= - // GPS监听通知相关方法(Java7语法,给外部监听者回调) + // GPS监听通知相关方法 // ========================================================================= public void notifyAllGpsListeners(PositionModel currentGpsPos) { if (currentGpsPos == null || mGpsListeners.isEmpty()) { @@ -939,102 +896,100 @@ public class MainService extends Service { GpsUpdateListener listener = ref.get(); if (listener != null) { notifySingleListener(listener, currentGpsPos); - } else { - iter.remove(); // 清理已回收监听者 - } - } - } - } + } else { + iter.remove(); + } + } + } + } - private void notifySingleListener(final GpsUpdateListener listener, final PositionModel currentGpsPos) { - if (Looper.myLooper() == Looper.getMainLooper()) { - listener.onGpsPositionUpdated(currentGpsPos); - } else { - new Handler(Looper.getMainLooper()).post(new Runnable() { + private void notifySingleListener(final GpsUpdateListener listener, final PositionModel currentGpsPos) { + if (Looper.myLooper() == Looper.getMainLooper()) { + listener.onGpsPositionUpdated(currentGpsPos); + } else { + new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { listener.onGpsPositionUpdated(currentGpsPos); } }); - } - } + } + } - private void notifyAllGpsStatusListeners(final String status) { - if (status == null || mGpsListeners.isEmpty()) { - return; - } - synchronized (mListenerLock) { - Iterator> iter = mGpsListeners.iterator(); - while (iter.hasNext()) { - WeakReference ref = iter.next(); - final GpsUpdateListener listener = ref.get(); - if (listener != null) { - // 主线程切换,避免UI异常(Java7匿名Runnable) - if (Looper.myLooper() == Looper.getMainLooper()) { - listener.onGpsStatusChanged(status); - } else { - new Handler(Looper.getMainLooper()).post(new Runnable() { + private void notifyAllGpsStatusListeners(final String status) { + if (status == null || mGpsListeners.isEmpty()) { + return; + } + synchronized (mListenerLock) { + Iterator> iter = mGpsListeners.iterator(); + while (iter.hasNext()) { + WeakReference ref = iter.next(); + final GpsUpdateListener listener = ref.get(); + if (listener != null) { + if (Looper.myLooper() == Looper.getMainLooper()) { + listener.onGpsStatusChanged(status); + } else { + new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { listener.onGpsStatusChanged(status); } }); - } - } else { - iter.remove(); // 清理无效弱引用 - } - } - } - } + } + } else { + iter.remove(); + } + } + } + } - public void registerGpsUpdateListener(GpsUpdateListener listener) { - if (listener == null) { - LogUtils.w(TAG, "registerGpsUpdateListener:监听者为空"); - return; - } - synchronized (mListenerLock) { - mGpsListeners.add(new WeakReference(listener)); - LogUtils.d(TAG, "GPS监听注册成功,当前数量:" + mGpsListeners.size()); - // 注册后推送当前GPS位置(避免监听者遗漏初始数据) - if (_mCurrentGpsPosition != null) { - notifySingleListener(listener, _mCurrentGpsPosition); - } - } - } + public void registerGpsUpdateListener(GpsUpdateListener listener) { + if (listener == null) { + LogUtils.w(TAG, "registerGpsUpdateListener:监听者为空"); + return; + } + synchronized (mListenerLock) { + mGpsListeners.add(new WeakReference(listener)); + LogUtils.d(TAG, "GPS监听注册成功,当前数量:" + mGpsListeners.size()); + if (_mCurrentGpsPosition != null) { + notifySingleListener(listener, _mCurrentGpsPosition); + } + } + } - public void unregisterGpsUpdateListener(GpsUpdateListener listener) { - if (listener == null) { - LogUtils.w(TAG, "unregisterGpsUpdateListener:监听者为空"); - return; - } - synchronized (mListenerLock) { - Iterator> iter = mGpsListeners.iterator(); - while (iter.hasNext()) { - WeakReference ref = iter.next(); - if (ref.get() == listener || ref.get() == null) { - iter.remove(); - LogUtils.d(TAG, "GPS监听反注册成功,当前数量:" + mGpsListeners.size()); - break; - } - } - } - } + public void unregisterGpsUpdateListener(GpsUpdateListener listener) { + if (listener == null) { + LogUtils.w(TAG, "unregisterGpsUpdateListener:监听者为空"); + return; + } + synchronized (mListenerLock) { + Iterator> iter = mGpsListeners.iterator(); + while (iter.hasNext()) { + WeakReference ref = iter.next(); + if (ref.get() == listener || ref.get() == null) { + iter.remove(); + LogUtils.d(TAG, "GPS监听反注册成功,当前数量:" + mGpsListeners.size()); + break; + } + } + } + } - // ========================================================================= - // LocalBinder 定义(与Activity绑定用,Java7内部类) - // ========================================================================= - public class LocalBinder extends android.os.Binder { - private MainService mService; +// ========================================================================= +// LocalBinder 定义(无修改) +// ========================================================================= + public class LocalBinder extends android.os.Binder { + private MainService mService; - public LocalBinder(MainService service) { - this.mService = service; - } + public LocalBinder(MainService service) { + this.mService = service; + } - public MainService getService() { - return mService; // 暴露MainService实例给绑定的Activity - } - } + public MainService getService() { + return mService; + } + } } diff --git a/positions/src/main/java/cc/winboll/studio/positions/utils/LocalMotionDetector.java b/positions/src/main/java/cc/winboll/studio/positions/utils/LocalMotionDetector.java new file mode 100644 index 0000000..0e2a186 --- /dev/null +++ b/positions/src/main/java/cc/winboll/studio/positions/utils/LocalMotionDetector.java @@ -0,0 +1,168 @@ +package cc.winboll.studio.positions.utils; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/05 15:49 + * @Describe LocalMotionDetector + */ +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import cc.winboll.studio.libappbase.LogUtils; + +/** + * 本机运动状态监测工具(无联网,纯传感器) + */ +public class LocalMotionDetector implements SensorEventListener { + public static final String TAG = "LocalMotionDetector"; + + // 配置参数(重点修改:调高运动阈值,适配坐立持机场景) + private static final float MOTION_THRESHOLD = 1.8f; // 从0.5f调高到1.8f(过滤坐立轻微晃动) + private static final long STATUS_CHECK_INTERVAL = 3000; // 3秒判断一次状态 + private static final int STEP_CHANGE_THRESHOLD = 2; // 3秒≥2步判定行走 + + private SensorManager mSensorManager; + private Sensor mAccelerometer; + private Sensor mStepCounter; + private Handler mMainHandler; + private MotionStatusCallback mCallback; + + private boolean mIsDetecting = false; + private float mLastAccelMagnitude = 0f; + private int mLastStepCount = 0; + private int mCurrentStepCount = 0; + private boolean mIsWalking = false; + + // 单例模式 + private static LocalMotionDetector sInstance; + public static LocalMotionDetector getInstance() { + if (sInstance == null) { + synchronized (LocalMotionDetector.class) { + if (sInstance == null) { + sInstance = new LocalMotionDetector(); + } + } + } + return sInstance; + } + + private LocalMotionDetector() { + mMainHandler = new Handler(Looper.getMainLooper()); + } + + /** + * 开始监测运动状态 + */ + public void startDetection(Context context, MotionStatusCallback callback) { + if (mIsDetecting) return; + mCallback = callback; + mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + + // 初始化传感器 + mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + mStepCounter = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER); + + // 注册传感器监听 + if (mAccelerometer != null) { + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL, mMainHandler); + } + if (mStepCounter != null) { + mSensorManager.registerListener(this, mStepCounter, SensorManager.SENSOR_DELAY_NORMAL, mMainHandler); + LogUtils.d(TAG, "计步传感器已启动"); + } else { + LogUtils.d(TAG, "设备不支持计步传感器,仅用加速度判断"); + } + + // 启动定时状态检测 + mMainHandler.postDelayed(mStatusCheckRunnable, STATUS_CHECK_INTERVAL); + mIsDetecting = true; + LogUtils.d(TAG, "运动状态监测已启动"); + } + + /** + * 停止监测 + */ + public void stopDetection() { + if (!mIsDetecting) return; + if (mSensorManager != null) { + mSensorManager.unregisterListener(this); + } + mMainHandler.removeCallbacksAndMessages(null); + mIsDetecting = false; + mIsWalking = false; + mCallback = null; + LogUtils.d(TAG, "运动状态监测已停止"); + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (!mIsDetecting) return; + switch (event.sensor.getType()) { + case Sensor.TYPE_ACCELEROMETER: + // 计算加速度幅度(保留原逻辑,阈值已调高) + float accelX = Math.abs(event.values[0]); + float accelY = Math.abs(event.values[1]); + float accelZ = Math.abs(event.values[2]); + mLastAccelMagnitude = accelX + accelY + accelZ; + break; + case Sensor.TYPE_STEP_COUNTER: + // 累计步数 + mCurrentStepCount = (int) event.values[0]; + break; + } + } + + /** + * 定时判断运动状态(优化逻辑:计步为0时,即使有轻微加速度也判定为静止) + */ + private final Runnable mStatusCheckRunnable = new Runnable() { + @Override + public void run() { + if (!mIsDetecting || mCallback == null) return; + + //LogUtils.d(TAG, "mStatusCheckRunnable run"); + + boolean newIsWalking = false; + // 结合计步器+加速度判断(优化:优先计步,无步数时严格按高阈值判断) + if (mStepCounter != null) { + int stepChange = mCurrentStepCount - mLastStepCount; + // 只有“步数达标” 或 “无步数但加速度远超坐立幅度”,才判定为行走 + newIsWalking = (stepChange >= STEP_CHANGE_THRESHOLD) + && (mLastAccelMagnitude >= MOTION_THRESHOLD); // 增加步数+加速度双重校验 + mLastStepCount = mCurrentStepCount; + } else { + // 无计步器时,仅用高阈值判断 + newIsWalking = mLastAccelMagnitude >= MOTION_THRESHOLD; + } + + // 状态变化时回调 + if (newIsWalking != mIsWalking) { + mIsWalking = newIsWalking; + String statusDesc = mIsWalking ? "行走状态" : "静止/低运动状态"; + LogUtils.d(TAG, "运动状态变化:" + statusDesc + " | 加速度幅度:" + mLastAccelMagnitude); // 增加日志便于调试 + mCallback.onMotionStatusChanged(mIsWalking, statusDesc); + } + + LogUtils.d(TAG, String.format("运动状态 newIsWalking %s", newIsWalking)); + + // 循环检测 + mMainHandler.postDelayed(this, STATUS_CHECK_INTERVAL); + } + }; + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) {} + + /** + * 运动状态回调接口 + */ + public interface MotionStatusCallback { + void onMotionStatusChanged(boolean isWalking, String statusDesc); + } +} +