You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

后台网络连接检测、Service中网络检测及短信监听离线同步问题咨询

后台网络连接检测、Service中网络检测及短信监听离线同步问题咨询

Hey Kriss,针对你提出的两个Android开发场景问题,我整理了实操性很强的解决方案,都是项目里常用的思路,一起来看看:

一、Service里怎么像Activity一样检测网络连接

其实Service作为Context的子类,获取网络状态的逻辑和Activity本质一致,只是要注意权限适配和高版本系统的限制:

1. 基础网络状态判断

首先得在AndroidManifest.xml里加必要权限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

然后可以写个工具方法(放在Service里或者单独的工具类都可以),兼顾新旧版本系统:

private boolean isNetworkAvailable(Context context) {
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    if (cm == null) return false;

    // Android 7.0+推荐用NetworkCapabilities(更准确)
    Network activeNetwork = cm.getActiveNetwork();
    if (activeNetwork != null) {
        NetworkCapabilities capabilities = cm.getNetworkCapabilities(activeNetwork);
        return capabilities != null && 
               (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) 
                || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR));
    }

    // 兼容旧版本的NetworkInfo方式(虽然已标记deprecated,但适配低版本还是能用)
    NetworkInfo legacyNetworkInfo = cm.getActiveNetworkInfo();
    return legacyNetworkInfo != null && legacyNetworkInfo.isConnected();
}

在Service里直接调用这个方法,就能快速判断当前是否有可用网络啦。

2. 实时监听网络状态变化

如果需要实时感知网络从断开到恢复的变化(这对你后面同步短信数据很重要),推荐用ConnectivityManager.NetworkCallback——替代旧的广播监听,高版本系统下更可靠:

private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        super.onAvailable(network);
        // 网络恢复啦!这里直接触发同步短信到服务器的逻辑
        syncUnsyncedSmsToServer();
    }

    @Override
    public void onLost(Network network) {
        super.onLost(network);
        // 网络断开了,你可以做一些本地状态标记,比如暂停同步任务
    }
};

// 在Service的onCreate里注册监听
@Override
public void onCreate() {
    super.onCreate();
    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    if (cm != null) {
        NetworkRequest networkRequest = new NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .build();
        cm.registerNetworkCallback(networkRequest, networkCallback);
    }
}

// 别忘了解绑!在Service销毁时执行
@Override
public void onDestroy() {
    super.onDestroy();
    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    if (cm != null) {
        cm.unregisterNetworkCallback(networkCallback);
    }
}

二、短信监听+离线缓存+网络恢复同步

你的需求核心是“特定短信存本地→离线时暂存→有网时自动同步”,这个场景的关键是本地持久化+网络触发+重试兜底:

1. 监听特定短信并存入本地DB

首先要申请权限:Android 10及以上需要READ_SMS和RECEIVE_SMS危险权限,记得要动态申请(用户同意后才能用)。然后用BroadcastReceiver监听短信:

public class TargetSmsReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
            Bundle bundle = intent.getExtras();
            if (bundle == null) return;

            Object[] pdus = (Object[]) bundle.get("pdus");
            if (pdus == null) return;

            for (Object pdu : pdus) {
                SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdu);
                String smsContent = sms.getMessageBody();
                String senderNumber = sms.getOriginatingAddress();

                // 筛选包含特定关键词的短信
                if (smsContent.contains("你的目标关键词")) {
                    // 存入本地DB,推荐用Room,给每条数据加个`isSynced`字段(默认false,表示未同步)
                    SmsRecord smsRecord = new SmsRecord();
                    smsRecord.setContent(smsContent);
                    smsRecord.setSender(senderNumber);
                    smsRecord.setIsSynced(false);
                    smsRecord.setCreateTime(System.currentTimeMillis());

                    // 调用Room的DAO插入数据
                    AppDatabase.getInstance(context).smsRecordDao().insert(smsRecord);
                }
            }
        }
    }
}

注意:Android 8.0+静态注册广播受限,建议在Service或者Application里动态注册Receiver:

// 在Service的onCreate里注册Receiver
TargetSmsReceiver smsReceiver = new TargetSmsReceiver();
IntentFilter filter = new IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION);
registerReceiver(smsReceiver, filter);

// 同样在onDestroy里解绑
unregisterReceiver(smsReceiver);

2. 离线缓存与网络恢复同步

没网的时候,所有符合条件的短信都存在本地DB,标记为未同步。当网络恢复时(就是前面NetworkCallback的onAvailable方法里),执行同步逻辑:

private void syncUnsyncedSmsToServer() {
    // 从DB查询所有未同步的短信
    List<SmsRecord> unsyncedRecords = AppDatabase.getInstance(this).smsRecordDao().getAllUnsyncedRecords();
    if (unsyncedRecords.isEmpty()) return;

    // 用Retrofit/OkHttp上传到服务器,这里是示例代码
    ApiService apiService = RetrofitClient.getInstance().getApiService();
    Call<Void> uploadCall = apiService.uploadSmsRecords(unsyncedRecords);
    uploadCall.enqueue(new Callback<Void>() {
        @Override
        public void onResponse(Call<Void> call, Response<Void> response) {
            if (response.isSuccessful()) {
                // 上传成功,把这些数据标记为已同步
                AppDatabase.getInstance(MyService.this).smsRecordDao().markRecordsAsSynced(unsyncedRecords);
            } else {
                // 上传失败,安排重试
                scheduleSyncRetry();
            }
        }

        @Override
        public void onFailure(Call<Void> call, Throwable t) {
            // 请求失败,同样触发重试
            scheduleSyncRetry();
        }
    });
}

3. 重试兜底(避免同步失败丢数据)

如果上传过程中网络又断了,或者服务器返回错误,推荐用WorkManager做后台重试——它会自动处理系统休眠、重启等情况,非常省心:

private void scheduleSyncRetry() {
    OneTimeWorkRequest syncWork = new OneTimeWorkRequest.Builder(SmsSyncWorker.class)
            // 指数退避重试:第一次等10秒,第二次20秒,以此类推,避免频繁请求服务器
            .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, OneTimeWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)
            .build();
    WorkManager.getInstance(this).enqueue(syncWork);
}

// 对应的Worker类,后台执行同步任务
public class SmsSyncWorker extends Worker {
    public SmsSyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        List<SmsRecord> unsyncedRecords = AppDatabase.getInstance(getApplicationContext()).smsRecordDao().getAllUnsyncedRecords();
        if (unsyncedRecords.isEmpty()) return Result.success();

        ApiService apiService = RetrofitClient.getInstance().getApiService();
        try {
            Response<Void> response = apiService.uploadSmsRecords(unsyncedRecords).execute();
            if (response.isSuccessful()) {
                AppDatabase.getInstance(getApplicationContext()).smsRecordDao().markRecordsAsSynced(unsyncedRecords);
                return Result.success();
            } else {
                // 上传失败,告诉WorkManager稍后重试
                return Result.retry();
            }
        } catch (IOException e) {
            e.printStackTrace();
            return Result.retry();
        }
    }
}

备注:内容来源于stack exchange,提问作者Kriss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 15:27:54