ConnectivityManager.CONNECTIVITY_ACTIONの廃止


94

Android Nでは、公式サイトで「Android NをターゲットとするアプリはCONNECTIVITY_ACTIONブロードキャストを受信しない」と記載されています。またJobScheduler、代替として使用できることも言及されています。ただし、JobSchedulerCONNECTIVITY_ACTIONブロードキャストとまったく同じ動作を提供しません。

私のAndroidアプリケーションでは、このブロードキャストを使用してデバイスのネットワーク状態を把握していました。私はこの状態がブロードキャストであるのCONNECTINGCONNECTED、それともCONNECTIVITY_ACTIONブロードキャストの助けを借りているのかを知りたかったので、私の要件に最も適していました。

現在は非推奨になっているので、現在のネットワーク状態を取得する別の方法を提案できますか?


10
そして、もしOPがいつかtargetSdkVersionNまでのアップを必要とする何らかの振る舞いを望んでいる場合はどうでしょうか?
マイケル、

1
まあ、私も私が私のアプリケーションをAndroidをターゲットにしない場合、ブロードキャストを受信することを知っています。しかし、私のアプリケーションはAndroid Nをサポートする必要があります。AndroidNで同じブロードキャスト動作を得るにはどうすればよいですか?私が試すことができる他のアプローチはありますか?@DavidWasser
Raghuram db

時には将来のことを心配するほうが理にかなっていると思うことがあります。これは純粋に実用的なプログラミング方法です。もちろん、コードが非推奨の機能を使用していないことを常に確認するように試みることができます。一方、非推奨の機能は通常長期間存続し、非推奨の機能がなくなる前にアプリが廃止される可能性があります。Android Nは非常に新しいので、心配することはあまりありません。まだ。ちょうど私の2セント。私が質問にコメントを書いて、「それをしないでください」が有効な答えであると示唆しなかったことに注意してください。
David Wasser

2
あなたは、Android N.でのみ利用可能な機能を使用したい場合は、Android N.あなたにあなたのアプリケーションをターゲットとしていない場合でも、AndroidのN上で実行することができますあなたのアプリは、AndroidのNをターゲットにする必要があり@Raghuramdb
デヴィッド・ワッサー

2
API29をターゲットにしている場合でもBroadcastReceiverandroid.net.conn.CONNECTIVITY_CHANGEインテントフィルターでを使用できますApplication.OnCreate。これをに登録するだけです。アプリを閉じても、更新はありません。
Pierre

回答:


97

サポートが終了するのは、バックグラウンドアプリケーションがネットワーク接続状態の変更を受信する機能です。

デビッド・ワッサーは、アプリのコンポーネントがインスタンス化される(破壊されない)、あなたがしている場合は、まだ接続の変更の通知を受けることができたプログラムで、あなたの受信機を登録します代わりにマニフェストにそれを行うので、その文脈で。

または、代わりにNetworkCallbackを使用できます。特に、onAvailableをオーバーライドする必要があります接続状態の変更に対しては。

スニペットをすばやくドラフトさせてください。

public class ConnectionStateMonitor extends NetworkCallback {

   final NetworkRequest networkRequest;

   public ConnectionStateMonitor() {
       networkRequest = new NetworkRequest.Builder()
           .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
           .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
           .build();
   }

   public void enable(Context context) {
       ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
       connectivityManager.registerNetworkCallback(networkRequest, this);
   }

   // Likewise, you can have a disable method that simply calls ConnectivityManager.unregisterNetworkCallback(NetworkCallback) too.

   @Override
   public void onAvailable(Network network) {
       // Do what you need to do here
   }
}

2
この手法は、アプリがフォアグラウンドで実行されている場合にのみ機能します。つまり、アプリがフォアグラウンドで実行されていない場合、接続イベントをリッスンする機能がなくなりましたか?持つ<アクションは、Android:名前=「android.net.conn.CONNECTIVITY_CHANGE」/> manifest.xmlには、もはやアンドロイドN.で何の影響も与えないで
Cheokヤンチェン

