Как использовать 3G-соединение в приложении для Android вместо Wi-Fi? - PullRequest
30 голосов
/ 25 марта 2010

Как использовать 3G-соединение в приложении для Android вместо Wi-Fi?

Я хочу подключить соединение 3G, есть ли пример кода для подключения к 3G вместо Wi-Fi?

Ответы [ 9 ]

61 голосов
/ 21 января 2011
/**
 * 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 секунд бездействия мобильная сеть автоматически отключается.

13 голосов
/ 25 марта 2010

Приложение T-Mobile «Моя учетная запись» делает это, если вы подключены к Wi-Fi-соединению, оно сообщает вам, что его программа не будет работать через WiFi, а затем спрашивает пользователя, хотят ли они отключить Wi-Fi-соединение. вы выбираете «Нет», затем приложение закрывается, если вы выбираете «Да», приложение отключает ваше 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)

Надеюсь, это поможет.

5 голосов
/ 25 марта 2010

Я думаю, что это невозможно из Java. Система отключает всю мобильную связь, если она подключена к беспроводной сети. Я думаю, что вы не можете запускать соединение 3G из вашей программы.

3 голосов
/ 16 июня 2016

Вот код, который работает на 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();
            }
        }
    });
}
2 голосов
/ 05 января 2013

Используйте диспетчер соединений и настройте параметры сети по своему усмотрению.

например:

dataManager  = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
dataManager.setNetworkPreference(ConnectivityManager.TYPE_MOBILE);
1 голос
/ 22 декабря 2013

Вдохновленный кодом в этом билете и использующим некоторые его части, вот сервис, который устанавливает 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;
        }
    }
}

Не забудьте добавить службу к вашему файлу манифеста.

1 голос
/ 04 февраля 2012

Это приложение активирует 3G и Wi-Fi соединение, отдавая предпочтение 3G !! Очень полезно http://www.redrails.com.br/2012/02/wireless-analyzer-for-android/

0 голосов
/ 31 мая 2018

В ответе @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();
            }

        }
    });
}
0 голосов
/ 25 января 2014

@ умка

  • Я думаю, приложение может связаться с теми хостами только через HIPRI, который оно запрашивает, может быть для других хостов (чьи маршруты не запрашиваются) использует сеть по умолчанию (MOBILE или WIFI).
  • При вызове stopUsingNetworkFeature () , Он проверит - есть ли другие приложения, использующие эту сеть, если да, то он проигнорирует ваш запрос на отключение этой сетевой функции.
  • одна из основных целей сети HIPRI заключается в том, что - если Wi-Fi включен и приложение хочет использовать мобильную сеть (3G) для доступа к конкретному хосту, оно может достичь через сеть HIPRI.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...