ConnectivityManager.CONNECTIVITY_ACTION 지원 중단됨


95

Android N에서는 공식 웹 사이트에 "Android N을 대상으로하는 앱은 CONNECTIVITY_ACTION 방송을 수신하지 않습니다"라고 언급되어 있습니다. 그리고 JobScheduler대안으로 사용할 수있는 것도 언급 됩니다. 그러나는 방송 JobScheduler과 똑같은 동작을 제공하지 않습니다 CONNECTIVITY_ACTION.

내 Android 애플리케이션에서이 브로드 캐스트를 사용하여 장치의 네트워크 상태를 알고있었습니다. 나는이 상태가 있다면 알고 싶어 CONNECTING하거나 CONNECTED의 도움으로 CONNECTIVITY_ACTION방송과 가장 잘 내 요구 사항에 적합했다.

이제 더 이상 사용되지 않으므로 현재 네트워크 상태를 가져 오는 대체 방법을 제안 할 수 있습니까?


10
그리고 OP가 언젠가 targetSdkVersionN 이상으로 업이 필요한 동작을 원한다면 어떻게 될까요?
Michael

1
글쎄요, 제 어플리케이션을 Android NI로 지정하지 않으면 방송이 수신된다는 것도 알고 있습니다. 하지만 내 애플리케이션은 Android N을 지원해야합니다. Android N에서 동일한 브로드 캐스트 동작을 얻으려면 어떻게해야합니까? 시도 할 수있는 다른 접근 방법이 있습니까? @DavidWasser
라구 람 DB

때로는 미래에 대한 걱정이 더 합리적이라고 생각합니다. 이것은 순전히 프로그래밍에 대한 실용적인 접근 방식입니다. 물론 코드가 더 이상 사용되지 않는 기능을 사용하지 않는지 항상 확인할 수 있습니다. 반면에 더 이상 사용되지 않는 기능은 일반적으로 오랫동안 유지되며 더 이상 사용되지 않는 기능이 사라지기 전에 앱의 수명이 종료 될 수 있습니다. Android N은 너무 새로워 서 걱정하지 않아도됩니다. 아직. 내 2 센트. 질문에 대한 의견을 썼고 "그렇게하지 마십시오"가 유효한 대답이라고 제안하지 않았습니다.
David Wasser

2
@Raghuramdb 앱을 Android N으로 타겟팅하지 않아도 Android N에서 실행할 수 있습니다. Android N에서만 사용할 수있는 기능을 사용하려면 Android N 만 타겟팅하면됩니다.
David Wasser

2
API29를 대상으로하는 경우에도 인 텐트 필터 BroadcastReceiver와 함께를 계속 사용할 android.net.conn.CONNECTIVITY_CHANGE수 있으며 Application.OnCreate. 앱이 종료되면 업데이트를받지 못합니다.
Pierre

답변:


101

더 이상 사용되지 않는 것은 백그라운드 애플리케이션이 네트워크 연결 상태 변경을 수신하는 기능입니다.

데이비드 WASSER는 응용 프로그램 구성 요소 인스턴스 (파괴되는 것이 아니다) 경우 당신은 여전히 연결 변경 알림을받을 수 있으며, 당신이 말했다 프로그램 수신기를 등록 하는 대신 매니페스트에 그 일의 그 문맥에.

또는 대신 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.net.conn.CONNECTIVITY_CHANGE"/> manifest.xml 더 이상 안드로이드 N.에 영향이없는에서
Cheok 연의 쳉

2
@CheokYanCheng AFAIK 맞습니다. 연결 이벤트를 수신하려면 포 그라운드에서 실행되는 프로세스가 있어야합니다. Android 프레임 워크 엔지니어가 내린 가정은 연결 이벤트를 수신하는 것이 주로 클라이언트와 서버 간의 데이터 동기화를 시작할 때를 알기 위해 수행되었다는 것입니다. 따라서 JobScheduler는 해당 사용 사례에 권장되는 방법입니다.
Amokrane Chentir 2011

27
롤 우리가 쓸 수 있습니다 지옥, 또 다른 10 안드로이드 업데이트 및 모두가 안녕하세요 세계 응용 프로그램은 무엇인지
DennisVA

1
NetworkCallback의 등록을 취소해야합니까 (예 : 활동의 onDestroy 메서드에서)?
Ruslan Berozov

2
당연히 예 @Ruslan하거나 등록 어떤 누출
DennisVA

35

Sayem's린트 문제 해결에 대한 답변을 업데이트 하겠습니다.

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
           }
    })

Btw 귀하의 솔루션에 대해 sayem에게 감사드립니다.


2
놀라운 솔루션!
box

2
라이브 데이터를 사용하고 이전 버전을 지원하는 매우 좋은 솔루션
Prakash Shukla

이것은 인터넷에서 사용할 수있는 최상의 솔루션입니다.
Karan Sharma

아주 좋은 해결책! 그러나 하나의 "아님"이 있습니다. onAvailable (network : Network?) 메서드를 사용하는 것은 잘못된 방법입니다. 인터넷을 사용할 수없는 경우에도 호출하기 때문입니다. onCapabilitiesChanged (network : Network, networkCapabilities : NetworkCapabilities)를 사용하고 networkCapabilities.hasCapability (NET_CAPABILITY_INTERNET) 및 networkCapabilities.hasCapability (NET_CAPABILITY_VALIDATED)를 확인하는 것이 좋습니다.
DmitryKanunnikoff

이 코드베이스 내에서 IP 및 네트워크 유형을 얻는 방법은 무엇입니까?
A_rmas

29

Android N에 대한 문서는 다음과 같이 설명합니다.

