Android: слушатель смены интернет-соединения

У меня уже есть этот код, который слушает изменения подключения -

public class NetworkStateReceiver extends BroadcastReceiver
{
  public void onReceive(Context context, Intent intent)
  {
    Log.d("app","Network connectivity change");

    if(intent.getExtras() != null)
    {
      NetworkInfo ni = (NetworkInfo) intent.getExtras().get(ConnectivityManager.EXTRA_NETWORK_INFO);
      if(ni != null && ni.getState() == NetworkInfo.State.CONNECTED)
      {
        Log.i("app", "Network " + ni.getTypeName() + " connected");
      }
    }

    if(intent.getExtras().getBoolean(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE))
    {
      Log.d("app", "There's no network connectivity");
    }
  }
}

И я проверяю интернет-соединение, используя этот код - Internet Check

Но проблема в том, что если сеть внезапно теряет подключение к Интернету без каких-либо изменений подключения, этот код бесполезен. Есть ли способ создать приемник Broadcast Receiver для изменения подключения к Интернету? У меня есть веб-приложение, и внезапные изменения подключения к Интернету могут вызвать проблемы.

14 ответов

Решение

Попробуй это

public class NetworkUtil {
    public static final int TYPE_WIFI = 1;
    public static final int TYPE_MOBILE = 2;
    public static final int TYPE_NOT_CONNECTED = 0;
    public static final int NETWORK_STATUS_NOT_CONNECTED = 0;
    public static final int NETWORK_STATUS_WIFI = 1;
    public static final int NETWORK_STATUS_MOBILE = 2;

    public static int getConnectivityStatus(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        if (null != activeNetwork) {
            if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
                return TYPE_WIFI;

            if(activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
                return TYPE_MOBILE;
        } 
        return TYPE_NOT_CONNECTED;
    }

    public static int getConnectivityStatusString(Context context) {
        int conn = NetworkUtil.getConnectivityStatus(context);
        int status = 0;
        if (conn == NetworkUtil.TYPE_WIFI) {
            status = NETWORK_STATUS_WIFI;
        } else if (conn == NetworkUtil.TYPE_MOBILE) {
            status = NETWORK_STATUS_MOBILE;
        } else if (conn == NetworkUtil.TYPE_NOT_CONNECTED) {
            status = NETWORK_STATUS_NOT_CONNECTED;
        }
        return status;
    }
}

И для BroadcastReceiver

public class NetworkChangeReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(final Context context, final Intent intent) {

        int status = NetworkUtil.getConnectivityStatusString(context);
        Log.e("Sulod sa network reciever", "Sulod sa network reciever");
        if ("android.net.conn.CONNECTIVITY_CHANGE".equals(intent.getAction())) {
            if (status == NetworkUtil.NETWORK_STATUS_NOT_CONNECTED) {
                new ForceExitPause(context).execute();
            } else {
                new ResumeForceExitPause(context).execute();
            }
       }
    }
}

Не забудьте поместить это в свой AndroidManifest.xml

 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <uses-permission android:name="android.permission.INTERNET" />
 <receiver
        android:name="NetworkChangeReceiver"
        android:label="NetworkChangeReceiver" >
        <intent-filter>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
        </intent-filter>
    </receiver>

Надеюсь, это поможет вам ура!

ConnectivityAction устарела в API 28+. Вместо этого вы можете использовать registerDefaultNetworkCallback до тех пор, пока вы поддерживаете API 24+.

В Котлине:

val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager?.let {
    it.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            //take action when network connection is gained
        }
        override fun onLost(network: Network?) {
            //take action when network connection is lost
        }
    })
}

Вот код Java, использующий registerDefaultNetworkCallback (а также registerNetworkCallback для API < 24):

ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        // network available
    }

    @Override
    public void onLost(Network network) {
        // network unavailable
    }
};

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

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    connectivityManager.registerDefaultNetworkCallback(networkCallback);
} else {
    NetworkRequest request = new NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
    connectivityManager.registerNetworkCallback(request, networkCallback);
}

Обновление:

Приложения, ориентированные на Android 7.0 (уровень API 24) и выше, не получают широковещательные сообщения CONNECTIVITY_ACTION, если они декларируют широковещательный получатель в своем манифесте. Приложения по-прежнему будут получать широковещательные сообщения CONNECTIVITY_ACTION, если они зарегистрируют свой BroadcastReceiver с помощью Context.registerReceiver() и этот контекст будет по-прежнему действителен.

Вам необходимо зарегистрировать получателя через registerReceiver() метод:

 IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
 mCtx.registerReceiver(new NetworkBroadcastReceiver(), intentFilter);

Это должно работать:

public class ConnectivityChangeActivity extends Activity {

    private BroadcastReceiver networkChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("app","Network connectivity change");
        }
    };

    @Override
    protected void onResume() {
        super.onResume();

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(networkChangeReceiver, intentFilter);
    }

    @Override
    protected void onPause() {
        super.onPause();

        unregisterReceiver(networkChangeReceiver);
    }
}

Я использовал этот метод как прослушиватель соединений. Работает на Lolipop +, язык Android JAVA.

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkRequest networkRequest = new NetworkRequest.Builder().build();
            connectivityManager.registerNetworkCallback(networkRequest, new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    super.onAvailable(network);
                    Log.i("Tag", "active connection");
                }

                @Override
                public void onLost(Network network) {
                    super.onLost(network);
                    Log.i("Tag", "losing active connection");
                    isNetworkConnected();
                }
            });
        } 

А также добавьте это разрешение в свой Android Manifest.xml

      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Привет из 2022 года.

В моей пользовательской модели представления я наблюдаю изменения состояния сети следующим образом:

      public class MyViewModel extends AndroidViewModel {
    private final MutableLiveData<Boolean> mConnected = new MutableLiveData<>();

    public MyViewModel(Application app) {
        super(app);

        ConnectivityManager manager = (ConnectivityManager)app.getSystemService(Context.CONNECTIVITY_SERVICE);

        if (manager != null && 
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

            NetworkRequest networkRequest = new NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .build();

            manager.registerNetworkCallback(networkRequest, new ConnectivityManager.NetworkCallback() {
                public void onAvailable(@NonNull Network network) {
                    mConnected.postValue(true);
                }

                public void onLost(@NonNull Network network) {
                    mConnected.postValue(false);
                }

                public void onUnavailable() {
                    mConnected.postValue(false);
                }
            });
        } else {
            mConnected.setValue(true);
        }
    }

    @NonNull
    public MutableLiveData<Boolean> getConnected() {
        return mConnected;
    }
}

И затем в моем действии или фрагменте я могу изменить пользовательский интерфейс, наблюдая:

      @Override
protected void onCreate(Bundle savedInstanceState) {
    MyViewModel vm = new ViewModelProvider(this).get(MyViewModel.class);

    vm.getConnected().observe(this, connected -> {
        // TODO change GUI depending on the connected value
    });
}
  1. сначала добавьте зависимость в свой код как implementation 'com.treebo:internetavailabilitychecker:1.0.4'
  2. реализует ваш класс с помощью InternetConnectivityListener.
public class MainActivity extends AppCompatActivity implements InternetConnectivityListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        InternetAvailabilityChecker.init(this);
        mInternetAvailabilityChecker = InternetAvailabilityChecker.getInstance();
        mInternetAvailabilityChecker.addInternetConnectivityListener(this);
    }

    @Override
    public void onInternetConnectivityChanged(boolean isConnected) {
        if (isConnected) {
            alertDialog = new AlertDialog.Builder(this).create();
            alertDialog.setTitle(" internet is connected or not");
            alertDialog.setMessage("connected");
            alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
            alertDialog.show();

    }
    else {
            alertDialog = new AlertDialog.Builder(this).create();
            alertDialog.setTitle("internet is connected or not");
            alertDialog.setMessage("not connected");
            alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
            alertDialog.show();

        }
    }
}

Я заметил, что никто не упомянул WorkManger решение, которое лучше и поддерживает большинство устройств Android.

