diff --git a/positions/build.properties b/positions/build.properties index 97274ced..1498f60c 100644 --- a/positions/build.properties +++ b/positions/build.properties @@ -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 diff --git a/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java b/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java index 4d825051..c254e8ae 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java +++ b/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java @@ -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 mLocalPosCache = new ArrayList(); - // 服务连接(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(); - - // 绑定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 servicePosList = mMainService.getPositionList(); -// if (servicePosList != null && !servicePosList.isEmpty()) { -// mLocalPosCache.clear(); -// mLocalPosCache.addAll(servicePosList); -// LogUtils.d(TAG, "从MainService同步位置数据完成:数量=" + mLocalPosCache.size()); -// } -// -// // 同步任务数据(无需本地缓存,Adapter直接从服务获取) -// ArrayList 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 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 } + diff --git a/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java b/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java index 17313f3e..ab60875d 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java +++ b/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java @@ -72,10 +72,10 @@ public class PositionAdapter extends RecyclerView.Adapter cachedPositionList, MainService mainService) { this.mContext = context; // 容错处理:避免传入null导致空指针 - this.mCachedPositionList = (mainService.getPositionList() != null) ? mainService.getPositionList() : new ArrayList(); + this.mCachedPositionList = (cachedPositionList != null) ? cachedPositionList : new ArrayList(); // 弱引用MainService:防止Adapter持有Service导致内存泄漏(Java 7 弱引用语法) this.mMainServiceRef = new WeakReference(mainService); // 初始化距离控件缓存(线程安全集合,适配多线程更新场景)