当前位置: 首页 > 知识库问答 >
问题:

Android Oreo上小部件的屏幕开/关广播侦听器

爱博达
2023-03-14

我有一个时钟小部件Android应用程序,我现在正试图更新到API 26的要求。

到目前为止,我使用了一个后台服务,它在启动时在其onCreate方法aBroadcastReceiver中注册,以接收系统广播,例如android。意图行动屏幕打开,Android。意图行动关掉屏幕,Android。意图行动时间到了,Android。意图行动时区已更改。然后,这项服务在屏幕关闭时暂停时钟,在屏幕重新打开时唤醒时钟,以节省电池。

在Oreo中,这类服务似乎不是一个选项,因为它必须在前台运行,并带有一个对用户没有意义的通知。此外,据我在文档中看到的,JobScheduler也帮不上我的忙,因为我没有发现可以将作业安排到屏幕打开时。

我尝试在AppWidgetProvider类中创建一个BroadcastRecencer,并将其注册到AppWidgetProvideronUpdate方法中以接收所述系统广播。这工作得很好,广播确实被接收到,但仅在屏幕关闭一段时间后;之后,应用程序似乎以某种方式被系统杀死,或者以其他方式停止工作而没有任何报告的错误或崩溃;但是如果我单击它,它会像往常一样打开配置活动。

我的问题是:

