Android - 在多个活动中实现LocationListener的最佳方法

41

我一直卡在这个问题上很久了。我正在开发一个应用程序,它在多个不同的活动中广泛使用位置信息。我找到的每个示例都在每个活动中使用单独的LocationListener,但在我的情况下,这种方法是不可行的。

我想知道在多个活动中跟踪用户位置最有效的方法是什么。目前,我已经创建了一个服务来实现LocationListener并使用广播来更新活动基类中静态的纬度和经度字段。这意味着该服务始终在运行,这并不理想。但是,如果我关闭服务并且只在需要时重新启动它,那么获取良好的定位需要时间。我需要在活动的onCreate()中获取位置数据。如果我尝试在活动基类中实现它,情况也是一样。如果我在onResume/onCreate中不断注册侦听器,并在onPause()中取消注册,那么开始接收更新所需的时间太长。我还尝试创建一个可以绑定的服务,因此只有当我需要位置时才启动它。但是我遇到了同样的问题,绑定服务并开始获取更新需要太长时间。

我现在使用的服务可以工作,但从我阅读的所有内容来看,我不应该为像这样的琐碎事情使用不断运行的服务。但是,应用程序的整个重点是根据用户当前位置提供相关数据。因此,我有一个后台运行的服务,定期提供更新。现在唯一引起我重新审视设计的主要问题是,我最近发现如果用户启动应用程序时没有开启GPS,然后随后启用它,则不会调用onProviderEnabled()。在这种情况下,应用程序无法识别GPS已启用,因此无法开始监听更新。

从查看示例代码来看,我认为我理解了LocationManager和LocationListener,但似乎无法将其应用到需要在多个活动中获取位置数据的情况中。非常感谢您能提供任何帮助或建议。

4个回答

43
我通常实现此要求的方式是使用绑定服务实现,就像SDK文档中的本地服务示例中的那个一样。显然,您已经熟悉了使用服务只创建一次所有位置代码的优势。
通过绑定访问服务允许服务启动和停止,因此当您的应用程序不在前台运行时(没有更多的Activity绑定时),服务将停止。我认为使这项工作良好的关键是在onStart()方法中绑定服务,并在onStop()方法中解除绑定,因为随着从一个Activity到另一个Activity的移动,这两个调用会重叠(第二个Activity在第一个Activity停止之前启动)。这可以防止服务在应用程序内部移动时死亡,并且只有在整个应用程序(或至少对位置感兴趣的任何部分)离开前台时才允许服务死亡。
使用绑定,您不必通过广播传递位置数据,因为Activity可以直接调用服务上的方法获取最新的位置信息。但是,广播仍然有优势,作为指示何时可用新更新的方法...但这只是成为通知听取Activity调用服务上的getLocation()方法。
这是我的想法,希望能有所帮助!

我以前走过这条路,但我不知道onStart和onStop会有这样的重叠。这很吸引我,我会尝试一下并在明天告诉你我的结果。谢谢! - d370urn3ur
1
只是想知道,如果关闭屏幕,这将如何工作?在onResume()上绑定服务并在onPause()上解除绑定是否明智,以便在屏幕开/关时打开/关闭它,并在活动切换时也能正常工作? - Grantland Chew
2
@Grantland 有道理,因为Activity仍在前台,所以Service将保持活动状态。如果服务在这些情况下需要自我限制,我会保持绑定不变,但会让它注册一个BroadcastReceiver来监听Intent.ACTION_SCREEN_ONIntent.ACTION_SCREEN_OFF 并关闭重型组件如GPS。将绑定机制移到onPause()/onResume()中会导致服务在应用程序使用过程中可能经常停止/重新启动,因为这些回调永远不会重叠,使服务经常陷入未绑定状态。 - devunwired
很棒的解决方案。我认为你需要像这样绑定BIND_AUTO_CREATE:bindService(intent, mServiceConn, BIND_AUTO_CREATE),有人可以确认吗? - Filippo Mazza

18

我遇到了同样的问题,我尝试使用Devunwired的好答案来解决它,但是我遇到了一些麻烦。我找不到停止服务的方法,当我完成我的应用程序时,GPS模块仍在运行。所以我找到了另一种方法:

我编写了一个名为 GPS.java 的类:

public class GPS {

    private IGPSActivity main;

    // Helper for GPS-Position
    private LocationListener mlocListener;
    private LocationManager mlocManager;

    private boolean isRunning;

