Приемник вещания, который всегда принимает вещание (даже в фоновом режиме) для уровня API +26 - PullRequest
0 голосов
/ 31 мая 2018

Я представляю это как Стиль вопросов и ответов , потому что я нашел эту идею работающей.И это исправление сложной проблемы для новичков с Android.

Google устарела при регистрации Broadcast Receiver в манифесте, как показано ниже, с уровня API 26+ (за исключением Некоторые )

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

Но, если кто-то хочет получить определенные изменения состояния устройства, такие как Изменения подключения к Интернету (что не разрешено) , когда приложение находится в фоновом режиме и если оноважно для любой функции его приложения, что он должен делать?

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

Это также относится к Xamarin Android.Play Store потребовал обновить SDK моих приложений до 8.0 Oreo, и куча вещей перестала работать над ним.

Документация Microsoft по вещательным приемникам довольно запутанная:

Приложения, предназначенные для Android 8.0 (уровень API 26) или выше, могут не регистрироваться статически для неявной трансляции.Приложения могут по-прежнему статически регистрироваться для явной трансляции.Существует небольшой список неявных трансляций, которые освобождены от этого ограничения.

Даже официальные документы от Google весьма непостижимы.

На Android Xamarin, который он использовал длядостаточно следовать этому шаблону:

[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 игнорирует эти конфигурации в Manifest (за исключением некоторых системных неявных действий ).Таким образом, это означает, что аннотации 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));

// ...
0 голосов
/ 31 мая 2018

Когда я просматривал документацию, у меня застряли глаза:

Приемники, зарегистрированные в контексте, получают широковещательные сообщения, пока их контекст регистрации действителен.Например, если вы регистрируетесь в контексте действия, вы получаете широковещательные сообщения до тех пор, пока действие не будет уничтожено.Если вы регистрируетесь в контексте приложения, вы получаете широковещательные сообщения, пока приложение работает.

Это практически означает, что если я могу удерживать контекст, зарегистрированный в нем широковещательный приемник будет работатьв фоновом режиме.

Для этого лучше всего будет использовать 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));
    }
}

Таким образом, моя цель состояла в том, чтобы автоматически входить в мой isp, когда я поднимаюсьWiFi моего андроида, и код работает гладко.Он никогда не выходит из строя (он работает 7 часов и 37 минут до сих пор и работает хорошо | не перезагружается ).


Чтобы сохранить работу приемника при перезагрузках, попробуйте манифестировать регистрируемые BOOT_COMPLETED действия.Он работает так же, как старый.

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


Обновление 1

Теперь, когда Google сделал один шаг, чтобы ограничить фоновое выполнение и какВ результате вы также сделаете услугу 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;
}
...