2
@CheokYanCheng AFAIK正解です。接続イベントをリッスンするには、フォアグラウンドで実行されるプロセスが必要です。Androidフレームワークエンジニアによる想定では、接続イベントのリッスンは、クライアントとサーバー間でデータの同期をいつ開始するかを知るために行われるというものでした。したがって、その使用例ではJobSchedulerが推奨される方法です。
Amokrane Chentir


1
NetworkCallbackを登録解除する必要がありますか(たとえば、アクティビティのonDestroyメソッドで)?
ルスランベロゾフ2018年

2
@Ruslanはい、もちろん、登録されているものは何でもリークします
DennisVA

34

Sayem'slintの問題を修正するための回答を更新します。

class ConnectionLiveData(val context: Context) : LiveData<Boolean>() {

    private var connectivityManager: ConnectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager

    private lateinit var connectivityManagerCallback: ConnectivityManager.NetworkCallback

    private val networkRequestBuilder: NetworkRequest.Builder = NetworkRequest.Builder()
        .addTransportType(android.net.NetworkCapabilities.TRANSPORT_CELLULAR)
        .addTransportType(android.net.NetworkCapabilities.TRANSPORT_WIFI)

    override fun onActive() {
        super.onActive()
        updateConnection()
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> connectivityManager.registerDefaultNetworkCallback(getConnectivityMarshmallowManagerCallback())
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> marshmallowNetworkAvailableRequest()
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> lollipopNetworkAvailableRequest()
            else -> {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    context.registerReceiver(networkReceiver, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")) // android.net.ConnectivityManager.CONNECTIVITY_ACTION
                }
            }
        }
    }

    override fun onInactive() {
        super.onInactive()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            connectivityManager.unregisterNetworkCallback(connectivityManagerCallback)
        } else {
            context.unregisterReceiver(networkReceiver)
        }
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private fun lollipopNetworkAvailableRequest() {
        connectivityManager.registerNetworkCallback(networkRequestBuilder.build(), getConnectivityLollipopManagerCallback())
    }

    @TargetApi(Build.VERSION_CODES.M)
    private fun marshmallowNetworkAvailableRequest() {
    connectivityManager.registerNetworkCallback(networkRequestBuilder.build(), getConnectivityMarshmallowManagerCallback())
    }

    private fun getConnectivityLollipopManagerCallback(): ConnectivityManager.NetworkCallback {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
           connectivityManagerCallback = object : ConnectivityManager.NetworkCallback() {
               override fun onAvailable(network: Network?) {
                   postValue(true)
               }

               override fun onLost(network: Network?) {
                   postValue(false)
               }
           }
           return connectivityManagerCallback
       } else {
           throw IllegalAccessError("Accessing wrong API version")
       }
    }

    private fun getConnectivityMarshmallowManagerCallback(): ConnectivityManager.NetworkCallback {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          connectivityManagerCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onCapabilitiesChanged(network: Network?, networkCapabilities: NetworkCapabilities?) {
                networkCapabilities?.let { capabilities ->
                    if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                        postValue(true)
                    }
                }
            }
            override fun onLost(network: Network?) {
                postValue(false)
            }
        }
        return connectivityManagerCallback
    } else {
        throw IllegalAccessError("Accessing wrong API version")
    }

    private val networkReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            updateConnection()
        }
    }

    private fun updateConnection() {
        val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
        postValue(activeNetwork?.isConnected == true)
    }
}

そして同じ使用法:

    val connectionLiveData = ConnectionLiveData(context)
        connectionLiveData.observe(this, Observer { isConnected ->
           isConnected?.let {
             // do job
           }
    })

ところで、あなたの解決策についてsayemに感謝します。


2
素晴らしいソリューション!
ボックス

2
ライブデータを使用し、古いバージョンをサポートする非常に優れたソリューション
Prakash Shukla

これは、インターネットで利用できる最良のソリューションです。
Karan Sharma

