React Native获取及监听网络状态——Android篇

开发应用过程中,我们往往需要获取/监听手机的网络状态,而RN提供的NetInfo api随着sdk的升级往往会出现许多的问题。所以本文将介绍如何按需自行编写获取手机网络状态的原生代码。

对于网络相关的功能,往往涉及到以下几个部分:

1. 主动获取手机的连接状态(是否联网,连接的是移动蜂窝网还是WiFi);

2. 监听网络变化(蜂窝网、WiFi、网络断开连接等),执行相应的操作(例如提示用户);

3. 获取SSID(手机连接的WiFi名称)以及用户ip地址等;

 

一. 主动获取手机的连接状态

在Android中获取手机的连接状态,需要通过ConnectivityManager实例以及NetworkInfo实例获取:

ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActivityNetworkInfo();
boolean isConnected = networkInfo != null && netwrokInfo.isConnected();  // 是否联网
boolean isConnectWifi = networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;  // 是否连接的WiFi
boolean isConnectedMobile = networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_MOBILE;;  // 是否连接的移动蜂窝网

二. 监听网络变化

监听网络变化一般都是通过在MainActivity中通过监听广播实现:

private IntentFilter connectiveIntentFilter;
private ConnectiveBroadcastReceiver connectiveBroadcastReceiver;

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // 监听网络状态变化的广播
    connectiveIntentFilter = new IntentFilter();
    connectiveIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    connectiveBroadcastReceiver = new ConnectiveBroadcastReceiver();
    registerReceiver(connectiveBroadcastReceiver, connectiveIntentFilter);
  }

class ConnectiveBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 网络发生变化执行回调 
    }
  }

接着可以在onReceive方法中通过NetInfo api获取变化后的网络属性。但是监听ConnectivityManager.CONNECTIVITY_ACTION这个广播有一个问题,就是当用户网络状态由移动网络切换到WiFi时,实际上会监听到两次广播:第一次是网络关闭,第二次则是网络切换到WiFi。这在App中需要实时提示用户网络连接状态的时候会非常恶心。

后来经过查阅官方文档,发现NetworkRequest api可以很好的解决这个问题,直接上代码,在自定义的NetUtils.java文件中:

/**
     * 监听网络变化(移动蜂窝网和WiFi)
     */
    public void registerNetworkChangeListener(Context context, NetworkChangeListener listener) {
        ConnectivityManager connectivityManager = getConnectivityManager(context);
        NetworkRequest.Builder builder = new NetworkRequest.Builder();
        NetworkRequest request = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .build();
        connectivityManager.requestNetwork(request, new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                super.onAvailable(network);
                if (isWifiConnected((context))) {
                    Log.d(TAG, "Wifi connected!");
                    if (listener != null) listener.onWiFiAvailable();
                } else if (isMobileConnected(context)) {
                    Log.d(TAG, "Cellular connected!");
                    if (listener != null) listener.onCellularAvailable();
                } else {
                    Log.d(TAG, "Not connected!");
                }
            }
        });
    }

    public interface NetworkChangeListener {
        void onWiFiAvailable();
        void onCellularAvailable();
    }

NetworkChangeListener接口定义了两个方法签名,onWiFiAvailable是当监听到网络切换到WiFi时需要执行的代码。onCellularAvailable则是监听到网络切换到移动网络时需要执行的代码。

使用如下, MainActivity中:

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    NetworkUtils.getInstance().registerNetworkChangeListener(this, new NetworkUtils.NetworkChangeListener() {
      @Override
      public void onWiFiAvailable() {
        sendNetChange("WIFI");
      }

      @Override
      public void onCellularAvailable() {
        sendNetChange("WWAN");
      }
    });
}

private void sendNetChange(String netState) {
    try {
      getReactInstanceManager().getCurrentReactContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
              .emit("netChange", netState);
    } catch (RuntimeException e) {
      e.printStackTrace();
    }
  }

在RN端,在入口组件(例如Splash.js,APP启动时加载,组件中可以根据登录状态动态加载不同的路由栈)中获取原生层发送过来的事件:

  const networkChangeFunc = (netState) => {
    LogUtil("netChange: ", netState);
    changeNetType(actionCreator.networkChange(netState));
  }  

  useEffect(() => {
    // 应用初始加载,主动获取一次当前网络状态
    getNetwork().then(netState => {
      networkChangeFunc(netState);
    });

    // 应用运行期间监听网络变化
    if (isAndroid) {
      DeviceEventEmitter.addListener("netChange", (netState) => {
        networkChangeFunc(netState);
      });
    } else {
      const eventEmitter = new NativeEventEmitter(NativeModules.ToolModule);
      eventEmitter.addListener("netChange", (netState) => {
        networkChangeFunc(netState);
      });
    }
  }, []);

我这里是将原生层监听到的网络变化保存到redux中。这样应用中任何子视图组件如果想要获取/监听当前的网络状态,直接到redux拿即可。不过需要注意的是,如果使用了redux-persist,记得把全局状态对象中保存网络状态的键添加到黑名单中。

 

3. 获取SSID(手机连接的WiFi名称)以及用户ip地址