У вас должен быть Worker с сетевым ограничением И он будет запущен, только если сеть доступна, то есть:

      val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val worker = OneTimeWorkRequestBuilder<MyWorker>().setConstraints(constraints).build()

А в worker вы делаете все, что хотите, после восстановления соединения, вы можете периодически увольнять воркера .

то есть:

внутри dowork() Перезвоните:

      notifierLiveData.postValue(info)

Согласно официальным документам :

  1. Определить сетевой запрос
      private val networkRequest = NetworkRequest.Builder().apply {
    // To check wifi and cellular networks for internet availability
    addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // Capabilities can be verified starting Android 6.0.
        // For a network with NET_CAPABILITY_INTERNET, 
        // it means that Internet connectivity was successfully detected
        addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        // Indicates that this network is available for use by apps,
        // and not a network that is being kept up in the background 
        // to facilitate fast network switching.
        addCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND)
    }
}.build()
  1. Настройка обратного вызова по сети
      private val networkCallback = object : ConnectivityManager.NetworkCallback() {
    private val networks = mutableListOf<Network>()

    override fun onAvailable(network: Network) {
        super.onAvailable(network)

        networks.add(network)
        Log.d("Has network --->", networks.any().toString())
    }

    override fun onLost(network: Network) {
        super.onLost(network)

        networks.remove(network)
        Log.d("Has network --->", networks.any().toString())
    }
}
  1. Зарегистрируйтесь для получения обновлений сети
      val connectivityService =
    applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityService.registerNetworkCallback(networkRequest, networkCallback)

Отвечая на это в 2023 году, поскольку доступна лучшая реализация

NetworkObserver.kt

      /**
 * This class is in charge of listening to the state of the network connection and notifying the
 * activity if the state of the connection changes.
 * */
class NetworkObserver constructor(
    private val context: Context,
    private val lifecycle: Lifecycle
  ) : DefaultLifecycleObserver {

  private lateinit var networkCallback: ConnectivityManager.NetworkCallback
  private var connectivityManager: ConnectivityManager? = null
  private val validNetworks = HashSet<Network>()

  private lateinit var job: Job
  private lateinit var coroutineScope: CoroutineScope

  // State Holder: Indicating either the network is available or not-available
  private val _networkAvailableStateFlow: MutableStateFlow<NetworkState> = MutableStateFlow(NetworkState.Available)

  val networkAvailableStateFlow
    get() = _networkAvailableStateFlow

  // ---> This variable can be accessed anytime to get the current state of the network
  val isConnected: Boolean
    get() = _isConnected.get()

  private val _isConnected = AtomicBoolean(false)

  override fun onCreate(owner: LifecycleOwner) {
    super.onCreate(owner)
    init()
  }

  override fun onStart(owner: LifecycleOwner) {
    super.onStart(owner)
    registerNetworkCallback()
    checkValidNetworks()
  }

  override fun onStop(owner: LifecycleOwner) {
    super.onStop(owner)
    unregisterNetworkCallback()
  }

  private fun init() {
    // Initialize the connectivity manager instance
    connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
  }

  private fun registerNetworkCallback() {
    if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
      // Observing the network should happen only when the life-cycle is in started state
      initCoroutine()
      initNetworkMonitoring()
    }
  }

  private fun unregisterNetworkCallback() {
    validNetworks.clear()
    connectivityManager?.unregisterNetworkCallback(networkCallback)
    job.cancel()
  }

  /**
   * Co-Routine used to monitor the connectivity
   */
  private fun initCoroutine() {
    // Create a job instance
    job = Job()
    // Provide a co-routine scope
    coroutineScope = CoroutineScope(Dispatchers.Default + job)
  }

  private fun initNetworkMonitoring() {
    networkCallback = createNetworkCallback()

    val networkRequest = NetworkRequest.Builder()
        .addCapability(NET_CAPABILITY_INTERNET)
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
        .build()
    connectivityManager?.registerNetworkCallback(networkRequest, networkCallback)
  }

  private fun createNetworkCallback() = object : ConnectivityManager.NetworkCallback() {
    override fun onAvailable(network: Network) {
      connectivityManager?.getNetworkCapabilities(network).also {
        if (it?.hasCapability(NET_CAPABILITY_INTERNET) == true) {
          validNetworks.add(network)
        }
      }
      checkValidNetworks()
    }

    override fun onLost(network: Network) {
      validNetworks.remove(network)
      checkValidNetworks()
    }
  }

  private fun checkValidNetworks() {
    coroutineScope.launch {
      _networkAvailableStateFlow.emit(
          if (validNetworks.size > 0){
            _isConnected.set(true)
            NetworkState.Available
          } else {
            _isConnected.set(false)
            NetworkState.Unavailable
          }
      )
    }
  }
}

