在我使用 Fused Location Client 的 android 应用程序中,为啥使用此代码时 GPS 会过时

Posted

技术标签:

【中文标题】在我使用 Fused Location Client 的 android 应用程序中,为啥使用此代码时 GPS 会过时【英文标题】:In my android app using Fused Location Client, Why am I getting stale GPS when using this code在我使用 Fused Location Client 的 android 应用程序中,为什么使用此代码时 GPS 会过时 【发布时间】:2021-10-12 12:47:28 【问题描述】:

我的应用会显示一个导航图,如果当前 GPS 位置可用,它会显示 那也是。我在一艘沿已知路线行驶的船上进行了测试。有时正确 位置显示。有时我会得到一个旧的 GPS 位置,即使是在要求重新读取时也是如此。 有时,它似乎会卡在一个旧位置上进行多次读取。当那件事发生时, 我运行了谷歌地图,它似乎清除了错误,我又开始获得一个新的位置。

// try to look at GPS....
        status1 = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
        if (status1 != PackageManager.PERMISSION_GRANTED)
           requestPermissions(new String[]Manifest.permission.ACCESS_FINE_LOCATION,10);
        
        status2 = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
        Object cancellationToken = null;
        if (status2 == PackageManager.PERMISSION_GRANTED)
            fusedLocationClient.getCurrentLocation(PRIORITY_HIGH_ACCURACY, (CancellationToken) cancellationToken)
                    .addOnSuccessListener(this, location -> // using lambda expression...

                       if (location != null)
                       GPSLatitude = location.getLatitude();
                        GPSLongitude = location.getLongitude();
                        gotGPS = true;
                        //if we have a location, and it is on chart, draw the blue dot....

谷歌地图比我更擅长阅读 GPS。我是否应该考虑另一种方法。

【问题讨论】:

你读了吗:Fused location provider doesn't seem to use GPS receiver 【参考方案1】:

欢迎加入遭受谷歌FusedLocationProvider 之手的人的俱乐部,我建议您改用LocationManager 与GPS 提供商。可用于在您的Activity 中请求位置

fun getLocation() 
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
        ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
         // do proper permissions handling
         return
    
    val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
    val gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
    if (gpsEnabled) 
        // Register for location updates
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0.0f, locationListener)
            
        // Or get last known location
        val location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
    


private val locationListener = LocationListener 
        // Do something with the location

有时FusedLocationProvider 会提供不准确的位置

在某些情况下,我发现 FusedLocationProvider 相当不准确,在最近的一次体验中,与您的体验没有什么不同,我无法在我的应用程序中使用 FusedLocationProvider 获得 GPS 锁定,当我打开谷歌地图时GPS锁销立即出现。所以我做了一些研究,果然我发现了FusedLocationProvider 的问题,我最终使用了LocationManger,至于它为什么会这样,我遇到的一些问题是

    它的Fused,意味着它使用多个来源(WiFi、网络、GPS 等),它会选择它认为合适的来源,或者混合所有来源来决定位置 FusedLocationProvider 的最大卖点是它针对电池使用进行了优化,所以只要它可以为您提供一个位置(通常准确,并非总是如此),如果它确定呼叫会不必要,它就不会撞到硬件电池负载 它似乎也在进行位置缓存,这可能是位置修复过时的原因,in the comments of this so question,用户分享了他们对此案例的经验

FusedLocationProvider引起的一些问题的链接

How FusedLocationProvider affected location accuracy of a data collection app which was earlier using LocationManger

FusedLocationProvider causing issues because of caching

Why LocationManager provides more accurate location

How FusedLocationProvider performs badly in some situations

How FusedLocationProvider provides inaccurate results, see the second highest voted answer

【讨论】:

谢谢你,WOZ,我很欣赏你所知道的努力和历史。这是一些非常好的读物,它解释了我收到的一些奇怪的值(距离几英里远的一个位置)。马上,无论如何,明天我将备份我的代码,然后使用 LocationManager 重新开始。【参考方案2】:

问题是您没有为tracks the location of assets 的应用使用推荐的方法。并且您尝试通过 getCurrentLocation 方法手动更新位置。事实上,getCurrentLocation 方法用于您不需要不断更新位置的情况。例如,您只需要一个用户的新位置,用于注册、附近搜索等。

当您需要持续跟踪资产时,您应该通过requestLocationUpdates 方法监听位置更新。根据official recommendation

适当地使用位置信息可以使用户受益 你的应用程序。例如,如果您的应用程序帮助用户在 步行或开车,或者如果您的应用程序跟踪资产的位置,它 需要定期获取设备的位置。

这里我实现了一个完整的例子

package com.example.locationtracker;

