2010-03-25 17 views
29

¿Cómo utilizar 3G Connection en una Aplicación Android en lugar de Wi-Fi?¿Cómo usar 3G Connection en una Aplicación Android en lugar de Wi-Fi?

Quiero conectar una conexión 3G, ¿hay algún código de muestra para conectar a 3G en lugar de Wi-Fi?

+1

¿Por qué no abre las preferencias de conexión inalámbrica y deja que el usuario deshabilite wifi él mismo. Bastante intrusivo, pero el usuario tiene el control. – MrSnowflake

+0

Estaba buscando una forma de hacerlo, pero estaba buscando las palabras "prioridad" (que creo que sería el nombre correcto) en lugar de "preferencia". Gracias por la gran pregunta y respuesta. –

+0

@MrSnowflake Si desea distribuir la carga a través de las interfaces de red, o confía en información confidencial en una interfaz y desea enrutar tráfico sensible a través del canal confiable y otro tráfico a través del canal no confiable, o le preocupa que una de las interfaces sea más es probable que se desconecte durante una descarga grande, o si solo desea otorgar al usuario permisos detallados y específicos de aplicación, no desea abrir la página de configuración * global * para abordar una preocupación * específica de la aplicación *. –

Respuesta

13

La aplicación "Mi cuenta" de T-Mobile hace esto, si está conectado a una conexión WiFi le dice que su programa no funcionará a través de WiFi y luego le pregunta al usuario si desea desactivar la conexión WiFi. Si eliges "No", la aplicación se cierra, si eliges "Sí", entonces la aplicación apaga tu conexión WiFi y luego continúa con la puesta en marcha.

Creo que este es un buen modelo a seguir, garantizará que su aplicación no se está ejecutando a través de WiFi y le permite al usuario decidir si quiere desactivar WiFi o no. Una mejora en este modelo sería volver a encender el wifi cuando el usuario se aleje de su aplicación.

I no has teste d el siguiente código, pero parece que debería funcionar (modificado de here)

utilizan los siguientes permisos en el manifiesto

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> 
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> 

y he aquí algo de código real para convertir wifi on/off

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); 
    } 
} 

Si no desea seguir esa ruta, parece que podría decirle al teléfono que prefiere usar la red de datos móviles en lugar de la red wifi.

El androide ConnectivityManager ofrece una función setNetworkPreference. Esta función no está realmente documentada ya que puede ver si hace clic en el enlace. Sin embargo, me gustaría dar vueltas con esto porque las constantes que se definen parecen indicar que puede establecer esto en TYPE_MOBILE o TYPE_WIFI y también hay una constante DEFAULT_NETWORK_PREFERENCE que se define como 0x00000001 que es lo mismo que TYPE_WIFI. Así que trate de obtener acceso a un ConnectivityManager llamando

Context.getSystemService(Context.CONNECTIVITY_SERVICE); 

y luego tratar de usar la función setNetworkPreference().

Parece que no requiere ningún permiso en el manifiesto, pero puede requerir el permiso CHANGE_NETWORK_STATE o algo similar.

Si lo hace demandar la función setNetworkPreference probablemente sería mejor establecer también la preferencia de las redes de nuevo a sus valores originales (recibido de getNetworkPreference)

espero que esto ayude.

+2

Aunque es útil, la respuesta a continuación de @Rainbowbreeze es la respuesta correcta. – Josh

4

Creo que eso no es posible desde Java. El sistema cierra todas las comunicaciones basadas en la red móvil si está conectado a una red inalámbrica. Creo que no puedes iniciar una conexión 3G desde tu programa.

+0

gracias q si el usuario móvil habilita ambos medios 3G y WiFi esa vez mi aplicación en ejecución falló debido a que está continuamente saltando WiFi a 3G y 3G a WiFi dar cualquier solución alternativa – Aswan

+2

No hay forma de evitar esto. Si el usuario tiene una conexión 3G muy llamativa, obtendrá conexión de red y conexión de red suelta todo el tiempo. Lo único que puede hacer es hacer solicitudes pequeñas y frecuentes a su servidor y no intente mantener una conexión abierta durante más tiempo. Y luego maneje los errores de red de forma adecuada, tal vez intentándolo nuevamente o aconsejando al usuario que lo intente más tarde con una conexión más estable. – Janusz

+0

