Android用热点互传文件的坑

当笔者接到这个需求的时候,下意识的认为,用热点一定可以完成互传。 当然笔者的自信是对的,只是里面有两个坑需要跳过去。

1. 热点问题

问题出在两个android10及以上手机。 面临的情况如下:

1. A手机创建热点, 并且建立一个ServerSocket。B手机连接A手机热点后, 访问A手机的ServerSocket,无法连接上

2. B手机在连接上A手机的热点后,也建立一个ServerSocket。同样,A手机也连接不上B手机的ServerSocket。

3. 不过A手机和B手机分别可以访问自己。

最后是如何解决问题的呢? 发现通过B手机的浏览器无法访问B手机自己,直接给你报网络没有连接的错误提示。可是B手机明明连接了热点。

这个情况给了我提示,想到系统可能偷懒了。因为A手机创建的热点无法访问外网(android 10开始的),所以B手机的网络状态也被标记为了无法访问外网。这让系统认为反正无法访问外网,所以干脆直接躺倒,不去尝试了。

那么强制指定一个网络行不行呢?行的!B手机在连接上A 手机的热点后,再调用一下connectivityManager.bindProcessToNetwork(network)就马上解决问题了。network是热点的网络。

完整代码如下:

    @RequiresApi(api = Build.VERSION_CODES.Q)
    private void connectWifiQ(Activity activity, String ssid, String password, OnWifiConnectCallback onWifiConnectCallback) {
        NetworkSpecifier specifier = null;

        specifier = new WifiNetworkSpecifier.Builder()
                .setSsidPattern(new PatternMatcher(ssid, PatternMatcher.PATTERN_PREFIX))
                .setWpa2Passphrase(password)
                .build();

        NetworkRequest request =
                new NetworkRequest.Builder()
                        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                        .setNetworkSpecifier(specifier)
                        .build();

        ConnectivityManager connectivityManager = (ConnectivityManager)
                mApplicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                // do success processing here..
                Log.d(TAG, "wifi-connect-succeed");
                try {
                    connectivityManager.bindProcessToNetwork(network);
                } catch (IllegalStateException e) {
                    Log.e(TAG, "ConnectivityManager.NetworkCallback.onAvailable: ", e);
                }

                onWifiConnectCallback.succeed();
              //  connectivityManager.unregisterNetworkCallback(this);
            }

            @Override
            public void onUnavailable() {
                // do failure processing here..
                Log.d(TAG, "wifi-connect-failed");
                onWifiConnectCallback.failed();
            }
        };

        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                connectivityManager.requestNetwork(request, networkCallback);
            }
        });
    }

onAvailable会回调好几次,想通过connectivityManager.unregisterNetworkCallback(this)使得只回调一次,那是不行的,会失效。你只能在自己的回调里去过滤。

2. getHostName()耗时问题

解决了热点问题后,B手机就可以访问A手机了。A手机也可以访问B手机了,但是网络速度超慢,一个普通请求要10秒左右才返回。C手机也连上A手机的热点,并创建ServerSocket。 B手机和C手机之间的网络速度一样奇慢无比。这就又奇怪了,明显又与常识不符。

原因分析思路如下:

这个问题可能是两种情况导致:

1. B手机真没有接到A手机的请求。

2. B手机接到A手机请求了,但是接到后,由于某些原因卡住了。

3. 每次都是10秒,挺可疑的。

如果是第一个原因,那就比较棘手了。可是该死的,下意识的认为是第一个原因导致的,由此浪费了不少时间。可能对第三方http server库太信任了,一直没去想是第二个原因引起的。后来才想到 是因为每次都是10秒,很可疑。

最后调试了一下,问题果然出在socket accept后, 调用了getHostName() 这个方法耗时特别长!

getHostName()调用的性能取决于JVM和目标主机的域名服务器之间的网络/技术堆栈的性能。可哪有什么host name, 都是IP地址直接访问的。问题就在于DNS的反向解析, 如果解析不成功, 它会一直尝试, 大概10秒的时间, 所以这10秒是阻塞的。

解决方案,就是删除这个方法的调用。 因为局域网里的通信,根本不需要知道对方的host name,也没有。 知道对方IP地址就好了。

这两个坑,现在看看是不大。但是在于分析的思路很重要。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值