Odpowiedzi:
Nie jestem pewien, czy interfejs API zapewnia bezpośrednio interfejs API, jeśli weźmiesz pod uwagę ten wątek :
Zastanawiałem się nad tym samym.
W moim przypadku mamBroadcastReceiver
implementację, która wywołujeContext#unregisterReceiver(BroadcastReceiver)
przekazanie siebie jako argumentu po przetworzeniu otrzymanej intencji.
Istnieje niewielka szansa, żeonReceive(Context, Intent)
metoda odbiornika zostanie wywołana więcej niż jeden raz, ponieważ jest zarejestrowana z wielomaIntentFilters
, co stwarza możliwośćIllegalArgumentException
wyrzuceniaContext#unregisterReceiver(BroadcastReceiver)
.W moim przypadku mogę zapisać prywatnego zsynchronizowanego członka do sprawdzenia przed wywołaniem
Context#unregisterReceiver(BroadcastReceiver)
, ale byłoby znacznie czystsze, gdyby interfejs API zapewniał metodę sprawdzania.
Brak funkcji API sprawdzającej, czy odbiornik jest zarejestrowany. Obejściem tego problemu jest umieszczenie kodu w plikutry catch block as done below.
try {
//Register or UnRegister your broadcast receiver here
} catch(IllegalArgumentException e) {
e.printStackTrace();
}
null
. Ale jak wskazałeś, idę z try catch
. Śmieszny.
public class MyReceiver extends BroadcastReceiver {
public boolean isRegistered;
/**
* register receiver
* @param context - Context
* @param filter - Intent Filter
* @return see Context.registerReceiver(BroadcastReceiver,IntentFilter)
*/
public Intent register(Context context, IntentFilter filter) {
try {
// ceph3us note:
// here I propose to create
// a isRegistered(Contex) method
// as you can register receiver on different context
// so you need to match against the same one :)
// example by storing a list of weak references
// see LoadedApk.class - receiver dispatcher
// its and ArrayMap there for example
return !isRegistered
? context.registerReceiver(this, filter)
: null;
} finally {
isRegistered = true;
}
}
/**
* unregister received
* @param context - context
* @return true if was registered else false
*/
public boolean unregister(Context context) {
// additional work match on context before unregister
// eg store weak ref in register then compare in unregister
// if match same instance
return isRegistered
&& unregisterInternal(context);
}
private boolean unregisterInternal(Context context) {
context.unregisterReceiver(this);
isRegistered = false;
return true;
}
// rest implementation here
// or make this an abstract class as template :)
...
}
MyReceiver myReceiver = new MyReceiver();
myReceiver.register(Context, IntentFilter); // register
myReceiver.unregister(Context); // unregister
ad 1
-- w odpowiedzi na:
To naprawdę nie jest tak eleganckie, ponieważ musisz pamiętać, aby ustawić flagę isRegistered po rejestracji. - Stealth Rabbi
- Dodano „bardziej eliptyczny sposób” w odbiorniku, aby zarejestrować i ustawić flagę
to nie zadziała Jeśli ponownie uruchomisz urządzenie lub Twoja aplikacja została zabita przez system operacyjny. - amin 6 godzin temu
@amin - zobacz czas życia w kodzie (niezarejestrowany w systemie przez wpis manifestu) zarejestrowany odbiorca :)
Korzystam z tego rozwiązania
public class ReceiverManager {
private static List<BroadcastReceiver> receivers = new ArrayList<BroadcastReceiver>();
private static ReceiverManager ref;
private Context context;
private ReceiverManager(Context context){
this.context = context;
}
public static synchronized ReceiverManager init(Context context) {
if (ref == null) ref = new ReceiverManager(context);
return ref;
}
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter intentFilter){
receivers.add(receiver);
Intent intent = context.registerReceiver(receiver, intentFilter);
Log.i(getClass().getSimpleName(), "registered receiver: "+receiver+" with filter: "+intentFilter);
Log.i(getClass().getSimpleName(), "receiver Intent: "+intent);
return intent;
}
public boolean isReceiverRegistered(BroadcastReceiver receiver){
boolean registered = receivers.contains(receiver);
Log.i(getClass().getSimpleName(), "is receiver "+receiver+" registered? "+registered);
return registered;
}
public void unregisterReceiver(BroadcastReceiver receiver){
if (isReceiverRegistered(receiver)){
receivers.remove(receiver);
context.unregisterReceiver(receiver);
Log.i(getClass().getSimpleName(), "unregistered receiver: "+receiver);
}
}
}
Masz kilka opcji
Możesz umieścić flagę w swojej klasie lub aktywności. Umieść zmienną boolowską w swojej klasie i spójrz na tę flagę, aby dowiedzieć się, czy masz zarejestrowanego odbiornika.
Utwórz klasę rozszerzającą odbiornik i tam możesz użyć:
Wzorzec singletonu dla tylko jednego wystąpienia tej klasy w twoim projekcie.
Zaimplementuj metody sprawdzania, czy odbiornik jest zarejestrowany.
Musisz użyć try / catch:
try {
if (receiver!=null) {
Activity.this.unregisterReceiver(receiver);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
Możesz to zrobić łatwo ....
1) utwórz zmienną boolowską ...
private boolean bolBroacastRegistred;
2) Po zarejestrowaniu odbiornika rozgłoszeniowego ustaw go na PRAWDA
...
bolBroacastRegistred = true;
this.registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
....
3) W onPause () zrób to ...
if (bolBroacastRegistred) {
this.unregisterReceiver(mReceiver);
bolBroacastRegistred = false
}
Po prostu, a teraz nie otrzymasz więcej komunikatu o błędzie wyjątku na onPause ().
Tip1: Zawsze używaj unregisterReceiver () w onPause (), a nie w onDestroy () Tip2: Nie zapomnij ustawić zmiennej bolBroadcastRegistred na FALSE podczas uruchamiania unregisterReceive ()
Sukces!
Użyłem Intent, aby poinformować Odbiorcę transmisji o wystąpieniu modułu głównego wątku działania programu Handler i użyłem komunikatu, aby przekazać wiadomość do głównej aktywności
Użyłem takiego mechanizmu, aby sprawdzić, czy odbiornik jest już zarejestrowany, czy nie. Czasami jest to potrzebne, gdy rejestrujesz swój odbiornik dynamiczny dynamicznie i nie chcesz, aby był dwa razy lub prezentujesz się użytkownikowi, jeśli odbiornik jest uruchomiony.
Główna aktywność:
public class Example extends Activity {
private BroadCastReceiver_example br_exemple;
final Messenger mMessenger = new Messenger(new IncomingHandler());
private boolean running = false;
static class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
running = false;
switch (msg.what) {
case BroadCastReceiver_example.ALIVE:
running = true;
....
break;
default:
super.handleMessage(msg);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter();
filter.addAction("pl.example.CHECK_RECEIVER");
br_exemple = new BroadCastReceiver_example();
getApplicationContext().registerReceiver(br_exemple , filter); //register the Receiver
}
// call it whenever you want to check if Broadcast Receiver is running.
private void check_broadcastRunning() {
/**
* checkBroadcastHandler - the handler will start runnable which will check if Broadcast Receiver is running
*/
Handler checkBroadcastHandler = null;
/**
* checkBroadcastRunnable - the runnable which will check if Broadcast Receiver is running
*/
Runnable checkBroadcastRunnable = null;
Intent checkBroadCastState = new Intent();
checkBroadCastState .setAction("pl.example.CHECK_RECEIVER");
checkBroadCastState .putExtra("mainView", mMessenger);
this.sendBroadcast(checkBroadCastState );
Log.d(TAG,"check if broadcast is running");
checkBroadcastHandler = new Handler();
checkBroadcastRunnable = new Runnable(){
public void run(){
if (running == true) {
Log.d(TAG,"broadcast is running");
}
else {
Log.d(TAG,"broadcast is not running");
}
}
};
checkBroadcastHandler.postDelayed(checkBroadcastRunnable,100);
return;
}
.............
}
Odbiornik transmisji:
public class BroadCastReceiver_example extends BroadcastReceiver {
public static final int ALIVE = 1;
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Bundle extras = intent.getExtras();
String action = intent.getAction();
if (action.equals("pl.example.CHECK_RECEIVER")) {
Log.d(TAG, "Received broadcast live checker");
Messenger mainAppMessanger = (Messenger) extras.get("mainView");
try {
mainAppMessanger.send(Message.obtain(null, ALIVE));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
.........
}
}
Osobiście używam metody wywoływania unregisterReceiver i połykania wyjątku, jeśli zostanie zgłoszony. Zgadzam się, że to brzydkie, ale najlepsza obecnie dostępna metoda.
Podniosłem żądanie funkcji, aby uzyskać metodę logiczną, aby sprawdzić, czy odbiornik jest zarejestrowany dodany do interfejsu API Androida. Wesprzyj go tutaj, jeśli chcesz, aby został dodany: https://code.google.com/p/android/issues/detail?id=73718
Rozumiem twój problem, napotkałem ten sam problem w mojej aplikacji. Dzwoniłem registerReceiver () wiele razy w aplikacji.
Prostym rozwiązaniem tego problemu jest wywołanie registerReceiver () w niestandardowej klasie aplikacji. Zapewni to, że odbiornik Broadcast będzie nazywany tylko jednym w całym cyklu życia aplikacji.
public class YourApplication extends Application
{
@Override
public void onCreate()
{
super.onCreate();
//register your Broadcast receiver here
IntentFilter intentFilter = new IntentFilter("MANUAL_BROADCAST_RECIEVER");
registerReceiver(new BroadcastReciever(), intentFilter);
}
}
Tak to zrobiłem, jest to zmodyfikowana wersja odpowiedzi udzielonej przez ceph3us i edytowanej przez slinden77 (między innymi usunąłem zwracane wartości metod, których nie potrzebowałem):
public class MyBroadcastReceiver extends BroadcastReceiver{
private boolean isRegistered;
public void register(final Context context) {
if (!isRegistered){
Log.d(this.toString(), " going to register this broadcast receiver");
context.registerReceiver(this, new IntentFilter("MY_ACTION"));
isRegistered = true;
}
}
public void unregister(final Context context) {
if (isRegistered) {
Log.d(this.toString(), " going to unregister this broadcast receiver");
context.unregisterReceiver(this);
isRegistered = false;
}
}
@Override
public void onReceive(final Context context, final Intent intent) {
switch (getResultCode()){
//DO STUFF
}
}
}
Następnie na zajęciach:
public class MyFragmentActivity extends SingleFragmentActivity{
MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
registerBroacastReceiver();
}
@Override
protected Fragment createFragment(){
return new MyFragment();
}
//This method is called by the fragment which is started by this activity,
//when the Fragment is done, we also register the receiver here (if required)
@Override
public void receiveDataFromFragment(MyData data) {
registerBroacastReceiver();
//Do some stuff
}
@Override
protected void onStop(){
unregisterBroacastReceiver();
super.onStop();
}
void registerBroacastReceiver(){
if (myBroadcastReceiver == null)
myBroadcastReceiver = new MyBroadcastReceiver();
myBroadcastReceiver.register(this.getApplicationContext());
}
void unregisterReceiver(){
if (MyBroadcastReceiver != null)
myBroadcastReceiver.unregister(this.getApplicationContext());
}
}
umieszczam ten kod w mojej działalności nadrzędnej
List registerReceivers = new ArrayList <> ();
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
registeredReceivers.add(System.identityHashCode(receiver));
return super.registerReceiver(receiver, filter);
}
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
if(registeredReceivers.contains(System.identityHashCode(receiver)))
super.unregisterReceiver(receiver);
}
Dla mnie działało:
if (receiver.isOrderedBroadcast()) {
requireContext().unregisterReceiver(receiver);
}
Oto, co zrobiłem, aby sprawdzić, czy nadawca jest już zarejestrowany, nawet jeśli zamkniesz aplikację (zakończ ())
Przy pierwszym uruchomieniu aplikacji wyślij najpierw transmisję, która zwróci wartość prawda / fałsz, w zależności od tego, czy nadawca nadal działa, czy nie.
Mój nadawca
public class NotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getExtras() != null && intent.getStringExtra("test") != null){
Log.d("onReceive","test");
return;
}
}
}
Moja główna aktywność
// init Broadcaster
private NotificationReceiver nr = new NotificationReceiver();
Intent msgrcv = new Intent("Msg");
msgrcv.putExtra("test", "testing");
boolean isRegistered = LocalBroadcastManager.getInstance(this).sendBroadcast(msgrcv);
if(!isRegistered){
Toast.makeText(this,"Starting Notification Receiver...",Toast.LENGTH_LONG).show();
LocalBroadcastManager.getInstance(this).registerReceiver(nr,new IntentFilter("Msg"));
}
Możesz użyć Sztyletu, aby utworzyć odniesienie do tego odbiornika.
Najpierw podaj:
@Provides
@YourScope
fun providesReceiver(): NotificationReceiver{
return NotificationReceiver()
}
Następnie wstrzyknij go tam, gdzie potrzebujesz (za pomocą constructor
lub pola injection
)
i po prostu przekażcie to registerReceiver
.
Również włóż to w try/catch
blok.
if( receiver.isOrderedBroadcast() ){
// receiver object is registered
}
else{
// receiver object is not registered
}
Wystarczy sprawdzić NullPointerException. Jeśli odbiornik nie istnieje, to ...
try{
Intent i = new Intent();
i.setAction("ir.sss.smsREC");
context.sendBroadcast(i);
Log.i("...","broadcast sent");
}
catch (NullPointerException e)
{
e.getMessage();
}