FusedLocationApi.getLastLocation 始终为空

Posted

技术标签:

【中文标题】FusedLocationApi.getLastLocation 始终为空【英文标题】:FusedLocationApi.getLastLocation always null 【发布时间】:2019-08-18 20:56:32 【问题描述】:

我想在我的 android 项目中简单地检索设备位置,为此我使用播放服务方法:

    protected synchronized void buildGoogleApiClient() 

    mGoogleApiClient = new GoogleApiClient.Builder( MainSearchActivity.this )
        .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() 
            @Override
            public void onConnected( Bundle bundle )
                Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
                if( location == null )
                    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, new LocationListener() 
                        @Override
                        public void onLocationChanged(Location location) 
                            lastLocation = location;
                        
                    );
                
            
            @Override
            public void onConnectionSuspended( int i )

            

        )
        .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() 
            @Override
            public void onConnectionFailed( ConnectionResult connectionResult )
                if( connectionResult.hasResolution() )
                    try 
                        // Start an Activity that tries to resolve the error
                        connectionResult.startResolutionForResult(MainSearchActivity.this, CONNECTION_FAILURE_RESOLUTION_REQUEST);
                    catch( IntentSender.SendIntentException e )
                        e.printStackTrace();
                    
                else
                    Utils.logger("Location services connection failed with code " + connectionResult.getErrorCode(), Utils.LOG_DEBUG );
                
            
        )
        .addApi(LocationServices.API)
        .build();

    mGoogleApiClient.connect();


public Location retrieveLastLocation()
    Location loc = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
    if( loc == null)
    

    
    return loc; //TODO: What if loc is null?

loc 变量始终为空。每次在不同的手机上都是如此。还有lastLocation,我尝试在onLocationChanged 中分配,永远不会改变。始终为空。

这些是我为应用设置的权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="com.vogella.android.locationapi.maps.permission.MAPS_RECEIVE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />

我只是不明白:为什么LocationServices 不能检索位置?我在我测试的所有三台设备上都启用了所有地理位置设置。

【问题讨论】:

我请求你看看这个答案***.com/a/35833552/3278589 com.google.android.gms:play-services-location:17.1.0 开始,现在有一个getCurrentLocation() 方法尝试主动获取用户的当前位置。 developers.google.com/android/reference/com/google/android/gms/… 【参考方案1】:

融合的Location Provider 只会在至少有一个客户端连接到它的情况下保持后台位置。现在只打开定位服务并不能保证存储最后一个已知位置。

一旦第一个客户端连接,它将立即尝试获取位置。如果您的活动是第一个连接的客户端,并且在onConnected() 中立即调用了getLastLocation(),那么第一个位置可能没有足够的时间到达..

我建议你先启动地图应用,这样至少有一些确认的位置,然后测试你的应用。

【讨论】:

那么你的意思是当用户在我们的应用中,并且我们需要他们的位置时,我们需要打开谷歌地图应用,然后希望用户回到我们的应用? 我并不是说你需要打开谷歌地图。我要说的是,当您致电融合提供商时,需要已经有一些可用的位置数据。毕竟这就是使用 Fusion Provider 的主要好处,不是吗? 那么我如何强制在我自己的应用中发生一些位置获取事件 在这种情况下,您需要通过常规的位置检索过程。毕竟,融合位置提供程序是一项“安全且不那么重要”的任务。 我认为这个博客实际上解决了这个问题:blog.teamtreehouse.com/beginners-guide-location-android【参考方案2】:

正如this帖子中所说,融合的位置提供者只有在至少有一个客户端连接到它时才会维护后台位置。

但我们可以通过以下方式跳过启动谷歌地图应用程序以获取最后位置的过程。

我们需要做的是

    我们必须向FusedLocationProviderClient请求位置更新 那么我们可以从FusedLocationProviderClient获取最后一个位置,它不会为空。

请求位置

