如何判断“移动网络数据”是启用还是禁用(即使通过 WiFi 连接)?

Posted

技术标签:

【中文标题】如何判断“移动网络数据”是启用还是禁用(即使通过 WiFi 连接)?【英文标题】:How to tell if 'Mobile Network Data' is enabled or disabled (even when connected by WiFi)? 【发布时间】:2012-09-30 04:17:04 【问题描述】:

我有一个应用程序,我希望能够使用它从远程查询中获取连接状态报告。

我想知道是否连接了 WiFi,以及是否通过移动网络启用了数据访问。

如果 WiFi 超出范围,我想知道我是否可以依赖移动网络。

问题是wifi连接时data enabled总是返回true,没有wifi连接时才能正常查询移动网络。

我看到的所有答案都建议轮询以查看当前连接是什么,但我想知道移动网络是否可用,如果我需要它,即使我目前可能通过 WiFi 连接。

有没有不轮询看是否连接就知道是否开启移动网络数据?

编辑

因此,当通过 WiFi 连接时,如果我转到设置并取消选择“启用数据”,然后在我的应用程序中执行以下操作:

 boolean mob_avail = 
 conMan.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isAvailable();

mob_avail 返回为“真”,但我已禁用移动网络数据,所以我希望它为“假”

如果我关闭 WiFi,则(正确地)没有连接,因为我已禁用移动网络数据。

那么当我通过WiFi连接时如何检查是否启用了移动网络数据?

更新

我查看了 ss1271 在 cmets 中建议的 getAllNetworkInfo()

我在以下 3 个条件下输出了返回的关于移动网络的信息

WiFi 关闭 - 移动数据开启

WiFi 开启 - 移动数据关闭

WiFi 开启 - 移动数据开启

得到以下结果:

WiFi 关闭:

mobile[HSUPA],状态:CONNECTED/CONNECTED,原因:未知,额外: 互联网,漫游:假,故障转移:假,isAvailable:真, featureId:-1,userDefault:false

WiFi 开启/移动关闭

NetworkInfo:类型:mobile[HSUPA],状态:DISCONNECTED/DISCONNECTED, 原因:connectionDisabled,额外:(无),漫游:false, 故障转移:false,isAvailable:true,featureId:-1,userDefault: 假的

WiFi 开启/移动设备开启

NetworkInfo:类型:mobile[HSPA],状态:DISCONNECTED/DISCONNECTED, 原因:connectionDisabled,额外:(无),漫游:false, 故障转移:false,isAvailable:true,featureId:-1,userDefault: 假的

所以你可以看到 isAvailable 每次都返回 true,并且状态仅在 WiFi 生效时显示为 Disconnected。

澄清

查看我的手机当前是否已通过移动网络连接。我AM试图确定用户是否已启用/禁用通过移动网络访问数据。他们可以通过转到设置 -> 无线和网络设置 -> 移动网络设置 -> 启用数据来打开和关闭此功能

【问题讨论】:

***.com/questions/12686899/… 同样的事情 你试过了吗:getAllNetworkInfo()? 我想不起来了,但我回家后会尽快尝试更新。我认为这会返回一组可用连接?那我需要遍历数组吗? 是的,它会返回一个数组。老实说,我以前没有使用过这个,所以你可能需要打印一些日志来看看它会为你带来什么。请告诉我结果,祝你好运 【参考方案1】:

以下代码将告诉您“移动数据”是否启用,无论当前是否有移动数据连接或 wifi 是否启用/激活。 这个代码仅适用于 android 2.3 (Gingerbread) 及更高版本。 实际上这段代码也适用于早期版本的 Android ;-)

    boolean mobileDataEnabled = false; // Assume disabled
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    try 
        Class cmClass = Class.forName(cm.getClass().getName());
        Method method = cmClass.getDeclaredMethod("getMobileDataEnabled");
        method.setAccessible(true); // Make the method callable
        // get the setting for "mobile data"
        mobileDataEnabled = (Boolean)method.invoke(cm);
     catch (Exception e) 
        // Some problem accessible private API
        // TODO do whatever error handling you want here
    

注意:您需要拥有android.permission.ACCESS_NETWORK_STATE 权限才能使用此代码。

【讨论】:

