概述
当笔者接到这个需求的时候,下意识的认为,用热点一定可以完成互传。 当然笔者的自信是对的,只是里面有两个坑需要跳过去。
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地址就好了。
这两个坑,现在看看是不大。但是在于分析的思路很重要。
最后
以上就是忧伤画板为你收集整理的Android用热点互传文件的坑的全部内容,希望文章能够帮你解决Android用热点互传文件的坑所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复