屏幕关闭/锁定后手机震动停止工作

12

我有一个检查网站更新的服务,我希望它能在发现更新时引起振动,即使屏幕关闭或锁定。

目前只有在屏幕没有关闭/锁定时才会有震动。即使屏幕关闭/锁定,所有其他功能都可以正常工作。

Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
   long[] pattern = new long[]{0, 400, 200, 400};
   if (Build.VERSION.SDK_INT >= 26) { // New API
     vibrator.vibrate(VibrationEffect.createWaveform(pattern,0));
   } else { // Old API
     vibrator.vibrate(pattern, 0);
}

即使手机屏幕关闭,我该如何使其震动?我尝试使用WakeLock,但貌似这不是问题的原因。

当屏幕开启时,一切都正常,我已经设置好了所有权限。


你在测试哪个设备? - Natig Babayev
1
使用高优先级通道的通知怎么样? - Saurabh
你不应该一直轮询你的网站以获取更新。这会在设备上使用大量数据,耗尽电池,并且还会给你的网站带来很大的负载。更好的解决方案是当有更新可用时,从服务器向你的应用程序发送通知。 - Ridcully
@Persson 任何一篇帖子回答了你的问题吗? - Anatolii
4个回答

3
无论你使用服务还是生命周期方法,都需要创建一个循环周期,以便在定义的模式完成后重新启动振动器。我使用处理程序按以下方式执行此操作。
Handler handlerVibrate;
Vibrator vibrator;
private void vibrate(){
    // this handler will keep vibrator running even with screen off (vibrator stops when the phone has the screen off)
    vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
    handlerVibrate = new Handler();
    handlerVibrate.post(new Runnable() {
        @Override
        public void run() {
            if(vibrator != null) {
                Log.d("mainActivity", "handler");
                long[] pattern = {0, 500, 500};
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    vibrator.vibrate(VibrationEffect.createWaveform(pattern, 0)); // repeat at index 0
                } else {
                    //deprecated in API 26
                    vibrator.vibrate(pattern, 0); // repeat at index 0
                }
            }
            if(CONDITIONS TO STOP/CONTINUE){
                // keep launching so that it keeps vibrating even with the screen off
                handlerVibrate.postDelayed(this, 1000);
            } else {
                if(vibrator != null) {
                    vibrator.cancel();
                }
            }
        }
    });
}

2
给出以下内容:
“我有一个服务,可以检查我的网站更新,我希望它能在屏幕关闭或锁定的情况下发现更新时震动手机。”
“即使手机处于关闭状态,我也想让手机震动。我尝试使用WakeLock,但这好像不是问题的原因?”
我得出结论,当您的应用程序在前台运行时,您的服务可以正常工作,但在后台运行时却无法正常工作。因此,您的目标Android API为26及更高版本。
在这种情况下,由于操作系统所施加的后台限制,您的更新服务会在一段时间后停止工作。显然,由于更新服务不再运行,因此它无法启动任何震动。
API >= 26的后台执行限制
从Android 8.0开始,应用程序在后台运行时会受到限制。因此,如文档中所述(请在此处阅读更多信息),您的服务将在一定时间后被系统杀死。
当一个应用程序进入后台时,它有几分钟的时间窗口仍被允许创建和使用服务。在该窗口结束时,应用程序将被视为处于空闲状态。此时,系统会停止应用程序的后台服务,就好像应用程序已调用了服务的Service.stopSelf()方法一样。
因此,如果您希望在应用程序处于后台时服务仍然可以继续运行,您仍然有以下选项:

WorkManager

WorkManager API使得轻松地调度可延迟的异步任务成为可能,这些任务即使应用程序退出或设备重新启动也有望运行。
WorkManager替换您的服务,定期运行以查询您的服务器。

JobScheduler

如果您使用WorkManager,则实际上不需要它。如果您的设备API是23及更高版本,则WorkManager将在内部使用它。

前台服务

将您的服务转换为前台服务,以便在其执行期间应用程序被视为在前台。但在这种情况下,您必须在执行更新检查时始终处于活动状态,因此这可能不是最佳解决方案。请阅读更多这里
结论
请记住,从API 26开始,后台服务应该被替换为WorkManager前台服务。否则,它们可能无法按预期执行,因为系统会在一定时间后将它们杀死。

1
你可以尝试添加一个接收器来控制屏幕的逻辑开/关:
public class ScreenReceiver extends BroadcastReceiver {

    public static boolean wasScreenOn = true;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            // DO WHATEVER YOU NEED TO DO HERE
            Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
            long[] pattern = new long[]{0, 400, 200, 400};
            // Only perform this pattern one time (-1 means "do not repeat")
            v.vibrate(pattern, -1);
            wasScreenOn = false;
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            // AND DO WHATEVER YOU NEED TO DO HERE
            wasScreenOn = true;
        }
    }

}

public class ExampleActivity extends Activity {

   @Override
    protected void onCreate() {
        // INITIALIZE RECEIVER
        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        BroadcastReceiver mReceiver = new ScreenReceiver();
        registerReceiver(mReceiver, filter);
        // YOUR CODE
    }

    @Override
    protected void onPause() {
        // WHEN THE SCREEN IS ABOUT TO TURN OFF
        if (ScreenReceiver.wasScreenOn) {
            // THIS IS THE CASE WHEN ONPAUSE() IS CALLED BY THE SYSTEM DUE TO A SCREEN STATE CHANGE
            System.out.println("SCREEN TURNED OFF");
        } else {
            // THIS IS WHEN ONPAUSE() IS CALLED WHEN THE SCREEN STATE HAS NOT CHANGED
        }
        super.onPause();
    }

    @Override
    protected void onResume() {
        // ONLY WHEN SCREEN TURNS ON
        if (!ScreenReceiver.wasScreenOn) {
            // THIS IS WHEN ONRESUME() IS CALLED DUE TO A SCREEN STATE CHANGE
            System.out.println("SCREEN TURNED ON");
        } else {
            // THIS IS WHEN ONRESUME() IS CALLED WHEN THE SCREEN STATE HAS NOT CHANGED
        }
        super.onResume();
    }

}

这里是一个活动生命周期: Activity Lifecycle 参考资料:

https://thinkandroid.wordpress.com/2010/01/24/handling-screen-off-and-screen-on-intents/


我相信你在这里回答了一个不同的问题。原帖提到,当屏幕关闭时,他的更新服务不会导致设备振动。然而,你回答的是如何在收到Intent.ACTION_SCREEN_OFF时启动振动。 - Anatolii

0

根据您的解释

我有一个服务,用于检查我的网站上的更新

您以某种频率检查网站的更新。在尝试连接服务器之前,请确保获取唤醒锁定,否则您的应用程序将无法连接到互联网。

如果屏幕关闭时无法获取唤醒锁定,您也可以尝试通过显示通知或按照this answer来编程地打开屏幕。

我认为问题可能是因为您可能在检测到更新后尝试获取唤醒锁定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接