LocationRequest mLocationRequest = LocationRequest.create();
mLocationRequest.setInterval(60000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationCallback mLocationCallback = new LocationCallback() 
    @Override
    public void onLocationResult(LocationResult locationResult) 
        if (locationResult == null) 
            return;
        
        for (Location location : locationResult.getLocations()) 
            if (location != null) 
                //TODO: UI updates.
            
        
    
;
LocationServices.getFusedLocationProviderClient(context).requestLocationUpdates(mLocationRequest, mLocationCallback, null);

获取最后的位置

LocationServices.getFusedLocationProviderClient(context).getLastLocation().addOnSuccessListener(new OnSuccessListener<Location>() 
        @Override
        public void onSuccess(Location location) 
            //TODO: UI updates.
        
    );

为了最好的结果,在Activity的onStart()中requestLocationUpdates,然后就可以得到最后的位置了。

【讨论】:

这是唯一对我有用的解决方案。我必须完全按照步骤操作,包括PRIORITY_HIGH_ACCURACY 和设置时间间隔。任何间隔似乎都可以解决问题。我还添加了setNumUpdates(1)。如果有人想知道,为了重现最后已知位置为空的问题,只需冷启动模拟器并立即致电getLastKnownLocation() 在融合的位置提供者客户端上调用getLastLocation() 有什么意义?比位置回调中的第一次更新更快地获取位置?此外,您可以在拨打requestLocationUpdates 后立即拨打getLastLocation() 吗? @AdamJohns getLastLocation() 只是从FusedLocationProviderClient 返回上次保存的位置,而requestLocationUpdates 只是用来触发FusedLocationProviderClient 请求位置,所以FusedLocationProviderClient 会尝试获取最新的location,基本上我们会在getLastLocation()方法返回null时使用这个方法。 @Gunaseelan 为什么不最初使用 requestLocationUpdates 而不是仅在 getLastLocation 返回 null 时使用? 你可以使用。使用FusedLocationProviderClient的实际目的是消耗电池电量。大多数时候getLastLocation 只返回您当前的位置,所以我们可以跳过requestLocationUpdates。更直接的答案是requestLocationUpdates 会消耗更多电池。【参考方案3】:

我认为在显示的代码中我看不到一个小失误。

mGoogleApiClient 已构建,但似乎未连接。您可以通过调用 mGoogleApiClient.isConnected() 来验证这一点。

您可以重写 onStart 方法并在那里调用 connect。或者你可以覆盖onResume(),以防你想在你的活动可见时访问该位置。

  @Override
protected void onStart() 
    super.onStart();
    if (mGoogleApiClient != null) 
        mGoogleApiClient.connect();
    

【讨论】:

哇,这对我有用,但使用它 onConnected() :) 谢谢先生【参考方案4】:

首先,创建一个LocationRequest 对象:

 // Create the LocationRequest object
 mLocationRequest = LocationRequest.create()
    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
    .setInterval(10 * 1000)        // 10 seconds, in milliseconds
    .setFastestInterval(1 * 1000); // 1 second, in milliseconds

然后,确保用户已授予使用位置的权限。如果有,从requestLocationUpdates获取位置如下:

void getLocation() 
    Location location = null;
    if (ContextCompat.checkSelfPermission(activity, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
        ActivityCompat.requestPermissions(activity, new String[]Manifest.permission.ACCESS_COARSE_LOCATION, 1);

        /*TODO!! INSERT CODE TO PROMPT USER TO GIVE PERMISSION*/

     else 
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    

    mLastLocation = location;
    LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,this);

如果您只需要一个位置而不需要持续监控,请务必删除更新。这样,您将永远不会得到空的 Location

欲了解更多信息,请转至http://blog.teamtreehouse.com/beginners-guide-location-android。

【讨论】:

requestLocationUpdates 回调仍然可以返回 null Location【参考方案5】:

如果 getLastLocation() 总是返回 null,试试这个技巧。我用它来解决我的问题。在您的活动中实现 LocationListener (import com.google.android.gms.location.LocationListener)。

protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);

        Context mContext = this;

        manager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);

        createLocationRequest();

        mGoogleApiClient = new GoogleApiClient.Builder(mContext)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();



