Как использовать 3G-соединение в приложении для Android вместо Wi-Fi?
Как использовать 3G-соединение в приложении для Android вместо Wi-Fi?
Я хочу подключить соединение 3G, есть ли пример кода для подключения к 3G вместо Wi-Fi?
9 ответов
Приложение T-Mobile "Моя учетная запись" делает это, если вы подключены к WiFi-соединению, оно сообщает, что их программа не будет работать через WiFi, а затем спрашивает пользователя, хотят ли они отключить WiFi-соединение. Если вы выберете " Нет ", затем приложение закрывается, если вы выберете" Да ", приложение отключит ваше WiFi-соединение, а затем продолжит запуск.
Я думаю, что это хорошая модель для подражания, она гарантирует, что ваше приложение не запускается по WiFi, и позволяет пользователю решать, хотят ли они отключить Wi-Fi или нет. Усовершенствованием этой модели будет включение Wi-Fi, когда пользователь отходит от вашего приложения.
Я не тестировал следующий код, но похоже, что он должен работать (изменено здесь)
используйте следующие разрешения в вашем манифесте
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
и вот некоторый фактический код для включения / выключения Wi-Fi
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);
}
}
Если вы не хотите идти по этому маршруту, похоже, вы сможете сказать телефону, что предпочитаете использовать мобильную сеть передачи данных, а не сеть Wi-Fi.
Android ConnectivityManager предлагает функцию setNetworkPreference. Эта функция на самом деле не документирована, так как вы можете сказать, если вы нажмете ссылку. Я бы возился с этим, хотя, потому что определенные константы, похоже, намекают на то, что вы можете установить это либо в TYPE_MOBILE, либо в TYPE_WIFI, а также есть константа DEFAULT_NETWORK_PREFERENCE, которая определена как 0x00000001, что совпадает с TYPE_WIFI. Поэтому попробуйте получить доступ к ConnectivityManager, позвонив
Context.getSystemService(Context.CONNECTIVITY_SERVICE);
а затем попробуйте использовать функцию setNetworkPreference().
По-видимому, для него не требуются какие-либо разрешения в манифесте, но может потребоваться разрешение CHANGE_NETWORK_STATE или что-то в этом роде.
Если вы подаете в суд на функцию setNetworkPreference, то, вероятно, было бы лучше установить сетевое предпочтение обратно к его первоначальным значениям (полученным из getNetworkPreference).
Надеюсь, это поможет.
/**
* 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;
}
И это для расчета адреса хоста:
/**
* 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;
}
И следующее разрешение должно быть добавлено в AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
Он работает только с Android 2.2 и выше, протестирован как на Nexus One, так и на LG Optimus, другие телефоны я не знаю, потому что некоторые методы ConnectivityMananger зависят от производителя. После 15-20 секунд бездействия мобильная сеть автоматически отключается.
Вот код, который работает на API 21+ (Lollipop, Marshmallow..). Я предпочитаю использовать 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();
}
}
});
}
Я думаю, что это невозможно с Java. Система отключает всю мобильную сеть, если она подключена к беспроводной сети. Я думаю, что вы не можете запускать соединение 3G из вашей программы.
Используйте диспетчер подключений и настройте параметры сети по своему усмотрению.
например:
dataManager = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
dataManager.setNetworkPreference(ConnectivityManager.TYPE_MOBILE);
Вдохновленный кодом в этом билете и использующим некоторые его части, вот сервис, который устанавливает 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;
}
}
Вот как я запускаю / останавливаю его при необходимости. Обратите внимание, что он также получает блокировки на процессоре и Wi-Fi, чтобы он мог работать, когда телефон спит (только экран). Wi-Fi необходим, потому что мое приложение является своего рода мостом между Wi-Fi и мобильными соединениями. Вам это может не понадобиться.
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;
}
}
}
Не забудьте добавить сервис к вашему файлу манифеста.
Это приложение активирует 3G и Wi-Fi соединение, отдавая предпочтение 3G! Очень полезный http://www.redrails.com.br/2012/02/wireless-analyzer-for-android/
В ответе @Northern Captain отсутствует код поиска, код поиска DNS.
Вот рабочий код:
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();
}
}
});
}
@umka
- Я думаю, что приложение может достичь только тех хостов через HIPRI, которые оно запросило, может быть для других хостов (чьи маршруты не запрашиваются) использует сеть по умолчанию (MOBILE или WIFI).
- При вызове stopUsingNetworkFeature() он проверит - есть ли какое-либо другое приложение, использующее эту сеть, если да, то оно проигнорирует ваш запрос на отключение этой сетевой функции.
- одна из основных целей сети HIPRI заключается в том, что - если Wi-Fi включен и приложение хочет использовать мобильную сеть (3G) для доступа к конкретному хосту, оно может достигнуть через сеть HIPRI.