Android中获取SSID需要通过WiFiManager实例和WiFiInfo实例完成,也很简单,唯一需要注意的是,在Android 9.0以上的系统中,获取SSID之前必须向用户动态申请位置权限(官方文档的解释是,开发人员可能会通过用户手机的WiFi信息获取到用户的位置等隐私信息,既然如此,不如就限制开发人员在获取WiFi相关的信息前必须向用户请求获取地理位置权限,从而达到告知用户风险,并由用户决定是否授予权限,保护用户隐私的目的) 。

AndroidManifest.xml中添加权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

获取SSID:

WifiManager wifiManager = (WifiManager)context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String ssid = wifiInfo.getSSID().replace("\"", "");

一定要记得在使用上面代码前确保用户已经授予地理位置权限。至于动态权限检查以及申请,使用RN提供的PermissionsAndroid或者是安卓原生的api,还是使用第三方库Easy Permission都可以。

另外,当我们需要用户连接特定的WiFi时,如果监听到用户打开的是移动网络,可以在App内添加一个按钮,帮助用户快速拉起系统设置WiFi的Activity。代码如下:

/**
     * 跳转设置WiFi界面
     */
    @ReactMethod
    public void jumpToWiFiSettings() {
        Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivityForResult(intent, MainActivity.REQUEST_SETTING_CODE, null);
    }

如果想要拉起设置应用的其他Activity,只需要修改对应intent的Action即可。

以上就是本文的全部内容,最后贴一下我目前在项目中用到的网络相关的工具类NetUtils.java,基本能够满足我开发中的需求,如果有问题的话,欢迎小伙伴们提出来哦~

public class NetworkUtils {
    private final String TAG = "NetworkUtils";
    private NetworkUtils() {}

    public static NetworkUtils getInstance() {
        return NetUtilsBuilder.instance;
    }

    private static class NetUtilsBuilder {
        private static final NetworkUtils instance = new NetworkUtils();
    }

    /**
     * 获取ConnectivityManager实例
     */
    private ConnectivityManager getConnectivityManager(Context context) {
        return (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
    }

    /**
     * 获取NetworkInfo实例
     */
    private NetworkInfo getNetworkInfo(Context context) {
        return getConnectivityManager(context).getActiveNetworkInfo();
    }

    /**
     * 获取WifiManager实例
     */
    private WifiManager getWifiManager(Context context) {
        return (WifiManager)context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
    }

    /**
     * 获取WifiInfo实例
     */
    private WifiInfo getWifiInfo(Context context) {
        return getWifiManager(context).getConnectionInfo();
    }

    /**
     * 网络是否可用
     */
    public boolean isConnected(Context context) {
        NetworkInfo networkInfo = getNetworkInfo(context);
        return networkInfo != null && networkInfo.isConnected();
    }

    /**
     * 判断当前是否连接的wifi
     */
    public boolean isWifi(Context context) {
        NetworkInfo networkInfo = getNetworkInfo(context);
        return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
    }

    /**
     * 判断当前是否连接的移动网络
     */
    public boolean isMobile(Context context) {
        NetworkInfo networkInfo = getNetworkInfo(context);
        return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_MOBILE;
    }

    /**
     * 是否连接的wifi且网络可用
     */
    public boolean isWifiConnected(Context context) {
        return isConnected(context) && isWifi(context);
    }

    /**
     * 是否连接的移动数据且网络可用
     */
    public boolean isMobileConnected(Context context) {
        return isConnected(context) && isMobile(context);
    }

    /**
     * 获取网络类型
     */
    public String getNetworkType(Context context) {
        if (isWifiConnected(context)) return "WIFI";
        if (isMobileConnected(context)) return "WWAN";
        return "NONE";
    }

    /**
     * 获取WiFi名称
     * tips: in Android 8.0 or higher needs the permission of ACCESS_FINE_LOCATION, or you'll get unknown ssid
     */
    public String getSSID(Context context) {
        return getWifiInfo(context).getSSID().replace("\"", "");
    }

    /**
     * 获取本机ip地址
     */
    public String getLocalIp(Context context) {
        return ipFormatter(getWifiInfo(context).getIpAddress());
    }

    private String ipFormatter(int ip) {
        return (ip & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "." + ((ip >> 24) & 0xFF);
    }

    /**
     * 监听网络变化(移动蜂窝网和WiFi)
     */
    public void registerNetworkChangeListener(Context context, NetworkChangeListener listener) {
        ConnectivityManager connectivityManager = getConnectivityManager(context);
        NetworkRequest.Builder builder = new NetworkRequest.Builder();
        NetworkRequest request = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .build();
        connectivityManager.requestNetwork(request, new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                super.onAvailable(network);
                if (isWifiConnected((context))) {
                    Log.d(TAG, "Wifi connected!");
                    if (listener != null) listener.onWiFiAvailable();
                } else if (isMobileConnected(context)) {
                    Log.d(TAG, "Cellular connected!");
                    if (listener != null) listener.onCellularAvailable();
                }
            }

            @Override
            public void onUnavailable() {
                super.onUnavailable();
                Log.d(TAG, "onUnavailable");
            }

            @Override
            public void onLosing(Network network, int maxMsToLive) {
                super.onLosing(network, maxMsToLive);
                Log.d(TAG, "onLosing");
            }

            @Override
            public void onLost(Network network) {
                super.onLost(network);
                Log.d(TAG, "onLost");
            }
        });
    }

    public interface NetworkChangeListener {
        void onWiFiAvailable();
        void onCellularAvailable();
    }
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值