Phone 屏幕关闭/锁定后振动停止工作

Phone vibration stops working after screen is off / locked

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

振动目前仅在屏幕未关闭/锁定时有效。 即使屏幕关闭/锁定,所有其他功能也可以使用。

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);
}

如何让 phone 在屏幕关闭的情况下振动? 我试过使用 WakeLock 但这似乎不是问题所在?

我设置了所有权限,因为它在屏幕打开时工作。

你可以尝试添加一个接收器来控制屏幕逻辑on/off:

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 生命周期:

参考:

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

根据你的解释

I have a service that checks for updates on my website

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

您也可以尝试通过显示通知或遵循 this answer 以编程方式打开屏幕,以防您在屏幕关闭时无法获得唤醒锁。

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

鉴于此:

I have a service that checks for updates on my website and I would like it to be able to cause a vibration when a update is found even if the screen is off or locked.

还有这个:

How can I vibrate a phone even if its screen is off? I've tried to use a WakeLock but that doesn't seem to be the problem?

我得出的结论是,当您的应用程序在前台时,您的服务可以正常运行,但在后台时则不会。因此,您的目标 Android API 很可能是 26 或更高。

在这种情况下,由于 OS 施加的后台限制,您的更新服务会在一段时间后停止工作。由于更新服务不再运行s,显然,它不能发起任何振动。

后台执行限制 API >= 26

从 Android 8.0 开始,应用程序在后台时可以 运行 受到限制。所以,你的服务会工作一段时间,然后它会被系统杀死,如文档中所述(请阅读更多here):

When an app goes into the background, it has a window of several minutes in which it is still allowed to create and use services. At the end of that window, the app is considered to be idle. At this time, the system stops the app's background services, just as if the app had called the services' Service.stopSelf() methods.

因此,如果您希望您的服务在应用程序处于后台时继续运行,您仍然有多种选择。其中一些如下

工作管理器

The WorkManager API makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or device restarts.

将您的服务替换为 WorkManager,它会定期 运行 查询您的服务器。

JobScheduler

好吧,如果你使用 WorkManager,你并不真的需要它。如果您的设备 API 是 23 及更高版本,它将由 WorkManager 内部使用。

前台服务

将您的服务转换为前台服务,以便应用程序在执行期间被视为在前台运行。但在这种情况下,它必须在您执行更新检查的整个过程中一直处于活动状态,因此它可能不是最佳解决方案。请阅读更多 here.

结论

请记住,从 API 26 开始,后台服务应替换为 WorkManagerForeground Service。否则,它们可能无法按预期执行,因为系统会在一定时间后杀死它们。

无论您使用服务还是生命周期方法,您都需要创建一个循环周期,以便您的振动器在完成您定义的模式后重新启动。

我使用处理程序按如下方式完成。

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();
                }
            }
        }
    });
}