非常に良い解決策です!ただし、「not」が1つあります。onAvailable(network:Network?)メソッドを使用するのは間違っています。インターネットが利用できない場合でも、このメソッドが呼び出されるためです。onCapabilitiesChanged(network:Network、networkCapabilities:NetworkCapabilities)を使用して、networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)およびnetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)を確認することをお勧めします。
DmitryKanunnikoff

このコードベース内のIPとネットワークタイプを取得する方法
A_rmas

28

Android Nのドキュメントには次のように記載されています。

Android Nをターゲットとするアプリは、これらのイベントの通知を要求するマニフェストエントリがある場合でも、CONNECTIVITY_ACTIONブロードキャストを受信しません。フォアグラウンドで実行されているアプリは、BroadcastReceiverで通知を要求した場合でも、メインスレッドでCONNECTIVITY_CHANGEをリッスンできます。

つまりBroadcastReceiver、アプリがフォアグラウンドで実行されている場合でも、ネットワーク接続の変更を検出するために登録できます。


素敵な微妙な漁獲:)
Amokrane Chentir

これは、フォアグラウンドにないとアプリがブロードキャストの受信を停止することを意味しますか?(たとえば、サービスでそれを聞くことができないのですか?)
サンディー

1
はっきりとは分かりませんが、確かめるためにテストする必要があります。ただし、ドキュメントを読むと、アプリがフォアグラウンドにない場合、ブロードキャストを取得できないように見えますIntent
David Wasser

2
ただし、バックグラウンドでの接続の変更を検出することは、すべてのSIPアプリ(VoIP)で必須です。これらのアプリは通常、何日もバックグラウンドで実行され、通話が着信した場合にのみフォアグラウンドにジャンプします(電話のダイヤラのように)。これらのアプリはバックグラウンドで自動的に再接続する必要があります。これにより、オフラインになるため、Androidプラットフォームからすべてのアプリ(独自のプッシュサーバーを持たないアプリ)が強制終了されます。常に。
グリスグラム2017年

firebase pushサービスを使用するだけです。
Pierre

21

Android Nのサポートについては、まず@Amokrane Chentirの回答を確認してください。

すべてのAPIレベルでサポートし、UIでそれを観察したい場合は、次のコードを確認してください。

NetworkConnectionのLiveData:

class ConnectionLiveData(val context: Context) : LiveData<Boolean>(){

    var  intentFilter = IntentFilter(CONNECTIVITY_ACTION)
    private var  connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
    private lateinit var networkCallback : NetworkCallback

    init {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            networkCallback = NetworkCallback(this)
        }
    }

    override fun onActive() {
        super.onActive()
        updateConnection()
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> connectivityManager.registerDefaultNetworkCallback(networkCallback)
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
                val builder = NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI)
                connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
            }
            else -> {
                context.registerReceiver(networkReceiver, intentFilter)
            }
        }
    }

    override fun onInactive() {
        super.onInactive()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            connectivityManager.unregisterNetworkCallback(networkCallback)
        } else{
            context.unregisterReceiver(networkReceiver)
        }
    }


    private val networkReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            updateConnection()
        }
    }

    fun updateConnection() {
        val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
        postValue(activeNetwork?.isConnectedOrConnecting == true)
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    class NetworkCallback(val liveData : ConnectionLiveData) : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network?) {
            liveData.postValue(true)
        }

        override fun onLost(network: Network?) {
            liveData.postValue(false)
        }
    }
}

UIで観察(アクティビティ/フラグメント):

val connectionLiveData = ConnectionLiveData(context)
    connectionLiveData.observe(this, Observer { 
       // do whatever you want with network connectivity change 
})

ところで、IntentFilter明示的に定義する必要はありません。:そのようなvar intentFilter = IntentFilter(CONNECTIVITY_ACTION)
ライアン・アマラル

あなたの提案をありがとう。onActiveで毎回オブジェクトを作成したくありませんでした。
Sayem 2018

