You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WebView定时任务与Partial WakeLock失效问题求助

解决WebView中JavaScript定时任务锁屏后停止的问题

这个问题我之前帮不少开发者排查过,核心原因其实是Android的后台限制机制在“搞鬼”——尤其是Android 6.0+引入的Doze模式,再加上很多国产厂商的自定义电池优化,Partial Wake Lock在这些机制面前优先级不够高,自然拦不住系统限制WebView的JS运行。

下面给你几个可行的解决方案,按实用性排序:

1. 用前台服务(Foreground Service)持有Wake Lock

前台服务会被系统认定为“用户主动关注的任务”,不会被Doze模式限制,只是需要显示一个常驻通知(这是Android的强制要求),确保App在后台能稳定运行。

步骤与代码示例:

  • 创建一个前台服务类,在其中管理Wake Lock:
public class KeepAliveService extends Service {
    private PowerManager.WakeLock mWakeLock;

    @Override
    public void onCreate() {
        super.onCreate();
        // 获取Partial Wake Lock(保持CPU运行,屏幕可关闭)
        PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp:KeepAliveLock");
        mWakeLock.acquire();

        // 创建前台通知(Android 8.0+必须绑定通知渠道)
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "BACKGROUND_CHANNEL")
                .setSmallIcon(R.drawable.ic_notification)
                .setContentTitle("后台服务运行中")
                .setContentText("正在保持API定时调用")
                .setPriority(NotificationCompat.PRIORITY_LOW);

        // 启动前台服务
        startForeground(1001, builder.build());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 释放Wake Lock,避免耗电
        if (mWakeLock != null && mWakeLock.isHeld()) {
            mWakeLock.release();
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
  • 在你的WebView所属Activity中启动服务:
Intent serviceIntent = new Intent(this, KeepAliveService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}
  • 别忘了在Manifest中注册服务和通知渠道(Android 8.0+需要):
<service android:name=".KeepAliveService" />

<!-- 通知渠道声明(可在Application标签内) -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_channel_id"
    android:value="BACKGROUND_CHANNEL" />

2. 把JS定时逻辑迁移到Native层

WebView在后台很容易被系统暂停或回收,不如把“每分钟调用API”的定时逻辑放到Android原生代码中,再定时触发WebView的JS方法。这样即使WebView被暂时冻结,Native的定时任务依然能稳定运行。

代码示例(用Handler实现):

private Handler mApiHandler = new Handler();
private Runnable mApiCallTask = new Runnable() {
    @Override
    public void run() {
        // 调用WebView中的JS方法(假设你的JS方法叫callApi())
        mWebView.evaluateJavascript("callApi()", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String value) {
                // 可在此处理JS返回的结果
            }
        });
        // 60秒后再次执行
        mApiHandler.postDelayed(this, 60 * 1000);
    }
};

// 在Activity的onResume中启动定时
@Override
protected void onResume() {
    super.onResume();
    mApiHandler.post(mApiCallTask);
}

// 注意:如果需要后台持续运行,不要在onPause中移除回调;否则可以移除避免耗电
@Override
protected void onPause() {
    super.onPause();
    // mApiHandler.removeCallbacks(mApiCallTask);
}

3. 请求忽略电池优化

引导用户把你的App加入系统电池优化白名单,这样Doze模式就不会限制App的后台活动。你可以通过代码申请权限,跳转到系统设置页面让用户确认。

代码示例:

PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    boolean isIgnoringOptimizations = powerManager.isIgnoringBatteryOptimizations(getPackageName());
    if (!isIgnoringOptimizations) {
        Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivity(intent);
    }
}

最后检查Wake Lock的使用是否正确

确保你在代码中调用了mWakeLock.acquire(),并且在Activity/Service销毁时调用mWakeLock.release()避免不必要的耗电。另外,确认你用的是PARTIAL_WAKE_LOCK类型——这个类型是保持CPU运行,允许屏幕关闭,正好符合你的需求,但它确实绕不过Doze模式,所以必须配合前面的方案使用。

内容的提问来源于stack exchange,提问作者phongnt

火山引擎 最新活动