应用程序关闭时,带有广播接收器的工作管理器不起作用

Posted

技术标签:

【中文标题】应用程序关闭时,带有广播接收器的工作管理器不起作用【英文标题】:Work Manager with Broadcast Receiver does not work when the app is closed 【发布时间】:2019-10-24 11:08:31 【问题描述】:

我只想保持蓝牙打开,为此我会监听蓝牙状态,如果它关闭,广播接收器可以启用它。我希望它在应用程序关闭时运行。因此,即使在应用程序关闭后(当它不工作时),我也试图运行蓝牙 广播接收r。为此,我了解到我需要使用 Work Manager 来支持所有设备。我试图结合广播接收器和工作管理器。但是当应用程序关闭时我无法让它运行。

这是我的 MainActivity.java 在这里我将工作​​请求排入队列。

package com.example.workmanagersample;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class).build();
        WorkManager.getInstance().enqueue(workRequest);

    

下面的类是我的MyWorker.java在这里我注册了receiver。

package com.example.workmanagersample;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.IntentFilter;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import androidx.work.Worker;
import androidx.work.WorkerParameters;


public class MyWorker extends Worker 
    private BlueToothBroadcastReceiver myReceiver;

    public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) 
        super(context, workerParams);
    

    /*
     * This method is responsible for doing the work
     * so whatever work that is needed to be performed
     * we will put it here
     *
     * For example, here I am calling the method displayNotification()
     * It will display a notification
     * So that we will understand the work is executed
     * */

    @NonNull
    @Override
    public Result doWork() 
        displayNotification("My Worker", "Hey I finished my work");


        setReceiver();

        return Worker.Result.success();
    

    /*
     * The method is doing nothing but only generating
     * a simple notification
     * If you are confused about it
     * you should check the Android Notification Tutorial
     * */
    private void displayNotification(String title, String task) 
        NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) 
            NotificationChannel channel = new NotificationChannel("simplifiedcoding", "simplifiedcoding", NotificationManager.IMPORTANCE_DEFAULT);
            notificationManager.createNotificationChannel(channel);
        

        NotificationCompat.Builder notification = new NotificationCompat.Builder(getApplicationContext(), "simplifiedcoding")
                .setContentTitle(title)
                .setContentText(task)
                .setSmallIcon(R.mipmap.ic_launcher);

        notificationManager.notify(1, notification.build());
    


    private void setReceiver() 
        myReceiver = new BlueToothBroadcastReceiver();
        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        getApplicationContext().registerReceiver(myReceiver, filter);

    

下面的类是我的 BlueToothBroadcastReceiver.java 在这里我听蓝牙状态是否改变,如果它关闭,我尝试打开它。应用程序运行时它正在工作。但是如果应用程序关闭但我无法实现它,我希望它也能工作。

package com.example.workmanagersample;

import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class BlueToothBroadcastReceiver extends BroadcastReceiver 
    @Override
    public void onReceive(Context context, Intent intent) 
        final String action = intent.getAction();

        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) 
            final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                    BluetoothAdapter.ERROR);
            switch (state) 
                case BluetoothAdapter.STATE_OFF:
                    setBluetooth(true);
                    // Bluetooth has been turned off;
                    break;
                case BluetoothAdapter.STATE_TURNING_OFF:
                    setBluetooth(true);
                    // Bluetooth is turning off;
                    break;
                case BluetoothAdapter.STATE_ON:
                    // Bluetooth has been on
                    break;
                case BluetoothAdapter.STATE_DISCONNECTING:
                    setBluetooth(true);
                    // Bluetooth is turning on
                    break;

                case BluetoothAdapter.STATE_DISCONNECTED:
                    setBluetooth(true);
                    // Bluetooth is turning on
                    break;
            
        
    

    public static boolean setBluetooth(boolean enable) 
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        boolean isEnabled = bluetoothAdapter.isEnabled();
        if (enable && !isEnabled) 
            return bluetoothAdapter.enable();
        
        else if(!enable && isEnabled) 
            return bluetoothAdapter.disable();
        
        // No need to change bluetooth state
        return true;
    

最后是我的 Manifest 文件;

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.workmanagersample">

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".BlueToothBroadcastReceiver"
            android:enabled="true">
            <intent-filter>
                <action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
                <action android:name="android.bluetooth.adapter.action.STATE_OFF"/>
                <action android:name="android.bluetooth.adapter.action.STATE_TURNING_OFF"/>
                <action android:name="android.bluetooth.adapter.action.STATE_ON"/>
                <action android:name="android.bluetooth.adapter.action.STATE_DISCONNECTING"/>
                <action android:name="android.bluetooth.adapter.action.STATE_DISCONNECTED"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>

我在周末研究后选择使用工作管理器,但当我关闭应用程序时它不起作用。我有什么遗漏或有任何限制吗?如果是这样,我该如何解决?任何帮助将非常感激!谢谢!