    public GPS(IGPSActivity main) {
        this.main = main;

        // GPS Position
        mlocManager = (LocationManager) ((Activity) this.main).getSystemService(Context.LOCATION_SERVICE);
        mlocListener = new MyLocationListener();
        mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
        // GPS Position END
        this.isRunning = true;
    }

    public void stopGPS() {
        if(isRunning) {
            mlocManager.removeUpdates(mlocListener);
            this.isRunning = false;
        }
    }

    public void resumeGPS() {
        mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
        this.isRunning = true;
    }

    public boolean isRunning() {
        return this.isRunning;
    }

    public class MyLocationListener implements LocationListener {

        private final String TAG = MyLocationListener.class.getSimpleName();

        @Override
        public void onLocationChanged(Location loc) {
            GPS.this.main.locationChanged(loc.getLongitude(), loc.getLatitude());
        }

        @Override
        public void onProviderDisabled(String provider) {
            GPS.this.main.displayGPSSettingsDialog();
        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

    }

}

这个类在需要 GPS 坐标的每个 Activity 中使用。每个 Activity 都必须实现以下接口(用于通信):

public interface IGPSActivity {
    public void locationChanged(double longitude, double latitude);
    public void displayGPSSettingsDialog();
}

现在我的主Activity看起来像这样:

public class MainActivity extends Activity implements IGPSActivity {

    private GPS gps;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        gps = new GPS(this);
    }


    @Override
    protected void onResume() { 
        if(!gps.isRunning()) gps.resumeGPS();   
        super.onResume();
    }

    @Override
    protected void onStop() {
        gps.stopGPS();
        super.onStop();
    }


    public void locationChanged(double longitude, double latitude) {
        Log.d(TAG, "Main-Longitude: " + longitude);
        Log.d(TAG, "Main-Latitude: " + latitude);
    }


    @Override
    public void displayGPSSettingsDialog() {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivity(intent);
    }
}

还有一个类似这样的第二个:

public class TEST4GPS extends Activity implements IGPSActivity{

    private GPS gps;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.gps = new GPS(this);
    }

    @Override
    public void locationChanged(double longitude, double latitude) {
        Log.d("TEST", "Test-Longitude: " + longitude);
        Log.d("TEST", "Test-Latitude: " + latitude);

    }

    @Override
    protected void onResume() { 
        if(!gps.isRunning()) gps.resumeGPS();   
        super. onResume();
    }

    @Override
    protected void onStop() {
        gps.stopGPS();
        super.onStop();
    }

    @Override
    public void displayGPSSettingsDialog() {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivity(intent);

    }
}

这个解决方案可能不如Devunwired提出的那么优美,但对我而言有效。 拜拜


喜欢你的解决方案!我做了一些改进,运行得很好!谢谢。 - Joubert Vasconcelos
@Spipau,我喜欢你在这里的答案。 我尝试了它,但对我没有用。 你有机会看一下我的代码,看看我做错了什么吗? http://stackoverflow.com/questions/29771717/android-gps-not-working-for-me - MrGibbage
2
@MrGibbage 抱歉,但我必须重新创建整个东西。我的答案已经有3年了,如果我再次在我的应用程序中实现全球GPS信号,它将完全不同。将跟踪代码放入您的应用程序中,并使用LocalBroadcastManager将位置发送到您选择的活动/片段。https://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html - Paul Spiesberger
它解决了我在多个活动中获取位置的问题。虽然我不得不从gms位置切换到android位置。 - fida1989

0

你可以让你的服务在位置坐标(lat & long)发生变化时将其写入sqlite数据库,这样你就不必担心服务绑定开销,并且你的坐标将在所有活动中都可以访问。

也许在你的主活动中,你可以检查GPS状态,如果它被关闭了,你可以提示用户启用它。一旦GPS被启用,就开始你的服务。


-1

这里有一种非常简单直接的方法来实现:

在你的主活动中:

private boolean mFinish;

@Override
public void onBackPressed() {
    mFinish = true;
}


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mFinish = false;
    ...
}

@Override
public void onPause(){
    super.onPause();
    if (mFinish) {
        // remove updates here
        mLocationManager.removeUpdates(mLocationListener);
    }
}

这将仅在您的主活动中按下“返回”时删除更新侦听器,但否则保持不变。

这不是最好的方法,但它是一个简单的复制粘贴解决方案。

确保不要调用位置侦听器,如果它已经在运行(例如屏幕旋转),否则您将在后台运行多个侦听器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接