При подключении к сети Wi-Fi намерение CONNECTIVITY_ACTION получено дважды
Автор вопроса: Torsten RömerОтветов: 12
В моем приложении есть BroadcastReceiver, который запускается как компонент через тег <receiver>, фильтруя android.net.conn.CONNECTIVITY_CHANGE намерения.
Моя цель - просто узнать, когда было установлено соединение Wi-Fi, поэтому в onReceive() я делаю следующее:
Он работает нормально, но я всегда получаю два одинаковых намерения примерно в течение одной секунды, когда устанавливается соединение Wi-Fi. Я попытался просмотреть любую информацию, которую мог получить из намерения, ConnectivityManager и WifiManager, но не могу найти ничего, что отличает эти два намерения.
В журнале есть по крайней мере еще один BroadcastReceiver, который также принимает два идентичных намерения.
Он работает на HTC Desire с Android 2.2.
Есть идеи, почему я, кажется, получаю "дублированное" намерение при подключении Wi-Fi или какая разница между ними?
youtube.com/playlist?list=PLrnPJCHvNZuBqr_0AS9BPX, хорошо разбираясь в методах реализации недавних методов вещания. приемники Anubhav Pandey 22.08.2019
Если рассматривать только ConnectivityManager.CONNECTIVITY_ACTION, я всегда получаю два намерения, содержащие идентичные экземпляры NetworkInfo (оба getType () == TYPE_WIFI и isConnected () == true) при подключении Wi-Fi - проблема, описанная в этом вопросе.
Когда используется только WifiManager.NETWORK_STATE_CHANGED_ACTION, при отключении Wi-Fi не транслируется намерение, но есть два намерения, содержащие разные экземпляры NetworkInfo, что позволяет определить одно событие при подключении Wi-Fi.
ПРИМЕЧАНИЕ. Я получил один отчет о сбое (NPE), в котором intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO) вернул значение null. Таким образом, даже если кажется, что это происходит крайне редко, было бы неплохо добавить нулевую проверку.
Ура, Торстен
Ответ 2 · рейтинг 12 · Dominic
Если вы слушаете WifiManager.NETWORK_STATE_CHANGED_ACTION, вы получите это дважды, потому что в NetworkInfo
isConnectedOrConnecting()
isConnected()
Первый раз isConnectedOrConnecting() возвращает true и isConnected()false Второй раз isConnectedOrConnecting() и isConnected() возвращают true
Ваше здоровье
Ответ 3 · рейтинг 7 · Mike
Это правильный способ зарегистрироваться для изменения подключения к API 21 и выше. Следующий код можно поместить в базовое действие, и таким образом вы можете ожидать, что каждый экран в вашем приложении (который наследуется от этого действия) получит эти обратные вызовы.
Сначала создайте сетевой обратный вызов, который будет отслеживать изменения подключения.
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private val networkCallback: ConnectivityManager.NetworkCallback = object : ConnectivityManager.NetworkCallback() {
// Implement the callback methods that are relevant to the actions you want to take.
// I have implemented onAvailable for connecting and onLost for disconnecting.
override fun onAvailable(network: Network?) {
super.onAvailable(network)
}
override fun onLost(network: Network?) {
super.onLost(network)
}
}
Затем зарегистрируйте и отмените регистрацию этого обратного вызова в соответствующих местах.
override fun onResume() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
cm?.registerNetworkCallback(NetworkRequest.Builder().build(), networkCallback)
}
}
И отмените регистрацию, когда это необходимо.
override fun onPause() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
cm?.unregisterNetworkCallback(networkCallback)
}
}
Обратите внимание, что есть проверка на Build.VERSION_CODES.LOLLIPOP. Эта функция доступна только в Lollipop и выше. Убедитесь, что у вас есть план обработки изменений статуса сети в устройствах Pre-Lollipop, если вы поддерживаете в своем приложении менее 21 API.
Ответ 4 · рейтинг 4 · Christopher Guray
Обновлен код Торстена, так что при отключении Wi-Fi действует только одна соответствующая трансляция.
Используется NetworkInfo.getDetailedState () == DetailState.DISCONNECTED для проверки.
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo = intent
.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
// Wifi is connected
Log.d("Inetify","Wifi is connected: " + String.valueOf(networkInfo));
}
} else if (intent.getAction().equals(
ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo = intent
.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (networkInfo.getDetailedState() == DetailedState.DISCONNECTED) {
// Wifi is disconnected
Log.d("Inetify","Wifi is disconnected: "+String.valueOf(networkInfo));
}
}
}
Ответ 5 · рейтинг 3 · JoxTraex
Если вы зарегистрировали действие как прослушиватель намерений, вы получите одно и то же сообщение дважды. В частности, вам нужно выбрать, хотите ли вы слушать на уровне пакета (XML) или на программном уровне.
Если вы настроите класс для широковещательного приемника и прикрепите к нему прослушивание, И вы прикрепите фильтр намерений к действию, то сообщение будет реплицировано дважды.
Надеюсь, это решит вашу проблему.
Ответ 6 · рейтинг 2 · Lalson
Я решил дважды вызов с помощью SharedPref with Time.
private static final Long SYNCTIME = 800L;
private static final String LASTTIMESYNC = "DATE";
SharedPreferences sharedPreferences;
private static final String TAG = "Connection";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Network connectivity change");
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
final ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo ni = connectivityManager.getActiveNetworkInfo();
if (ni != null && ni.isConnected()) {
if(System.currentTimeMillis()-sharedPreferences.getLong(LASTTIMESYNC, 0)>=SYNCTIME)
{
sharedPreferences.edit().putLong(LASTTIMESYNC, System.currentTimeMillis()).commit();
// Your code Here.
}
}
else if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) {
Log.d(TAG, "There's no network connectivity");
}
}
Потому что есть небольшая задержка между 1. вызовом и 2. вызовом (около 200 миллисекунд). Таким образом, в IF со временем второй вызов прекратится, и только первый будет продолжен.
Я решил эту проблему, воспользовавшись дополнительным намерением для NetworkInfo. В приведенном ниже примере событие onReceive запускается только один раз, если Wi-Fi подключен или мобильный.
if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
boolean screenIsOn = false;
// Prüfen ob Screen on ist
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
screenIsOn = pm.isInteractive();
} else {
screenIsOn = pm.isScreenOn();
}
if (Helper.isNetworkConnected(context)) {
if (networkInfo.isConnected() && networkInfo.isAvailable()) {
Log.v(logTAG + "onReceive", "connected");
if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
Log.v(logTAG + "onReceive", "mobile connected");
} else if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
Log.v(logTAG + "onReceive", "wifi connected");
}
}
}
и мой помощник:
public static boolean isNetworkConnected(Context ctx) {
ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
return ni != null;
}
Ответ 9 · рейтинг 1 · zhengcheng wang
Если вы хотите получить его только один раз, вы можете просто управлять им с помощью переменных.
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
NetworkInfo activeNetwork = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (activeNetwork != null) { // connected to the internet
if (activeNetwork.isConnected() && !isUpdated) {
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
// connected to wifi
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
// connected to the mobile provider's data plan
}
isUpdated = true;
} else {
isUpdated = false;
}
}
}
Ответ 10 · рейтинг 1 · FNordy Tuba68
При включении WIFI,
При включенных МОБИЛЬНЫХ данных отправляются две широковещательные передачи: широковещательная передача №1: МОБИЛЬНЫЕ данные отключены и широковещательная передача №2: подключен Wi-Fi.
Если МОБИЛЬНЫЕ данные ВЫКЛЮЧЕНЫ, отправляется только одна трансляция: Трансляция №1: WIFI подключен.
Аналогичное поведение можно наблюдать при выключении WIFI в двух вышеуказанных условиях.
Чтобы различать их, следуйте пунктам 2 и 3 ниже:
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "*** Action: " + intent.getParcelableExtra("networkInfo"));
NetworkInfo netInfo = intent.getParcelableExtra("networkInfo");
if(intent.getAction().equalsIgnoreCase("android.net.conn.CONNECTIVITY_CHANGE")) {
ConnectivityManager connectivityManager
= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
if (activeNetInfo != null) {
if (netInfo.getType() == ConnectivityManager.TYPE_WIFI) {
if (netInfo.getState().name().contains("DISCONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
Log.d(TAG, "WIFI disconnect created this broadcast. MOBILE data ON."); // #1
} else if (netInfo.getState().name().contains("CONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
Log.d(TAG, "WIFI connect created this broadcast."); // #2
}
} else if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
if (netInfo.getState().name().contains("DISCONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
Log.d(TAG, "MOBILE data disconnect created this broadcast. WIFI ON."); // #3
} else if (netInfo.getState().name().contains("CONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
Log.d(TAG, "MOBILE data connect created this broadcast."); // #4
}
}
} else {
Log.d(TAG, "No network available");
}
}
}
Ответ 11 · Oleksiy
Я поступил так: просто сохранил состояние сети и затем сравнил его, чтобы увидеть, было ли изменение.
public class ConnectivityChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
boolean previouslyConnected = MyApp.getInstance().isNetworkPreviouslyConnected();
boolean currentlyConnected = MyApp.getInstance().isNetworkConnected();
if (previouslyConnected != currentlyConnected) {
// do something and reset
MyApp.getInstance().resetNetworkPreviouslyConnected();
}
}
}
Если вы выбрали именно такой подход, важно сбросить его в onResume фрагмента или действия, чтобы оно сохраняло текущее значение:
@Override
public void onResume() {
super.onResume();
MyApp.getInstance().resetNetworkPreviouslyConnected();
}
Я сделал это в своем BaseFragment, родительском для всех фрагментов в моем приложении.
Ответ 12 · Wooyeol Jung
проверьте networkType из намерения и сравните activeNetworkInfo.getType ()