20250930_211825_255
This commit is contained in:
		| @@ -70,7 +70,7 @@ dependencies { | |||||||
|     //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' |     //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' | ||||||
|     //api 'androidx.fragment:fragment:1.1.0' |     //api 'androidx.fragment:fragment:1.1.0' | ||||||
|      |      | ||||||
|     api 'cc.winboll.studio:libaes:15.9.3' |     api 'cc.winboll.studio:libaes:15.10.2' | ||||||
|     api 'cc.winboll.studio:libapputils:15.8.5' |     api 'cc.winboll.studio:libapputils:15.10.2' | ||||||
|     api 'cc.winboll.studio:libappbase:15.9.5' |     api 'cc.winboll.studio:libappbase:15.10.9' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #Created by .winboll/winboll_app_build.gradle | #Created by .winboll/winboll_app_build.gradle | ||||||
| #Tue Sep 30 17:50:26 HKT 2025 | #Tue Sep 30 13:02:23 GMT 2025 | ||||||
| stageCount=4 | stageCount=4 | ||||||
| libraryProject= | libraryProject= | ||||||
| baseVersion=15.0 | baseVersion=15.0 | ||||||
| publishVersion=15.0.3 | publishVersion=15.0.3 | ||||||
| buildCount=0 | buildCount=9 | ||||||
| baseBetaVersion=15.0.4 | baseBetaVersion=15.0.4 | ||||||
|   | |||||||
| @@ -2,20 +2,20 @@ | |||||||
| <manifest | <manifest | ||||||
|     xmlns:android="http://schemas.android.com/apk/res/android" |     xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     package="cc.winboll.studio.positions"> |     package="cc.winboll.studio.positions"> | ||||||
| 	 |  | ||||||
| 	<!-- 1. 定位必需权限声明 --> |  | ||||||
|     <!-- 精确定位权限(GPS+网络定位,核心) --> |  | ||||||
|     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> |  | ||||||
|     <!-- 粗略定位权限(仅安卓12+需要,兼容低版本可加) --> |  | ||||||
|     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> |  | ||||||
|     <!-- 网络权限(可选,用于网络定位,提升室内定位精度) --> |  | ||||||
|     <uses-permission android:name="android.permission.INTERNET" /> |  | ||||||
|  |  | ||||||
|     <!-- 2. 声明定位硬件支持(可选,告诉系统应用需要定位功能) --> |     <!-- 只能在前台获取精确的位置信息 --> | ||||||
|  |     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> | ||||||
|  |  | ||||||
|  |     <!-- 只有在前台运行时才能获取大致位置信息 --> | ||||||
|  |     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> | ||||||
|  |  | ||||||
|  |     <!-- 拥有完全的网络访问权限 --> | ||||||
|  |     <uses-permission android:name="android.permission.INTERNET"/> | ||||||
|  |  | ||||||
|     <uses-feature |     <uses-feature | ||||||
|         android:name="android.hardware.location.gps" |         android:name="android.hardware.location.gps" | ||||||
|         android:required="false" /> <!-- false=无GPS也能使用(网络定位) --> |         android:required="false"/> | ||||||
| 		 |  | ||||||
|     <application |     <application | ||||||
|         android:allowBackup="true" |         android:allowBackup="true" | ||||||
|         android:icon="@drawable/ic_launcher" |         android:icon="@drawable/ic_launcher" | ||||||
| @@ -45,11 +45,13 @@ | |||||||
|         <activity android:name=".GlobalApplication$CrashActivity"/> |         <activity android:name=".GlobalApplication$CrashActivity"/> | ||||||
|  |  | ||||||
|         <activity android:name="cc.winboll.studio.positions.activities.LocationActivity"/> |         <activity android:name="cc.winboll.studio.positions.activities.LocationActivity"/> | ||||||
| 			 |  | ||||||
| 		<!-- 4. 谷歌定位服务版本声明(避免版本兼容问题) --> |  | ||||||
|         <meta-data |         <meta-data | ||||||
|             android:name="com.google.android.gms.version" |             android:name="com.google.android.gms.version" | ||||||
|             android:value="@integer/google_play_services_version" /> |             android:value="@integer/google_play_services_version"/> | ||||||
|  |  | ||||||
|  |         <service android:name="cc.winboll.studio.positions.services.DistanceRefreshService"/> | ||||||
|  |  | ||||||
|     </application> |     </application> | ||||||
|  |  | ||||||
| </manifest> | </manifest> | ||||||
| @@ -1,17 +1,17 @@ | |||||||
| package cc.winboll.studio.positions; | package cc.winboll.studio.positions; | ||||||
|  |  | ||||||
|  | import android.content.Intent; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
|  | import android.view.View; | ||||||
| import androidx.appcompat.app.AppCompatActivity; | import androidx.appcompat.app.AppCompatActivity; | ||||||
| import androidx.appcompat.widget.Toolbar; | import androidx.appcompat.widget.Toolbar; | ||||||
|  | import cc.winboll.studio.libappbase.LogActivity; | ||||||
| import cc.winboll.studio.libappbase.LogView; | import cc.winboll.studio.libappbase.LogView; | ||||||
| import com.hjq.toast.ToastUtils; |  | ||||||
| import android.view.View; |  | ||||||
| import cc.winboll.studio.positions.activities.LocationActivity; | import cc.winboll.studio.positions.activities.LocationActivity; | ||||||
| import android.content.Intent; | import com.hjq.toast.ToastUtils; | ||||||
|  |  | ||||||
| public class MainActivity extends AppCompatActivity { | public class MainActivity extends AppCompatActivity { | ||||||
|  |  | ||||||
|     LogView mLogView; |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |     protected void onCreate(Bundle savedInstanceState) { | ||||||
| @@ -20,19 +20,13 @@ public class MainActivity extends AppCompatActivity { | |||||||
|  |  | ||||||
| 		Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar); | 		Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar); | ||||||
| 		setSupportActionBar(toolbar); | 		setSupportActionBar(toolbar); | ||||||
|  |  | ||||||
|         mLogView = findViewById(R.id.logview); |  | ||||||
|          |  | ||||||
|         ToastUtils.show("onCreate"); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected void onResume() { |  | ||||||
|         super.onResume(); |  | ||||||
|         mLogView.start(); |  | ||||||
|     } |  | ||||||
| 	 |  | ||||||
| 	public void onPositions(View view) { | 	public void onPositions(View view) { | ||||||
| 		startActivity(new Intent(this, LocationActivity.class)); | 		startActivity(new Intent(this, LocationActivity.class)); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	public void onLog(View view) { | ||||||
|  | 		LogActivity.startLogActivity(this); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -411,29 +411,28 @@ public class LocationActivity extends AppCompatActivity { | |||||||
|      * 活动销毁:停止定位 + 停止距离刷新定时器(避免内存泄漏) |      * 活动销毁:停止定位 + 停止距离刷新定时器(避免内存泄漏) | ||||||
|      */ |      */ | ||||||
|     @Override |     @Override | ||||||
|     protected void onDestroy() { | 	protected void onDestroy() { | ||||||
|         super.onDestroy(); | 		super.onDestroy(); | ||||||
|  |  | ||||||
|         // 1. 停止定位监听(释放定位资源) | 		// 1. 停止定位监听(原逻辑不变) | ||||||
|         if (fusedLocationClient != null && locationCallback != null) { | 		if (fusedLocationClient != null && locationCallback != null) { | ||||||
|             fusedLocationClient.removeLocationUpdates(locationCallback); | 			fusedLocationClient.removeLocationUpdates(locationCallback); | ||||||
|         } | 		} | ||||||
|  |  | ||||||
|         // 2. 停止Adapter的距离刷新定时器(关键:避免Timer持有Context导致内存泄漏) | 		// 2. 关键:调用Adapter的stopTimer(内部已实现服务解绑,避免内存泄漏) | ||||||
|         if (positionAdapter != null) { | 		if (positionAdapter != null) { | ||||||
|             positionAdapter.stopTimer(); | 			positionAdapter.stopTimer(); // 此方法已重构为:解绑服务+清理资源 | ||||||
|         } | 		} | ||||||
|  |  | ||||||
|         // 3. 最后同步一次数据(确保所有修改保存) |  | ||||||
|         try { |  | ||||||
|             if (mPositionList != null && !mPositionList.isEmpty()) { |  | ||||||
|                 PositionModel.saveBeanList(this, mPositionList, PositionModel.class); |  | ||||||
|             } |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             e.printStackTrace(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  | 		// 3. 最后同步一次数据(原逻辑不变) | ||||||
|  | 		try { | ||||||
|  | 			if (mPositionList != null && !mPositionList.isEmpty()) { | ||||||
|  | 				PositionModel.saveBeanList(this, mPositionList, PositionModel.class); | ||||||
|  | 			} | ||||||
|  | 		} catch (Exception e) { | ||||||
|  | 			e.printStackTrace(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 辅助工具:dp转px(适配不同屏幕分辨率) |      * 辅助工具:dp转px(适配不同屏幕分辨率) | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -22,6 +22,8 @@ public class PositionModel extends BaseBean { | |||||||
| 	double latitude; | 	double latitude; | ||||||
| 	// 位置备注(空值时显示“无备注”) | 	// 位置备注(空值时显示“无备注”) | ||||||
| 	String memo; | 	String memo; | ||||||
|  | 	// 定位点与指定点实时距离大小 | ||||||
|  | 	double realPositionDistance; | ||||||
| 	// 是否启用实时距离计算 | 	// 是否启用实时距离计算 | ||||||
| 	boolean isEnableRealPositionDistance; | 	boolean isEnableRealPositionDistance; | ||||||
| 	// 是否显示简单视图(true=简单视图,false=编辑视图) | 	// 是否显示简单视图(true=简单视图,false=编辑视图) | ||||||
| @@ -45,6 +47,14 @@ public class PositionModel extends BaseBean { | |||||||
| 		this.isEnableRealPositionDistance = false; | 		this.isEnableRealPositionDistance = false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	public void setRealPositionDistance(double realPositionDistance) { | ||||||
|  | 		this.realPositionDistance = realPositionDistance; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public double getRealPositionDistance() { | ||||||
|  | 		return realPositionDistance; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// ---------------------- Getter/Setter(确保字段有效性) ---------------------- | 	// ---------------------- Getter/Setter(确保字段有效性) ---------------------- | ||||||
| 	public void setPositionId(String positionId) { | 	public void setPositionId(String positionId) { | ||||||
| 		this.positionId = (positionId == null || positionId.trim().isEmpty()) ? genPositionId() : positionId; | 		this.positionId = (positionId == null || positionId.trim().isEmpty()) ? genPositionId() : positionId; | ||||||
|   | |||||||
| @@ -0,0 +1,340 @@ | |||||||
|  | package cc.winboll.studio.positions.services; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> | ||||||
|  |  * @Date 2025/09/30 19:53 | ||||||
|  |  * @Describe DistanceRefreshService | ||||||
|  |  */ | ||||||
|  | import android.app.Service; | ||||||
|  | import android.content.Intent; | ||||||
|  | import android.os.Binder; | ||||||
|  | import android.os.Handler; | ||||||
|  | import android.os.IBinder; | ||||||
|  | import android.os.Looper; | ||||||
|  | import android.os.Message; | ||||||
|  | import android.util.Log; | ||||||
|  |  | ||||||
|  | import cc.winboll.studio.libappbase.LogUtils; | ||||||
|  | import cc.winboll.studio.positions.R; | ||||||
|  | import cc.winboll.studio.positions.models.PositionModel; | ||||||
|  | import cc.winboll.studio.positions.models.PositionTaskModel; | ||||||
|  | import cc.winboll.studio.positions.utils.NotificationUtils; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Iterator; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Timer; | ||||||
|  | import java.util.TimerTask; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 距离刷新服务:独立管理定时器,负责实时距离计算、任务触发判断、发送UI更新消息 | ||||||
|  |  * 特性:1. 自启动(绑定后自动启动定时器) 2. 数据与Activity/Adapter同步 3. 本地消息通知UI更新 | ||||||
|  |  * Java 7 适配:移除Lambda、Stream,使用匿名内部类+迭代器,明确泛型声明 | ||||||
|  |  */ | ||||||
|  | public class DistanceRefreshService extends Service { | ||||||
|  |     // 常量定义 | ||||||
|  |     public static final String TAG = "DistanceRefreshService"; | ||||||
|  |     public static final long REFRESH_INTERVAL = 5000; // 5秒刷新一次 | ||||||
|  |     public static final int MSG_UPDATE_DISTANCE = 1001; | ||||||
|  |     public static final String KEY_POSITION_ID = "key_position_id"; | ||||||
|  |     public static final String KEY_DISTANCE_TEXT = "key_distance_text"; | ||||||
|  |     public static final String KEY_DISTANCE_COLOR = "key_distance_color"; | ||||||
|  |  | ||||||
|  |     // 核心成员变量(Java7:明确泛型初始化) | ||||||
|  |     private Timer mDistanceTimer; | ||||||
|  |     private Handler mMainHandler; | ||||||
|  |     private PositionModel mCurrentGpsPosition; | ||||||
|  |     private ArrayList<PositionModel> mPositionList; | ||||||
|  |     private ArrayList<PositionTaskModel> mAllPositionTasks; | ||||||
|  |     private Map<String, Integer> mVisibleDistanceViewTags = new HashMap<String, Integer>(); | ||||||
|  |     private OnDistanceUpdateReceiver mUpdateReceiver; | ||||||
|  |  | ||||||
|  |     // 数据同步与消息接收接口(Activity/Adapter实现) | ||||||
|  |     public interface OnDistanceUpdateReceiver { | ||||||
|  |         void onDistanceUpdate(String positionId, String distanceText, int distanceColor); | ||||||
|  |         int getColorRes(int resId); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 服务绑定器(用于外部获取服务实例) | ||||||
|  |     public class DistanceBinder extends Binder { | ||||||
|  |         public DistanceRefreshService getService() { | ||||||
|  |             return DistanceRefreshService.this; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private final IBinder mBinder = new DistanceBinder(); | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onCreate() { | ||||||
|  |         super.onCreate(); | ||||||
|  |         Log.d(TAG, "DistanceRefreshService onCreate"); | ||||||
|  |         // 初始化主线程Handler(Java7:匿名内部类实现handleMessage) | ||||||
|  |         mMainHandler = new Handler(Looper.getMainLooper()) { | ||||||
|  |             @Override | ||||||
|  |             public void handleMessage(Message msg) { | ||||||
|  |                 super.handleMessage(msg); | ||||||
|  |                 if (msg.what == MSG_UPDATE_DISTANCE && mUpdateReceiver != null) { | ||||||
|  |                     // 解析消息(Java7:显式调用getData(),避免链式调用) | ||||||
|  |                     String positionId = msg.getData().getString(KEY_POSITION_ID); | ||||||
|  |                     String distanceText = msg.getData().getString(KEY_DISTANCE_TEXT); | ||||||
|  |                     int distanceColor = msg.getData().getInt(KEY_DISTANCE_COLOR); | ||||||
|  |                     LogUtils.d(TAG, "接收消息→转发更新:位置ID=" + positionId + ",距离文本=" + distanceText); | ||||||
|  |  | ||||||
|  |                     mUpdateReceiver.onDistanceUpdate(positionId, distanceText, distanceColor); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         // 初始化数据集(Java7:明确泛型类型) | ||||||
|  |         mPositionList = new ArrayList<PositionModel>(); | ||||||
|  |         mAllPositionTasks = new ArrayList<PositionTaskModel>(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public IBinder onBind(Intent intent) { | ||||||
|  |         // 绑定服务时启动定时器(确保仅启动一次) | ||||||
|  |         if (mDistanceTimer == null) { | ||||||
|  |             startDistanceTimer(); | ||||||
|  |             Log.d(TAG, "DistanceRefreshService onBind - 定时器首次启动"); | ||||||
|  |         } else { | ||||||
|  |             Log.d(TAG, "DistanceRefreshService onBind - 定时器已在运行,无需重复启动"); | ||||||
|  |         } | ||||||
|  |         return mBinder; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 启动定时器:核心逻辑(距离计算、任务触发、发送UI更新消息) | ||||||
|  |      * Java7:使用匿名内部类实现TimerTask,显式调用cancel+purge | ||||||
|  |      */ | ||||||
|  |     private void startDistanceTimer() { | ||||||
|  |         // 先停止旧定时器(避免残留任务) | ||||||
|  |         if (mDistanceTimer != null) { | ||||||
|  |             mDistanceTimer.cancel(); | ||||||
|  |             mDistanceTimer.purge(); // 清空已取消的任务,释放资源 | ||||||
|  |         } | ||||||
|  |         mDistanceTimer = new Timer(); | ||||||
|  |         // Java7:匿名内部类实现TimerTask的run方法(替代Lambda) | ||||||
|  |         mDistanceTimer.scheduleAtFixedRate(new TimerTask() { | ||||||
|  | 				@Override | ||||||
|  | 				public void run() { | ||||||
|  | 					LogUtils.d(TAG, "定时器触发→开始计算距离,可见位置数量=" + mVisibleDistanceViewTags.size()); | ||||||
|  | 					calculateAndSendDistanceUpdates(); | ||||||
|  | 					checkAndTriggerTasks(); | ||||||
|  | 				} | ||||||
|  | 			}, 0, REFRESH_INTERVAL); // 立即执行,之后每5秒执行一次 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 计算距离并发送UI更新消息 | ||||||
|  |      * Java7:使用迭代器遍历Map(替代forEach Lambda),显式空判断 | ||||||
|  |      */ | ||||||
|  |     private void calculateAndSendDistanceUpdates() { | ||||||
|  |         // 无可见控件/数据/接收器时,直接返回 | ||||||
|  |         if (mVisibleDistanceViewTags.isEmpty()) { | ||||||
|  |             LogUtils.d(TAG, "无需要更新的可见控件,跳过计算"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (mPositionList.isEmpty()) { | ||||||
|  |             LogUtils.d(TAG, "位置列表为空,无法计算距离"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (mUpdateReceiver == null) { | ||||||
|  |             LogUtils.d(TAG, "未设置消息接收器(Adapter未绑定),无法发送更新"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Java7:使用Iterator遍历Map.Entry(替代forEach Lambda) | ||||||
|  |         Iterator<Map.Entry<String, Integer>> entryIterator = mVisibleDistanceViewTags.entrySet().iterator(); | ||||||
|  |         while (entryIterator.hasNext()) { | ||||||
|  |             Map.Entry<String, Integer> entry = entryIterator.next(); | ||||||
|  |             String positionId = entry.getKey(); | ||||||
|  |             PositionModel targetModel = findPositionModelById(positionId); | ||||||
|  |  | ||||||
|  |             if (targetModel == null) { | ||||||
|  |                 // 位置模型不存在时,发送“无效位置”消息 | ||||||
|  |                 sendDistanceUpdateMessage( | ||||||
|  | 					positionId, | ||||||
|  | 					"实时距离:位置无效", | ||||||
|  | 					mUpdateReceiver.getColorRes(R.color.colorRed) | ||||||
|  |                 ); | ||||||
|  |                 LogUtils.d(TAG, "位置ID=" + positionId + " 未找到对应模型,发送无效提示"); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // 距离计算与文本生成 | ||||||
|  |             String distanceText; | ||||||
|  |             int distanceColor; | ||||||
|  |             if (!targetModel.isEnableRealPositionDistance()) { | ||||||
|  |                 distanceText = "实时距离:未启用"; | ||||||
|  |                 distanceColor = mUpdateReceiver.getColorRes(R.color.colorGrayText); | ||||||
|  |             } else if (mCurrentGpsPosition == null) { | ||||||
|  |                 // 无GPS时持续发送“等待定位”(避免默认文本回落) | ||||||
|  |                 distanceText = "实时距离:等待GPS定位"; | ||||||
|  |                 distanceColor = mUpdateReceiver.getColorRes(R.color.colorGrayText); | ||||||
|  |                 LogUtils.d(TAG, "位置ID=" + positionId + " 无GPS数据,发送等待提示"); | ||||||
|  |             } else { | ||||||
|  |                 try { | ||||||
|  |                     // 计算距离(复用PositionModel静态方法) | ||||||
|  |                     double distanceM = PositionModel.calculatePositionDistance(mCurrentGpsPosition, targetModel, false); | ||||||
|  |                     judgeTaskBingoStatus(targetModel, (int) distanceM); | ||||||
|  |                     // 格式化距离文本(Java7:显式类型转换,避免自动拆箱问题) | ||||||
|  |                     if (distanceM < 1000) { | ||||||
|  |                         distanceText = String.format("实时距离:%.1f 米", distanceM); | ||||||
|  |                     } else { | ||||||
|  |                         distanceText = String.format("实时距离:%.1f 千米", distanceM / 1000); | ||||||
|  |                     } | ||||||
|  |                     distanceColor = mUpdateReceiver.getColorRes(R.color.colorEnableGreen); | ||||||
|  |                     LogUtils.d(TAG, "位置ID=" + positionId + " 计算完成:" + distanceText); | ||||||
|  |                 } catch (IllegalArgumentException e) { | ||||||
|  |                     distanceText = "实时距离:数据无效"; | ||||||
|  |                     distanceColor = mUpdateReceiver.getColorRes(R.color.colorRed); | ||||||
|  |                     LogUtils.e(TAG, "位置ID=" + positionId + " 计算异常:" + e.getMessage()); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // 发送消息到UI线程 | ||||||
|  |             sendDistanceUpdateMessage(positionId, distanceText, distanceColor); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 检查任务触发状态并发送通知 | ||||||
|  |      * Java7:使用迭代器遍历任务列表(替代forEach Lambda) | ||||||
|  |      */ | ||||||
|  |     private void checkAndTriggerTasks() { | ||||||
|  |         if (mAllPositionTasks.isEmpty()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         // Java7:Iterator遍历ArrayList(替代forEach Lambda) | ||||||
|  |         Iterator<PositionTaskModel> taskIterator = mAllPositionTasks.iterator(); | ||||||
|  |         while (taskIterator.hasNext()) { | ||||||
|  |             PositionTaskModel task = taskIterator.next(); | ||||||
|  |             if (task.isBingo() && task.isEnable()) { | ||||||
|  |                 NotificationUtils.show(getApplicationContext(), task.getTaskId(), task.getPositionId(), task.getTaskDescription()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 判断任务触发状态(仅修改数据,UI更新由Adapter处理) | ||||||
|  |      * Java7:迭代器遍历任务列表,显式状态判断 | ||||||
|  |      */ | ||||||
|  |     private void judgeTaskBingoStatus(PositionModel model, int distanceM) { | ||||||
|  |         String targetPosId = model.getPositionId(); | ||||||
|  |         // Java7:Iterator遍历任务列表 | ||||||
|  |         Iterator<PositionTaskModel> taskIterator = mAllPositionTasks.iterator(); | ||||||
|  |         while (taskIterator.hasNext()) { | ||||||
|  |             PositionTaskModel task = taskIterator.next(); | ||||||
|  |             if (targetPosId.equals(task.getPositionId())) { | ||||||
|  |                 boolean oldBingoState = task.isBingo(); | ||||||
|  |                 boolean newBingoState = false; | ||||||
|  |  | ||||||
|  |                 // 根据任务条件判断新状态(Java7:if-else替代三元表达式,增强可读性) | ||||||
|  |                 if (task.isGreaterThan()) { | ||||||
|  |                     newBingoState = task.isEnable() && distanceM > task.getDiscussDistance(); | ||||||
|  |                 } else if (task.isLessThan()) { | ||||||
|  |                     newBingoState = task.isEnable() && distanceM < task.getDiscussDistance(); | ||||||
|  |                 } else { | ||||||
|  |                     newBingoState = task.isEnable(); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // 更新任务状态(仅状态变化时更新) | ||||||
|  |                 if (newBingoState != oldBingoState) { | ||||||
|  |                     task.setIsBingo(newBingoState); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 发送距离更新消息(通过Handler发送到UI线程) | ||||||
|  |      * Java7:显式创建Message,避免obtainMessage链式调用 | ||||||
|  |      */ | ||||||
|  |     private void sendDistanceUpdateMessage(String positionId, String distanceText, int distanceColor) { | ||||||
|  |         Message msg = mMainHandler.obtainMessage(MSG_UPDATE_DISTANCE); | ||||||
|  |         // Java7:显式调用put方法(替代链式put) | ||||||
|  |         msg.getData().putString(KEY_POSITION_ID, positionId); | ||||||
|  |         msg.getData().putString(KEY_DISTANCE_TEXT, distanceText); | ||||||
|  |         msg.getData().putInt(KEY_DISTANCE_COLOR, distanceColor); | ||||||
|  |         mMainHandler.sendMessage(msg); | ||||||
|  |         LogUtils.d(TAG, "发送消息→位置ID=" + positionId + ",距离文本=" + distanceText); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据位置ID查找位置模型(工具方法) | ||||||
|  |      * Java7:迭代器遍历位置列表(替代stream().filter()) | ||||||
|  |      */ | ||||||
|  |     private PositionModel findPositionModelById(String positionId) { | ||||||
|  |         // Java7:Iterator遍历ArrayList | ||||||
|  |         Iterator<PositionModel> modelIterator = mPositionList.iterator(); | ||||||
|  |         while (modelIterator.hasNext()) { | ||||||
|  |             PositionModel model = modelIterator.next(); | ||||||
|  |             if (positionId.equals(model.getPositionId())) { | ||||||
|  |                 return model; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // ---------------------- 对外API(供Activity/Adapter调用) ---------------------- | ||||||
|  |     public void setOnDistanceUpdateReceiver(OnDistanceUpdateReceiver receiver) { | ||||||
|  |         this.mUpdateReceiver = receiver; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void syncCurrentGpsPosition(PositionModel currentGpsPosition) { | ||||||
|  |         this.mCurrentGpsPosition = currentGpsPosition; | ||||||
|  |         LogUtils.d(TAG, "同步GPS位置:纬度=" + (currentGpsPosition != null ? currentGpsPosition.getLatitude() : 0.0f)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void syncPositionList(ArrayList<PositionModel> positionList) { | ||||||
|  |         if (positionList != null) { | ||||||
|  |             this.mPositionList.clear(); | ||||||
|  |             this.mPositionList.addAll(positionList); | ||||||
|  |             LogUtils.d(TAG, "同步位置列表:数量=" + positionList.size()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void syncAllPositionTasks(ArrayList<PositionTaskModel> allPositionTasks) { | ||||||
|  |         if (allPositionTasks != null) { | ||||||
|  |             this.mAllPositionTasks.clear(); | ||||||
|  |             this.mAllPositionTasks.addAll(allPositionTasks); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void addVisibleDistanceView(String positionId) { | ||||||
|  |         if (positionId != null && !mVisibleDistanceViewTags.containsKey(positionId)) { | ||||||
|  |             mVisibleDistanceViewTags.put(positionId, 0); // 用0占位,仅标记存在 | ||||||
|  |             LogUtils.d(TAG, "添加可见位置ID:" + positionId + ",当前可见数量=" + mVisibleDistanceViewTags.size()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void removeVisibleDistanceView(String positionId) { | ||||||
|  |         if (positionId != null) { | ||||||
|  |             mVisibleDistanceViewTags.remove(positionId); | ||||||
|  |             LogUtils.d(TAG, "移除可见位置ID:" + positionId + ",当前可见数量=" + mVisibleDistanceViewTags.size()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void clearVisibleDistanceViews() { | ||||||
|  |         mVisibleDistanceViewTags.clear(); | ||||||
|  |         LogUtils.d(TAG, "清空所有可见位置ID"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onDestroy() { | ||||||
|  |         super.onDestroy(); | ||||||
|  |         Log.d(TAG, "DistanceRefreshService onDestroy - 定时器销毁"); | ||||||
|  |         // 销毁定时器,避免内存泄漏(Java7:显式判断非空) | ||||||
|  |         if (mDistanceTimer != null) { | ||||||
|  |             mDistanceTimer.cancel(); | ||||||
|  |             mDistanceTimer.purge(); | ||||||
|  |         } | ||||||
|  |         // 清空数据,解除引用(避免内存泄漏) | ||||||
|  |         mCurrentGpsPosition = null; | ||||||
|  |         mPositionList.clear(); | ||||||
|  |         mAllPositionTasks.clear(); | ||||||
|  |         mVisibleDistanceViewTags.clear(); | ||||||
|  |         mUpdateReceiver = null; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| @@ -32,18 +32,11 @@ | |||||||
| 			android:text="Positions" | 			android:text="Positions" | ||||||
| 			android:onClick="onPositions"/> | 			android:onClick="onPositions"/> | ||||||
|  |  | ||||||
| 	</LinearLayout> | 		<Button | ||||||
|  | 			android:layout_width="wrap_content" | ||||||
| 	<LinearLayout | 			android:layout_height="wrap_content" | ||||||
| 		android:orientation="vertical" | 			android:text="Log" | ||||||
| 		android:layout_width="match_parent" | 			android:onClick="onLog"/> | ||||||
| 		android:layout_height="0dp" |  | ||||||
| 		android:layout_weight="1.0"> |  | ||||||
|  |  | ||||||
| 		<cc.winboll.studio.libappbase.LogView |  | ||||||
| 			android:layout_width="match_parent" |  | ||||||
| 			android:layout_height="match_parent" |  | ||||||
| 			android:id="@+id/logview"/> |  | ||||||
|  |  | ||||||
| 	</LinearLayout> | 	</LinearLayout> | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 ZhanGSKen
					ZhanGSKen