>

  • 如果我不想运行前台服务,如何正确收听API 26上的屏幕开/关广播?

    是否可以收听来自AppWidgetProvider类本身的系统广播,方法是在其中注册BroadcastRecencer,或者甚至注册AppWidgetProvider本身来接收系统事件(无论如何AppWidgetProviderBroadcastRecencer的扩展)。

    为什么我的AppWidgetProvider在一段睡眠时间后会突然停止接收广播的系统意图?

    编辑:

    我在Android留档中找到了以下内容,这似乎是我的问题2和3的答案。

    注意:不能从BroadcastReceiver组件调用此方法;也就是说,来自应用程序清单中声明的BroadcastReceiver。但是,从另一个在运行时已向registerReceiver(BroadcastReceiver,IntentFilter)注册的BroadcastReceiver调用此方法是可以的,因为这样一个注册的BroadcastReceiver的生存期与注册它的对象相关联。

    我的结论是,我在AppWidgetProvider中使用和注册BroadcastRecencer违反了本规范。

    我将保留这篇文章,因为其他人可能会发现这些信息很有用,我的问题1仍然有效。

  • 共有1个答案

    程卓君
    2023-03-14

    下面是我在Android API 26(Oreo)及以上版本中收听SCREEN_OFF和SCREEN_ON广播的步骤。这个答案与widget无关,但找到一些解决方法可能会有所帮助。

    我正在使用作业调度器来注册和注销广播接收器,它可以监听屏幕关闭和屏幕打开操作。

    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.os.Build;
    import android.os.PowerManager;
    import android.support.annotation.NonNull;
    import android.util.Log;
    
    import com.evernote.android.job.Job;
    import com.evernote.android.job.JobManager;
    import com.evernote.android.job.JobRequest;
    
    import java.util.concurrent.TimeUnit;
    
    
    public class LockScreenJob extends Job {
    
        private static final String TAG = LockScreenJob.class.getSimpleName();
    
        public static final String TAG_P = "periodic_job_tag";
        public static final String TAG_I = "immediate_job_tag";
    
        //Used static refrence of broadcast receiver for ensuring if it's already register or not NULL
        // then first unregister it and set to null before registering it again.
        public static UnlockReceiver aks_Receiver = null;
    
        @Override
        @NonNull
        protected Result onRunJob(Params params) {
            // run your job here
    
            String jobTag = params.getTag();
    
            if (BuildConfig.DEBUG) {
                Log.i(TAG, "Job started! " + jobTag);
            }
    
            PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
    
            boolean isInteractive = false;
            // Here we check current status of device screen, If it's Interactive then device screen is ON.
            if (Build.VERSION.SDK_INT >= 20) {
                isInteractive = pm.isInteractive();
            } else {
                isInteractive = pm.isScreenOn();
            }
    
            try {
                if (aks_Receiver != null) {
                    getContext().getApplicationContext().unregisterReceiver(aks_Receiver); //Use 'Application Context'.
                }
            } catch (Exception e) {
                if (BuildConfig.DEBUG) {
                    e.printStackTrace();
                }
            } finally {
                aks_Receiver = null;
            }
    
            try {
                //Register receiver for listen "SCREEN_OFF" and "SCREEN_ON" action.
    
                IntentFilter filter = new IntentFilter("android.intent.action.SCREEN_OFF");
                filter.addAction("android.intent.action.SCREEN_ON");
                aks_Receiver = new UnlockReceiver();
                getContext().getApplicationContext().registerReceiver(aks_Receiver, filter); //use 'Application context' for listen brodcast in background while app is not running, otherwise it may throw an exception.
            } catch (Exception e) {
                if (BuildConfig.DEBUG) {
                    e.printStackTrace();
                }
            }
    
            if (isInteractive)
            {
              //TODO:: Can perform required action based on current status of screen.
            }
    
            return Result.SUCCESS;
        }
    
        /**
         * scheduleJobPeriodic: Added a periodic Job scheduler which run on every 15 minute and register receiver if it's unregister. So by this hack broadcast receiver registered for almost every time w.o. running any foreground/ background service. 
         * @return
         */
        public static int scheduleJobPeriodic() {
            int jobId = new JobRequest.Builder(TAG_P)
                    .setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5))
                    .setRequiredNetworkType(JobRequest.NetworkType.ANY)
                    .build()
                    .schedule();
    
            return jobId;
        }
    
        /**
         * runJobImmediately: run job scheduler immediately so that broadcasr receiver also register immediately
         * @return
         */
        public static int runJobImmediately() {
            int jobId = new JobRequest.Builder(TAG_I)
                    .startNow()
                    .build()
                    .schedule();
    
            return jobId;
        }
    
        /**
         * cancelJob: used for cancel any running job by their jobId.
         * @param jobId
         */
        public static void cancelJob(int jobId) {
            JobManager.instance().cancel(jobId);
        }
    }
    

    我的JobCrator类LockScreenJobCreator是:

    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    
    import com.evernote.android.job.Job;
    import com.evernote.android.job.JobCreator;
    
    public class LockScreenJobCreator implements JobCreator {
    
        @Override
        @Nullable
        public Job create(@NonNull String tag) {
            switch (tag) {
                case LockScreenJob.TAG_I:
                    return new LockScreenJob();
                case LockScreenJob.TAG_P:
                    return new LockScreenJob();
                default:
                    return null;
            }
        }
    }
    

    BroadcastRecencer类UnlockRecencer是:

    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.util.Log;
    
    public class UnlockReceiver extends BroadcastReceiver {
    
        private static final String TAG = UnlockReceiver.class.getSimpleName();
    
        @Override
        public void onReceive(Context appContext, Intent intent) {
    
            if (BuildConfig.DEBUG) {
                Log.i(TAG, "onReceive: " + intent.getAction());
            }
    
            if (intent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_OFF))
            {
              //TODO:: perform action for SCREEN_OFF
            } else if (intent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_ON)) {
              //TODO:: perform action for SCREEN_ON
            }
        }
    
    }
    

    将JobCreator类添加到应用程序类中,如下所示:

    public class AksApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
    
           JobManager.create(this).addJobCreator(new LockScreenJobCreator());   
    
           //TODO: remaing code
        }
    
    }
    

    别忘了在AndroidManifest中定义应用程序类。xml

    所有这些之后,我从我的活动中启动作业调度器,如下所示:

    import android.support.v7.app.AppCompatActivity;
    
    public class LockScreenActivity extends AppCompatActivity {
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            runJobScheduler();
    
            //TODO: other code
        }
    
        @Override
        protected void onStop() {
          super.onStop();
    
          cancelImmediateJobScheduler();
    
          //TODO: other code
        }
    
        /**
         * runJobScheduler(): start immidiate job scheduler and pending job schedulaer from 
           your main Activity.
         */
        private void runJobScheduler() {
            Set<JobRequest> jobSets_I = null, jobSets_P = null;
            try {
                jobSets_I = JobManager.instance().getAllJobRequestsForTag(LockScreenJob.TAG_I);
                jobSets_P = JobManager.instance().getAllJobRequestsForTag(LockScreenJob.TAG_P);
    
                if (jobSets_I == null || jobSets_I.isEmpty()) {
                    LockScreenJob.runJobImmediately();
                }
                if (jobSets_P == null || jobSets_P.isEmpty()) {
                    LockScreenJob.scheduleJobPeriodic();
                }
    
                //Cancel pending job scheduler if mutiple instance are running.
                if (jobSets_P != null && jobSets_P.size() > 2) {
                    JobManager.instance().cancelAllForTag(LockScreenJob.TAG_P);
                }
            } catch (Exception e) {
                if (Global_Var.isdebug) {
                    e.printStackTrace();
                }
            } finally {
                if (jobSets_I != null) {
                    jobSets_I.clear();
                }
                if (jobSets_P != null) {
                    jobSets_P.clear();
                }
                jobSets_I = jobSets_P = null;
            }
        }
    
    
        /**
         * cancelImmediateJobScheduler: cancel all instance of running job scheduler by their 
          TAG name. 
         */
        private void cancelImmediateJobScheduler() {  
                JobManager.instance().cancelAllForTag(LockScreenJob.TAG_I);
        }
    }
    

    通过运行这样的作业调度程序,我可以在不运行任何前台或后台服务的情况下,监听屏幕关闭和屏幕打开操作。我在API 26上测试了上述代码,它运行良好。

     类似资料:
    • Laravel广播的想法很棒,但是很难弄清楚如何让它工作。即使在阅读文档多次,看了许多教程...也许我只是错过了一些小事。谁知道呢? 我在Homestead上的Laravel 5.4项目中运行以下代码: 广播的配置设置为Redis。我安装了Laravel回声服务器,并使用laravel-echo-server start运行此功能,并在控制台中看到类似于[11:15:16 AM]-v7y-5DsM

    • 我想实现一个切换按钮,Android。小装置。开关(可从API v.14获得)。 但我不知道如何为按钮添加事件侦听器。它应该是一个“onClick”监听器吗?我怎么知道它是否被切换为“开”?

    • 所以,我有一个项目在Flatter中,我试图建立一个卡片列表,其中的内容取决于我的OrderModel类,我试图使用Provider来实现这一点,但我得到了以下错误: ════════ 异常捕获调度程序库 ══════════════════════════ 尝试从小部件树外部侦听提供程序公开的值。 这很可能是由调用Provider.of的事件处理程序(如按钮的onP)引起的,该事件处理程序没有传

    • 我目前正在使用mail-listener5软件包来断言电子邮件地址收件箱中的电子邮件数量。 我的测试框架是nightwatchjs。 目前,我的命令脚本从电子邮件地址收件箱(标题为getInboxEmails)中获取电子邮件的数量; 我的nightwatchjs测试脚本如下所示; 当我运行这个nightwatchjs脚本时,输出以下内容; 但是,浏览器不会关闭。 我有点期待这一点,因为在我的get

    • 我有一个通过TestNG运行并行测试的selenium项目。当测试失败时,我有一个listener类来捕获屏幕截图。类如下 在我的测试中,我有一个AfterMethod来清理测试 如果逐个运行测试,则捕获正确的浏览器屏幕截图,但如果运行parrallel,testsit捕获错误的测试浏览器。我认为问题可能是以下之一 after方法已经退出浏览器(有时这是一种情况,因此我必须添加hasQuit布尔值

    • 要运行Kafka,需要在文件。有两种设置我不理解。 有人可以解释侦听器和广告侦听器属性之间的区别吗? 留档说: 侦听器:套接字服务器侦听的地址。 和 advertised.listeners:主机名和端口代理将向生产者和消费者做广告。 我什么时候必须使用哪个设置?