广播接收器总是接收API等级+26的广播(即使在后台)

时间:2018-05-31 15:42:19

标签: android android-intent service broadcastreceiver android-broadcastreceiver

我认为这是 Q& A style 因为我发现这个想法有效。对于使用Android的初学者而言,这是一个难以破解的难题。

  

Google已弃用将Broadcast Receiver注册到API Level 26+以下的清单(Some除外)

<receiver android:name=".MyBroadcastReceiver"  android:exported="true">
    <intent-filter>
        <action android:name="android.net.wifi.STATE_CHANGE" />
    </intent-filter>
</receiver>

但是,如果有人希望在应用程序处于后台时接收特定设备状态更改,例如 Internet连接更改(不允许),并且它对应用程序的任何功能都很重要,那么他应该这样做吗?

2 个答案:

答案 0 :(得分:3)

当我浏览文档时,我的眼睛卡在了这里:

  

只要注册上下文有效,上下文注册的接收器就会收到广播。例如,如果您在其中注册   在活动上下文中,只要活动是,您就会收到广播   没有被破坏。如果您在应用程序上下文中注册,那么您   只要应用程序正在运行,就会收到广播。

这实际上意味着如果我可以持有一个Context,那么在其中注册的广播接收者将在后台运行。

为此,Service将是最佳做法。

这是STICKY_SERVICE的代码,它在被杀后再次启动,因此上下文仍然有效

<强> AlwaysOnService.class

package app.exploitr.auto;

import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.support.annotation.Nullable;

public class AlwaysOnService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        registerReceiver(new ClickReceiver(), new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));
        return Service.START_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onLowMemory() {  // rem this if you want it always----
        stopSelf();
        super.onLowMemory();
    }
}

现在,实际做事的接收者:

<强> ClickReceiver.class

package app.exploitr.auto;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import java.util.Objects;

public class ClickReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, Intent intent) {

        switch (Objects.requireNonNull(intent.getAction())) {

            case AutoJob.NOTIFICATION_CANCEL_TAG:
                System.out.println("Not related");
                break;

            case AutoJob.LOGIN_CANCEL_TAG:
                System.out.println("Not related");
                break;

            case "android.net.conn.CONNECTIVITY_CHANGE":
                System.out.println("Oops! It works...");
                break;
        }
    }
}

从任何活动类启动代码

private void setUpBackOffWork() {
    if (DataMan.getInstance(getBaseContext()).getPeriodic()) {
        AutoJob.schedulePeriodic();
        //Not related
    }
    if (DataMan.getInstance(getBaseContext()).getPureAutoLogin()) {
        startService(new Intent(this, AlwaysOnService.class));
    }
}

所以我的目标是当我打开Android的WiFi时自动登录我的isp,代码运行顺畅。它永远不会失败(它运行7小时37分钟直到现在并且运行良好| 没有重新启动)。

要使接收器在重新启动后保持运行,请尝试显示可注册的BOOT_COMPLETED操作。它就像旧的一样。

<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>

更新1

现在,谷歌采取了一步来限制后台执行和因此,您还必须使服务成为foreground服务。所以,程序如下。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, new Intent(THIS_SERVICE_CLASS_NAME.this, ACTIVITY_TO_TARGET.class), 0);

    /*Handle Android O Notifs as they need channel when targeting 28th SDK*/
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        NotificationChannel notificationChannel = new NotificationChannel(
        "download_check_channel_id", 
        "Channel name",
        NotificationManager.IMPORTANCE_LOW);

        if (notificationManager != null) {
            notificationManager.createNotificationChannel(notificationChannel);
        }

        builder = new Notification.Builder(this.getBaseContext(), notificationChannel.getId())
                .setContentTitle("Hi! I'm service")
                .setContentIntent(pendingIntent)
                .setOngoing(true);

        notification = builder.build();
        startForeground("StackOverflow".length(), notification);
    }

    return START_STICKY;
}

答案 1 :(得分:2)

这也适用于Xamarin Android。 Play商店要求将我的应用程序的SDK升级到8.0 Oreo,并且一堆东西停止了工作。

Microsoft的documentation on Broadcast Receivers非常令人困惑:

  

面向Android 8.0(API级别26)或更高版本的应用可能不会静态注册隐式广播。应用仍可以静态注册显式广播。一小部分隐式广播不受此限制。

甚至Google的official docs都难以理解。

在Xamarin Android上,它足以满足以下要求:

[BroadcastReceiver]
[IntentFilter(new string[] {MyReceiver.MyAction})]
public class MyReceiver : BroadcastReceiver
{
    public const String MyAction = "com.mytest.services.MyReceiver.MyAction";

    public override void OnReceive (Context context, Intent intent)
    {
        // ...
    }
}

IntentFilter注释指示编译器在构建过程中将接收器和意图过滤器注册添加到清单文件中。但是从目标SDK v8.0(Oreo / API 26)及更高版本开始,Android会忽略清单上的这些配置(some system implicit actions除外)。因此,这意味着IntentFilter批注仅适用于那些异常,并且要使您的广播接收者接收广播,需要在执行时注册它们:

#if DEBUG
[Application(Debuggable=true)]
#else
[Application(Debuggable=false)]
#endif
public class MyApplication: Application
{
    public override void OnCreate ()
    {
        base.OnCreate ();

        Context.RegisterReceiver(new MyReceiver(), new IntentFilter(MyReceiver.MyAciton));
    }
}

也可以仅在活动的生命周期中注册接收者,如@Toaster所述。您可以继续正常发送广播:

// ...

ApplicationContext.SendBroadcast(new Intent(MyReceiver.MyAction));

// ...