文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android 监听网络状态变化

2023-10-11 07:33

关注

此篇存在的主要意义在于解决用户使用app中网络状态发生了变化,需要我们去动态监听网络连接状态(有网、无网)、网络类型 (包括wifi、移动网络 -> 3G、4G等等)

在这里插入图片描述

文章目录

门前授课

关于网络状态的监听,主要是基于 Android 广播 - BroadcaseReceiver组件
~

同时关于广播的注册方面,从Android7.0开始已经初步进行限制,所以尽可能采用动态注册,献文如下:

Apps targeting Android 7.0 (API level 24) and higher do not receive this
broadcast if they declare the broadcast receiver in their manifest. Apps
will still receive broadcasts if they register their BroadcastReceiver with
Context.registerReceiver() and that context is still valid.
直译如下
针对Android 7.0
(API级别24)或更高的应用程序,如果它们在其manifest中声明广播接收器,则不会接收此广播。如果应用程序将其BroadcastReceiver注册为context
. registerreceiver()并且该context仍然有效,那么应用程序仍然会接收广播

Google在Android7.0时虽已对广播添加了限制,但是Android8.0后基于安全原因又一次加强了限制,且已声明:
应用无法使用其清单的大部分隐式广播(即,并非专门针对此应用的广播)

针对于此我们在本篇均使用动态注册广播,稍微科普一下,广播的两种注册方式 ~

Here:不论使用哪种注册方式均需在AndroidMainest清单文件里面进行注册

动态广播提示:广播注册是一个创建的过程,那么必然也要有一个销毁的过程,从而防止内存泄露,那么销毁就在是onDestroy中unregisterReceiver广播 ~

Activity/Fragment - 注销广播

