Как остановить эту же трансляцию (из PendingIntent) из очереди? - PullRequest
0 голосов
/ 12 декабря 2018

Сценарий

Я реализовал виджет приложения Android.

При нажатии на виджет запускается трансляция с использованием PendingIntent.getBroadcast(...).

Я хочу создать сетьзапрос внутри onReceive Broadcast Receiver.

(Вы спрашиваете, почему я не использую PendingIntent.getService(...) и не запускаю IntentService? Хорошо, это естественная идея, но, к сожалению, из-за фоновых ограничений, сервисне может быть запущено, если приложение не на переднем плане. Вы можете взглянуть на этот пост .)

Задача

Чтобы доказать, что это работает, я реализовал примерBroadcastReceiver:

class WidgetClickBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {
        if (context == null || intent == null) return

        Log.i("Sira", "onReceive called")

        val pendingResult = goAsync()
        Observable.just(true).delay(3, TimeUnit.SECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                Log.i("Sira", "Fake completion of network call")
                pendingResult.finish()
        }
    }

}

Да, это работает.

Тем не менее, я заметил, что если я нажму на виджет несколько раз, несколько трансляций будут создаваться и помещаться в очередь один за другим до предыдущегоpendingResult.finish() называется.

Это может быть объяснено документацией goAsync():

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

Поэтому я хочу знать, есть ли способ предотвратить запуск одной и той же трансляции несколько раз, если она уже находится вочередь?

Или каким-либо другим способом предотвратить очереди вызовов из-за сумасшедших нажатий на виджет?

1 Ответ

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

Изменить 2 : возможное решение для виджета:
Сохраните timestamp в SharedPreferences (для каждого действия, если оно вам нужно), как только ваше действие будет завершено.
Как только onReceive вызывается снова, проверьте timestamp для вашей предпочтительной millis дельты и запустите действие снова, только если дельта достаточно длинна.

Edit1 : ответниже не работает для виджетов , я оставлю это для всех, кто ищет "обычный" случай

Я пробовал довольно много вещей (включая использованиеHandler и Reflection), наконец, я пришел к следующему решению: когда вы получаете сообщение, которое вы не хотите получать снова, unregister (это конкретное действие) и register, когда действиесделанный.BroadcastReceiver ниже и вот полный пример проекта

package com.exmplae.testbroadcastreceiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

public class SelfRegisteringBroadcastReceiver extends BroadcastReceiver {

    private static final String TAG = "SelfRegisteringBR";
    public static final String TEST_ACTION1 = "TEST_ACTION1";
    public static final String TEST_ACTION2 = "TEST_ACTION2";
    private final ArrayList<String> registeredActions = new ArrayList<>();
    private final ILogListener logListener;
    private final Object registeringLock = new Object();

    public static IntentFilter getIntentFilter() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(TEST_ACTION1);
        intentFilter.addAction(TEST_ACTION2);

        return intentFilter;
    }

    public SelfRegisteringBroadcastReceiver(ILogListener logListener) {
        this.logListener = logListener;
        registeredActions.add(TEST_ACTION1);
        registeredActions.add(TEST_ACTION2);
    }

    private void register(Context context, String action) {
        synchronized (registeringLock) {
            if (!registeredActions.contains(action)) {
                registeredActions.add(action);
                context.unregisterReceiver(this);
                register(context);
            }
        }
    }

    private void register(Context context) {
        IntentFilter intentFilter = new IntentFilter();
        for (String action : registeredActions) {
            intentFilter.addAction(action);
        }

        context.registerReceiver(this, intentFilter);
    }

    private void unregister(Context context, String action) {
        synchronized (registeringLock) {
            if (registeredActions.contains(action)) {
                registeredActions.remove(action);
                context.unregisterReceiver(this);
                register(context);
            }
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        logListener.d(TAG, "onReceive");
        if (intent == null) {
            logListener.e(TAG, "intent = null");
            return;
        }

        String action = intent.getAction();
        if (action == null) {
            logListener.e(TAG, "action = null");
            return;
        }

        //noinspection IfCanBeSwitch
        if (action.equals(TEST_ACTION1)) {
            doAction1(context, TEST_ACTION1);
        } else if (action.equals(TEST_ACTION2)) {
            doAction2();
        } else {
            logListener.e(TAG, "Received unknown action: " + action);
        }
    }

    private void doAction1(final Context context, final String actionName) {
        logListener.d(TAG, "doAction1 start (and unregister)");
        unregister(context, actionName);
        Observable.just(true).delay(10, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Boolean>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        logListener.d(TAG, "doAction1 - onSubscribe");
                    }

                    @Override
                    public void onNext(Boolean aBoolean) {
                        logListener.d(TAG, "doAction1 - onNext");
                    }

                    @Override
                    public void onError(Throwable e) {
                        logListener.e(TAG, "doAction1 - onError");
                    }

                    @Override
                    public void onComplete() {
                        logListener.d(TAG, "doAction1 - onComplete (and register)");
                        register(context, actionName);
                    }
                });

        logListener.d(TAG, "doAction1 end");
    }

    private void doAction2() {
        logListener.d(TAG, "doAction2 start");
        Observable.just(true).delay(3, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Boolean>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        logListener.d(TAG, "doAction2 - onSubscribe");
                    }

                    @Override
                    public void onNext(Boolean aBoolean) {
                        logListener.d(TAG, "doAction2 - onNext");
                    }

                    @Override
                    public void onError(Throwable e) {
                        logListener.e(TAG, "doAction2 - onError");
                    }

                    @Override
                    public void onComplete() {
                        logListener.d(TAG, "doAction2 - onComplete");
                    }
                });

        logListener.d(TAG, "doAction2 end");
    }

}
...