Android N을 타겟팅하는 앱은 이러한 이벤트 알림을 요청하는 매니페스트 항목이 있더라도 CONNECTIVITY_ACTION 브로드 캐스트를 수신하지 않습니다. 포 그라운드에서 실행되는 앱은 BroadcastReceiver로 알림을 요청하는 경우 기본 스레드에서 CONNECTIVITY_CHANGE를 계속 수신 할 수 있습니다.

BroadcastReceiver, 네트워크 연결의 변경 사항을 감지하기 위해 앱이 포 그라운드에서 실행중인 경우를 계속 등록 할 수 있습니다 .


니스 미묘한 캐치 :
Amokrane Chentir

이것은 앱이 포 그라운드에 있지 않으면 브로드 캐스트 수신을 중지한다는 것을 의미합니까? (내가 예를 들어 서비스에서 수신 할 수 있도록?)
sundie

1
확실하지 않습니다. 확실하게 테스트해야합니다. 그러나 문서를 읽으면 앱이 포 그라운드에 있지 않으면 브로드 캐스트를받을 수없는 것처럼 보입니다 Intent.
David Wasser

2
그러나 백그라운드에서 연결 변경을 감지하려면 모든 sip-apps (VoIP)에 대해 필수입니다. 이러한 앱은 일반적으로 며칠 동안 백그라운드에서 실행되며 전화가 올 때만 전경으로 이동합니다 (전화 걸기처럼) .. . 이러한 앱 은 백그라운드에서 자동으로 다시 연결 되어야 합니다. 오프라인 상태가되므로 Android 플랫폼에서 자체 푸시 서버가없는 모든 앱을 종료합니다. 항상.
Grisgram

firebase 푸시 서비스를 사용하십시오.
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 
})

btw, IntentFilter명시 적으로 정의 할 필요가 없습니다 . 다음과 같이 :var intentFilter = IntentFilter(CONNECTIVITY_ACTION)
Ryan Amaral

제안 해 주셔서 감사합니다. onActive에서 매번 개체를 만들고 싶지 않았습니다.
Sayem

2 개의 전역 변수 / 속성 ( intentFilterconnectivityManager)은 명시 적으로 유형 ( IntentFilterConnectivityManager각각)을 정의 할 필요가 없음을 의미합니다 .
Ryan Amaral

대부분의 항목은 (불행히도) 더 이상 사용되지 않습니다.
Andrew

7

며칠 전 같은 문제가 발생하여 Android-Job 라이브러리를 사용하기로 결정했습니다.

이 라이브러리 용도 JobSchedular, GcmNetworkManager그리고 BroadcastReceiver어떤 안드로이드 버전에 따라 응용 프로그램에서 실행 중입니다.

일을 시작하는 것은 상당히 쉽습니다

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

이 목적으로 Android-Job을 사용하는 것은 정말 좋은 해결책이 아닙니다. 한 번 또는 주기적으로 지정된 시간에 작업을 실행하는 것을 의미합니다. 알람 등에 대한 소급 호환성 지원을 제공하기위한 것입니다. 이것은 API가 변경된 이유에 대한 전체 아이디어에 위배되며 다음을 읽고 있습니다. developer.android.com/training/monitoring-device-state/… 이유를 빠르게 이해할 수 있습니다.
pedronveloso

유일한 문제는 Android N에서는 향후 최소 15 분 동안 만 예약 할 수 있다는 것입니다
Fire Crow

4

나는 Sayam의 답변을 기반으로 하지만 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

4

@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"}")
})

2

안드로이드 N (누가)를 대상으로 앱은받지 않습니다 CONNECTIVITY_ACTION(참조 매니페스트에 정의 된 방송을 벨테을 ).

가능한 해결책:

Android O-백그라운드에서 연결 변경 감지를 참조하십시오.


1

@rds가 제안한 답변에 동의합니다 .

것을 명심 마십시오 CONNECTIVITY_ACTION는 API 레벨 28에서 더 이상 사용되지 않습니다.

앱이 종료 되었음에도 불구하고 Wifi 상태 (연결 / 연결 해제)를 감지해야한다는 요구 사항이 있고 최신 버전을 대상으로 지정하려는 경우 선택의 여지가별로 없습니다.

당신은 사용해야합니다 connectivityManager.registerNetworkCallback(networkRequest, networkCallback)

질문은 BroadcastReceiver를 사용할 수 없다는 것입니다.

WorkManager (Periodic Request) 인 경우 JobScheduler 이상을 사용할 수 있습니다. 왜주기적인 이유는 OneTimeRequest 인 경우 한 번만 실행할 수 있고 앱이 포 그라운드에있는 동안 계속 수신 할 수 있기 때문입니다.

문서 내용 :

콜백은 애플리케이션이 종료되거나 링크 #unregisterNetworkCallback (NetworkCallback)}이 호출 될 때까지 계속 호출됩니다.

앱이 종료되거나 최근 앱 목록에서 제거되면 networkCallback이들을 수 없습니다.

따라서 앱이 지속적으로 수신하도록하려면 이러한주기적인 작업이 필요합니다. 기간은 얼마입니까? 그것은 당신에게 달려 있으며 경우에 따라 다릅니다.

나는 그것이 약간 추한 방법이라는 것을 알고 있지만 이것이 방법입니다. 한 가지 문제는 사용자의 기기가 잠자기 모드이거나 앱이 대기 상태 인 경우 작업이 지연 될 수 있다는 것입니다.


또한 일부 사용자 정의 된 EMUI에서 MIUI Android OS workManager (주기적 작업)가 항상 올바르게 작동 할 필요는 없습니다.
Kebab Krabby

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 
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.