private void createLocationRequest()
    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationRequest.setInterval(SET_INTERVAL);
    mLocationRequest.setFastestInterval(FASTEST_INTERVAL);

onStart 或 onResume(我更喜欢 onResume)

@Override
protected void onResume() 
    super.onResume();
    if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER) ) 
        if (mGoogleApiClient != null) 
            mGoogleApiClient.connect();
        
    else
        // Showyourmesg();  
    


@Override
protected void onPause() 
    super.onPause();
    if (mGoogleApiClient != null) 
        mGoogleApiClient.disconnect();
    


protected void startLocationUpdates()
    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
            mLocationRequest,this);


protected void stopLocationUpdates() 
    LocationServices.FusedLocationApi.removeLocationUpdates(
            mGoogleApiClient, this);

现在,在您的 onLocationChanged 方法中检查 googleApiClient 是否已连接。如果已连接,请断开连接,然后重新连接。

@Override
public void onLocationChanged(Location location) 
    Log.d("SplashAct", "LocatinChngListner, loc: " + location.getLatitude() + "," + location.getLongitude());

    if (mGoogleApiClient != null)
        if (mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting())
            mGoogleApiClient.disconnect();
            mGoogleApiClient.connect();
         else if (!mGoogleApiClient.isConnected())
            mGoogleApiClient.connect();
        

最后,在你的 onConntected() 方法中

@Override
  public void onConnected(Bundle bundle) 

    Log.d("ACTIVITY", "ApiClient: OnConnected");

    mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
            mGoogleApiClient);

    if (mLastLocation == null)
        startLocationUpdates(); // bind interface if your are not getting the lastlocation. or bind as per your requirement.
    

    if (mLastLocation != null)
        while (latitude == 0 || longitude == 0)
            pDialog.setMessage("Getting Location");
            pDialog.show();

            latitude = mLastLocation.getLatitude();
            longitude = mLastLocation.getLongitude();

            if (latitude != 0 && longitude != 0)
                stopLocationUpdates(); // unbind the locationlistner here or wherever you want as per your requirement.
                pDialog.dismiss(); // location data received, dismiss dialog, call your method or perform your task.
            
        
    

我们反复尝试连接到 googleApiClient,即使它已经连接,以便我们可以获取 lastLocation 数据。这将取决于 LocationRequest 间隔。您也可以在 onLocationChanged 中获取位置数据,然后从那里执行您的任务。

【讨论】:

您的 while 循环是否会导致无限循环,或者您是否总是得到一个位置?如果手机没有连接会发生什么?如果没有连接的 GoogleApiClient,如何调用 onLocationChanged()?您似乎想从 onLocationChanged 事件而不是 onConnected 事件调用 getLastLocation()。 @JohnWard 是的,我总是得到位置,而我的 while 循环从未进入无休止模式,尽管是的,有时即使在得到位置后我也无法执行进一步的任务,因为我在两者上都执行了任务onconnected 中 lastLocation == null 和 lastLocation != null 的情况。现在我已将其更改为 if-else 条件,并且效果很好。连通性?连接到互联网或 GPS?完全需要连接到 gps。我猜你不能在不连接 GoogleApiClient 的情况下调用 onLocChange。如果它在 OnConctd 中提供了良好的 loc,则从 OnLocChngd 获取。【参考方案6】:

我正在使用以下代码根据最新的 android 文档获取位置 https://developer.android.com/training/location/retrieve-currenthttps://developer.android.com/training/location/receive-location-updates

MainActivity.java

