如何在 Android 应用程序中使用 3G 连接而不是 Wi-fi?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2513713/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use 3G Connection in Android Application instead of Wi-fi?
提问by Jeyavel
How to use 3G Connection in Android Application instead of Wi-fi?
如何在 Android 应用程序中使用 3G 连接而不是 Wi-fi?
I want to connect a 3G connection, is there any sample code to connect to 3G instead of Wi-fi?
我想连接 3G 连接,是否有任何示例代码可以连接到 3G 而不是 Wi-fi?
采纳答案by snctln
The T-Mobile 'My Account" app does this, if you are connected to a WiFi connection it tells you that their program will not work over WiFi and then asks the user if they want to turn off the WiFi connection. If you choose "No" then the application exits, if you choose "Yes" then the app turns off your WiFi connection and then continues with starting up.
T-Mobile 的“我的帐户”应用程序会执行此操作,如果您连接到 WiFi 连接,它会告诉您他们的程序将无法通过 WiFi 运行,然后询问用户是否要关闭 WiFi 连接。如果您选择“否”,则应用程序退出,如果您选择“是”,则应用程序将关闭您的 WiFi 连接,然后继续启动。
I think this is a good model to follow, it will ensure that your app is not being ran over WiFi and allows the user to decide if they want to turn off WiFi or not. An improvement on this model would be to turn wifi back on when the user navigates away from your app.
我认为这是一个很好的模型,它将确保您的应用程序不会通过 WiFi 运行,并允许用户决定是否要关闭 WiFi。此模型的改进是在用户离开您的应用程序时重新打开 wifi。
I haven't tested the following code, but it looks like it should work (modified from here)
我还没有测试以下代码,但看起来它应该可以工作(从这里修改)
use the following permissions in your manifest
在您的清单中使用以下权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
and here is some actual code to turn wifi on/off
这是一些打开/关闭wifi的实际代码
private WifiManager wifiManager;
@Override
public void onCreate(Bundle icicle)
{
....................
wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
if(wifiManager.isWifiEnabled())
{
wifiManager.setWifiEnabled(false);
}
else
{
wifiManager.setWifiEnabled(true);
}
}
If you do not want to go down that route it looks like you might be able to tell the phone that you would prefer to use the mobile data network rather than the wifi network.
如果您不想走那条路线,您似乎可以告诉手机您更愿意使用移动数据网络而不是 wifi 网络。
The Android ConnectivityManageroffers a function setNetworkPreference. This function is not really documented as you can tell if you click the link. I would paly around with it though because the constants that are defined seem to hint that you can set this to either TYPE_MOBILEor TYPE_WIFIand there is a DEFAULT_NETWORK_PREFERENCEconstant as well that is defined as being 0x00000001 which is the same as TYPE_WIFI. So try getting access to a ConnectivityManager by calling
Android ConnectivityManager提供了一个函数setNetworkPreference。此功能并未真正记录在案,因为您可以通过单击链接来判断。不过,我会使用它,因为定义的常量似乎暗示您可以将其设置为TYPE_MOBILE或TYPE_WIFI,并且还有一个DEFAULT_NETWORK_PREFERENCE常量定义为 0x00000001,与 TYPE_WIFI 相同。因此,尝试通过调用访问 ConnectivityManager
Context.getSystemService(Context.CONNECTIVITY_SERVICE);
and then try using the setNetworkPreference() function.
然后尝试使用 setNetworkPreference() 函数。
It doesn't appear to require any permissions in the manifest but it might require the CHANGE_NETWORK_STATE permission or something along those lines.
它似乎不需要清单中的任何权限,但它可能需要 CHANGE_NETWORK_STATE 权限或类似的东西。
If you do sue the setNetworkPreference function it would probably be best to also set the Network Preference back to its original values (received from getNetworkPreference)
如果您确实起诉 setNetworkPreference 函数,最好也将网络首选项设置回其原始值(从 getNetworkPreference 接收)
I hope this helps.
我希望这有帮助。
回答by Rainbowbreeze
/**
* Enable mobile connection for a specific address
* @param context a Context (application or activity)
* @param address the address to enable
* @return true for success, else false
*/
private boolean forceMobileConnectionForAddress(Context context, String address) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == connectivityManager) {
Log.debug(TAG_LOG, "ConnectivityManager is null, cannot try to force a mobile connection");
return false;
}
//check if mobile connection is available and connected
State state = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
Log.debug(TAG_LOG, "TYPE_MOBILE_HIPRI network state: " + state);
if (0 == state.compareTo(State.CONNECTED) || 0 == state.compareTo(State.CONNECTING)) {
return true;
}
//activate mobile connection in addition to other connection already activated
int resultInt = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
Log.debug(TAG_LOG, "startUsingNetworkFeature for enableHIPRI result: " + resultInt);
//-1 means errors
// 0 means already enabled
// 1 means enabled
// other values can be returned, because this method is vendor specific
if (-1 == resultInt) {
Log.error(TAG_LOG, "Wrong result of startUsingNetworkFeature, maybe problems");
return false;
}
if (0 == resultInt) {
Log.debug(TAG_LOG, "No need to perform additional network settings");
return true;
}
//find the host name to route
String hostName = StringUtil.extractAddressFromUrl(address);
Log.debug(TAG_LOG, "Source address: " + address);
Log.debug(TAG_LOG, "Destination host address to route: " + hostName);
if (TextUtils.isEmpty(hostName)) hostName = address;
//create a route for the specified address
int hostAddress = lookupHost(hostName);
if (-1 == hostAddress) {
Log.error(TAG_LOG, "Wrong host address transformation, result was -1");
return false;
}
//wait some time needed to connection manager for waking up
try {
for (int counter=0; counter<30; counter++) {
State checkState = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
if (0 == checkState.compareTo(State.CONNECTED))
break;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
//nothing to do
}
boolean resultBool = connectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
Log.debug(TAG_LOG, "requestRouteToHost result: " + resultBool);
if (!resultBool)
Log.error(TAG_LOG, "Wrong requestRouteToHost result: expected true, but was false");
return resultBool;
}
And this for calculate host address:
这用于计算主机地址:
/**
* This method extracts from address the hostname
* @param url eg. http://some.where.com:8080/sync
* @return some.where.com
*/
public static String extractAddressFromUrl(String url) {
String urlToProcess = null;
//find protocol
int protocolEndIndex = url.indexOf("://");
if(protocolEndIndex>0) {
urlToProcess = url.substring(protocolEndIndex + 3);
} else {
urlToProcess = url;
}
// If we have port number in the address we strip everything
// after the port number
int pos = urlToProcess.indexOf(':');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
// If we have resource location in the address then we strip
// everything after the '/'
pos = urlToProcess.indexOf('/');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
// If we have ? in the address then we strip
// everything after the '?'
pos = urlToProcess.indexOf('?');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
return urlToProcess;
}
/**
* Transform host name in int value used by {@link ConnectivityManager.requestRouteToHost}
* method
*
* @param hostname
* @return -1 if the host doesn't exists, elsewhere its translation
* to an integer
*/
private static int lookupHost(String hostname) {
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
return -1;
}
byte[] addrBytes;
int addr;
addrBytes = inetAddress.getAddress();
addr = ((addrBytes[3] & 0xff) << 24)
| ((addrBytes[2] & 0xff) << 16)
| ((addrBytes[1] & 0xff) << 8 )
| (addrBytes[0] & 0xff);
return addr;
}
And following permission must be added to AndroidManifest.xml
并且必须在 AndroidManifest.xml 中添加以下权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
It works only with android 2.2 and above, tested on both Nexus One and on LG Optimus, other phones I don't know because some method of ConnectivityMananger are vendor-specific. After 15-20 seconds of inactivity, mobile network is automatically disconnected.
它仅适用于 android 2.2 及更高版本,在 Nexus One 和 LG Optimus 上进行了测试,我不知道其他手机,因为 ConnectivityMananger 的某些方法是特定于供应商的。闲置 15-20 秒后,移动网络将自动断开连接。
回答by Janusz
I think that is not possible from Java. The system shuts down all mobile network based communication if connected to a wireless network. I think that you aren't allowed to start a 3G connection from you program.
我认为这在 Java 中是不可能的。如果连接到无线网络,系统将关闭所有基于移动网络的通信。我认为您不允许从您的程序启动 3G 连接。
回答by Northern Captain
Here is the code that works on API 21+ (Lollipop, Marshmallow..). I prefer to use OkHttp with Network.getSocketFactory(), but Network.openURLConnection()also works fine.
这是适用于 API 21+(棒棒糖、棉花糖 ..)的代码。我更喜欢将 OkHttp 与Network.getSocketFactory()一起使用,但Network.openURLConnection()也可以正常工作。
private void doTest()
{
display("Requesting CELLULAR network connectivity...");
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
connectivityManager.requestNetwork(request, new ConnectivityManager.NetworkCallback()
{
/**
* Called when the framework connects and has declared a new network ready for use.
* This callback may be called more than once if the {@link Network} that is
* satisfying the request changes.
*
* This method will be called on non-UI thread, so beware not to use any UI updates directly.
*
* @param network The {@link Network} of the satisfying network.
*/
@Override
public void onAvailable(final Network network)
{
display("Got available network: " + network.toString());
try
{
final InetAddress address = network.getByName("navalclash.com");
display("Resolved host2ip: " + address.getHostName() + " -> " + address.getHostAddress());
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
display("Do request test page from remote http server...");
if(okHttpClient == null)
{
okHttpClient = new OkHttpClient.Builder().socketFactory(network.getSocketFactory()).build();
}
Request request = new Request.Builder()
.url("http://navalclash.com")
.build();
try (Response response = okHttpClient.newCall(request).execute())
{
display("RESULT:\n" + response.body().string());
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
});
}
回答by Shibin Francis
Use connection manager and set network preference as you want.
使用连接管理器并根据需要设置网络首选项。
for example:
例如:
dataManager = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
dataManager.setNetworkPreference(ConnectivityManager.TYPE_MOBILE);
回答by Cynichniy Bandera
Inspired by code in this ticket and using some parts of it, here is service that establishes hipri mobile and keeps it running.
受此票证中的代码启发并使用其中的某些部分,这里是建立 hipri mobile 并使其保持运行的服务。
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicBoolean;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.State;
import android.os.IBinder;
import android.util.Log;
public class HipriService extends Service {
private AtomicBoolean enabledMobile = new AtomicBoolean(false);
public boolean enableMobileConnection() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == cm) {
Log.d(TAG, "ConnectivityManager is null, cannot try to force a mobile connection");
return false;
}
/*
* Don't do anything if we are connecting. On the other hands re-new
* connection if we are connected.
*/
State state = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
Log.d(TAG, "TYPE_MOBILE_HIPRI network state: " + state);
if (0 == state.compareTo(State.CONNECTING))
return true;
/*
* Re-activate mobile connection in addition to other connection already
* activated
*/
int resultInt = cm.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
//Log.d(TAG, "startUsingNetworkFeature for enableHIPRI result: " + resultInt);
//-1 means errors
// 0 means already enabled
// 1 means enabled
// other values can be returned, because this method is vendor specific
if (-1 == resultInt) {
Log.e(TAG, "Wrong result of startUsingNetworkFeature, maybe problems");
return false;
}
if (0 == resultInt) {
Log.d(TAG, "No need to perform additional network settings");
return true;
}
return requestRouteToHost(this, Uploader.ServerAddress);
}
private Thread pingerThread = null;
private void startMobileConnection() {
enabledMobile.set(true);
pingerThread = new Thread(new Runnable() {
@Override
public void run() {
while (enabledMobile.get()) {
/*
* Renew mobile connection. No routing setup is needed. This
* should be moved to 3g monitoring service one day.
*/
enableMobileConnection();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// do nothing
}
}
}
});
pingerThread.start();
}
private void stopMobileConnection() {
enabledMobile.set(false);
disableMobileConnection();
pingerThread.interrupt();
pingerThread = null;
}
public void disableMobileConnection() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
cm.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
}
public final static int inetAddressToInt(InetAddress inetAddress) {
byte[] addrBytes;
int addr;
addrBytes = inetAddress.getAddress();
addr = ((addrBytes[3] & 0xff) << 24) | ((addrBytes[2] & 0xff) << 16) | ((addrBytes[1] & 0xff) << 8)
| (addrBytes[0] & 0xff);
return addr;
}
public final static InetAddress lookupHost(String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
return null;
}
}
private boolean requestRouteToHost(Context context, String hostname) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == cm) {
Log.d(TAG, "ConnectivityManager is null, cannot try to force a mobile connection");
return false;
}
/* Wait some time needed to connection manager for waking up */
try {
for (int counter = 0; enabledMobile.get() && counter < 30; counter++) {
State checkState = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
Log.i(TAG, "Waiting for mobile data on. State " + checkState);
if (0 == checkState.compareTo(State.CONNECTED))
break;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
//nothing to do
}
if (!enabledMobile.get()) {
Log.d(TAG, "Mobile data is turned off while waiting for routing.");
return false;
}
State checkState = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
if (0 != checkState.compareTo(State.CONNECTED)) {
Log.e(TAG, "Mobile data is still turned off after 30 sec of waiting.");
return false;
}
Log.i(TAG, "Adding routing for " + hostname);
InetAddress inetAddress = lookupHost(hostname);
if (inetAddress == null) {
Log.e(TAG, "Failed to resolve " + hostname);
return false;
}
int hostAddress = inetAddressToInt(inetAddress);
boolean resultBool = cm.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
Log.d(TAG, "requestRouteToHost result: " + resultBool);
if (!resultBool)
Log.e(TAG, "Wrong requestRouteToHost result: expected true, but was false");
return resultBool;
}
@Override
public void onCreate() {
super.onCreate();
startMobileConnection();
}
@Override
public void onDestroy() {
stopMobileConnection();
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Here is how I start/stop it when needed. Note that it also gets locks on cpu and wifi so that it may run when the phone sleeps (screen only). Wifi is needed because my app is kind of bridge between wifi and mobile connections. You may not need it.
这是我在需要时启动/停止它的方法。请注意,它还会锁定 cpu 和 wifi,以便它可以在手机睡眠时运行(仅限屏幕)。需要 Wifi 是因为我的应用程序是 wifi 和移动连接之间的桥梁。你可能不需要它。
public void startMobileData() {
if (!enabledMobile.get()) {
enabledMobile.set(true);
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, "Wifi Wakelock");
wifiLock.acquire();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
partialLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "3G Wakelock");
partialLock.acquire();
startService(new Intent(this, HipriService.class));
}
}
public void stopMobileData() {
if (enabledMobile.get()) {
enabledMobile.set(false);
Log.i(TAG, "Disabled mobile data");
stopService(new Intent(this, HipriService.class));
if (partialLock != null) {
partialLock.release();
partialLock = null;
}
if (wifiLock != null) {
wifiLock.release();
wifiLock = null;
}
}
}
Don't forget to add service to you manifest file.
不要忘记向清单文件添加服务。
回答by Luiz Carvalho
This aplication activate 3G and Wifi connection, Giving preference to 3G!! Very useful http://www.redrails.com.br/2012/02/wireless-analyzer-for-android/
此应用程序激活3G和Wifi连接,优先使用3G!!非常有用http://www.redrails.com.br/2012/02/wireless-analyzer-for-android/
回答by simple
@umka
@umka
- i think, app can only reach to those hosts through HIPRI which it has requested, may be for other hosts(whose routes are not requested) is using the default network(MOBILE or WIFI).
- On calling stopUsingNetworkFeature(), It will check -- is there any other app is using this network, if yes, then it will ignore your request to down this network feature.
- one of the main purpose of HIPRI network is that - if wifi is on and an app wants to use mobile netwrok(3G)to reach the particular host, it can reach through HIPRI network.
- 我认为,应用程序只能通过它请求的 HIPRI 到达那些主机,可能是其他主机(未请求其路由)使用默认网络(移动或 WIFI)。
- 在调用stopUsingNetworkFeature() 时,它会检查 - 是否有任何其他应用程序正在使用此网络,如果是,则它将忽略您关闭此网络功能的请求。
- HIPRI 网络的主要目的之一是 - 如果 wifi 处于打开状态并且应用程序想要使用移动网络(3G)到达特定主机,它可以通过 HIPRI 网络到达。
回答by Tiago
There is a missing piece of code on @Northern Captain answer, the DNS lookup code.
@Northern 船长答案中缺少一段代码,即 DNS 查找代码。
Here is a working code:
这是一个工作代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Log.d("NetworkDns", "Requesting CELLULAR network connectivity...");
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
connectivityManager.requestNetwork(request, new ConnectivityManager.NetworkCallback()
{
@Override
public void onAvailable(final Network network)
{
Log.d("NetworkDns", "Got available network: " + network.toString());
try
{
final InetAddress address = network.getByName("www.website.com");
Log.d("NetworkDns", "Resolved host2ip: " + address.getHostName() + " -> " + address.getHostAddress());
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
Log.d("NetworkDns", "Do request test page from remote http server...");
OkHttpClient okHttpClient = null;
if(okHttpClient == null)
{
okHttpClient = new OkHttpClient.Builder()
.socketFactory(network.getSocketFactory())
.dns(new Dns() {
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
if (network != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
List<InetAddress> addresses = Arrays.asList(network.getAllByName(hostname));
Log.d("NetworkDns", "List : " + addresses);
return addresses;
}
return SYSTEM.lookup(hostname);
}
}).build();
}
Request request = new Request.Builder()
.url("http://www.website.com")
.build();
try (Response response = okHttpClient.newCall(request).execute())
{
Log.d("NetworkDns", "RESULT:\n" + response.body().string());
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
});
}