つまり、2つのグローバル変数/プロパティ(intentFilterおよびconnectivityManager)は、それらのタイプ(IntentFilterおよびConnectivityManagerそれぞれ)を明示的に定義する必要はありません。
ライアンアマラル2018

7

私は数日前に同じ問題に遭遇し、このライブラリAndroid-Jobを使用することにしました

このライブラリはJobSchedularGcmNetworkManagerおよびBroadcastReceiverどのAndroidのバージョンに応じて、アプリが実行されています。

仕事を始めるのはかなり簡単です

new JobRequest.Builder(DemoSyncJob.TAG)
            .setRequiresCharging(true)
            .setRequiresDeviceIdle(false)
            .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) // this is what gets the job done
            .build()
            .schedule();

1
私は同じスケジューラを試して、このような例外が発生しました。制約なしでジョブを構築しようとしていますが、これは許可されていません。これを解決するために私たちを助けていただけますか?
Sanket Kachhela 2017年

この目的でAndroidジョブを使用することは、実際にはあまり良い解決策ではありません。これは、指定した時間に1回または定期的に実行することを目的としています。これは、アラームなどの互換性をサポートするためのものです。これは、APIが変更された理由の全体的な考えに反します。次の資料を読んでください: developer.android.com/training/monitoring-device-state/… 理由がすぐにわかります。
pedronveloso

唯一の問題は、Android Nでは、今後15分以上しかスケジュールできないということです
Fire Crow

4

私はSayamの回答に基づいたKotlin実装を書きましたが、はありませんLiveData。私は(現時点で)ConnectivityManager#registerDefaultNetworkCallbackAndroid Nougatをターゲットとする最新のAPIメソッド()を呼び出すことにしました。

/**
 * Observes network connectivity by consulting the [ConnectivityManager].
 * Observing can run infinitely or automatically be stopped after the first response is received.
 */
class ConnectivityObserver @JvmOverloads constructor(

        val context: Context,
        val onConnectionAvailable: () -> Unit,
        val onConnectionLost: () -> Unit = {},
        val shouldStopAfterFirstResponse: Boolean = false

) {

    private val connectivityManager
        get() = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    @Suppress("DEPRECATION")
    private val intentFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)

    private val broadCastReceiver = object : BroadcastReceiver() {

        @Suppress("DEPRECATION")
        override fun onReceive(context: Context?, intent: Intent?) {
            if (ConnectivityManager.CONNECTIVITY_ACTION != intent?.action) {
                return
            }
            val networkInfo = connectivityManager.activeNetworkInfo
            if (networkInfo != null && networkInfo.isConnectedOrConnecting) {
                onConnectionAvailable.invoke()
            } else {
                onConnectionLost.invoke()
            }
            if (shouldStopAfterFirstResponse) {
                stop()
            }
        }

    }

    private lateinit var networkCallback: ConnectivityManager.NetworkCallback

    init {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            networkCallback = object : ConnectivityManager.NetworkCallback() {

                override fun onAvailable(network: Network) {
                    super.onAvailable(network)
                    onConnectionAvailable.invoke()
                    if (shouldStopAfterFirstResponse) {
                        stop()
                    }
                }

                override fun onLost(network: Network?) {
                    super.onLost(network)
                    onConnectionLost.invoke()
                    if (shouldStopAfterFirstResponse) {
                        stop()
                    }
                }
            }
        }
    }

    fun start() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            // Decouple from component lifecycle, use application context.
            // See: https://developer.android.com/reference/android/content/Context.html#getApplicationContext()
            context.applicationContext.registerReceiver(broadCastReceiver, intentFilter)
        } else {
            connectivityManager.registerDefaultNetworkCallback(networkCallback)
        }
    }

    fun stop() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            context.applicationContext.unregisterReceiver(broadCastReceiver)
        } else {
            connectivityManager.unregisterNetworkCallback(networkCallback)
        }
    }

}

使用法:

val onConnectionAvailable = TODO()
val connectivityObserver = ConnectivityObserver(context, onConnectionAvailable)
connectivityObserver.start()
connectivityObserver.stop()

または:

val onConnectionAvailable = TODO()
val onConnectionLost = TODO()
ConnectivityObserver(context, 
    onConnectionAvailable, 
    onConnectionLost, 
    shouldStopAfterFirstResponse = true
).start()

AndroidManifest.xmlにACCESS_NETWORK_STATE権限を追加することを忘れないでください:

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

皆様からの有益なコメントや改善点をお待ちしております。


1
メインスレッドのアクティビティ(コンテキスト)で「ビューに触れる」ことができるように、コールバックが何かを変更する必要が(context as AppCompatActivity).runOnUiThread(object: Runnable{ override fun run() { onConnectionAvailable.invoke() } })ありましたonConnectionAvailable.invoke()。も同じですonConnectionLost.invoke()
АндрейВоробьев

はい、ユースケースによってはスレッドを切り替える必要があるかもしれません。私はそれをクラスの一部にはしませんが、代わりにクラスの消費者にこれを処理させます。しかし、ヒントをありがとう。
JJD

2

Android N(Nougat)をターゲットとするアプリCONNECTIVITY_ACTIONは、マニフェストで定義されたブロードキャストを受信しません(Svelteを参照)。

可能な解決策:

Android O-バックグラウンドで接続の変更を検出するもご覧ください


1

答えに同意します @rdsによって提案されました。

CONNECTIVITY_ACTIONはAPIレベル28で廃止されることに注意してください。

アプリが強制終了されているにもかかわらずWifiの状態(接続/切断)を検出する必要があり、最新バージョンをターゲットにしたい場合は、あまり選択肢がありません。

使用する必要があります connectivityManager.registerNetworkCallback(networkRequest, networkCallback)

問題は、BroadcastReceiverを使用できないことです。それでは、どうすればよいでしょうか。

JobSchedulerを使用するか、WorkManager(定期的な要求)の場合はそれ以上を使用できます。OneTimeRequestの場合、アプリがフォアグラウンドにある間は1回しか実行できず、リッスンし続けることができるので、なぜ定期的であるのか。

ドキュメンテーションは言う:

アプリケーションが終了するか、リンク#unregisterNetworkCallback(NetworkCallback)}が呼び出されるまで、コールバックは引き続き呼び出されます。

アプリが強制終了されるか、最近のアプリリストから削除されると、networkCallbackはリッスンできなくなります。

したがって、アプリを継続的にリッスンさせるには、このような定期的なジョブが必要です。期間はどのくらいにする必要がありますか?それはあなた次第であり、ケース・ツー・ケースに依存します。

私はそれが少し醜い方法であることを知っていますが、これはその方法です。1つの課題は、ユーザーのデバイスがDozeモードであるか、アプリがスタンバイ状態である場合、ジョブが遅延する可能性があることです。


また、大幅にカスタマイズされた一部のEMUIでは、MIUI Android OSのworkManager(定期的なタスク)が常に正しく動作する必要がないことに注意してください。
ケバブクラビー

1

registerNetworkCallbackメソッドを使用してネットワークコールバックを登録すると、トリガーされない場合と、誤検知がトリガーされる場合があります。

  1. インターネット接続でアプリを起動すると onAvailableメソッドがトリガーされます。
  2. しかし、アプリを起動したときにデバイスにインターネット接続がない場合は、 NetworkCallback呼び出され(p。1のために非常に奇妙です)
  3. Wi-Fi接続はあるがインターネット接続がない場合は、onAvailableメソッドがトリガーされます。また、インターネット接続の監視を期待しているため、これは誤検知の動作だと思います。

以下のコードでわかるように、デフォルトではインターネット接続が利用可能で、変更された場合にのみトリガーされます。誤検知のトリガーはありません。

これこの回答を要約してください(ただし、API> = 21の場合のみ):

