BroadcastReceiver получает несколько одинаковых сообщений для одного события - PullRequest
33 голосов
/ 07 декабря 2011

Я зарегистрировал получателя, который прослушивает сетевые события:

<receiver 
    android:label="NetworkConnection"
    android:name=".ConnectionChangeReceiver" >
    <intent-filter >
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

Приемник также очень прост:

public class ConnectionChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) {
                Log.v("@@@","Receiver : " + activeNetInfo);
        } else {
            Log.v("@@@","Receiver : " + "No network");
        }
    }
}

Проблема в том, что при подключении к Wi-Fi я получаю 3 одинаковых сообщенияподряд:

Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true
Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true
Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true

Все они «СОЕДИНЕНЫ / ПОДКЛЮЧЕНЫ» (Разве они не должны быть чем-то вроде CONNECTING / OBTAINING_IPADDR и т. д.), поэтому проблема в том, как мне сказать, когдадействительно связано?У меня есть некоторые процедуры, которые я хочу сделать, когда Wi-Fi действительно подключен, и я не хочу, чтобы они вызывались три раза подряд.

PS: 3G отправляет только одно сообщение, поэтому здесь нет проблем.

Обновление:

Похоже, что это проблема конкретного устройства.

Для теста я взял2 Desire HD и 4 случайных телефона Android (разные модели Aquos и некоторые безымянные китайские вещи).Как на DHD, так и на одном случайном телефоне при подключении к Wi-Fi я получил 3 сообщения, на остальных телефонах - только одно.WTF.

Ответы [ 6 ]

56 голосов
/ 07 декабря 2011

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

Предполагая, что вы получаете сообщение об отключении, когда Wi-Fi отключен, я думаю, первый из них правильныйодна и две другие по какой-то причине являются эхом.

Чтобы знать, что сообщение было вызвано, у вас может быть статическое логическое значение, которое переключается между подключением и отключением и вызывает ваши подпрограммы только при получениисвязь и логическое значение верно.Что-то вроде:

public class ConnectionChangeReceiver extends BroadcastReceiver {
    private static boolean firstConnect = true;

    @Override
    public void onReceive(Context context, Intent intent) {
        final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        final NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) {
            if(firstConnect) { 
                // do subroutines here
                firstConnect = false;
            }
        }
        else {
            firstConnect= true;
        }
    }
}
9 голосов
/ 16 марта 2015

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

Когда тип соединения меняется, он, очевидно, будет работать. Когда устройство выходит из соединения, activeNetworkInfo будет нулевым, а currentType будет NO_CONNECTION_TYPE, как в случае по умолчанию.

public class ConnectivityReceiver extends BroadcastReceiver {

    /** The absence of a connection type. */
    private static final int NO_CONNECTION_TYPE = -1;

    /** The last processed network type. */
    private static int sLastType = NO_CONNECTION_TYPE;

    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager connectivityManager = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
        final int currentType = activeNetworkInfo != null
                ? activeNetworkInfo.getType() : NO_CONNECTION_TYPE;

        // Avoid handling multiple broadcasts for the same connection type
        if (sLastType != currentType) {
            if (activeNetworkInfo != null) {
                boolean isConnectedOrConnecting = activeNetworkInfo.isConnectedOrConnecting();
                boolean isWiFi = ConnectivityManager.TYPE_WIFI == currentType;
                boolean isMobile = ConnectivityManager.TYPE_MOBILE == currentType;

                // TODO Connected. Do your stuff!
            } else {
                // TODO Disconnected. Do your stuff!
            }

            sLastType = currentType;
        }
}
3 голосов
/ 19 марта 2016

В моем случае я регистрировал свои BroadcastReceivers в onResume и только отменял их регистрацию в onDestroy.

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

Установка вашего вещательного приемника в нужном месте с точки зрения жизненного цикла активности позволит вам прекратить получать несколько запутанных звонков.

1 голос
/ 22 декабря 2016

Зарегистрируйте свой LocalBroadcastreceiver в oncreate(), а не в onResume(). незарегистрированный в onDestroy

0 голосов
/ 07 августа 2018

Меня беспокоит подход, предложенный Александром, в том, что он не учитывает изменения в сети одного типа, например, из одной сети WiFi в другую.

Я предлагаю сравнить extraInfo активной сети, которая содержит имя сети, например, WiFi SSID или имя мобильной сети, например, VZW

 String currentNetworkName = "";

 ConnectivityManager connectivityManager =
            ((ConnectivityManager) context.getSystemService(
                    Context.CONNECTIVITY_SERVICE));

    NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
    boolean connected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
    if (connected) {
        // prevent duplicate connect broadcasts
        String extraInfo = activeNetwork.getExtraInfo();
        if(! currentNetworkName.equals(extraInfo)) {
            // to do: handle network changes
            currentNetworkName = extraInfo;
        }
    } else {
        Log.d(TAG, "is not connected");
        isConnected = false;
        currentNetworkName = "";
    }
0 голосов
/ 06 августа 2015

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

Приемник вещания:

public class ConnectionChangeReceiver extends BroadcastReceiver {
    private static boolean firstConnect = true;

    @Override
    public void onReceive(Context context, Intent intent) {
        final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        final NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) {
            startService();
        }   
    }
}

Услуги:

public class MyService extends Service {
    private boolean mRunning;

    @Override
    public void onCreate() {
        super.onCreate();
        mRunning = false;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (!mRunning) {
            mRunning = true;
            uploadTheData();
        }
        return super.onStartCommand(intent, flags, startId);
    }
}
...