美文网首页
Android和IOS实现Wifi及4G双通道

Android和IOS实现Wifi及4G双通道

作者: 张老虎 | 来源:发表于2018-10-14 19:46 被阅读322次

#+AUTHOR 张老虎

前言

由于项目需要使用wifi连接硬件,同时要求能够使用手机网络上网(4G),将方法整理如下

Android实现

原理概述

Android系统在5.1及以上开始支持Wifi和数据网络同时开启,方法有:

  1. 在Api Level21及以上,代表手机网卡的类Network中有bindSocket()方法,可以将单个Socket绑定至某网卡;
  2. 或者调用getSocketFactory()方法,直接工厂模式生产Socket;
  3. 在Api Level23及以上(即Android 6.0), 可直接调用ConnectivityManager的bindProcessToNetwork()方法, 将整个进程的网络请求都绑定到某网卡,虽说最方便, 但是目前5.1到6.0之间的手机还有百分之20多(数据来源), 不容小觑,因此没有采用这个方法

系统设置

手机为节电,默认设置为打开Wifi即关闭手机网络,需要在开发者选项中打开[始终开启数据连接]才能在Wifi开启情况下获取到4G网卡。
在应用中可提示用户打开开发者选项,打开后在开发者选项的[网络]分组下,开启[始终开启数据连接
打开开发者选项跳转方法:

    //判断是否已经开启开发者选项
    val enableAdb = Settings.Secure.getInt(contentResolver, Settings.Global.ADB_ENABLED, 0) > 0
    val i = Intent(Settings.ACTION_SETTINGS)
    if (enableAdb) {
        //已打开, 直接跳转至开发者选项
        i.action = ACTION_APPLICATION_DEVELOPMENT_SETTINGS
    } else {
        //未打开, 跳转到关于手机页面,连续点击版本号至手机提示已打开开发者选项
        //In some cases, a matching Activity may not exist, so ensure you
        //* safeguard against this.
        i.action = ACTION_DEVICE_INFO_SETTINGS
    }
    startActivity(i)

实际使用中,发现小米和华为部分机型需要“修改系统设置”权限才可正常获取网卡信息, 申请权限代码如下:

    if (!Settings.System.canWrite(getApplicationContext())) {
        val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri.parse("package:$packageName"))
        startActivity(intent)
    }

获取网卡方法如下, 此处可以在子线程循环检查,或者在网络变化的广播中接收通知:

    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    Network etherNetwork = null;
    for (Network network : connectivityManager.getAllNetworks()) {
        NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
        //在所有开启的网卡中过滤4G网卡
        if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
            etherNetwork = network;
            LinkProperties lp = connectivityManager.getLinkProperties(network);
            for (InetAddress addr : lp.getDnsServers()) {
                //保存4G网卡DNS设置,如果需要DNS解析会用到,我是在修改七牛SDK是用到的
                dnsServers.add(addr);
            }
        }
    }
    if (etherNetwork == null) {
        //没有获取到4G网卡,说明没有开启4G网络或没有打开[始终开启数据连接], 可提示用户去开启
    }
    
    //虽然这里已经获取到4G网络了,但在实际使用中发现某些机型还需要执行下面的requestNetwork(),
    //在回调触发时,4G网络才真正可用,所以上面获取etherNetwork只是判断网卡有没有启用
    NetworkRequest.Builder builder = new NetworkRequest.Builder();
    builder.addCapability(NET_CAPABILITY_INTERNET);
    //强制使用蜂窝数据网络-移动数据
    builder.addTransportType(TRANSPORT_CELLULAR);
    NetworkRequest build = builder.build();
    
    //检查有没有修改系统设置权限
    if (!Settings.System.canWrite(getApplicationContext())) {
        //申请修改系统设置权限,代码在上面,这里不重复了
    } else {
        connectivityManager.requestNetwork(build, new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                super.onAvailable(network);
                Log.i(TAG, "切换4G网卡"); 
                //拿到网卡,并且网卡可用,切换网络请求到4G网卡,修改方法见下一个小节
                ApiManager.getInstance().changeTo4G(PhotoApplication.this, network, dnsServers);
            }
        });
    }

部分第三方框架

Retrofit、Glide和七牛SDK都是基于OkHTTP的,因此重点在于OKHTTP的网络切换,我使用的OKHTTP版本3.9.1, 可以设置SocketFactory和DNS解析器,
SocketFactory直接从Network中获取,下面还需要一个DNS解析器,我基于七牛的Happy-DNS改了一个

七牛Happy-DNS

源码放在github, 构造方法没变,修改了DnsManager.queryInetAddress()方法,增加了一个Network参数, 用法见OKHTTP设置方法

OKHTTP设置方法

    IResolver r1 = AndroidDnsServer.defaultResolver();
    IResolver r2 = null;
    try {
        r2 = new Resolver(InetAddress.getByName("119.29.29.29"));
    } catch (UnknownHostException ex) {
        //这里其实不会触发, 传入的不是Host而是IP
        ex.printStackTrace();
    }
    final DnsManager dns = new DnsManager(new NetworkInfo(NetworkInfo.NetSatus.MOBILE, NetworkInfo.ISP_GENERAL, dnsServers), arrayOf(r1, r2), null);
    OkHttpClient okClientData = new OkHttpClient.Builder()
                           .socketFactory(net4G.getSocketFactory())
                           .dns(new Dns() {
                               @Override
                               public List<InetAddress> lookup(String hostname)  {
                                   //DNSManager修改的地方在这里使用
                                   return listOf(dns.queryInetAdress(new Domain(hostname), net4G));
                               }})
                           .build();

Retrofit及其他一些寄语OKHTTP的网络请求库

修改了OKHTTP,Retrofit就没什么可说了,直接传入上面构造好的OkHttpClient就行了

Glide

Glide使用OKHttp,要在build.gradle中添加依赖:

    implementation('com.github.bumptech.glide:okhttp3-integration:4.5.0') {
            exclude group: 'glide-parent'
    }

然后就简单啦, 传入上面创建好的OKHTTPClient

    val factory = OkHttpUrlLoader.Factory(okHttpClient)
    Glide.get(context).registry.replace(GlideUrl::class.java, InputStream::class.java, factory)

七牛SDK改造

七牛本来的SDK没有传入OKHTTPClient的方法,所以对其进行了改造,由于最近七牛改动较大,不合并他们的代码了,源码地址

  1. 增加了绑定网卡功能
  2. 原来的上传字节数组没有进度回调,增加上传ByteBuffer接口(含有进度回调)

IPhone

原理

IPhone上在当前网络没有设置DNS服务器时,需要DNS解析的网络请求会自动使用可用网卡,而不需要DNS解析的,还会走当前网卡,
所以是比较简单的。

方法

Wifi连接上硬件后,记下自动分配的IP和子网掩码,修改为静态IP,填写IP和子网掩码,DNS和路由IP都置空。
在应用中,需要与硬件通信的请求,直接指定目的IP和端口,需要联网的HTTP请求,正常使用。

相关文章

网友评论

      本文标题:Android和IOS实现Wifi及4G双通道

      本文链接:https://www.haomeiwen.com/subject/oqetzftx.html