不推荐使用ConnectivityManager.CONNECTIVITY_ACTION

Rag*_* db 75 android

在Android N中,官方网站上提到"针对Android N的应用不接收CONNECTIVITY_ACTION广播".并且还提到JobScheduler可以用作替代方案.但是JobScheduler它没有提供与CONNECTIVITY_ACTION广播完全相同的行为.

在我的Android应用程序中,我使用此广播来了解设备的网络状态.我想知道,如果在该状态CONNECTINGCONNECTED的帮助下CONNECTIVITY_ACTION广播和它是最适合我的要求.

现在它已被弃用,任何人都可以建议我获得当前网络状态的替代方法吗?

Amo*_*tir 86

将弃用的是后台应用程序接收网络连接状态更改的能力.

正如David 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
   }
}
Run Code Online (Sandbox Code Playgroud)

  • NetworkCallback仅适用于API级别21+ (22认同)
  • 大声笑什么地狱,另外10个Android更新和我们将能够写的所有是一个你好世界的应用程序 (12认同)
  • 如果应用程序在前台运行,这种技术只能工作.这是否意味着,当应用程序未在前台运行时,我们不再能够监听连接事件?在manifest.xml中使用<action android:name ="android.net.conn.CONNECTIVITY_CHANGE"/>不再对Android N有任何影响. (2认同)
  • @CheokYanCheng AFAIK 是正确的。您需要有一个在前台运行的进程来侦听连接事件。似乎 Android 框架工程师做出的假设是,监听连接事件主要是为了知道何时开始在客户端和服务器之间同步数据。因此,JobScheduler 是该用例的推荐方式。 (2认同)
  • 我是否需要取消注册 NetworkCallback(例如,在活动的 onDestroy 方法中)? (2认同)
  • @Ruslan 是的,当然,否则您将泄漏已注册的任何内容 (2认同)

Dav*_*ser 23

Android N的文档指出:

针对Android N的应用不会收到CONNECTIVITY_ACTION广播,即使他们有明确的条目来请求通知这些事件.如果应用程序通过BroadcastReceiver请求通知,则在前台运行的应用程序仍然可以在其主线程上侦听CONNECTIVITY_CHANGE.

这意味着,BroadcastReceiver如果您的应用程序在前台运行,您仍然可以注册,以便检测网络连接的变化.

  • 但是对于任何 sip-apps (VoIP) 来说,检测后台的连接变化是强制性的……这些应用程序通常在后台运行数天,只有在有来电时才跳转到前台(就像你的电话拨号器一样)。 . 这些应用*需要*在后台自动重新连接。这会杀死所有来自 android 平台的应用程序(没有自己的推送服务器),因为它们将处于离线状态。总是。 (2认同)

Keb*_*bby 19

我将更新Sayem's修复lint问题的答案向我展示.

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

同样的用法:

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

顺便说一下,感谢你说出你的错觉.

  • 惊人的解决方案! (3认同)
  • 使用实时数据并支持旧版本的非常好的解决方案 (3认同)
  • 这是互联网上可用的最佳解决方案。 (2认同)
  • 非常好的解决方案!但有一个“不” - 使用 onAvailable(network: Network?) 方法是错误的方法,因为它甚至会调用 Internet 不可用的情况。最好使用 onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) 并检查 networkCapability.hasCapability(NET_CAPABILITY_INTERNET) 和 networkCapability.hasCapability(NET_CAPABILITY_VALIDATED)。 (2认同)

Say*_*yem 15

请首先检查@Amokrane Chentir对Android N支持的回答.

对于那些想要支持所有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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在UI中观察(活动/片段):

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


Dmi*_*off 8

基于@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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并且使用它与原始答案几乎相同(例如,如果从活动中观察):

ConnectivityWatcher(this).observe(this, Observer {
    Log.i("*-*-*", "is internet available? - ${if (it) "Yes" else "No"}")
})
Run Code Online (Sandbox Code Playgroud)

  • 迄今为止最有帮助的答案。此解决方案的唯一问题是,使用 ConnectivityManager.CONNECTIVITY_ACTION 和广播接收器,当应用程序在飞行模式打开或移动数据/WIFI 关闭的情况下启动时,可能会发出错误值。问题是 ConnectivityManager.NetworkCallback() 在这种情况下不会触发。为了克服这个问题,您可以将 ConnectivityWatcher 的默认值设置为“false”,或者观察该值并将其传递给 ViewModel 中的 MutableLiveData,并使用协程在一定延迟(例如 1000ms)后将该值设置为 false(如果该值为 null) (2认同)

Nom*_*que 7

几天前我遇到了同样的问题,我决定使用这个Android-Job

此库使用JobSchedular,GcmNetworkManagerBroadcastReceiver取决于运行该应用程序的Android版本.

开始工作相当容易

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