public class MainActivity extends AppCompatActivity 
private static final int REQUEST_CHECK_SETTINGS = 1;
private static final int REQUEST_GRANT_PERMISSION = 2;
private FusedLocationProviderClient fusedLocationClient;
LocationRequest locationRequest;
private Location currentLocation;
private LocationCallback locationCallback;
Button getUpdates,removeUpdates;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
    createLocationRequest();
    settingsCheck();
    getUpdates = findViewById(R.id.button);
    removeUpdates = findViewById(R.id.button2);

    getUpdates.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ) 
                ActivityCompat.requestPermissions(MainActivity.this,new String[]Manifest.permission.ACCESS_FINE_LOCATION,REQUEST_GRANT_PERMISSION);
                return;
            
            if(locationCallback==null)
                buildLocationCallback();
            if(currentLocation==null)
                fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
        
    );
    removeUpdates.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ) 
                ActivityCompat.requestPermissions(MainActivity.this,new String[]Manifest.permission.ACCESS_FINE_LOCATION,REQUEST_GRANT_PERMISSION);
                return;
            
            if(locationCallback!=null)
                fusedLocationClient.removeLocationUpdates(locationCallback);
        
    );


protected void createLocationRequest() 
    locationRequest = LocationRequest.create();
    locationRequest.setInterval(10000);
    locationRequest.setFastestInterval(5000);
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);


// Check for location settings
public void settingsCheck() 
    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
            .addLocationRequest(locationRequest);

    SettingsClient client = LocationServices.getSettingsClient(this);
    Task<LocationSettingsResponse> task = client.checkLocationSettings(builder.build());
    task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() 
        @Override
        public void onSuccess(LocationSettingsResponse locationSettingsResponse) 
            // All location settings are satisfied. The client can initialize
            // location requests here.
            Log.d("TAG", "onSuccess: settingsCheck");
            getCurrentLocation();
        
    );

    task.addOnFailureListener(this, new OnFailureListener() 
        @Override
        public void onFailure(@NonNull Exception e) 
            if (e instanceof ResolvableApiException) 
                // Location settings are not satisfied, but this can be fixed
                // by showing the user a dialog.
                Log.d("TAG", "onFailure: settingsCheck");
                try 
                    // Show the dialog by calling startResolutionForResult(),
                    // and check the result in onActivityResult().
                    ResolvableApiException resolvable = (ResolvableApiException) e;
                    resolvable.startResolutionForResult(MainActivity.this,
                            REQUEST_CHECK_SETTINGS);
                 catch (IntentSender.SendIntentException sendEx) 
                    // Ignore the error.
                
            
        
    );


public void getCurrentLocation()
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
        ActivityCompat.requestPermissions(this,new String[]Manifest.permission.ACCESS_FINE_LOCATION,1);
        return;
    
    fusedLocationClient.getLastLocation()
            .addOnSuccessListener(this, new OnSuccessListener<Location>() 
                @Override
                public void onSuccess(Location location) 
                    Log.d("TAG", "onSuccess: getLastLocation");
                    // Got last known location. In some rare situations this can be null.
                    if (location != null) 
                        currentLocation=location;
                        Log.d("TAG", "onSuccess:latitude "+location.getLatitude());
                        Log.d("TAG", "onSuccess:longitude "+location.getLongitude());
                    else
                        Log.d("TAG", "location is null");
                        buildLocationCallback();
                    
                
            );


private void buildLocationCallback() 
    locationCallback = new LocationCallback() 
        @Override
        public void onLocationResult(LocationResult locationResult) 
            if (locationResult == null) 
                return;
            
            for (Location location : locationResult.getLocations()) 
                // Update UI with location data
                currentLocation=location;
                Log.d("TAG", "onLocationResult: "+currentLocation.getLatitude());
            
        ;
    ;


//called after user responds to location permission popup
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode==REQUEST_GRANT_PERMISSION)
        getCurrentLocation();
    