class ConnectionManager @Inject constructor(
    private val connectivityManager: ConnectivityManager,
    private val disposable: CompositeDisposable,
    private val singleTransformer: SingleTransformer<*, *>
) : LiveData<Boolean>() {

    private var isNetworkAvailable = true

    private val builder = NetworkRequest.Builder()
        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)

    private val callback = object : ConnectivityManager.NetworkCallback() {

        override fun onAvailable(network: Network) {
            ping()
        }

        override fun onLost(network: Network) {
            ping()
        }
    }

    private fun ping() {
        disposable.add(
            Single.fromCallable {
                try {
                    val timeoutMs = 1500
                    val socket = Socket()
                    val socketAddress = InetSocketAddress("8.8.8.8", 53)

                    socket.connect(socketAddress, timeoutMs)
                    socket.close()
                    true
                } catch (e: IOException) {
                    false
                }
            }
                .compose(singleTransformer as SingleTransformer<Boolean, Boolean>)
                .subscribeBy {
                    if (isNetworkAvailable != it){
                        value = it
                        isNetworkAvailable = it
                    }
                }
        )
    }

    override fun onActive() {
        ping()
        connectivityManager.registerNetworkCallback(builder.build(), callback)
    }

    override fun onInactive() {
        disposable.clear()
        connectivityManager.unregisterNetworkCallback(callback)
    }
}

依存関係を提供する方法

@Provides
fun provideTransformer(): SingleTransformer<Boolean, Boolean> {
    return SingleTransformer<Boolean, Boolean> { upstream: Single<Boolean> ->
        upstream.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
    }
}

@Singleton
@Provides
fun provideConnectivityManager(context: Context): ConnectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

@Singleton
@Provides
fun provideConnectionManager(connectivityManager: ConnectivityManager, singleTransformer: SingleTransformer<Boolean, Boolean>): ConnectionManager =
        ConnectionManager(connectivityManager, singleTransformer)

そして使い方:

@Inject
lateinit var connectionManager: ConnectionManager

//....

viewLifecycleOwner.observe(connectionManager) { isInternetAvailable ->
    // TODO 
}

1

@KebabKrabbyの答えに基づいて:

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Context.CONNECTIVITY_SERVICE
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.ConnectivityManager.CONNECTIVITY_ACTION
import android.net.ConnectivityManager.EXTRA_NO_CONNECTIVITY
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.os.Build
import androidx.lifecycle.LiveData

class ConnectivityWatcher(
    private val context: Context
): LiveData<Boolean>() {

    private lateinit var networkCallback: ConnectivityManager.NetworkCallback
    private lateinit var broadcastReceiver: BroadcastReceiver

    override fun onActive() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
            networkCallback = createNetworkCallback()
            cm.registerDefaultNetworkCallback(networkCallback)
        } else {
            val intentFilter = IntentFilter(CONNECTIVITY_ACTION)
            broadcastReceiver = createBroadcastReceiver()
            context.registerReceiver(broadcastReceiver, intentFilter)
        }
    }

    override fun onInactive() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
            cm.unregisterNetworkCallback(networkCallback)
        } else {
            context.unregisterReceiver(broadcastReceiver)
        }
    }

    private fun createNetworkCallback() = object : ConnectivityManager.NetworkCallback() {

        override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
        ) {
            val isInternet = networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)
            val isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)
            postValue(isInternet && isValidated)
        }

        override fun onLost(network: Network) {
            postValue(false)
        }
    }

    private fun createBroadcastReceiver() = object : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent?) {
            val isNoConnectivity = intent?.extras?.getBoolean(EXTRA_NO_CONNECTIVITY) ?: true
            postValue(!isNoConnectivity)
        }
    }
}

そして、それを元の回答とほとんど同じように使用します(たとえば、アクティビティから観察する場合):

ConnectivityWatcher(this).observe(this, Observer {
    Log.i("*-*-*", "is internet available? - ${if (it) "Yes" else "No"}")
})
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.