Lo siento mucho, pero las cosas en Android son realmente tan malas. He probado 2 enchufes BSD enlazados a Wi-Fi y otro a direcciones IP 3G. Android realmente bloquea un socket (no recibe paquetes) cuando cambia a otro tipo de red. Si utiliza '' 'connectivityManager.requestRouteToHost (ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);' '' con '' 'connectivityManager.startUsingNetworkFeature (ConnectivityManager.TYPE_MOBILE," enableHIPRI ");' '' tiene un socket que enlaza con otra dirección recibir un paquete que no corresponde, más malo. –

60
/** 
* 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; 
} 

Y esto para calcular la dirección del host:

/** 
* 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; 
} 

Y siguiendo el permiso hay que añadir a AndroidManifest.xml

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> 
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> 

Sólo funciona con Android 2.2 y superiores, se evalúa en ambos Nexus One y el LG Optimus, otros teléfonos no sé porque algún método de ConnectivityMananger son específicos del proveedor. Después de 15-20 segundos de inactividad, la red móvil se desconecta automáticamente.

+2

No puedo votar lo suficiente. Esto hace exactamente lo que necesitaba y funcionaba como un amuleto sin tener que cambiar nada. Y es la única solución buena que encontré para ese problema en Internet. – Robber

+2

Fui un poco rápido con "funcionó como un encanto". Tengo que llamar a startUsingNetworkFeature regularmente (cada 30 segundos más o menos) o me desconectará incluso mientras estoy transmitiendo datos. Con esta solución no he encontrado otros problemas todavía. ¡Gracias! – Robber

+0

Además de esta excelente respuesta, si alguien quiere encender una conexión de datos apagada, le sugiero que agregue una llamada al método desde esta respuesta: http://stackoverflow.com/a/11555457/2347363 que se puede llamar antes de esta línea: "for (int counter = 0; counter <30; counter ++)" de esta manera si el cambio de datos está desactivado, funcionará igual – Antilope

2

utilice el administrador de conexión y establezca la preferencia de red como desee. buena luk

por ejemplo: dataManager = (ConnectivityManager) getSystemService (CONNECTIVITY_SERVICE); dataManager.setNetworkPreference (ConnectivityManager.TYPE_MOBILE);

+1

ConnectivityManager.setNetworkPreference() está a punto de quedarse obsoleto. Pero lo que es más importante, si obtiene NetworkPreference() antes de cambiar, devolverá ConnectivityManager.TYPE_MOBILE. Establecerlo allí no hace ninguna diferencia. En cuanto a HIPRI en sí, funciona de una manera bastante extraña. Primero, permite conexiones a todos los hosts, no solo a aquellos explícitamente solicitados por el enrutamiento. Segundo, cuando lo apagas con la llamada stopUsingFeature ...(), no se desactivará y seguirá activo. En tercer lugar, todas las aplicaciones lo usarán incluso si WiFi está disponible, lo que contradice la documentación. –

1

Inspirado por el código en este ticket y usando algunas partes del mismo, este es un servicio que establece hipri mobile y lo mantiene funcionando.

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; 
    } 

} 

Así es como lo comienzo/detengo cuando es necesario. Tenga en cuenta que también se bloquea en la CPU y wifi para que se ejecute cuando el teléfono duerme (solo en la pantalla). Se necesita Wifi porque mi aplicación es una especie de puente entre wifi y conexiones móviles. Puede que no lo necesites.

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; 
     } 
    } 
} 

No olvide agregar el servicio a su archivo de manifiesto.

0

@umka

  • creo, aplicación sólo puede llegar a través de los anfitriones HIPRI que ha solicitado, puede ser para otros huéspedes (cuyas rutas no solicitado) es el uso de la red por defecto (móvil o Wi-Fi)
  • Al llamar al stopUsingNetworkFeature(), Comprobará si hay alguna otra aplicación utilizando esta red, si es así, ignorará su solicitud para desactivar esta característica de red.
  • uno de los propósitos principales de la red HIPRI es que, si el wifi está encendido y una aplicación desea usar la red móvil (3G) para llegar al host en particular, puede hacerlo a través de la red HIPRI.
3

Aquí está el código que funciona en API 21+ (Lollipop, Marshmallow ..). Prefiero usar OkHttp con Network.getSocketFactory(), pero Network.openURLConnection() también funciona bien.

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(); 
      } 
     } 
    }); 
} 
+0

OkHttp no parece funcionar para mí (lo cual es extraño), pero lo hizo network.openURLConnection(). – user1691694

Cuestiones relacionadas