java    @Override    protected void onDestroy() {        super.onDestroy();        //netWorkReceiver 之前已注册的广播        if (netWorkReceiver != null) {            unregisterReceiver(netWorkReceiver);        }
具体实现

此处就是本文的核心内容了,网络的实时动态监听指的是全局实时监听网络状态,针对的对象非一个事件而是整个APP应用项目 ~

AndroidMainfests - 添加权限

java    

创建 - 广播接受者

这里主要实时判断网络连接状态,同时判断网络类型 ~

为何说是基础版,因为这里虽然实现了功能,但是会重复收到网络变动的广播,太影响业务逻辑了!很有可能导致其他并发情况!

监听效果
在这里插入图片描述
监听实现 - NetworkReceiver(广播接收者)

javapackage nk.com.networklinstener;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.util.Log;public class NetworkReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        // 监听网络连接,包括wifi和移动数据的打开和关闭,以及连接上可用的连接都会接到监听        // 特殊注意:如果if条件生效,那么证明当前是有连接wifi或移动网络的,如果有业务逻辑最好把esle场景酌情考虑进去!        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {            //获取联网状态的NetworkInfo对象            NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);            if (info != null) {                //如果当前的网络连接成功并且网络连接可用                if (NetworkInfo.State.CONNECTED == info.getState() && info.isAvailable()) {                    if (info.getType() == ConnectivityManager.TYPE_WIFI || info.getType() == ConnectivityManager.TYPE_MOBILE) {                        Log.e("TAG", getConnectionType(info.getType()) + "已连接");                    }                } else {                    Log.e("TAG", getConnectionType(info.getType()) + "已断开");                }            }        }    }        private String getConnectionType(int type) {        String connType = "";        if (type == ConnectivityManager.TYPE_MOBILE) {            connType = "3-4G网络数据";        } else if (type == ConnectivityManager.TYPE_WIFI) {            connType = "WIFI网络";        }        return connType;    }}

这里在判断网络连接状态的同时主要防止收到多条网络连接回调,从而导致逻辑错乱,所以采用时间校验实现类似同步锁的效果

监听效果
在这里插入图片描述
监听实现 - NetworkReceiver(广播接收者)

javapackage com.nk.machine.receiver;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.util.Log;import com.nk.machine.base.MainActivity;import java.text.SimpleDateFormat;public class NetWorkReceiver extends BroadcastReceiver {    private static long WIFI_TIME = 0;    private static long ETHERNET_TIME = 0;    private static long NONE_TIME = 0;    private static int LAST_TYPE = -3;    private static String TAG = "TAG";    @Override    public void onReceive(Context context, Intent intent) {        // 监听网络连接,包括wifi和移动数据的打开和关闭,以及连接上可用的连接都会接到监听        // 特殊注意:如果if条件生效,那么证明当前是有连接wifi或移动网络的,如果有业务逻辑最好把esle场景酌情考虑进去!        if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {            long time = getTime();            if (time != WIFI_TIME && time != ETHERNET_TIME && time != NONE_TIME) {                final int netWorkState = getNetWorkState(context);                if (netWorkState == 0 && LAST_TYPE != 0) {                    WIFI_TIME = time;                    LAST_TYPE = netWorkState;                    Log.e(TAG, "wifi:" + time);                } else if (netWorkState == 1 && LAST_TYPE != 1) {                    ETHERNET_TIME = time;                    LAST_TYPE = netWorkState;                    Log.e(TAG, "数据网络:" + time);                } else if (netWorkState == -1 && LAST_TYPE != -1) {                    NONE_TIME = time;                    LAST_TYPE = netWorkState;                    Log.e(TAG, "无网络:" + time);                }            }        }    }    public long getTime() {        SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");        String date = sDateFormat.format(new java.util.Date());        return Long.valueOf(date);    }        private static final int NETWORK_NONE = -1; //无网络连接    private static final int NETWORK_WIFI = 0; //wifi    private static final int NETWORK_MOBILE = 1; //数据网络    //判断网络状态与类型    public static int getNetWorkState(Context context) {        ConnectivityManager connectivityManager = (ConnectivityManager)                context.getSystemService(Context.CONNECTIVITY_SERVICE);        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();        if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {            if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_WIFI)) {                return NETWORK_WIFI;            } else if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_MOBILE)) {                return NETWORK_MOBILE;            }        } else {            return NETWORK_NONE;        }        return NETWORK_NONE;    }}

MainActivity - 动态注册广播

javapackage nk.com.networklinstener;import android.annotation.SuppressLint;import android.content.IntentFilter;import android.net.ConnectivityManager;import android.net.wifi.WifiManager;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;public class MainActivity extends AppCompatActivity {    private NetworkReceiver netWorkReceiver;    @SuppressLint("SetJavaScriptEnabled")    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //注册网络状态监听广播        netWorkReceiver = new NetworkReceiver();        IntentFilter filter = new IntentFilter();        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);        registerReceiver(netWorkReceiver, filter);    }    @Override    protected void onDestroy() {        super.onDestroy();        if (netWorkReceiver != null) {            unregisterReceiver(netWorkReceiver);        }    }}
异常场景

首先AndroidManifest加入以下权限

java

此异常场景指的是app在运行时有崩溃异常 - SecurityException,错误如下 ~

javajava.lang.SecurityException: com.xxx.xxx was not granted  either of these permissions: android.permission.CHANGE_NETWORK_STATE, android.permission.WRITE_SETTINGS.    at android.os.Parcel.readException(Parcel.java:1602)    at android.os.Parcel.readException(Parcel.java:1555)    at android.net.IConnectivityManager$Stub$Proxy.requestNetwork(IConnectivityManager.java:2064)    at android.net.ConnectivityManager.sendRequestForNetwork(ConnectivityManager.java:2470)    at android.net.ConnectivityManager.requestNetwork(ConnectivityManager.java:2509)    at com.superrtc.call.NetworkMonitorAutoDetect$ConnectivityManagerDelegate.requestMobileNetwork(NetworkMonitorAutoDetect.java)    at com.superrtc.call.NetworkMonitorAutoDetect.(NetworkMonitorAutoDetect.java)    at com.superrtc.mediamanager.XReachability.setAutoDetectConnectivityStateInternal(XReachability.java)    at com.superrtc.mediamanager.XReachability.startMonitoring(XReachability.java)    at com.superrtc.mediamanager.EMediaManager$7.run(EMediaManager.java)    at android.os.Handler.handleCallback(Handler.java:743)    at android.os.Handler.dispatchMessage(Handler.java:95)    at android.os.Looper.loop(Looper.java:150)    at com.superrtc.util.LooperExecutor.run(LooperExecutor.java)

按理说android.permission.CHANGE_NETWORK_STATE这一个权限,是普通权限,只在Manifest中声明就可以获取。

出现这个问题很不科学啊,再仔细去看看错误日志,发现报错只发生在6.0这个版本和部分6.0.1版本中。

那问题应该是出在6.0
版本中,之后去查资料后发现,在6.0版本中这个权限默认是被拒绝,无法获取这个权限。所以,在需要个权限的时候会出现权限问题导致应用因为权限问题崩溃。这个在stackoverflow中有人讨论过了,去看看

知道问题的原因之后的解决方案就简单了,既然CHANGE_NETWORK_STATE权限获取不到,那只好想办法打开WRITE_SETTINGS这个权限了。

跳转到应用程序设置页打开WRITE_SETTINGS这个权限:

javaIntent goToSettings = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);goToSettings.setData(Uri.parse("package:" + getPackageName()));startActivity(goToSettings);
兴趣扩展

网络广播Actiion的三种类型

WifiManager.WIFI_STATE_CHANGED_ACTION

这个监听wifi的打开与关闭,与wifi的连接无关;示例如下,主要查看一下 wifi连接 - 关闭过程

监听效果
在这里插入图片描述
示例代码

javapackage nk.com.networklinstener;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.net.wifi.WifiManager;import android.util.Log;public class NetworkReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        // 监听wifi的打开与关闭,与wifi的连接无关        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {            int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);            Log.e("TAG", "wifiState:" + wifiState);            switch (wifiState) {                case WifiManager.WIFI_STATE_ENABLING:                    Log.e("TAG", "wifi状态:打开中");                    break;                case WifiManager.WIFI_STATE_ENABLED://主用:打开状态                    Log.e("TAG", "wifi状态:已打开");                    break;                case WifiManager.WIFI_STATE_DISABLING:                    Log.e("TAG", "wifi状态:关闭中");                    break;                case WifiManager.WIFI_STATE_DISABLED://主用:关闭状态                    Log.e("TAG", "wifi状态:已关闭");                    break;                case WifiManager.WIFI_STATE_UNKNOWN:                    Log.e("TAG", "wifi状态:无法识别");                    break;                default:                    Log.e("TAG", "wifi状态:未知");                    break;            }        }    }}

WifiManager.NETWORK_STATE_CHANGED_ACTION

这个监听wifi的连接状态即是否连上了一个有效无线路由,当上边广播的状态是WifiManager.WIFI_STATE_DISABLING(wifi
关闭中)、WIFI_STATE_DISABLED(wifi 已关闭)的时候,根本不会接到这个广播。

在上边广播接到广播是WifiManager.WIFI_STATE_ENABLED(wifi
已打开)状态的同时也会接到这个广播,当然刚打开wifi肯定还没有连接到有效的无线

示例代码

javapackage nk.com.networklinstener;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.net.wifi.WifiManager;import android.util.Log;public class NetworkReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        // 这个监听wifi的连接状态即是否连上了一个有效无线路由,具体如上所述        if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) {            Parcelable parcelableExtra = intent                    .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);            if (null != parcelableExtra) {                NetworkInfo networkInfo = (NetworkInfo) parcelableExtra;                State state = networkInfo.getState();                // 当然,这边可以更精确的确定状态                boolean isConnected = state == State.CONNECTED;                Log.e(TAG1, "isConnected" + isConnected);                if (isConnected) {                    APP.getInstance().setWifi(true);                } else {                    APP.getInstance().setWifi(false);                }            }        }    }}

ConnectivityManager.CONNECTIVITY_ACTION

这个监听网络连接的设置,包括wifi和移动数据的打开和关闭;

此监听的作用范围最大!不论wifi的打开、关闭,或是连接上可用的链接都会接到监听;当然有利必有弊,那就是因为范围太广,所以效率相比上方的俩个监听要慢一些!

如果上方的俩个监听需求可用满足我们的业务需求,那么可以优先适用上方俩种监听方式搭配使用;但个人建议如果业务需求上面俩个监听满足不了的话,还是可以采用懒人方式直接用此监听!

Of Course :这里所谓的示例代码,就是最早看到的动态监听的实现部分了!

监听常用

此处主要借鉴与

方法一:API23时已过时

java//猜测:APP.getInstance()应该是一个关于网络相关的单例模式,主要用于app其他地方使用    void getNetwork(Context context){        State wifiState = null;        State mobileState = null;        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context                .CONNECTIVITY_SERVICE);        wifiState = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();        mobileState = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();        Log.d(TAG1,"wifi状态:" + wifiState + "\n mobile状态:" + mobileState);        // 手机网络连接成功        if (wifiState != null && mobileState != null                && State.CONNECTED != wifiState                && State.CONNECTED == mobileState) {            Log.d("tag", "手机2g/3g/4g网络连接成功");            APP.getInstance().setMobile(true);            APP.getInstance().setWifi(false);            APP.getInstance().setConnected(true);        }        // 无线网络连接成功        else if (wifiState != null && State.CONNECTED == wifiState) {            Log.d("tag", "无线网络连接成功");            APP.getInstance().setMobile(false);            APP.getInstance().setWifi(true);            APP.getInstance().setConnected(true);        }        // 手机没有任何的网络        else if (wifiState != null && mobileState != null                && State.CONNECTED != wifiState                && State.CONNECTED != mobileState) {            Log.d("tag", "手机没有任何的网络");            APP.getInstance().setMobile(false);            APP.getInstance().setWifi(false);            APP.getInstance().setConnected(false);        }    }

方法二:此处和我在上方的网络实时监听大致相同

java//猜测:APP.getInstance()应该是一个关于网络相关的单例模式,主要用于app其他地方使用    void getNetwork(Context context) {        ConnectivityManager manager = (ConnectivityManager) context                .getSystemService(Context.CONNECTIVITY_SERVICE);        Log.i("tag", "CONNECTIVITY_ACTION");        NetworkInfo activeNetwork = manager.getActiveNetworkInfo();        // connected to the internet        if (activeNetwork != null) {            if (activeNetwork.isConnected()) {                if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {                    // connected to wifi                    APP.getInstance().setWifi(true);                    Log.e("tag", "当前WiFi连接可用 ");                } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {                    // connected to the mobile provider's data plan                    APP.getInstance().setMobile(true);                    Log.e("tag", "当前移动网络连接可用 ");                }            } else {                Log.e("tag", "当前没有网络连接,请确保你已经打开网络 ");            }        } // not connected to the internet        else {            Log.e("tag", "当前没有网络连接,请确保你已经打开网络 ");            APP.getInstance().setWifi(false);            APP.getInstance().setMobile(false);            APP.getInstance().setConnected(false);        }    }

闲来无事正好看到了屏幕打开/关闭的的监听广播,特此记录一番

javapackage nk.com.networklinstener;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;public class ScreenReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        switch (intent.getAction()) {            case Intent.ACTION_SCREEN_OFF:                Log.e("屏幕广播","屏幕被关闭");                break;            case Intent.ACTION_SCREEN_ON:                Log.e("屏幕广播","屏幕被打开");                break;        }    }}

ConnectivityManager监听网络更改

ConnectivityManager API 提供一个更强大的方法,用于仅在满足指定的网络条件时请求回调。首先我们获取到系统的
ConnectivityManager 服务, 并且使用 registerNetworkCallbackNetworkRequest
对象传递给系统,最终系统会通过 ConnectivityManager.NetworkCallback 回调,将网络变更情况告知给应用。

java//获取ConnectivityManager ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);//定义ConnectivityManager.NetworkCallback回调方法callback = new ConnectivityManager.NetworkCallback() {    // 可用网络接入    public void onCapabilitiesChanged(@NotNull Network network, @NotNull NetworkCapabilities networkCapabilities) {        super.onCapabilitiesChanged(network, networkCapabilities);        LogUtils.d("onCapabilitiesChanged");        checkNetworkCapabilities(networkCapabilities);    }    @Override    public void onLost(@NonNull Network network) {        super.onLost(network);        if (cm != null) {            Network activeNetwork = cm.getActiveNetwork();            if (activeNetwork == null) {                //连接不到可用网络                return;            }            NetworkCapabilities networkCapabilities = cm.getNetworkCapabilities(activeNetwork);            checkNetworkCapabilities(networkCapabilities);        }    }};NetworkRequest.Builder builder = new NetworkRequest.Builder();if (cm != null) {    cm.registerNetworkCallback(builder.build(), callback);}//判断当前网络连接情况private void checkNetworkCapabilities(NetworkCapabilities networkCapabilities) {    if (networkCapabilities == null) {        return;    }    // 表明网络连接成功    if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {        if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||                networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) {            // 使用WI-FI            LogUtils.d("WIFI network");        } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||                networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {            // 使用蜂窝网络            LogUtils.d("mobile network");        } else {            // 未知网络,包括蓝牙、VPN、LoWPAN            LogUtils.d("unknown network");        }    } else {        //网络连接失败    }}

来源地址:https://blog.csdn.net/2301_77835649/article/details/130663659

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