太棒了!绝对完美。非常感谢,这让我损失了 1/3 的声誉,但它确实满足了我的需求,所以积分花得很值。我可以请您对代码进行一些解释吗?它与我一直在尝试的方法以及向我建议的解决方案不同。我说你使用反射是对的吗?如果是这样的话,我对反思有点模糊。它是一个后门把戏还是它是真正受支持的 Android 功能?无论如何,再次感谢。你的明星! 哇。非常感谢你的评论!很高兴我能帮助你。是的,这段代码使用了 Java 反射。出于某种愚蠢的原因,Android 核心开发人员决定不在公共 API 中公开获取和设置“启用移动数据”标志的方法。所以这些方法是存在的,但它们被标记为“私有”。使用反射,我们可以获得很多关于类的信息,甚至是那些被认为是“衍生”的信息。由于这是一个“不受支持的 API”,它可能无法在所有设备上运行,并且可能会在未来的 Android 版本中进行更改。但是,它现在似乎可以在大多数设备上运行。 @DavidWasser 太棒了。但是我需要提出一个问题,如果使用 proguard "getMobileDataEnabled" 字符串将被转换为其他导致错误结果的东西。 Proguard 不适用于反射 这可行,但它带来了很大的风险,因为 API 开发人员可以随时删除私有方法 as long as you are using reflection on things that can not be obfuscated, such as frameworks or libraries 我不知道。谢谢【参考方案2】:

我已经升级了 Allesio 的答案。 Settings.Secure 的 mobile_data int 已从 4.2.2 移至 Settings.Global。 如果您想知道移动网络是否已启用,即使已启用并连接了 wifi,请尝试此代码。

更新以检查 SIM 卡是否可用。感谢您指出murat。

boolean mobileYN = false;

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (tm.getSimState() == TelephonyManager.SIM_STATE_READY) 
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1)
    
        mobileYN = Settings.Global.getInt(context.getContentResolver(), "mobile_data", 1) == 1;
    
    else
        mobileYN = Settings.Secure.getInt(context.getContentResolver(), "mobile_data", 1) == 1;
    

【讨论】:

我在 Settings.Global developer.android.com/reference/android/provider/… 下没有看到任何这样的常量“mobile_data”。我想知道它是如何工作的,或者你。 @LoveForDroid 它是 Settings.Global 类的隐藏成员。可以在aosp源码中找到。 Settings.Global.getInt(context.getContentResolver(), "mobile_data",1) 虽然设备上没有 SIM 卡,但返回 1。 @Murat 我添加了一些代码来检查 sim 是否可用。 我已经看到至少一个设备的设置中的“禁用移动数据”选项不会更改此设置(但确实会导致 TelephonyManager.isDataEnabled() 返回 false)。可能是设置中的错误,因为向下滑动菜单中的移动数据图标也没有显示禁用,但它确实禁用了移动数据。【参考方案3】:

一种方法是检查用户是否在“设置”中激活了移动数据,如果 wifi 关闭,很可能会使用该数据。 这有效(经过测试),并且它不使用反射,尽管它在 API 中使用了一个隐藏值:

boolean mobileDataAllowed = Settings.Secure.getInt(getContentResolver(), "mobile_data", 1) == 1;

根据 API,您需要检查 Settings.Global 而不是 Settings.Secure,正如 @user1444325 所指出的那样。

来源: Android API call to determine user setting "Data Enabled"

【讨论】:

哇,这将比显然不适用于棒棒糖的反射方法要简洁得多。这是针对哪些版本的 android 进行测试的? 针对 4.4.4 和 5.0.1 测试。 针对 4.2.2 成功测试 如果没有 SIM 将返回 true,您应该添加 SIM 验证以及 如果设备上没有移动数据(没有移动调制解调器),则返回 true。【参考方案4】:

@sNash 的功能很好用。但在少数设备中,即使数据被禁用,我发现它也会返回 true。 所以我找到了一个替代解决方案,它在 Android API 中。

TelephonyManager 的getDataState() 方法将非常有用。

我用上面使用的函数更新了@snash 的函数。当蜂窝数据被禁用时,下面的函数返回 false,否则返回 true。

