窗口速切换数据丢失问题解决
This commit is contained in:
		| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Thu Oct 02 19:13:53 GMT 2025 | ||||
| #Thu Oct 02 19:24:27 GMT 2025 | ||||
| stageCount=8 | ||||
| libraryProject= | ||||
| baseVersion=15.0 | ||||
| publishVersion=15.0.7 | ||||
| buildCount=46 | ||||
| buildCount=48 | ||||
| baseBetaVersion=15.0.8 | ||||
|   | ||||
| @@ -23,9 +23,9 @@ import cc.winboll.studio.libappbase.ToastUtils; | ||||
| import cc.winboll.studio.positions.R; | ||||
| import cc.winboll.studio.positions.adapters.PositionAdapter; | ||||
| import cc.winboll.studio.positions.models.PositionModel; | ||||
| import cc.winboll.studio.positions.models.PositionTaskModel; | ||||
| import cc.winboll.studio.positions.services.MainService; | ||||
| import java.util.ArrayList; | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
|  | ||||
| /** | ||||
|  * Java 7 语法适配: | ||||
| @@ -39,39 +39,60 @@ public class LocationActivity extends Activity { | ||||
|     private RecyclerView mRvPosition; | ||||
|     private PositionAdapter mPositionAdapter; | ||||
|      | ||||
|     // MainService 引用+绑定状态 | ||||
|     // MainService 引用+绑定状态(AtomicBoolean 确保多线程状态可见性) | ||||
|     private MainService mMainService; | ||||
|     private boolean isServiceBound = false; | ||||
|     private final AtomicBoolean isServiceBound = new AtomicBoolean(false); | ||||
|     // 标记 Adapter 是否已初始化(避免重复初始化/销毁后初始化) | ||||
|     private final AtomicBoolean isAdapterInited = new AtomicBoolean(false); | ||||
|  | ||||
| 	// ---------------------- 新增:GPS监听核心变量 ---------------------- | ||||
|     private MainService.GpsUpdateListener mGpsUpdateListener; // GPS监听实例 | ||||
|     private PositionModel mCurrentGpsPos; // 缓存当前GPS位置(供页面使用) | ||||
|     // 本地位置缓存(解决服务数据未同步时Adapter空数据问题) | ||||
|     private final ArrayList<PositionModel> mLocalPosCache = new ArrayList<PositionModel>(); | ||||
|  | ||||
|  | ||||
|     // 服务连接(Java 7 匿名内部类实现) | ||||
|     // 服务连接(Java 7 匿名内部类实现,强化状态同步+数据预加载) | ||||
|     private ServiceConnection mServiceConnection = new ServiceConnection() { | ||||
|         @Override | ||||
|         public void onServiceConnected(ComponentName name, IBinder service) { | ||||
|             // 假设 MainService 用 LocalBinder 暴露实例(Java 7 强转) | ||||
|             MainService.LocalBinder binder = (MainService.LocalBinder) service; | ||||
|             mMainService = binder.getService(); | ||||
|             isServiceBound = true; | ||||
|             // 1. 安全获取服务实例(避免强转失败+服务未就绪) | ||||
|             if (!(service instanceof MainService.LocalBinder)) { | ||||
|                 LogUtils.e(TAG, "服务绑定失败:Binder类型不匹配(非MainService.LocalBinder)"); | ||||
|                 isServiceBound.set(false); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             LogUtils.d(TAG, "MainService绑定成功,开始同步数据"); | ||||
|             // 从MainService同步初始数据(位置+任务) | ||||
| 			//syncDataFromMainService(); | ||||
|             // 初始化Adapter(传入MainService实例,确保任务数据从服务获取) | ||||
|              | ||||
| 			registerGpsListener(); | ||||
|             try { | ||||
|                 MainService.LocalBinder binder = (MainService.LocalBinder) service; | ||||
|                 mMainService = binder.getService(); | ||||
|                 // 2. 标记服务绑定成功(原子操作,确保多线程可见) | ||||
|                 isServiceBound.set(true); | ||||
|                 LogUtils.d(TAG, "MainService绑定成功,开始同步数据+初始化Adapter"); | ||||
|  | ||||
| 			initPositionAdapter(); | ||||
|                 // 3. 同步服务数据到本地缓存(核心:先同步数据,再初始化Adapter) | ||||
|                 syncDataFromMainService(); | ||||
|                 // 4. 注册GPS监听(确保监听在Adapter前初始化,数据不丢失) | ||||
|                 registerGpsListener(); | ||||
|                 // 5. 初始化Adapter(传入本地缓存+服务实例,数据非空) | ||||
|                 initPositionAdapter(); | ||||
|  | ||||
|             } catch (Exception e) { | ||||
|                 LogUtils.d(TAG, "服务绑定后初始化失败:" + e.getMessage()); | ||||
|                 isServiceBound.set(false); | ||||
|                 mMainService = null; | ||||
|                 showToast("服务初始化失败,无法加载数据"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void onServiceDisconnected(ComponentName name) { | ||||
|             LogUtils.w(TAG, "MainService断开连接,清空引用"); | ||||
|             LogUtils.w(TAG, "MainService断开连接,清空引用+标记状态"); | ||||
|             // 1. 清空服务引用+标记绑定状态 | ||||
|             mMainService = null; | ||||
|             isServiceBound = false; | ||||
|             isServiceBound.set(false); | ||||
|             // 2. 标记Adapter未初始化(下次绑定需重新初始化) | ||||
|             isAdapterInited.set(false); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| @@ -80,132 +101,171 @@ public class LocationActivity extends Activity { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_location); | ||||
|  | ||||
|         // 初始化视图+本地缓存 | ||||
|         // 1. 初始化视图(优先执行,避免Adapter初始化时视图为空) | ||||
|         initView(); | ||||
|         //mLocalPosCache = new ArrayList<PositionModel>(); | ||||
|  | ||||
|         // 绑定MainService(确保Activity启动时就拿到服务实例) | ||||
|         // 2. 初始化GPS监听(提前创建,避免绑定服务后空指针) | ||||
|         initGpsUpdateListener(); | ||||
|         // 3. 绑定MainService(最后执行,确保视图/监听已就绪) | ||||
|         bindMainService(); | ||||
| 		 | ||||
| 		 | ||||
|  | ||||
| 		// 初始化GPS监听(提前创建,避免空指针) | ||||
| 		initGpsUpdateListener(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 初始化视图(RecyclerView) | ||||
|      * 初始化视图(RecyclerView)- 确保视图先于Adapter初始化 | ||||
|      */ | ||||
|     private void initView() { | ||||
|         mRvPosition = (RecyclerView) findViewById(R.id.rv_position_list); | ||||
|         // Java 7 显式设置布局管理器(LinearLayoutManager) | ||||
|         // 1. 显式设置布局管理器(避免Adapter设置时无布局管理器崩溃) | ||||
|         LinearLayoutManager layoutManager = new LinearLayoutManager(this); | ||||
|         layoutManager.setOrientation(LinearLayoutManager.VERTICAL); | ||||
|         mRvPosition.setLayoutManager(layoutManager); | ||||
|         // 2. 初始化本地缓存(避免首次加载时缓存为空) | ||||
|         mLocalPosCache.clear(); | ||||
|         LogUtils.d(TAG, "视图初始化完成(布局管理器+本地缓存已就绪)"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 绑定MainService(Java 7 显式Intent) | ||||
|      * 绑定MainService(Java 7 显式Intent,强化绑定安全性) | ||||
|      */ | ||||
|     private void bindMainService() { | ||||
|         Intent serviceIntent = new Intent(this, MainService.class); | ||||
|         // 绑定服务(BIND_AUTO_CREATE:服务不存在时自动创建) | ||||
|         bindService(serviceIntent, mServiceConnection, BIND_AUTO_CREATE); | ||||
|         LogUtils.d(TAG, "发起MainService绑定请求"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 从MainService同步数据(位置+任务) | ||||
|      */ | ||||
| //    private void syncDataFromMainService() { | ||||
| //        if (!isServiceBound || mMainService == null) { | ||||
| //            LogUtils.w(TAG, "同步数据失败:MainService未绑定"); | ||||
| //            showToast("服务未就绪,无法加载数据"); | ||||
| //            return; | ||||
| //        } | ||||
| // | ||||
| //        // 同步位置数据(从服务获取最新列表) | ||||
| //        ArrayList<PositionModel> servicePosList = mMainService.getPositionList(); | ||||
| //        if (servicePosList != null && !servicePosList.isEmpty()) { | ||||
| //            mLocalPosCache.clear(); | ||||
| //            mLocalPosCache.addAll(servicePosList); | ||||
| //            LogUtils.d(TAG, "从MainService同步位置数据完成:数量=" + mLocalPosCache.size()); | ||||
| //        } | ||||
| // | ||||
| //        // 同步任务数据(无需本地缓存,Adapter直接从服务获取) | ||||
| //        ArrayList<PositionTaskModel> serviceTaskList = mMainService.getAllTasks(); | ||||
| //        LogUtils.d(TAG, "从MainService同步任务数据完成:数量=" + serviceTaskList.size()); | ||||
| //    } | ||||
|  | ||||
|     /** | ||||
|      * 初始化PositionAdapter(核心:传入MainService实例) | ||||
|      */ | ||||
|     private void initPositionAdapter() { | ||||
|         if (mMainService == null) { | ||||
|             LogUtils.e(TAG, "初始化Adapter失败:MainService为空"); | ||||
|         // 1. 避免重复绑定(快速重建Activity时防止多绑定) | ||||
|         if (isServiceBound.get()) { | ||||
|             LogUtils.w(TAG, "无需重复绑定:MainService已绑定"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Java 7 显式初始化Adapter,传入上下文+本地位置缓存+MainService实例 | ||||
|         mPositionAdapter = new PositionAdapter(this, mMainService); | ||||
|  | ||||
|         // 设置Adapter回调(处理位置删除/保存,最终同步到MainService) | ||||
|         mPositionAdapter.setOnDeleteClickListener(new PositionAdapter.OnDeleteClickListener() { | ||||
| 				@Override | ||||
| 				public void onDeleteClick(int position) { | ||||
| 					// 删除逻辑:先删本地缓存,再调用MainService接口删服务数据 | ||||
| 					if (position < 0 || position >= mMainService.getPositionList().size()) { | ||||
| 						LogUtils.w(TAG, "删除位置失败:无效索引=" + position); | ||||
| 						return; | ||||
| 					} | ||||
| 					PositionModel deletePos = mMainService.getPositionList().get(position); | ||||
| 					if (deletePos != null && !deletePos.getPositionId().isEmpty()) { | ||||
| 						// 1. 调用MainService接口删除服务端数据 | ||||
| 						mMainService.removePosition(deletePos.getPositionId()); | ||||
| 						// 2. 删除本地缓存数据 | ||||
| 						//mPositionList.remove(position); | ||||
| 						// 3. 通知Adapter刷新 | ||||
| 						mPositionAdapter.notifyItemRemoved(position); | ||||
| 						showToast("删除位置成功:" + deletePos.getMemo()); | ||||
| 						LogUtils.d(TAG, "删除位置完成:ID=" + deletePos.getPositionId() + "(已同步MainService)"); | ||||
| 					} | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
|         mPositionAdapter.setOnSavePositionClickListener(new PositionAdapter.OnSavePositionClickListener() { | ||||
| 				@Override | ||||
| 				public void onSavePositionClick(int position, PositionModel updatedPos) { | ||||
| 					// 保存逻辑:先更本地缓存,再调用MainService接口更新服务数据 | ||||
| 					if (!isServiceBound || mMainService == null) { | ||||
| 						LogUtils.w(TAG, "保存位置失败:MainService未绑定"); | ||||
| 						showToast("服务未就绪,保存失败"); | ||||
| 						return; | ||||
| 					} | ||||
| 					if (position < 0 || position >= mMainService.getPositionList().size()) { | ||||
| 						LogUtils.w(TAG, "保存位置失败:无效索引=" + position); | ||||
| 						return; | ||||
| 					} | ||||
|  | ||||
| 					// 1. 调用MainService接口更新服务端数据 | ||||
| 					mMainService.updatePosition(updatedPos); | ||||
| 					// 2. 更新本地缓存数据 | ||||
| 					//mLocalPosCache.set(position, updatedPos); | ||||
| 					// 3. 通知Adapter刷新(可选,Adapter已本地同步) | ||||
| 					mPositionAdapter.notifyItemChanged(position); | ||||
| 					showToast("保存位置成功:" + updatedPos.getMemo()); | ||||
| 					LogUtils.d(TAG, "保存位置完成:ID=" + updatedPos.getPositionId() + "(已同步MainService)"); | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
|         // 设置Adapter到RecyclerView | ||||
|         mRvPosition.setAdapter(mPositionAdapter); | ||||
|         LogUtils.d(TAG, "PositionAdapter初始化完成(已绑定MainService)"); | ||||
|         Intent serviceIntent = new Intent(this, MainService.class); | ||||
|         // 2. 绑定服务(BIND_AUTO_CREATE:服务不存在时自动创建,增加绑定成功率) | ||||
|         boolean bindSuccess = bindService(serviceIntent, mServiceConnection, BIND_AUTO_CREATE); | ||||
|         if (!bindSuccess) { | ||||
|             LogUtils.e(TAG, "发起MainService绑定请求失败(服务未找到/系统限制)"); | ||||
|             showToast("服务绑定失败,无法加载位置数据"); | ||||
|         } else { | ||||
|             LogUtils.d(TAG, "MainService绑定请求已发起"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 显示Toast(Java 7 显式Toast.makeText) | ||||
|      * 从MainService同步数据到本地缓存(核心解决Adapter空数据问题) | ||||
|      * 作用:1. 服务数据优先同步到本地,Adapter基于本地缓存初始化 | ||||
|      *      2. 避免服务数据更新时直接操作Adapter,通过缓存中转 | ||||
|      */ | ||||
|     private void syncDataFromMainService() { | ||||
|         // 1. 安全校验(服务未绑定/服务空,用本地缓存兜底) | ||||
|         if (!isServiceBound.get() || mMainService == null) { | ||||
|             LogUtils.w(TAG, "同步数据:服务未就绪,使用本地缓存(当前缓存量=" + mLocalPosCache.size() + ")"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             // 2. 从服务获取最新位置数据(同步操作,确保数据拿到后再返回) | ||||
|             ArrayList<PositionModel> servicePosList = mMainService.getPositionList(); | ||||
|             // 3. 同步到本地缓存(清空旧数据+添加新数据,避免重复) | ||||
|             synchronized (mLocalPosCache) { // 加锁避免多线程操作缓存冲突 | ||||
|                 mLocalPosCache.clear(); | ||||
|                 if (servicePosList != null && !servicePosList.isEmpty()) { | ||||
|                     mLocalPosCache.addAll(servicePosList); | ||||
|                 } | ||||
|             } | ||||
|             LogUtils.d(TAG, "数据同步完成:服务位置数=" + (servicePosList == null ? 0 : servicePosList.size())  | ||||
|                       + ",本地缓存数=" + mLocalPosCache.size()); | ||||
|  | ||||
|         } catch (Exception e) { | ||||
|             LogUtils.d(TAG, "同步服务数据失败:" + e.getMessage()); | ||||
|             // 异常时保留本地缓存,避免Adapter无数据 | ||||
|             LogUtils.w(TAG, "同步失败,使用本地缓存兜底(缓存量=" + mLocalPosCache.size() + ")"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 初始化PositionAdapter(核心优化:基于本地缓存初始化,避免空数据) | ||||
|      */ | ||||
|     private void initPositionAdapter() { | ||||
|         // 1. 多重安全校验(避免销毁后初始化/重复初始化/依赖未就绪) | ||||
|         if (isAdapterInited.get() || !isServiceBound.get() || mMainService == null || mRvPosition == null) { | ||||
|             LogUtils.w(TAG, "Adapter初始化跳过:"  | ||||
|                       + "已初始化=" + isAdapterInited.get()  | ||||
|                       + ",服务绑定=" + isServiceBound.get()  | ||||
|                       + ",视图就绪=" + (mRvPosition != null)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             // 2. 基于本地缓存初始化Adapter(缓存已同步服务数据,非空) | ||||
|             mPositionAdapter = new PositionAdapter(this, mLocalPosCache, mMainService); | ||||
|  | ||||
|             // 3. 设置删除回调(删除时同步服务+本地缓存+Adapter) | ||||
|             mPositionAdapter.setOnDeleteClickListener(new PositionAdapter.OnDeleteClickListener() { | ||||
|                 @Override | ||||
|                 public void onDeleteClick(int position) { | ||||
|                     // 安全校验(索引有效+服务绑定+缓存非空) | ||||
|                     if (position < 0 || position >= mLocalPosCache.size() || !isServiceBound.get() || mMainService == null) { | ||||
|                         LogUtils.w(TAG, "删除位置失败:索引无效/服务未就绪(索引=" + position + ",缓存量=" + mLocalPosCache.size() + ")"); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     PositionModel deletePos = mLocalPosCache.get(position); | ||||
|                     if (deletePos != null && !deletePos.getPositionId().isEmpty()) { | ||||
|                         // 步骤1:调用服务删除(确保服务数据一致性) | ||||
|                         mMainService.removePosition(deletePos.getPositionId()); | ||||
|                         // 步骤2:删除本地缓存(确保缓存与服务同步) | ||||
|                         synchronized (mLocalPosCache) { | ||||
|                             mLocalPosCache.remove(position); | ||||
|                         } | ||||
|                         // 步骤3:通知Adapter刷新(基于缓存操作,避免空数据) | ||||
|                         mPositionAdapter.notifyItemRemoved(position); | ||||
|                         showToast("删除位置成功:" + deletePos.getMemo()); | ||||
|                         LogUtils.d(TAG, "删除位置完成:ID=" + deletePos.getPositionId() + "(服务+缓存已同步)"); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             // 4. 设置保存回调(保存时同步服务+本地缓存+Adapter) | ||||
|             mPositionAdapter.setOnSavePositionClickListener(new PositionAdapter.OnSavePositionClickListener() { | ||||
|                 @Override | ||||
|                 public void onSavePositionClick(int position, PositionModel updatedPos) { | ||||
|                     // 安全校验(索引有效+服务绑定+数据非空) | ||||
|                     if (!isServiceBound.get() || mMainService == null  | ||||
|                         || position < 0 || position >= mLocalPosCache.size() || updatedPos == null) { | ||||
|                         LogUtils.w(TAG, "保存位置失败:服务未就绪/索引无效/数据空"); | ||||
|                         showToast("服务未就绪,保存失败"); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     // 步骤1:调用服务更新(确保服务数据一致性) | ||||
|                     mMainService.updatePosition(updatedPos); | ||||
|                     // 步骤2:更新本地缓存(确保缓存与服务同步) | ||||
|                     synchronized (mLocalPosCache) { | ||||
|                         mLocalPosCache.set(position, updatedPos); | ||||
|                     } | ||||
|                     // 步骤3:通知Adapter刷新(基于缓存操作,避免空数据) | ||||
|                     mPositionAdapter.notifyItemChanged(position); | ||||
|                     showToast("保存位置成功:" + updatedPos.getMemo()); | ||||
|                     LogUtils.d(TAG, "保存位置完成:ID=" + updatedPos.getPositionId() + "(服务+缓存已同步)"); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             // 5. 设置Adapter到RecyclerView(最后一步,确保Adapter已配置完成) | ||||
|             mRvPosition.setAdapter(mPositionAdapter); | ||||
|             // 6. 标记Adapter已初始化(避免重复初始化) | ||||
|             isAdapterInited.set(true); | ||||
|             LogUtils.d(TAG, "PositionAdapter初始化完成(基于本地缓存,数据量=" + mLocalPosCache.size() + ")"); | ||||
|  | ||||
|         } catch (Exception e) { | ||||
|             LogUtils.d(TAG, "Adapter初始化失败:" + e.getMessage()); | ||||
|             isAdapterInited.set(false); | ||||
|             mPositionAdapter = null; | ||||
|             showToast("位置列表初始化失败,请重试"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 显示Toast(Java 7 显式Toast.makeText,避免空Context) | ||||
|      */ | ||||
|     private void showToast(String content) { | ||||
|         if (isFinishing() || isDestroyed()) { // 避免Activity销毁后弹Toast崩溃 | ||||
|             LogUtils.w(TAG, "Activity已销毁,跳过Toast:" + content); | ||||
|             return; | ||||
|         } | ||||
|         Toast.makeText(this, content, Toast.LENGTH_SHORT).show(); | ||||
|     } | ||||
| 	 | ||||
| @@ -214,36 +274,46 @@ public class LocationActivity extends Activity { | ||||
|      * 新增位置(调用服务addPosition(),可选:用当前GPS位置初始化新位置) | ||||
|      */ | ||||
|     public void addNewPosition(View view) { | ||||
|         // 1. 隐藏软键盘 | ||||
|         // 1. 隐藏软键盘(避免软键盘遮挡操作) | ||||
|         InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); | ||||
|         if (imm != null && getCurrentFocus() != null) { | ||||
|             imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); | ||||
|         } | ||||
|  | ||||
|         // 2. 安全校验(服务未绑定,不允许新增) | ||||
|         if (!isServiceBound.get() || mMainService == null) { | ||||
|             LogUtils.w(TAG, "新增位置失败:MainService未绑定"); | ||||
|             showToast("服务未就绪,无法新增位置"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 3. 创建新位置模型(优化:优先用当前GPS位置初始化,无则用默认值) | ||||
|         PositionModel newPos = new PositionModel(); | ||||
|         newPos.setPositionId(PositionModel.genPositionId()); // 生成唯一ID(需PositionModel实现) | ||||
|         // (新增)用当前GPS位置初始化新位置(提升用户体验,无需手动输入经纬度) | ||||
|         if (mCurrentGpsPos != null) { | ||||
|             newPos.setLongitude(mCurrentGpsPos.getLongitude()); | ||||
|             newPos.setLatitude(mCurrentGpsPos.getLatitude()); | ||||
|             newPos.setMemo("当前GPS位置(可编辑)"); | ||||
|         } else { | ||||
|             // 无GPS位置时用默认值 | ||||
|             newPos.setLongitude(116.404267); // 北京经度 | ||||
|             newPos.setLatitude(39.915119);  // 北京纬度 | ||||
|             newPos.setLongitude(116.404267); // 北京经度(默认值) | ||||
|             newPos.setLatitude(39.915119);  // 北京纬度(默认值) | ||||
|             newPos.setMemo("默认位置(可编辑备注)"); | ||||
|         } | ||||
|         newPos.setIsSimpleView(true);   // 默认简单视图 | ||||
|         newPos.setIsEnableRealPositionDistance(true); // 启用距离计算(依赖GPS) | ||||
|  | ||||
|         // 4. 调用服务PUBLIC方法新增 | ||||
|         // 4. 调用服务新增+同步本地缓存(确保缓存与服务一致) | ||||
|         mMainService.addPosition(newPos); | ||||
|         LogUtils.d(TAG, "通过服务新增位置:ID=" + newPos.getPositionId() + ",纬度=" + newPos.getLatitude()); | ||||
|         synchronized (mLocalPosCache) { | ||||
|             mLocalPosCache.add(newPos); | ||||
|         } | ||||
|         LogUtils.d(TAG, "通过服务新增位置:ID=" + newPos.getPositionId() + ",纬度=" + newPos.getLatitude() + "(缓存已同步)"); | ||||
|  | ||||
|         // 5. 刷新缓存+Adapter(显示新增结果+距离) | ||||
|         mPositionAdapter.notifyDataSetChanged(); | ||||
|         Toast.makeText(this, "新增位置成功(已启用GPS距离计算)", Toast.LENGTH_SHORT).show(); | ||||
|         // 5. 刷新Adapter(基于缓存操作,确保数据立即显示) | ||||
|         if (isAdapterInited.get() && mPositionAdapter != null) { | ||||
|             mPositionAdapter.notifyItemInserted(mLocalPosCache.size() - 1); | ||||
|         } | ||||
|         showToast("新增位置成功(已启用GPS距离计算)"); | ||||
|     } | ||||
|  | ||||
| 	// ---------------------- 新增:GPS监听初始化+注册/反注册(核心适配逻辑) ---------------------- | ||||
| @@ -251,95 +321,141 @@ public class LocationActivity extends Activity { | ||||
|      * 初始化GPS监听:实现MainService.GpsUpdateListener,接收实时GPS数据 | ||||
|      */ | ||||
|     private void initGpsUpdateListener() { | ||||
| 		LogUtils.d(TAG, "initGpsUpdateListener()"); | ||||
|         LogUtils.d(TAG, "initGpsUpdateListener()"); | ||||
|         mGpsUpdateListener = new MainService.GpsUpdateListener() { | ||||
|             // 回调1:GPS位置更新(实时接收经纬度,更新缓存+刷新Adapter) | ||||
|             @Override | ||||
|             public void onGpsPositionUpdated(PositionModel currentGpsPos) { | ||||
|                 if (currentGpsPos == null) { | ||||
|                     LogUtils.w(TAG, "GPS位置更新:数据为空"); | ||||
|                 if (currentGpsPos == null || isFinishing() || isDestroyed()) { | ||||
|                     LogUtils.w(TAG, "GPS位置更新:数据为空或Activity已销毁"); | ||||
|                     return; | ||||
|                 } | ||||
|                 // 缓存当前GPS位置(供页面其他逻辑使用) | ||||
|                 mCurrentGpsPos = currentGpsPos; | ||||
|                 LogUtils.d(TAG, String.format("收到GPS更新:纬度=%.4f,经度=%.4f" | ||||
| 											  , currentGpsPos.getLatitude(), currentGpsPos.getLongitude())); | ||||
| 				((TextView)findViewById(R.id.tv_latitude)).setText(String.format("当前纬度:%f", currentGpsPos.getLatitude())); | ||||
| 				((TextView)findViewById(R.id.tv_longitude)).setText(String.format("当前经度:%f", currentGpsPos.getLongitude())); | ||||
| 			} | ||||
|                                               , currentGpsPos.getLatitude(), currentGpsPos.getLongitude())); | ||||
|                 // 安全更新UI(避免Activity销毁后操作视图崩溃) | ||||
|                 ((TextView)findViewById(R.id.tv_latitude)).setText(String.format("当前纬度:%f", currentGpsPos.getLatitude())); | ||||
|                 ((TextView)findViewById(R.id.tv_longitude)).setText(String.format("当前经度:%f", currentGpsPos.getLongitude())); | ||||
|             } | ||||
|  | ||||
|             // 回调2:GPS状态变化(如开启/关闭、信号弱,提示用户) | ||||
|             @Override | ||||
|             public void onGpsStatusChanged(String status) { | ||||
|                 if (status == null) return; | ||||
|                 if (status == null || isFinishing() || isDestroyed()) return; | ||||
|                 LogUtils.d(TAG, "GPS状态变化:" + status); | ||||
|                 // 显示GPS状态(可通过TextView在页面上展示,此处用Toast示例) | ||||
|                 if (status.contains("未开启") || status.contains("权限") || status.contains("失败")) { | ||||
|                     // 异常状态:弹出提示引导用户处理 | ||||
|                     ToastUtils.show("GPS提示:" + status); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| 	/** | ||||
|      * 注册GPS监听:调用MainService的PUBLIC方法,绑定监听 | ||||
|      */ | ||||
|     private void registerGpsListener() { | ||||
| 		LogUtils.d(TAG, "registerGpsListener()"); | ||||
|         if (mMainService == null || mGpsUpdateListener == null) { | ||||
|             LogUtils.w(TAG, "GPS监听注册失败:服务未初始化或监听未创建"); | ||||
|         // 安全校验(避免Activity销毁/服务未绑定/监听为空时注册) | ||||
|         if (isFinishing() || isDestroyed() || !isServiceBound.get() || mMainService == null || mGpsUpdateListener == null) { | ||||
|             LogUtils.w(TAG, "GPS监听注册跳过:Activity状态异常/依赖未就绪"); | ||||
|             return; | ||||
|         } | ||||
|         // 调用MainService的registerGpsUpdateListener方法注册 | ||||
|         mMainService.registerGpsUpdateListener(mGpsUpdateListener); | ||||
|         LogUtils.d(TAG, "GPS监听已注册"); | ||||
|         try { | ||||
|             mMainService.registerGpsUpdateListener(mGpsUpdateListener); | ||||
|             LogUtils.d(TAG, "GPS监听已注册"); | ||||
|         } catch (Exception e) { | ||||
|             LogUtils.d(TAG, "GPS监听注册失败:" + e.getMessage()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 反注册GPS监听:调用MainService的PUBLIC方法,解绑监听(核心防内存泄漏) | ||||
|      * 反注册GPS监听:调用MainService的PUBLIC方法,解绑监听(核心防内存泄漏+数据异常) | ||||
|      */ | ||||
|     private void unregisterGpsListener() { | ||||
|         // 避免Activity销毁后调用服务方法(防止空指针/服务已解绑) | ||||
|         if (mMainService == null || mGpsUpdateListener == null) { | ||||
|             LogUtils.w(TAG, "GPS监听反注册失败:服务未初始化或监听未创建"); | ||||
|             LogUtils.w(TAG, "GPS监听反注册跳过:服务/监听未初始化"); | ||||
|             return; | ||||
|         } | ||||
|         // 调用MainService的unregisterGpsUpdateListener方法反注册 | ||||
|         mMainService.unregisterGpsUpdateListener(mGpsUpdateListener); | ||||
|         LogUtils.d(TAG, "GPS监听已反注册"); | ||||
|         try { | ||||
|             mMainService.unregisterGpsUpdateListener(mGpsUpdateListener); | ||||
|             LogUtils.d(TAG, "GPS监听已反注册"); | ||||
|         } catch (Exception e) { | ||||
|             LogUtils.d(TAG, "GPS监听反注册失败:" + e.getMessage()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 页面可见时同步数据(解决快速切回时数据未更新问题) | ||||
|      * 场景:快速关闭再打开Activity,服务已绑定但数据未重新同步 | ||||
|      */ | ||||
|     @Override | ||||
|     protected void onResume() { | ||||
|         super.onResume(); | ||||
|         // 1. 服务已绑定但Adapter未初始化:重新同步数据+初始化Adapter | ||||
|         if (isServiceBound.get() && mMainService != null && !isAdapterInited.get()) { | ||||
|             LogUtils.d(TAG, "onResume:服务已绑定但Adapter未初始化,重新同步数据"); | ||||
|             syncDataFromMainService(); | ||||
|             initPositionAdapter(); | ||||
|         } | ||||
|         // 2. 服务已绑定且Adapter已初始化:刷新数据(确保与服务同步) | ||||
|         else if (isServiceBound.get() && mMainService != null && isAdapterInited.get() && mPositionAdapter != null) { | ||||
|             syncDataFromMainService(); | ||||
|             mPositionAdapter.notifyDataSetChanged(); | ||||
|             LogUtils.d(TAG, "onResume:刷新位置数据(与服务同步)"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 页面不可见时暂停操作(避免后台操作导致数据异常) | ||||
|      */ | ||||
|     @Override | ||||
|     protected void onPause() { | ||||
|         super.onPause(); | ||||
|         // 避免后台时仍执行UI刷新(如GPS更新触发的视图操作) | ||||
|         LogUtils.d(TAG, "onPause:页面不可见,暂停UI相关操作"); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         LogUtils.d(TAG, "onDestroy:开始释放资源"); | ||||
|  | ||||
|         // 1. 释放Adapter资源(反注册服务监听,避免内存泄漏) | ||||
|         // 1. 反注册GPS监听(优先执行,避免服务持有Activity引用导致内存泄漏) | ||||
|         unregisterGpsListener(); | ||||
|  | ||||
|         // 2. 释放Adapter资源(反注册可能的监听,避免内存泄漏) | ||||
|         if (mPositionAdapter != null) { | ||||
|             mPositionAdapter.release(); | ||||
|             mPositionAdapter = null; // 清空引用,帮助GC回收 | ||||
|             LogUtils.d(TAG, "Adapter资源已释放"); | ||||
|         } | ||||
|  | ||||
|         // 2. 解绑MainService(避免Activity销毁后服务仍被持有) | ||||
|         if (isServiceBound) { | ||||
|             unbindService(mServiceConnection); | ||||
|             LogUtils.d(TAG, "MainService解绑完成"); | ||||
|         // 3. 解绑MainService(最后执行,确保其他资源已释放) | ||||
|         if (isServiceBound.get()) { | ||||
|             try { | ||||
|                 unbindService(mServiceConnection); | ||||
|                 LogUtils.d(TAG, "MainService解绑完成"); | ||||
|             } catch (IllegalArgumentException e) { | ||||
|                 // 捕获“服务未绑定”异常(快速开关时可能出现,避免崩溃) | ||||
|                 LogUtils.d(TAG, "解绑MainService失败:服务未绑定(可能已提前解绑)"); | ||||
|             } | ||||
|             // 重置绑定状态+服务引用 | ||||
|             isServiceBound.set(false); | ||||
|             mMainService = null; | ||||
|         } | ||||
|  | ||||
| 		unregisterGpsListener(); | ||||
|         // 4. 清空本地缓存+GPS引用(帮助GC回收) | ||||
|         synchronized (mLocalPosCache) { | ||||
|             mLocalPosCache.clear(); | ||||
|         } | ||||
|         mCurrentGpsPos = null; | ||||
|         mGpsUpdateListener = null; | ||||
|         isAdapterInited.set(false); | ||||
|         LogUtils.d(TAG, "所有资源释放完成(onDestroy执行结束)"); | ||||
|     } | ||||
|  | ||||
| 	public static class LocalBinder extends android.os.Binder { | ||||
|         // 持有 MainService 实例引用 | ||||
|         private MainService mService; | ||||
|  | ||||
|         // 构造时传入服务实例 | ||||
|         public LocalBinder(MainService service) { | ||||
|             this.mService = service; | ||||
|         } | ||||
|  | ||||
|         // 对外提供获取服务实例的方法(供Activity调用) | ||||
|         public MainService getService() { | ||||
|             return mService; | ||||
|         } | ||||
|     } | ||||
|     // ---------------------- 移除重复定义:LocalBinder 统一在 MainService 中定义 ---------------------- | ||||
|     // 说明:原LocationActivity中的LocalBinder是重复定义(MainService已实现),会导致类型强转失败 | ||||
|     // 此处删除该类,确保Activity绑定服务时强转的是MainService中的LocalBinder | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -72,10 +72,10 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde | ||||
|     // ========================================================================= | ||||
|     // 构造函数(Java 7 风格:初始化依赖+注册任务监听) | ||||
|     // ========================================================================= | ||||
|     public PositionAdapter(Context context, MainService mainService) { | ||||
|     public PositionAdapter(Context context, ArrayList<PositionModel> cachedPositionList, MainService mainService) { | ||||
|         this.mContext = context; | ||||
|         // 容错处理:避免传入null导致空指针 | ||||
|         this.mCachedPositionList = (mainService.getPositionList() != null) ? mainService.getPositionList() : new ArrayList<PositionModel>(); | ||||
|         this.mCachedPositionList = (cachedPositionList != null) ? cachedPositionList : new ArrayList<PositionModel>(); | ||||
|         // 弱引用MainService:防止Adapter持有Service导致内存泄漏(Java 7 弱引用语法) | ||||
|         this.mMainServiceRef = new WeakReference<MainService>(mainService); | ||||
|         // 初始化距离控件缓存(线程安全集合,适配多线程更新场景) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 ZhanGSKen
					ZhanGSKen