import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.os.Looper;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.example.locationtracker.databinding.ActivityMainBinding;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.material.snackbar.Snackbar;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity 


    private boolean isTracking = false;
    private FusedLocationProviderClient fusedLocationClient;
    private LocationCallback locationCallback;
    private LocationRequest locationRequest;
    private LocationRecycleViewAdapter locationRecycleViewAdapter;
    private final ArrayList<String> locations = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        com.example.locationtracker.databinding.ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.location.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        locationRecycleViewAdapter = new LocationRecycleViewAdapter(getApplicationContext(), locations);
        binding.location.setAdapter(locationRecycleViewAdapter);
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        locationRequest = LocationRequest.create();
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        locationRequest.setInterval(12000);
        locationRequest.setFastestInterval(2000);
        locationRequest.setWaitForAccurateLocation(true);
        locationRequest.setSmallestDisplacement(0);

        locationCallback = new LocationCallback() 
            @Override
            public void onLocationResult(@NonNull LocationResult locationResult) 
                for (Location location : locationResult.getLocations()) 
                    System.out.println("New Location Received");
                    String loc = String.format("lat: %s, lon: %s", location.getLatitude(), location.getLongitude());
                    locations.add(loc);
                    locationRecycleViewAdapter.notifyDataSetChanged();
                    /*locationRecycleViewAdapter = new LocationRecycleViewAdapter(getApplicationContext(), locations);
                    binding.location.setAdapter(locationRecycleViewAdapter);*/
                
            
        ;
        binding.fab.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                if (!isTracking) 
                    Snackbar.make(view, "Starting Location Tracking", Snackbar.LENGTH_LONG)
                            .show();
                    startLocationUpdates();

                 else 
                    Snackbar.make(view, "Stopping Location Tracking", Snackbar.LENGTH_LONG)
                            .show();
                    stopLocationUpdates();
                
            
        );
    

    @Override
    protected void onResume() 
        super.onResume();
        if (isTracking) 
            startLocationUpdates();
        
    

    private static final int PERMISSION_REQUEST_CODE = 200;

    private void startLocationUpdates() 
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
            requestPermissions(new String[]Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, PERMISSION_REQUEST_CODE);
            return;
        
        fusedLocationClient.requestLocationUpdates(locationRequest,
                locationCallback,
                Looper.getMainLooper());
        isTracking = true;
    

    private void stopLocationUpdates() 
        fusedLocationClient.removeLocationUpdates(locationCallback);
        isTracking = false;
    

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.locationtracker">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <application
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.LocationTracker">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.LocationTracker.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

输出

重要提示

确保您同时拥有 FINECOARSE 位置权限

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
如果您想以最快的时间间隔检索资产的位置,

COARSE LocationPRIORITY_HIGH_ACCURACY非常重要。以下设置也很重要。

locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(12000);
locationRequest.setFastestInterval(2000);
locationRequest.setWaitForAccurateLocation(true);
locationRequest.setSmallestDisplacement(0); 

【讨论】:

我可能误报了我的要求。我只需要在第一次绘制地图时或用户手动刷新后的新位置,不超过每 10 或 20 秒,通常永远不会。一次性的事情。所以,当我要求 GPS 时,就是我希望它有效的时候,我不会很快敲定它。这就是为什么问这样的问题。不过,我确信我可以在您的回复中找到有用的东西,谢谢。 @user1644002 欢迎您。即使位置刷新间隔很大,我也建议您使用这种方法,并且只将 locationRequest.setInterval 更改为所需的值。【参考方案3】:

我建议尝试使用间隔更新当前

        if (ActivityCompat.checkSelfPermission(mActivity, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
            return;
        
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, new LocationListener() 
            @Override
            public void onLocationChanged(Location location) 
             log.w("Get locations:: "+ location.getLatitude());
                newLocation(location);
//                locationManager.removeUpdates(this);
            

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

            

            @Override
            public void onProviderEnabled(String provider) 

            

            @Override
            public void onProviderDisabled(String provider) 

            
        );

【讨论】:

【参考方案4】:

这是我现在工作的java代码。

locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);


// try to look at GPS....
        status1 = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
        if (status1 != PackageManager.PERMISSION_GRANTED)
           requestPermissions(new String[]Manifest.permission.ACCESS_FINE_LOCATION,10);
        
        status2 = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
        if (status2 == PackageManager.PERMISSION_GRANTED)
           gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            if (gpsEnabled) // Register for location updates, each minute (60,000 milliseconds)
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 0.0f, locationListenerGPS);
LocationListener locationListenerGPS=new LocationListener() 
        @Override
        public void onLocationChanged(android.location.Location location) 
            GPSLatitude = location.getLatitude();
            GPSLongitude = location.getLongitude();
            gotGPS = true;

            reDraw();

            //String msg="New Latitude: "+latitude + "New Longitude: "+longitude;
            //Toast.makeText(mContext,msg,Toast.LENGTH_LONG).show();
        

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

        

        @Override
        public void onProviderEnabled(String provider) 

        

        @Override
        public void onProviderDisabled(String provider) 

        
    ;
///

based on Kotlin code from WOZ, it works well 

【讨论】:

以上是关于在我使用 Fused Location Client 的 android 应用程序中,为啥使用此代码时 GPS 会过时的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Service 类中使用 Fused Location Provider?

Android:关闭位置时,Google Fused Location 不起作用

Fused Location Provider:有没有办法检查位置更新是不是失败?

Fused Location Provider 无故停止

使用 Location Manager 和 Fused Location Provider Api 获取位置有啥区别?

使用 Fused Location Provider 查找位置源