【问题讨论】:

【参考方案1】:

WorkManager 旨在执行任务,即使您的应用程序在后台。您可以为您的 Worker 类分配一些约束,以便它们仅在满足这些约束时执行(即,如果您需要将一些数据上传到服务器,则 WiFi 连接上的约束可用)。

是 WorkManager 使用广播接收器(最高 API 级别 22)或 JobScheduler 的原因:了解这些约束何时发生变化。

正如其他人回答的那样,您需要使用服务(如果您需要长时间运行,前台服务可能是一个好主意)。 您应该评估的几件事:

    首先检查前台服务的场景。 Consider this blog post 然后是documentation on foreground services。 最后是Bluetooth overview。

【讨论】:

【参考方案2】:

您需要在应用程序关闭后保持广播接收器运行,您将 通过服务类实现这一点,您可能希望查看this 答案

【讨论】:

据我所知,这不适用于 >= Android 6 。首先,我使用IntentService 实现了相同的项目,然后是Service。但他们也没有工作,当我搜索时,我明白如果我需要为更高的设备运行它,我需要使用 Work Manager。所以我最终选择了工作经理。但是,这也不起作用。 我应该把它放在WorkManager.getInstance().enqueue(workRequest);这一行下面吗? 查看此链接medium.com/androiddevelopers/workmanager-basics-beba51e94048 哦,它说广播接收器对 api 23 及更高版本已禁用。 developer.android.com/reference/androidx/work/package-summary我还能做什么?应该有一种方法可以在应用关闭时监听蓝牙状态的变化。 使用 JobScheduler 作为 WorkManager for api 23 >【参考方案3】:

背景

现在开始实施,您似乎有一些提示,因为我看到您有待处理的通知。因为这部分非常接近解决方案。所以实际上你必须使用前台Service(不仅仅是这里提到的Service,因为它可能会被系统终止)。

最后,我相信你有运行这个应用程序的用例。因为它对用户来说可能非常耗电,而且您知道自己在做什么。

解决方案

实现前台Service 并从应用程序启动它。确保您有内部通知。以及您对 Receiver 订阅的实现。

class LiveBroadcast : Service() 

    private var myReceiver: BlueToothBroadcastReceiver? = null

    override fun onCreate() 

        // Init Forgeround Notification
        val pendingIntent: PendingIntent =
            Intent(this, ExampleActivity::class.java).let  notificationIntent ->
                PendingIntent.getActivity(this, 0, notificationIntent, 0)
            

        val notification: Notification = Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
                .setContentTitle(getText(R.string.notification_title))
                .setContentText(getText(R.string.notification_message))
                .setSmallIcon(R.drawable.icon)
                .setContentIntent(pendingIntent)
                .setTicker(getText(R.string.ticker_text))
                .build()

        // Notification ID cannot be 0.
        startForeground(ONGOING_NOTIFICATION_ID, notification)

        // Start your BluetoothReceiver
        myReceiver = new BlueToothBroadcastReceiver()
        getApplicationContext().registerReceiver(
            myReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
    

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int 
        return START_STICKY
    

    override fun onBind(intent: Intent): IBinder? 
        // Pass
    

    override fun onDestroy() 
        // Pass
    

【讨论】:

你的回答错了,看这个:***.com/questions/50343578/…我们可以在doWork()里面启动蓝牙接收器 你应该看到这个:youtube.com/… @SychiSingh 我确实从答案中删除了Worker 提及。但是Service 的后台工作有不同的目的,在你的情况下,我认为它应该是前台服务。无论如何,我的更改在测试期间运行良好。 告诉我一件事,暂时忘记这个案子。假设每当网络连接时,我想从 Room DB 和所有剩余的未同步项目中获取数据,需要上传到 Firestore(应用程序是否打开/关闭/终止等都无关紧要)。我的 API 级别支持是 21 到 30。我应该使用什么? @SychiSingh 只需放置一个前台通知,所有后台工作都会按预期进行。如果需要,您可以获得“忽略电池优化”权限。中国制造商以在关闭应用程序以节省电池时停止 bg 进程而闻名,因此您可能希望将应用程序保留在 bg 中,而不要从最近将其关闭。除此之外,如果您的代码在应用程序处于前台时运行良好,那么如果您添加前台通知,它应该可以正常运行。

以上是关于应用程序关闭时,带有广播接收器的工作管理器不起作用的主要内容,如果未能解决你的问题,请参考以下文章

应用程序未运行时广播接收器不起作用

带有活动的广播接收器服务反馈不起作用

Android:更改联系人时,带有广播接收器的前台服务停止工作

地理围栏未触发(未决意图和广播接收器)

Linux 上 C 中的 UDP 发送方和 Windows 上 Qt 中的接收方不起作用? [关闭]

jQuery 选择器,包括属性和同级选择器不起作用