从 IntentService 获取位置时向死线程上的处理程序发送消息

发布于 2024-12-24 18:58:17 字数 4521 浏览 1 评论 0原文

我的应用程序需要定期进行位置修复,即使手机未唤醒时也是如此。 为此,我使用 IntentService 以及 Commonsware 慷慨提供的模式。 https://github.com/commonsguy/cwac-wakeful

要获取位置修复,我依赖以下代码由一位名为 Fedor 的成员提供。 在 Android 上获取用户当前位置的最简单、最可靠的方法是什么?。我稍微修改它以返回 null 而不是获取最后一个已知位置

它们在不组合时都工作得很好:我可以从 Activity 中的 Fedor 类获取位置修复,并且我可以使用 Commonsware 做一些基本的事情(例如记录某些内容)班级。

当我尝试从 Commonsware 的 WakefulIntentService 子类获取位置修复时,就会出现问题。 LocationListeners 不会对 locationUpdates 做出反应,我收到这些警告(我还不明白线程、处理程序等的微妙之处……)。有人可以帮助我了解问题所在吗?谢谢,祝大家新年快乐:)

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)

这是我的 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);
}

这里是 Fedor 类的副本(稍作修改:不需要 GPS 也不需要最后已知位置)

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

}

}

My app needs location fixes on regular basis, even when the phone is not awake.
To do this, I am using IntentService with the pattern generously provided by Commonsware.
https://github.com/commonsguy/cwac-wakeful

To get location fixes, I rely on the following code provided by a member called Fedor.
What is the simplest and most robust way to get the user's current location on Android?. I slightly modified it to return null instead of getting the last known location

They both work perfectly fine when not combined: I can get a location fix from Fedor's class in an Activity, and I can do some basic stuff ( like logging something) using Commonsware class.

The problem occurs when I am trying to get location fixes from the Commonsware's WakefulIntentService subclass. The LocationListeners do not react to locationUpdates and I get these warnings (I don't get yet the subtleties of threads, handlers, ...) . Can someone help me understand what the problem is? Thanks and I wish you all a very happy new year :)

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)

This is my WakefulIntentService subclass:

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

And here a copy of Fedor's class (slightly modified: no need of GPS nor last known location)

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

}

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

铃予 2024-12-31 18:58:17

您无法安全地从 IntentService 注册侦听器,因为 IntentService 一旦 onHandleIntent()(又名 doWakefulWork())就会消失) 完成。相反,您需要使用常规服务,并处理超时等详细信息(例如,用户位于地下室且无法获取 GPS 信号)。

You cannot safely register listeners from an IntentService, as the IntentService goes away as soon as onHandleIntent() (a.k.a., doWakefulWork()) completes. Instead, you will need to use a regular service, plus handle details like timeouts (e.g., the user is in a basement and cannot get a GPS signal).

木落 2024-12-31 18:58:17

我也遇到过类似的情况,我使用了android Looper工具,效果很好:
http://developer.android.com/reference/android/os/Looper.html

具体来说,在调用设置侦听器的函数之后,我调用:

Looper.loop();

这会阻止当前线程,这样它就不会死亡,但同时让它处理事件,这样你就有机会得到当您的监听器被调用时收到通知。一个简单的循环会阻止您在调用侦听器时收到通知。

当调用侦听器并且我处理完其数据时,我输入:

Looper.myLooper().quit();

I've had a similar situation, and I've used the android Looper facility to good effect:
http://developer.android.com/reference/android/os/Looper.html

concretely, just after calling the function setting up the the listener, I call:

Looper.loop();

That blocks the current thread so it doesn't die but at the same time lets it process events, so you'll have a chance to get notified when your listener is called. A simple loop would prevent you from getting notified when the listener is called.

And when the listener is called and I'm done processing its data, I put:

Looper.myLooper().quit();
心奴独伤 2024-12-31 18:58:17

对于像我这样的人:我遇到了与 AsyncTask 相关的类似问题。据我了解,该类假定它是在 UI(主)线程上初始化的。

解决方法(几种可能的方法之一)是将以下内容放入 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();
}

不要忘记在 AndroidManifest.xml 中提及它

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:name="MyApplication"
    >

:)

For others like me: I had a similar problem related to AsyncTask. As I understand, that class assumes that it is initialized on the UI (main) thread.

A workaround (one of several possible) is to place the following in MyApplication class:

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

(do not forget to mention it in AndroidManifest.xml:

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:name="MyApplication"
    >

)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文