sealed class NetworkState {
  object Unavailable : NetworkState()
  object Available : NetworkState()
}

Применение

          // --> Initialize the network observer in your activity or fragment
    networkObserver = NetworkObserver(this, lifecycle)
    lifecycle.addObserver(networkObserver)

    // --> Use live data to observe the network changes
    networkObserver.networkAvailableStateFlow.asLiveData().observe(this, Observer { networkState ->
      when (networkState) {
        NetworkState.Unavailable -> SnackBarDisplay.showNetworkUnavailableAlert(binding.root)
        NetworkState.Available -> SnackBarDisplay.removeNetworkUnavailableAlert()
      }
    })

Я разместил полное решение в моем GitHub здесь

ссылка https://developer.android.com/training/monitoring-device-state/connectivity-status-type

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

Объявите NetworkRequest, описывающий потребности вашего приложения в сетевом подключении. Следующий код создает запрос для сети, которая подключена к Интернету и использует Wi-Fi или сотовую связь для типа транспорта.

добавить это в onCreate

      NetworkRequest networkRequest = new NetworkRequest.Builder()
    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
    .build();

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

Наиболее часто реализуемые функции в NetworkCallback включают следующее:

onAvailable() указывает, что устройство подключено к новой сети, которая удовлетворяет требованиям к возможностям и типу транспорта, указанным в NetworkRequest. onLost() указывает, что устройство потеряло соединение с сетью. onCapabilitiesChanged() указывает, что возможности сети изменились. Объект NetworkCapabilities предоставляет информацию о текущих возможностях сети.

добавить слушателя

      private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(@NonNull Network network) {
    super.onAvailable(network);
}

@Override
public void onLost(@NonNull Network network) {
    super.onLost(network);
}

@Override
public void onCapabilitiesChanged(@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
    super.onCapabilitiesChanged(network, networkCapabilities);
    final boolean unmetered = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
}};

Зарегистрируйтесь для получения сетевых обновлений. После объявления NetworkRequest и NetworkCallback используйте функции requestNetwork() или registerNetworkCallback() для поиска сети для подключения с устройства, которое удовлетворяет NetworkRequest. Затем о состоянии сообщается NetworkCallback.

Зарегистрируйтесь в onCreate

      ConnectivityManager connectivityManager =
    (ConnectivityManager) getSystemService(ConnectivityManager.class);
connectivityManager.requestNetwork(networkRequest, networkCallback);

implementation 'com.treebo:internetavailabilitychecker:1.0.1'

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        InternetAvailabilityChecker.init(this);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        InternetAvailabilityChecker.getInstance().removeAllInternetConnectivityChangeListeners();
    }
}

Это моя реализация, которую вы можете предоставить в области применения:

class NetworkStateHelper @Inject constructor(
        private val context: Context
) {
    private val cache: BehaviorSubject<Boolean> = BehaviorSubject.create()

    private val receiver = object : BroadcastReceiver() {
        override fun onReceive(c: Context?, intent: Intent?) {
            cache.onNext(isOnlineOrConnecting())
        }
    }

    init {
        val intentFilter = IntentFilter()
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
        context.registerReceiver(receiver, intentFilter)
        cache.onNext(isOnlineOrConnecting())
    }

    fun subscribe(): Observable<Boolean> {
        return cache
    }

    fun isOnlineOrConnecting(): Boolean {
        val cm = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val netInfo = cm.activeNetworkInfo
        return netInfo != null && netInfo.isConnectedOrConnecting
    }
}
Другие вопросы по тегам