在我需要以编程方式连接没有互联网的 WiFi 设备的 android 应用程序上工作。这是一个代码:
private void connectToWiFi(final String ssid, String password) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
final ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
request.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); // Internet not required
ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
String networkSSID = getNetworkSsid();
if (networkSSID.equals(ssid)) {
connectivityManager.bindProcessToNetwork(network);
}
}
@Override
public void onUnavailable() {
super.onUnavailable();
}
@Override
public void onLost(@NonNull Network network) {
super.onLost(network);
}
};
connectivityManager.registerNetworkCallback(request.build(), networkCallback);
WifiConfiguration config = new WifiConfiguration();
config.SSID = String.format("\"%s\"", ssid);
int netId = -1;
List<WifiConfiguration> apList = wifiManager.getConfiguredNetworks();
for (WifiConfiguration i : apList) {
if (i.SSID != null && i.SSID.equals("\"" + ssid + "\"")) {
netId = i.networkId;
}
}
// Add network in Saves network list if it is not available in list
if (netId == -1) {
if (TextUtils.isEmpty(password)) {
Log.d(TAG, "====== Connect to open network");
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
} else {
Log.d(TAG, "====== Connect to secure network");
config.preSharedKey = String.format("\"%s\"", password);
}
netId = wifiManager.addNetwork(config);
}
Log.d(TAG, "Connect to Network : " + netId);
wifiManager.enableNetwork(netId, true);
} else {
// For Android 10 and above
// WifiNetworkSpecifier code
}
}
使用的权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
以上代码适用于提供互联网连接的网络。 但在大多数情况下,对于没有互联网连接的网络,它会失败。
当尝试连接到无法访问互联网的 WiFi 配置时,我们需要将我们的应用进程绑定到该网络,或者配置我们的 HTTP 客户端使用该网络的套接字工厂,否则系统将断开与该 WiFi 网络的连接。
根据 本指南 ,bindProcessToNetwork 已经应用。 但是无论是否使用 bindToProcess() / 通过套接字工厂路由流量,系统都会与网络断开连接。它不允许与没有互联网的 WiFi 保持连接。 真是令人惊讶,Android 在限制网络连接之前从未考虑过物联网设备用例。
当尝试连接到由我的应用程序或系统设置应用程序或任何其他应用程序添加的 WiFi 网络时,系统日志显示:
UID xxxxx does not have permission to update configuration
当应用程序尝试连接具有互联网的设备时,即使该网络是由其他应用程序添加的,也不会发生相同的错误。
因此允许应用程序连接到以前的配置,尽管“权限不足”,并且流量会暂时通过该网络路由。但几秒钟后,网络断开,系统尝试与其他一些支持互联网的网络重新关联。
在 Moto G5 Plus、Android 8.1.0 上进行测试,但我认为这是一个平台错误,而不是特定于设备的错误。大多数情况下,这个错误是从 Android 7 或其他系统引入的,因为它以前是有效的。
这个问题有什么解决方案吗? Android 有付费支持选项吗?
提前致谢。
你提到的问题可能不是因为你在Issuetracker提供的代码。一旦我解决了我的设备 (Android 9) 中的问题,即使在完全撤消后我也无法复制错误。我什至尝试过设备重启、应用程序重新安装甚至恢复出厂设置。试试这个。
在设置 --> 应用程序和权限中,测试应用程序请求位置权限。给它权限。
Java代码即可
private void connectToWiFi(final String ssid, String password) {
if (connectivityManager != null) {
try {
connectivityManager.bindProcessToNetwork(null);
connectivityManager.unregisterNetworkCallback(networkCallback);
} catch (Exception e) {
Log.d(TAG, "Connectivity Manager is already unregistered");
}
}
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
if(isOnline()==false){
request.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
String networkSSID = getNetworkSsid();
Log.e(TAG, "Network is available - " + networkSSID);
if (networkSSID.equals(ssid)) {
if(isOnline()==false){
Log.e(TAG, "bindProcessToNetwork");
connectivityManager.bindProcessToNetwork(network);
}
}
}
@Override
public void onUnavailable() {
super.onUnavailable();
Log.e(TAG, "Network is Unavailable");
}
@Override
public void onLost(@NonNull Network network) {
super.onLost(network);
Log.e(TAG, "Lost Network Connection");
}
};
connectivityManager.registerNetworkCallback(request.build(), networkCallback);
if (!wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(true);
}
WifiConfiguration config = new WifiConfiguration();
config.SSID = String.format("\"%s\"", ssid);
config.status = WifiConfiguration.Status.ENABLED;
int netId = -1;
List<WifiConfiguration> apList = wifiManager.getConfiguredNetworks();
Log.d(TAG, "List Size : " + apList.size());
for (WifiConfiguration i : apList) {
if (i.SSID != null && i.SSID.equals("\"" + ssid + "\"")) {
netId = i.networkId;
}
}
// Add network in Saves network list if it is not available in list
if (netId == -1) {
if (TextUtils.isEmpty(password)) {
Log.e(TAG, "====== Connect to open network");
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
} else {
Log.e(TAG, "====== Connect to secure network");
config.preSharedKey = String.format("\"%s\"", password);
}
netId = wifiManager.addNetwork(config);
}
Log.d(TAG, "Connect to network : " + netId);
wifiManager.enableNetwork(netId, true);
}
public boolean isOnline() {
Runtime runtime = Runtime.getRuntime();
try {
Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8");
int exitValue = ipProcess.waitFor();
return (exitValue == 0);
}
catch (IOException e) { e.printStackTrace(); }
catch (InterruptedException e) { e.printStackTrace(); }
return false;
}
如果您的目标是在特定网络中使用您的代码,那么您可以转到您的接入点界面,只需将 DHCP 设置设置为静态,并为设备提供适当的 IP。
可以在这里找到更多信息
现在,如果您的目标网络无法访问其DHCP设置,基于该link您可以告诉您的设备保持连接到网络,无论它是否有互联网连接。
NetworkRequest.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder = new NetworkRequest.Builder();
//set the transport type do WIFI
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
connectivityManager.requestNetwork(builder.build(), new
ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Build.VERSION.RELEASE.equalsIgnoreCase("6.0")) {
if (!Settings.System.canWrite(mActivity)) {
Intent goToSettings = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
goToSettings.setData(Uri.parse("package:" +
mActivity.getPackageName()));
mActivity.startActivity(goToSettings);
}
}
connectivityManager.bindProcessToNetwork(null);
if (mSsid.contains("my_iot_device-xxxxxxxxx"))
connectivityManager.bindProcessToNetwork(network);
else {
//This method was deprecated in API level 23
ConnectivityManager.setProcessDefaultNetwork(null);
if (mSsid.contains("my_iot_device-xxxxxxxxx"))
ConnectivityManager.setProcessDefaultNetwork(network);
}
connectivityManager.unregisterNetworkCallback(this);
}
}
});
}
有时让你的无线收音机保持清醒是很好的,所以你需要使用
WiFiLock
保持它。
//Create a wifi manager instance first
WifiManager mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
//and then create your wifiLock
WifiManager.WifiLock mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "wifiLock");
if(!mWifiLock.isHeld())
mWifiLock.acquire();
稍后如果你想实现它
if(mWifiLock.isHeld())
mWifiLock.release();
此代码帮助我保持与网络的连接,即使它无法访问互联网。
替换:
connectivityManager.registerNetworkCallback
链接与
connectivityManager.requestNetwork
链接requestNetwork
上的相关文档来自上面的链接:
只要这个请求未完成,平台会尝试 保持匹配此请求的最佳网络,同时始终 如果可能,尝试将请求匹配到更好的网络。
注意上述文档不包含在
registerNetworkCallback
.
Android 连接堆栈将拆除未使用的网络。这是通过
requestNetwork
网络的请求数量来跟踪的。作为 registerNetworkCallback
的一部分提交的请求不计算在内,因此就连接堆栈而言,您请求的 Wi-Fi 网络没有被 requested 因此可以被拆除。
这不适用于 default 网络,但是只有具有互联网功能的网络才有资格成为默认网络,这不适用于您的情况。