developer tip

ConnectivityManager.CONNECTIVITY_ACTION 지원 중단됨

copycodes 2020. 10. 21. 08:15
반응형

ConnectivityManager.CONNECTIVITY_ACTION 지원 중단됨


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

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

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


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

데이비드 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
   }
}

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

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

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


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

    override fun onActive() {
        super.onActive()
        updateConnection()
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> connectivityManager.registerDefaultNetworkCallback(getConnectivityManagerCallback())
            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() {
        val builder = NetworkRequest.Builder()
            .addTransportType(android.net.NetworkCapabilities.TRANSPORT_CELLULAR)
            .addTransportType(android.net.NetworkCapabilities.TRANSPORT_WIFI)
        connectivityManager.registerNetworkCallback(builder.build(), getConnectivityManagerCallback())
    }

    private fun getConnectivityManagerCallback(): 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("Should not happened")
        }
    }

    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 당신의 solusion에 대한 sayem 감사합니다.


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

며칠 전 같은 문제가 발생하여 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();

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

가능한 해결책:

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


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

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

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

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

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

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

문서 내용 :

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

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

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

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


나는 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" />

도움이되는 의견과 개선 사항을 기다리겠습니다.

참고 URL : https://stackoverflow.com/questions/36421930/connectivitymanager-connectivity-action-deprecated

반응형