//called after user responds to location settings popup
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    super.onActivityResult(requestCode, resultCode, data);
    Log.d("TAG", "onActivityResult: ");
    if(requestCode==REQUEST_CHECK_SETTINGS && resultCode==RESULT_OK)
        getCurrentLocation();
    if(requestCode==REQUEST_CHECK_SETTINGS && resultCode==RESULT_CANCELED)
        Toast.makeText(this, "Please enable Location settings...!!!", Toast.LENGTH_SHORT).show();

XML 文件

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
tools:context="com.example.locationexample.MainActivity">

<Button
    android:id="@+id/button"
    android:layout_
    android:layout_
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="88dp"
    android:text="getLocationUpdates"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.502"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@+id/button2"
    android:layout_
    android:layout_
    android:layout_marginBottom="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:text="RemoveLocationUpdates"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/button"
    app:layout_constraintVertical_bias="0.363" /</android.support.constraint.ConstraintLayout>

清单文件

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

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

【讨论】:

【参考方案7】:

如果您使用的是 Android Marshmallow (API 23) 或更新版本的 Android,您可能会遇到同样的问题,因为未授予权限。您应该明确地 request permission at run time 获取位置信息,或者,如果这是一个测试项目,您可以在手机上 grant location permission from app settings。

【讨论】:

【参考方案8】:

这是确切的答案和解释。

LocationClient getLastLocation() return null

【讨论】:

【参考方案9】:

我的位置为空,我发现地图应用程序没有安装在模拟器上。我安装了它并启用了设备位置,然后问题解决了

【讨论】:

【参考方案10】:

Android - Kotlin

如果您需要 android 中的用户位置,我正在使用这个我的 功能建议

// Get user location after getting permission.
private fun findUserLocation() 

    if (ActivityCompat.checkSelfPermission(requireContext(),
            Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
        && ActivityCompat.checkSelfPermission(requireContext(),
            Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
    ) 
        // TODO: Check location permission. Some one is missing.
    
    else 
        val locationRequest = LocationRequest.create() // Create location request.
        locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY // Set priority.

        val locationCallback: LocationCallback = object : LocationCallback() 
            override fun onLocationResult(locationResult: LocationResult) 
                for (location in locationResult.locations) 
                    if (location != null) 

                        // TODO: Show your code here. 
                        // Such as:
                        val lat = location.latitude  
                        val lon = location.longitude
                    
                
            
        

        // Create a location provider client and send request for getting location.
        val client = LocationServices.getFusedLocationProviderClient(requireContext())
        client.requestLocationUpdates(locationRequest, locationCallback, null)
    

【讨论】:

【参考方案11】:

这与 Amit K. Saha 的回答有关,但对我来说,在我的一台 Lollipop 设备上,我不断收到 null。所以我打开了地图应用程序,然后我得到了下面的屏幕,要求我打开所有爵士乐以改善我的位置。

一旦我这样做了一次,我的设备只需拨打getLastLocation() 即可接收位置;

我认为,对于可能尚未使用其地图应用的用户,我认为必须在应用安装时请求这些权限。

【讨论】:

您应该按照here 的描述从您的应用中触发该对话框。【参考方案12】:

我安装了一个模拟器,它有谷歌播放服务并且可以工作

【讨论】:

【参考方案13】:

要发出单个位置请求,您可以使用

方法1-> getCurrentLocation()

cancellationTokenSource = CancellationTokenSource()

val currentLocationTask = LocationServices
        .getFusedLocationProviderClient(requireContext()).getCurrentLocation(
            LocationRequest.PRIORITY_HIGH_ACCURACY,
            cancellationTokenSource.token
        )

您可以使用,取消请求,

cancellationTokenSource.cancel()

方法2-> setNumUpdates(1)

mLocationRequest
        .setNumUpdates(1)

【讨论】:

目前他们更改了 API,requestLocationUpdates() 无法获取 null 值。

以上是关于FusedLocationApi.getLastLocation 始终为空的主要内容,如果未能解决你的问题,请参考以下文章