private boolean checkMobileDataIsEnabled(Context context)
        boolean mobileYN = false;

        TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        if (tm.getSimState() == TelephonyManager.SIM_STATE_READY) 
            TelephonyManager tel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
//          if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1)
//          
//              mobileYN = Settings.Global.getInt(context.getContentResolver(), "mobile_data", 0) == 1;
//          
//          else
//              mobileYN = Settings.Secure.getInt(context.getContentResolver(), "mobile_data", 0) == 1;
//          
            int dataState = tel.getDataState();
            Log.v(TAG,"tel.getDataState() : "+ dataState);
            if(dataState != TelephonyManager.DATA_DISCONNECTED)
                mobileYN = true;
            

        

        return mobileYN;
    

【讨论】:

【参考方案5】:

你可以试试这样的:

ConnectivityManager conMan = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

//mobile
State mobile = conMan.getNetworkInfo(0).getState();

//wifi
State wifi = conMan.getNetworkInfo(1).getState();


if (mobile == NetworkInfo.State.CONNECTED || mobile == NetworkInfo.State.CONNECTING) 

    //mobile

else if (wifi == NetworkInfo.State.CONNECTED || wifi == NetworkInfo.State.CONNECTING) 

    //wifi

如果你有兴趣,如果你真的连接,使用

NetworkInfo.State.CONNECTED 

仅,而不是

NetworkInfo.State.CONNECTED || NetworkInfo.State.CONNECTING

【讨论】:

这行不通,正如我向第一个(和第二个)响应者指出的那样,我不想查看我是否通过移动连接,我想查看用户是否有移动数据启用。 (他们可以通过转到设置 -> 无线和网络设置 -> 移动网络设置 -> 启用数据来打开和关闭此功能) @KevinBradshaw,您可以使用它来检查是否启用了移动数据。如果启用了移动数据,那么您将获得 NetworkInfo.State.CONNECTED 或 NetworkInfo.State.CONNECTING == mobile。因为如果开启了移动数据,那么系统会尝试连接网络或者被连接。 我认为您需要尝试一下。禁用手机上的移动网络数据(取消选中设置 -> 无线和网络设置 -> 移动网络设置 -> 启用数据)。运行您的代码,您将看到它会报告存在移动连接。 NetworkInfo State 不考虑用户是否启用了移动数据。请参阅接受的答案,了解我发现的唯一工作方式。【参考方案6】:

由于 ConnectivityManager.allNetworkInfo 已弃用,Android 建议使用 getNetworkCapabilities

fun isOnMobileData(): Boolean 
    val connectivityManager =
        context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
    val all = connectivityManager.allNetworks

    return all.any 
        val capabilities = connectivityManager.getNetworkCapabilities(it)
        capabilities?.hasTransport(TRANSPORT_CELLULAR) == true
    

【讨论】:

【参考方案7】:

使用 TelephonyManager

TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);

tm.isDataEnabled()

根据安卓文档

https://developer.android.com/reference/android/telephony/TelephonyManager.html#isDataEnabled()

【讨论】:

必须说只有API 26版本才可以使用【参考方案8】:

我认为使用 NetworkInfo class 和 isConnected 应该可以工作:

ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);

return info != NULL || info.isConnected();

并检查移动数据是否已连接。在我测试之前我不能确定。明天我才能做到。

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

if(tm .getDataState() == tm .DATA_CONNECTED)
   return true;

【讨论】:

这行不通,正如我在第一反应中指出的那样,我不想查看我是否通过移动设备连接我正在查看用户是否启用了移动数据。 (htey 可以通过转到设置 -> 无线和网络设置 -> 移动网络设置 -> 启用数据来打开和关闭此功能) 您尝试过 TelephonyManager 吗?它应该显示数据是否启用。【参考方案9】:

这是针对此问题的 xamarin 解决方案:

    public static bool IsMobileDataEnabled()
    
        bool result = false;

        try
        
            Context context = //get your context here or pass it as a param

            if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr1)
            
                //Settings comes from the namespace Android.Provider
                result = Settings.Global.GetInt(context.ContentResolver, "mobile_data", 1) == 1;
            
            else
            
                result = Settings.Secure.GetInt(context.ContentResolver, "mobile_data", 1) == 1;
            
        
        catch (Exception ex)
        
            //handle exception
        

        return result;
    

