android - mensajes - mandar whatsapp sin agregar numero
Enviar mensaje a un controlador sobre un hilo muerto al obtener una ubicaciĆ³n de un servicio de intendencia (3)
No puede registrar a los oyentes de manera segura desde un servicio de IntentService
, ya que IntentService
desaparece tan pronto como se completa onHandleIntent()
(aka, doWakefulWork()
). En cambio, necesitará usar un servicio regular, además de manejar detalles como tiempos de espera (por ejemplo, el usuario está en un sótano y no puede obtener una señal de GPS).
Mi aplicación necesita correcciones de ubicación regularmente, incluso cuando el teléfono no está activo. Para hacer esto, estoy usando IntentService con el patrón proporcionado generosamente por Commonsware. https://github.com/commonsguy/cwac-wakeful
Para obtener soluciones de ubicación, confío en el siguiente código proporcionado por un miembro llamado Fedor. ¿Cuál es la forma más simple y sólida de obtener la ubicación actual del usuario en Android? . Lo modifiqué ligeramente para devolver nulo en lugar de obtener la última ubicación conocida
Ambos funcionan perfectamente bien cuando no están combinados: Puedo obtener una corrección de ubicación de la clase de Fedor en una Actividad, y puedo hacer algunas cosas básicas (como iniciar sesión en algo) usando la clase de Commonsware.
El problema ocurre cuando intento obtener soluciones de ubicación de la subclase WakefulIntentService de Commonsware. Los LocationListeners no reaccionan a LocationUpdates y recibo estas advertencias (todavía no conozco las sutilezas de los hilos, los manipuladores, ...). ¿Alguien puede ayudarme a entender cuál es el problema? Gracias y les deseo a todos un muy feliz año nuevo :)
12-31 14:09:33.664: W/MessageQueue(3264): Handler (android.location.LocationManager$ListenerTransport$1) {41403330} sending message to a Handler on a dead thread
12-31 14:09:33.664: W/MessageQueue(3264): java.lang.RuntimeException: Handler (android.location.LocationManager$ListenerTransport$1) {41403330} sending message to a Handler on a dead thread
12-31 14:09:33.664: W/MessageQueue(3264): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:196)
12-31 14:09:33.664: W/MessageQueue(3264): at android.os.Handler.sendMessageAtTime(Handler.java:473)
12-31 14:09:33.664: W/MessageQueue(3264): at android.os.Handler.sendMessageDelayed(Handler.java:446)
12-31 14:09:33.664: W/MessageQueue(3264): at android.os.Handler.sendMessage(Handler.java:383)
12-31 14:09:33.664: W/MessageQueue(3264): at android.location.LocationManager$ListenerTransport.onLocationChanged(LocationManager.java:193)
12-31 14:09:33.664: W/MessageQueue(3264): at android.location.ILocationListener$Stub.onTransact(ILocationListener.java:58)
12-31 14:09:33.664: W/MessageQueue(3264): at android.os.Binder.execTransact(Binder.java:338)
12-31 14:09:33.664: W/MessageQueue(3264): at dalvik.system.NativeStart.run(Native Method)
Esta es mi subclase WakefulIntentService:
public class AppService extends WakefulIntentService {
public static final String TAG = "AppService";
public AppService() {
super("AppService");
}
public LocationResult locationResult = new LocationResult() {
@Override
public void gotLocation(final Location location) {
if (location == null)
Log.d(TAG, "location could not be retrieved");
else
sendMessage(location);
}
};
@Override
protected void doWakefulWork(Intent intent) {
PhoneLocation myLocation;
myLocation = new PhoneLocation();
myLocation.getLocation(getApplicationContext(), locationResult);
}
Y aquí una copia de la clase de Fedor (ligeramente modificada: sin necesidad de GPS ni última ubicación conocida)
public class PhoneLocation {
public static String TAG = "MyLocation";
Timer timer1;
LocationManager lm;
LocationResult locationResult;
boolean gps_enabled=false;
boolean network_enabled=false;
public boolean getLocation(Context context, LocationResult result)
{
//I use LocationResult callback class to pass location value from MyLocation to user code.
locationResult=result;
if(lm==null) lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
//exceptions will be thrown if provider is not permitted.
try{gps_enabled=lm.isProviderEnabled(LocationManager.GPS_PROVIDER);}catch(Exception ex){}
try{network_enabled=lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);}catch(Exception ex){}
//don''t start listeners if no provider is enabled
if(!gps_enabled && !network_enabled)
return false;
if(network_enabled) lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
timer1=new Timer();
timer1.schedule(new ReturnNullLocation(), 20000);
return true;
}
LocationListener locationListenerNetwork = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
class ReturnNullLocation extends TimerTask {
@Override
public void run() {
lm.removeUpdates(locationListenerNetwork);
locationResult.gotLocation(null);
}
}
public static abstract class LocationResult{
public abstract void gotLocation(Location location);
}
}
Para otros como yo: tuve un problema similar relacionado con AsyncTask
. Según entiendo, esa clase asume que se inicializa en el hilo de la interfaz de usuario (principal).
Una solución alternativa (una de varias posibles) es colocar lo siguiente en la clase MyApplication
:
@Override
public void onCreate() {
// workaround for http://code.google.com/p/android/issues/detail?id=20915
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {}
super.onCreate();
}
(no olvides mencionarlo en AndroidManifest.xml
:
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name="MyApplication"
>
)
Tuve una situación similar y utilicé las instalaciones de Android Looper con buenos resultados: http://developer.android.com/reference/android/os/Looper.html
En concreto, justo después de llamar a la función que configura el oyente, llamo:
Looper.loop();
Eso bloquea el hilo actual para que no muera, pero al mismo tiempo le permite procesar eventos, por lo que tendrá la oportunidad de recibir notificaciones cuando se llame a su oyente. Un bucle simple evitaría que recibas notificaciones cuando se llame al oyente.
Y cuando se llama al oyente y termino de procesar sus datos, puse:
Looper.myLooper().quit();