安卓 - 如何接收广播意图 ACTION_SCREEN_ON/OFF?

68
    <application>
         <receiver android:name=".MyBroadcastReceiver" android:enabled="true">
                <intent-filter>
                      <action android:name="android.intent.action.ACTION_SCREEN_ON"></action>
                      <action android:name="android.intent.action.ACTION_SCREEN_OFF"></action>
                </intent-filter>
         </receiver>
...
    </application>

MyBroadcastReceiver仅将foo输出到日志中。 什么都不做。 有什么建议吗? 我需要分配任何权限来捕获意图吗?

6个回答

70

我相信这些操作只能由在Java代码中注册的接收器(通过registerReceiver())接收,而不能通过在清单中注册的接收器接收。


1
好的,我刚刚弄明白了。这背后的原理是什么? - ohnoes
20
在Android中,似乎不支持使用清单文件注册接收器的情况,特别是当它们确实不想启动新进程时。例如,对于电池信息操作(如BATTERY_LOW),您将看到相同的效果。除此之外,我没有更多的解释 - 因为我没有编写相关代码 :-) - CommonsWare
2
@CommonsWare 那么当按下电源按钮时,我该如何注册registerReceiver()? - Venky
1
@dirtydexter:没有这样的列表,而且我认为这两个广播应该可以从清单或通过registerReceiver()注册的接收器中获取。 - CommonsWare
1
@PrimožKralj:在Android 8.0+上,大多数都必须通过编程方式注册--例外情况在a白名单中。对于早期版本的Android,在一般情况下,您可以尝试它。如果在清单中注册后无法正常工作,请通过在Stack Overflow和类似资源中研究广播操作来确认结果。理想情况下,这将有更好的文档说明。 - CommonsWare
显示剩余2条评论

32

或者你可以使用电源管理器来检测屏幕锁定。

@Override
protected void onPause()
{
    super.onPause();

    // If the screen is off then the device has been locked
    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    boolean isScreenOn;
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
        isScreenOn = powerManager.isInteractive();
    } else {
        isScreenOn = powerManager.isScreenOn();
    }

    if (!isScreenOn) {

        // The screen has been locked 
        // do stuff...
    }
}

2
+1 对于使用 PowerManager 的方法。如果有人不想使用 BroadcastReceiver,这是一个非常好的选择。 - Simon Dorociak
3
我想补充一下,虽然这种方法适用于绝大多数情况,但在特定情况下可能会失败。例如,在可以快速开关屏幕的设备(如Galaxy S4)中,结合proximityLock使用时可能会出现问题。如果你快速触发锁定以关闭并重新打开屏幕,则isScreenOn实际上会在onPause()中返回true。 - i Code 4 Food
@iCode4Food,您所描述的是正确的,但该如何解决呢? - zionpi
@SimonDorociak 我应该在哪个服务中添加这个?(针对一个服务) - Sarah cartenz

31
"android.intent.action.HEADSET_PLUG"
"android.intent.action.ACTION_SCREEN_ON"
"android.intent.action.ACTION_SCREEN_OFF"

其中三个无法使用Manifest进行注册。 Android核心为它们添加了"Intent.FLAG_RECEIVER_REGISTERED_ONLY"(也许是的..我只在"HEADSET_PLUG"的情况下检查了代码)。

因此,我们应该使用"动态注册"。 像下面这样...

private BroadcastReceiver mPowerKeyReceiver = null;

private void registBroadcastReceiver() {
    final IntentFilter theFilter = new IntentFilter();
    /** System Defined Broadcast */
    theFilter.addAction(Intent.ACTION_SCREEN_ON);
    theFilter.addAction(Intent.ACTION_SCREEN_OFF);

    mPowerKeyReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String strAction = intent.getAction();

            if (strAction.equals(Intent.ACTION_SCREEN_OFF) || strAction.equals(Intent.ACTION_SCREEN_ON)) {
                // > Your playground~!
            }
        }
    };

    getApplicationContext().registerReceiver(mPowerKeyReceiver, theFilter);
}

private void unregisterReceiver() {
    int apiLevel = Build.VERSION.SDK_INT;

    if (apiLevel >= 7) {
        try {
            getApplicationContext().unregisterReceiver(mPowerKeyReceiver);
        }
        catch (IllegalArgumentException e) {
            mPowerKeyReceiver = null;
        }
    }
    else {
        getApplicationContext().unregisterReceiver(mPowerKeyReceiver);
        mPowerKeyReceiver = null;
    }
}

+1,不错。需要注意的是Build.VERSION.SDK没有被弃用。 - Vikram.exe
1
"unregisterReceiver()" 代码部分对我非常有用。谢谢 @cmcromance !! +1 - SilSur

7
我将这样实现:在我的主活动的onCreate()中注册接收器,只需事先定义接收器即可:
    lockScreenReceiver = new LockScreenReceiver();
    IntentFilter lockFilter = new IntentFilter();
    lockFilter.addAction(Intent.ACTION_SCREEN_ON);
    lockFilter.addAction(Intent.ACTION_SCREEN_OFF);
    lockFilter.addAction(Intent.ACTION_USER_PRESENT);
    registerReceiver(lockScreenReceiver, lockFilter);

然后是onDestroy()方法:

    unregisterReceiver(lockScreenReceiver);

在接收器中,您必须捕获以下情况:
public class LockScreenReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (intent != null && intent.getAction() != null)
        {
            if (intent.getAction().equals(Intent.ACTION_SCREEN_ON))
            {
                // Screen is on but not unlocked (if any locking mechanism present)
            }
            else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF))
            {
                // Screen is locked
            }
            else if (intent.getAction().equals(Intent.ACTION_USER_PRESENT))
            {
                // Screen is unlocked
            }
        }
    }
}

0

这是@cmcromance的Kotlin版本(感谢您的回答。请不要忘记给原始答案点赞)

private var mPowerKeyReceiver: BroadcastReceiver? = null

    private fun registBroadcastReceiver() {
        val theFilter = IntentFilter()
        /** System Defined Broadcast  */
        theFilter.addAction(Intent.ACTION_SCREEN_ON)
        theFilter.addAction(Intent.ACTION_SCREEN_OFF)

        mPowerKeyReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {

                Log.e("onReceive", "onReceive called")
                val strAction = intent!!.action

//                if (strAction == Intent.ACTION_SCREEN_OFF || strAction == Intent.ACTION_SCREEN_ON) {
                if (strAction == Intent.ACTION_SCREEN_ON) {
                    // > Your playground~!
                    Log.e("strAction", strAction)
                    val intent = Intent(context, SplashScreenMainAppActivity::class.java)
                    startActivity(intent)
                }

            }
        }

        applicationContext.registerReceiver(mPowerKeyReceiver, theFilter)
    }

    private fun unregisterReceiver() {
        val apiLevel = Build.VERSION.SDK_INT

        if (apiLevel >= 7) {
            try {
                applicationContext.unregisterReceiver(mPowerKeyReceiver)
            } catch (e: IllegalArgumentException) {
                mPowerKeyReceiver = null
            }

        } else {
            applicationContext.unregisterReceiver(mPowerKeyReceiver)
            mPowerKeyReceiver = null
        }
    }

0

新的操作键已更新!

<intent-filter>
    <action android:name="android.intent.action.SCREEN_ON" />
    <action android:name="android.intent.action.SCREEN_OFF" />
</intent-filter>

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