PS:请确保您拥有此代码的所有权限。

【讨论】:

【参考方案10】:

您必须使用 ConnectivityManager,NetworkInfo 的详细信息可以在here 找到

【讨论】:

但我不是想看看我是如何连接的,我想看看是否启用了移动数据【参考方案11】:
To identify which SIM or slot is making data connection active in mobile, we need to register action android:name="android.net.conn.CONNECTIVITY_CHANGE"  with permission   
uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" &    uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"

    public void onReceive(Context context, Intent intent) 
 if (android.net.conn.CONNECTIVITY_CHANGE.equalsIgnoreCase(intent
                .getAction())) 

IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
 IConnectivityManager service =  IConnectivityManager.Stub.asInterface(b);
NetworkState[] states = service.getAllNetworkState();

 for (NetworkState state : states) 

                if (state.networkInfo.getType() == ConnectivityManager.TYPE_MOBILE
                        && state.networkInfo.isConnected()) 

 TelephonyManager mTelephonyManager = (TelephonyManager) context
                        .getSystemService(Context.TELEPHONY_SERVICE);
         int slotList =   0, 1 ;
          int[] subId = SubscriptionManager.getSubId(slotList[0]);
          if(mTelephonyManager.getDataEnabled(subId[0])) 
             // this means data connection is active for SIM1 similary you 
             //can chekc for SIM2 by slotList[1]
               .................
          



【讨论】:

【参考方案12】:
    ConnectivityManager cm = (ConnectivityManager) activity
                        .getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo info = cm.getActiveNetworkInfo();
                String networkType = "";
    if (info.getType() == ConnectivityManager.TYPE_WIFI) 
                    networkType = "WIFI";
                 
else if (info.getType() == ConnectivityManager.TYPE_MOBILE) 

                    networkType = "mobile";
    

【讨论】:

【参考方案13】:

根据android文档https://developer.android.com/training/monitoring-device-state/connectivity-monitoring#java

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

NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
                      activeNetwork.isConnectedOrConnecting();

【讨论】:

【参考方案14】:

这是其他两个答案的简单解决方案:

        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
            return tm.isDataEnabled();

         else 
            return tm.getSimState() == TelephonyManager.SIM_STATE_READY && tm.getDataState() != TelephonyManager.DATA_DISCONNECTED;
        

【讨论】:

【参考方案15】:

好吧,有一种解决方法可以检查数据连接是否打开。但我不确定它是否适用于所有设备。你需要检查一下。 (它适用于 Android 一台设备)

long data = TrafficStats.getMobileRxBytes();
if(data > 0)
    //Data is On

else
    //Data is Off

如果您不了解此方法,它会返回自设备启动以来通过移动网络接收的总字节数。当您关闭移动数据连接时,它将返回零 (0)。当您打开时,它将再次返回总字节数。但您需要注意,使用此解决方法时可能会出现问题。

重启手机时该方法也会返回0,因为计算是从0字节开始的。

【讨论】:

【参考方案16】:
private boolean haveMobileNetworkConnection() 
        boolean haveConnectedMobile = false;

        ConnectivityManager cm = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo[] netInfo = cm.getAllNetworkInfo();

        for (NetworkInfo ni : netInfo) 

            if (ni.getTypeName().equalsIgnoreCase("MOBILE"))
                if (ni.isConnected())
                    haveConnectedMobile = true;
        
        return haveConnectedMobile;
    

注意:您需要获得 android.permission.ACCESS_NETWORK_STATE 权限才能使用此代码

【讨论】:

【参考方案17】:

似乎有一个简单的 API 可以工作

TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.isDataEnabled();

【讨论】:

以上是关于如何判断“移动网络数据”是启用还是禁用(即使通过 WiFi 连接)?的主要内容,如果未能解决你的问题,请参考以下文章

java ssm 框架解决思路 状态 操作 启用 禁用 启用 禁用 启用 禁用 点击“禁用” 启动状态就变为“禁用”

即使 MaterialButton 被禁用,如何让波纹启用?

是否可以使用 setup api 来判断设备是否已启用

如何确定用户帐户是启用还是禁用

如何检查“显示通知”是启用还是禁用?

如何打开并检查 Play Protect 是启用还是禁用