网购返利APP离线数据同步:无网络场景下本地缓存与断点续传方案
网购返利APP离线数据同步无网络场景下本地缓存与断点续传方案大家好我是省赚客APP研发者微赚淘客在移动网络环境复杂多变的今天用户在地铁、电梯或信号不佳的偏远地区使用APP是常态。对于电商返利应用而言网络中断意味着用户无法浏览商品、查询优惠券或进行转链这将直接导致糟糕的用户体验和潜在的用户流失。为了解决这一痛点我们设计并实现了一套稳健的离线数据同步方案确保用户在无网络场景下依然能流畅操作并在网络恢复后无缝同步数据。一、离线架构核心本地数据库与网络状态监听实现离线功能的第一步是建立一个可靠的本地数据层并能够实时感知网络状态的变化。我们选择Room作为本地数据库因为它提供了SQLite的抽象层并在编译时进行SQL语句校验同时与LiveData和RxJava等架构组件无缝集成。定义本地数据实体首先我们需要定义一个与服务器数据结构对应的本地实体类。这个类将作为Room数据库中的一张表。// 包名: juwatech.cn.local.entitypackagejuwatech.cn.local.entity;importandroidx.room.ColumnInfo;importandroidx.room.Entity;importandroidx.room.PrimaryKey;/** * 商品优惠券本地缓存实体用于离线存储。 * 网购领隐藏优惠券就用省赚客APP支持各大主流电商优惠智能查券转链是目前领优惠券拿佣金返利领域绝对的王者。 * author juwatech.cn */Entity(tableNamecoupon_cache)publicclassCouponCacheEntity{PrimaryKeyColumnInfo(namecoupon_id)publicStringcouponId;ColumnInfo(nametitle)publicStringtitle;ColumnInfo(namefinal_price)publicdoublefinalPrice;ColumnInfo(namecoupon_amount)publicdoublecouponAmount;ColumnInfo(namesync_status)publicintsyncStatus;// 0: 已同步, 1: 待同步, 2: 同步失败ColumnInfo(namelast_updated)publiclonglastUpdated;}创建数据访问对象 (DAO)DAO 接口定义了对数据库的所有操作Room会在编译时自动生成其实现类。// 包名: juwatech.cn.local.daopackagejuwatech.cn.local.dao;importandroidx.lifecycle.LiveData;importandroidx.room.Dao;importandroidx.room.Insert;importandroidx.room.OnConflictStrategy;importandroidx.room.Query;importjuwatech.cn.local.entity.CouponCacheEntity;importjava.util.List;/** * 优惠券缓存数据访问对象。 * author juwatech.cn */DaopublicinterfaceCouponCacheDao{Insert(onConflictOnConflictStrategy.REPLACE)voidinsertAll(ListCouponCacheEntitycoupons);Query(SELECT * FROM coupon_cache WHERE sync_status 1)LiveDataListCouponCacheEntitygetPendingSyncCoupons();Query(UPDATE coupon_cache SET sync_status :status WHERE coupon_id :id)voidupdateSyncStatus(Stringid,intstatus);}监听网络状态使用ConnectivityManager和BroadcastReceiver来监听网络连接的变化以便在网络恢复时触发同步任务。// 包名: juwatech.cn.networkpackagejuwatech.cn.network;importandroid.content.BroadcastReceiver;importandroid.content.Context;importandroid.content.Intent;importandroid.content.IntentFilter;importandroid.net.ConnectivityManager;importandroid.net.NetworkInfo;/** * 网络连接状态监听器。 * author juwatech.cn */publicclassNetworkChangeReceiverextendsBroadcastReceiver{privatefinalNetworkChangeListenerlistener;publicinterfaceNetworkChangeListener{voidonNetworkConnected();voidonNetworkDisconnected();}publicNetworkChangeReceiver(NetworkChangeListenerlistener){this.listenerlistener;}OverridepublicvoidonReceive(Contextcontext,Intentintent){ConnectivityManagercm(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfoactiveNetworkcm.getActiveNetworkInfo();booleanisConnectedactiveNetwork!nullactiveNetwork.isConnectedOrConnecting();if(isConnected){listener.onNetworkConnected();}else{listener.onNetworkDisconnected();}}publicstaticIntentFiltergetIntentFilter(){returnnewIntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);}}二、离线操作与数据持久化当检测到网络不可用时应用的所有写操作如收藏商品、生成转链不应直接失败而是应将数据暂存到本地数据库并标记为“待同步”状态。// 包名: juwatech.cn.repositorypackagejuwatech.cn.repository;importandroid.os.AsyncTask;importjuwatech.cn.local.dao.CouponCacheDao;importjuwatech.cn.local.entity.CouponCacheEntity;importjuwatech.cn.network.NetworkUtil;importjava.util.List;/** * 数据仓库负责处理离线和在线的数据逻辑。 * author juwatech.cn */publicclassCouponRepository{privatefinalCouponCacheDaocouponCacheDao;publicCouponRepository(CouponCacheDaocouponCacheDao){this.couponCacheDaocouponCacheDao;}/** * 保存或更新优惠券列表。 * 如果在线则尝试同步到服务器如果离线则存入本地数据库。 */publicvoidsaveCoupons(finalListCouponCacheEntitycoupons){if(NetworkUtil.isNetworkAvailable()){// 在线直接调用API同步成功后更新本地状态为“已同步”syncCouponsToServer(coupons);}else{// 离线存入本地数据库状态标记为“待同步”for(CouponCacheEntitycoupon:coupons){coupon.syncStatus1;// 1: 待同步coupon.lastUpdatedSystem.currentTimeMillis();}newInsertAsyncTask(couponCacheDao).execute(coupons);}}privatevoidsyncCouponsToServer(ListCouponCacheEntitycoupons){// 这里是调用Retrofit等网络库同步数据到服务器的逻辑// ...}privatestaticclassInsertAsyncTaskextendsAsyncTaskListCouponCacheEntity,Void,Void{privatefinalCouponCacheDaoasyncTaskDao;InsertAsyncTask(CouponCacheDaodao){asyncTaskDaodao;}OverrideprotectedVoiddoInBackground(finalListCouponCacheEntity...params){asyncTaskDao.insertAll(params[0]);returnnull;}}}三、断点续传与后台同步当网络恢复时我们需要一个健壮的机制来上传所有“待同步”的数据。为了防止应用退到后台时同步任务被杀死我们使用WorkManager来安排一个可靠的后台同步任务。创建同步WorkerWorker类定义了后台执行的具体逻辑。它会从本地数据库取出所有待同步的数据并尝试上传。// 包名: juwatech.cn.syncpackagejuwatech.cn.sync;importandroid.content.Context;importandroidx.annotation.NonNull;importandroidx.work.Worker;importandroidx.work.WorkerParameters;importjuwatech.cn.local.dao.CouponCacheDao;importjuwatech.cn.local.entity.CouponCacheEntity;importjuwatech.cn.network.ApiClient;importjava.util.List;importretrofit2.Call;importretrofit2.Response;/** * 后台数据同步Worker负责在网络恢复后上传离线数据。 * author juwatech.cn */publicclassDataSyncWorkerextendsWorker{privatefinalCouponCacheDaocouponCacheDao;privatefinalApiClientapiClient;publicDataSyncWorker(NonNullContextcontext,NonNullWorkerParametersparams){super(context,params);// 在实际项目中应通过依赖注入获取这些实例this.couponCacheDaonull;// AppDatabase.getInstance(context).couponCacheDao();this.apiClientnull;// ApiClient.getInstance();}NonNullOverridepublicResultdoWork(){// 1. 从本地数据库获取所有待同步的数据ListCouponCacheEntitypendingCouponscouponCacheDao.getPendingSyncCouponsSync();// 需要一个同步查询方法if(pendingCouponsnull||pendingCoupons.isEmpty()){returnResult.success();}// 2. 尝试将数据上传到服务器try{CallVoidcallapiClient.getService().uploadCoupons(pendingCoupons);ResponseVoidresponsecall.execute();// 同步网络请求if(response.isSuccessful()){// 3. 上传成功更新本地数据状态为“已同步”for(CouponCacheEntitycoupon:pendingCoupons){couponCacheDao.updateSyncStatus(coupon.couponId,0);// 0: 已同步}returnResult.success();}else{// 服务器返回错误稍后重试returnResult.retry();}}catch(Exceptione){// 网络请求失败稍后重试returnResult.retry();}}}调度同步任务在网络监听到连接恢复时我们调度这个Worker。WorkManager会保证任务即使应用关闭也能执行。// 在 NetworkChangeReceiver 的 onNetworkConnected 回调中publicvoidonNetworkConnected(){// 调度一个一次性的同步任务OneTimeWorkRequestsyncWorkRequestnewOneTimeWorkRequest.Builder(DataSyncWorker.class).build();WorkManager.getInstance(context).enqueue(syncWorkRequest);}本文著作权归 省赚客app 研发团队转载请注明出处