Android 融合位置提供者似乎不使用 GPS 接收器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21022297/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:50:39  来源:igfitidea点击:

Fused location provider doesn't seem to use GPS receiver

androidgeolocationgpsgoogle-play-services

提问by cja

Android 4.3 on Moto G, Android 4.4.2 on Nexus 7 2012, Android 4.4.2 on Nexus 5. Android Studio 0.4.

Moto G 上的 Android 4.3、Nexus 7 2012 上的 Android 4.4.2、Nexus 5 上的 Android 4.4.2。Android Studio 0.4。

I don't want to receive regular location updates, I just want an accurate location when the user presses a button.

我不想收到定期的位置更新,我只想在用户按下按钮时获得准确的位置。

I have followed this example: https://developer.android.com/training/location/retrieve-current.html

我遵循了这个例子:https: //developer.android.com/training/location/retrieve-current.html

In manifest file:

在清单文件中:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

I check that Play Services are available using GooglePlayServicesUtil.isGooglePlayServicesAvailable

我使用 GooglePlayServicesUtil.isGooglePlayServicesAvailable 检查 Play 服务是否可用

In main activity:

在主要活动中:

//in activity onCreate method
mLocationClient = new LocationClient(this, this, this);

@Override
protected void onStart() {
    mLocationClient.connect();
    super.onStart();
}

@Override
protected void onStop() {
    mLocationClient.disconnect();
    super.onStop();
}

//in button onclick method    
mCurrentLocation = mLocationClient.getLastLocation();

I have no SIM card. If I enable Wifi then sometimes I get an accurate location. Other times mCurrentLocation is null.

我没有 SIM 卡。如果我启用 Wifi,那么有时我会得到准确的位置。其他时候 mCurrentLocation 为空。

If I disable Wifi then mCurrentLocation is always null.

如果我禁用 Wifi,则 mCurrentLocation 始终为空。

I am testing outside in several locations always with a clear view of the sky. I waited three minutes in each location.

我在外面的几个地方进行测试,总是能看到清晰的天空。我在每个位置都等了三分钟。

I never see the GPS icon appear on the Android notification bar at the top of the screen.

我从未在屏幕顶部的 Android 通知栏中看到 GPS 图标。

I have these location settings: enter image description here

我有这些位置设置: 在此处输入图片说明

A GPS Test app manages to use GPS successfully indoors on the same device with Wi-Fi disabled so GPS is working: enter image description here

GPS 测试应用程序设法在禁用 Wi-Fi 的同一设备上成功地在室内使用 GPS,因此 GPS 工作正常: 在此处输入图片说明

Registering for location updates, as at https://developer.android.com/training/location/receive-location-updates.html, doesn't work either. Registered method never called.

注册位置更新,如https://developer.android.com/training/location/receive-location-updates.html,也不起作用。从未调用过的注册方法。

What am I doing wrong?

我究竟做错了什么?

采纳答案by cja

I solved it. The problem was that "Let Google apps access your location" was turned off: enter image description here

我解决了。问题是“让 Google 应用访问您的位置”已关闭: 在此处输入图片说明

When I turn it on I get GPS readings and when it's off I don't.

当我打开它时,我会得到 GPS 读数,而当它关闭时,我不会。

I had left it off for two reasons:

出于两个原因,我放弃了它:

  1. I'm developing an app to be used to lots of devices at a company and I want minimum manual configuration to be necessary

  2. The screen says clearly "This setting affects Google apps only." I know that Play Services is Google software but I didn't think Google would expect an end user to understand that.

  1. 我正在开发一个应用程序以用于公司的许多设备,我希望需要最少的手动配置

  2. 屏幕上清楚地显示“此设置仅影响 Google 应用”。我知道 Play Services 是 Google 的软件,但我不认为 Google 会期望最终用户理解这一点。

Then I got the Android 4.4.2 update and the location settings page has changed. It appears that I can have Google Location Reporting turned off and still get GPS readings from the fused location provider: enter image description here

然后我得到了 Android 4.4.2 更新,位置设置页面也发生了变化。看来我可以关闭 Google 位置报告,但仍然可以从融合的位置提供程序获取 GPS 读数: 在此处输入图片说明

So maybe Google realised that the setting was confusing and improved it. Either way, I'd have saved a lot of time if I'd got 4.4.2 a few days ago.

因此,也许 Google 意识到该设置令人困惑并对其进行了改进。不管怎样,如果几天前我得到 4.4.2,我会节省很多时间。

回答by Saran

The problem is with getLastLocation()because it uses a cached location. I had the same problem as I also tried to use this simple approach. Since, I have switched to listening to updates (and stopping after 1st successfull update automatically).

问题getLastLocation()在于它使用缓存位置。我遇到了同样的问题,因为我也尝试使用这种简单的方法。从那以后,我已切换到收听更新(并在第一次成功更新后自动停止)。

This is my code that works.

这是我的工作代码。

First, the check for availability in Application (not essential, can be in Activity and without keeping of result):

首先,检查 Application 中的可用性(不是必需的,可以在 Activity 中并且不保留结果):

public class MainApp extends Application {
  public static enum PlayServices {
    NOT_CHECKED, AVAILABLE, UNAVAILABLE
  };
  public static PlayServices mPlayServices = PlayServices.NOT_CHECKED;

  @Override
  public void onCreate() {
    super.onCreate();

    if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) {
      MainApp.mPlayServices = MainApp.PlayServices.AVAILABLE;
    }
  }
}

Then, on to the Activity:

然后,进入活动:

public class MainActivity extends SherlockFragmentActivity implements
  GooglePlayServicesClient.ConnectionCallbacks,
  GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {

In its onCreate():

在其onCreate()

if (MainApp.mPlayServices != MainApp.PlayServices.UNAVAILABLE) {
  mLocationClient = new LocationClient(this, this, this);

  mLocationRequest = LocationRequest.create();
  mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
  mLocationRequest.setInterval(5000);
  mLocationRequest.setNumUpdates(1);
  mLocationRequest.setFastestInterval(1000);

  mUpdatesRequested = false;
  MainApp.prefs.edit().putBoolean(MainApp.KEY_LOCATION_UPDATES_REQUESTED, mUpdatesRequested)
      .commit();
}

The rest of the MainActivityclass:

其余MainActivity班级:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  Log.d(this.getClass().getSimpleName(), "onActivityResult(" + requestCode + ", " + resultCode
      + ")");
  // Decide what to do based on the original request code
  switch (requestCode) {
    case MainApp.PLAY_CONNECTION_FAILURE_RESOLUTION_REQUEST:
      /*
       * If the result code is Activity.RESULT_OK, try
       * to connect again
       */
      switch (resultCode) {
        case Activity.RESULT_OK:
          // here we want to initiate location requests!
          mLocationClient = new LocationClient(this, this, this);

          break;
      }
      break;
  }
}

@Override
public void onConnected(Bundle dataBundle) {
  Log.d(this.getClass().getSimpleName(), "onConnected()");

  Log.d(this.getClass().getSimpleName(), "Google Play Services are available.");
  MainApp.mPlayServices = MainApp.PlayServices.AVAILABLE;

  if (!mUpdatesRequested) {

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

    boolean gps_enabled = false;
    try {
      gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    } catch (Exception ex) {
    }

    boolean network_enabled = false;
    try {
      network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    } catch (Exception ex) {
    }

    // don't start listeners if no provider is enabled
    MainApp.locEnabled = gps_enabled || network_enabled;

    if (!MainApp.locEnabled) {
      // we have access to PlayServices, but user has disabled location visibility --> alert him
      alertLocationOff();
    } else {
      mLocationClient.requestLocationUpdates(mLocationRequest, this);
      mUpdatesRequested = true;
    }
  }
}

@Override
public void onDisconnected() {
  Log.d(this.getClass().getSimpleName(), "onDisconnected()");
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
  Log.d(this.getClass().getSimpleName(), "onConnectionFailed()");

  Log.d(this.getClass().getSimpleName(), "Google Play Services not available.");
  MainApp.mPlayServices = MainApp.PlayServices.UNAVAILABLE;

  /*
   * Google Play services can resolve some errors it detects.
   * If the error has a resolution, try sending an Intent to
   * start a Google Play services activity that can resolve
   * error.
   */
  if (connectionResult.hasResolution()) {
    try {
      // Start an Activity that tries to resolve the error
      connectionResult.startResolutionForResult(this,
          MainApp.PLAY_CONNECTION_FAILURE_RESOLUTION_REQUEST);
      /*
       * Thrown if Google Play services canceled the original
       * PendingIntent
       */
    } catch (IntentSender.SendIntentException e) {
      // Log the error
      e.printStackTrace();
    }
  } else {
    /*
     * If no resolution is available, display a dialog to the
     * user with the error.
     */
    GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();
  }
}

@SuppressLint("NewApi")
@Override
public void onLocationChanged(Location location) {
  Log.d(this.getClass().getSimpleName(), "onLocationChanged(), location=" + location);

  if (location != null) {
    boolean present = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
      present = Geocoder.isPresent();
    }

    if (present) {
      (new ExtractLocationTask(this)).execute(location);
    } else {
      Log.e(this.getClass().getSimpleName(), "Geocoder not present");
      MainApp.mPlayServices = MainApp.PlayServices.UNAVAILABLE;
    }
  }
}


private class ExtractLocationTask extends AsyncTask<Location, Void, Boolean> {
  Context mContext;

  public ExtractLocationTask(Context context) {
    super();
    mContext = context;
  }

  @Override
  protected Boolean doInBackground(Location... params) {
    Log.d(getClass().getSimpleName(), "ExtractLocationTask.onPreExecute()");

    boolean found = false;
    try {
      Geocoder geoCoder_local = new Geocoder(mContext, Locale.getDefault());
      Geocoder geoCoder_en = new Geocoder(mContext, Locale.ENGLISH);

      List<Address> addresses_local = geoCoder_local.getFromLocation(params[0].getLatitude(),
          params[0].getLongitude(), 10);
      List<Address> addresses_en = geoCoder_en.getFromLocation(params[0].getLatitude(),
          params[0].getLongitude(), 10);

      if (addresses_local != null && addresses_local.size() > 0) {

        // do what you want with location info here

        // based on mLocationRequest.setNumUpdates(1), no need to call
        // removeLocationUpdates()

        MainApp.locEnabled = true;

        mUpdatesRequested = false;
        MainApp.prefs.edit()
            .putBoolean(MainApp.KEY_LOCATION_UPDATES_REQUESTED, mUpdatesRequested).commit();

        found = true;
      }
    } catch (IOException e) {
      Log.e(this.getClass().getSimpleName(), "Exception: ", e);
    }

    return found;
  }

  @Override
  protected void onPostExecute(Boolean found) {
    Log.d(getClass().getSimpleName(), "ExtractLocationTask.onPostExecute()");

    if (found) {
      // update UI etc.
    } else if (!mUpdatesReRequested) {
      mLocationClient.requestLocationUpdates(mLocationRequest, (LocationListener) mContext);
      mUpdatesRequested = true;
      mUpdatesReRequested = true;
    }
  }
}

I hope this will help you get it to work!

我希望这会帮助你让它工作!

回答by Andrew

Location provider won't wake up GPS until some of clients ask (subscribe) for high precision location (as described in examples given by other users). GPS test app doesn't use location provider but uses old "direct" way of obtaining location.

位置提供者不会唤醒 GPS,直到某些客户端请求(订阅)高精度位置(如其他用户提供的示例中所述)。GPS 测试应用程序不使用位置提供程序,而是使用旧的“直接”获取位置的方式。

Also there is expiry mechanism, which removes information about last location after some time if it is believed to be stale.

还有一个过期机制,如果它被认为是陈旧的,它会在一段时间后删除有关最后一个位置的信息。

Summing up all above it is really possible that LP(Location Provider) has nothing to give you.

综上所述,LP(Location Provider)真的有可能没有什么可以给你的。

回答by vinay Maneti

I think your are testing your application in Indoor , it doesn't works..

我认为您正在 Indoor 中测试您的应用程序,它不起作用..

and code flow..

和代码流..

public void setUpLocationClientIfNeeded() {
        createLocReq();
        if (mLocationClient == null) {
            mLocationClient = new LocationClient(getApplicationContext(), this, // ConnectionCallbacks
                    this); // OnConnectionFailedListener
        }
    }

    private void createLocReq() {
        if (mLocationRequest == null) {
            // Create a new global location parameters object
            mLocationRequest = LocationRequest.create();
            // Set the update interval
            mLocationRequest.setInterval(LocationServices.UPDATE_INTERVAL_IN_MILLISECONDS);
            // Use high accuracy
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            // Set the interval ceiling to one minute
            mLocationRequest.setFastestInterval(LocationServices.FAST_INTERVAL_CEILING_IN_MILLISECONDS);

            mLocationClient = new LocationClient(getApplicationContext(), this, this);
            mLocationClient.connect();
        }
    }

    public void updateLocation(Location location) {
        if (lastTrackedLat == 0.0) {
            lastTrackedLat = location.getLatitude();
        }
        if (lastTrackedLng == 0.0) {
            lastTrackedLng = location.getLongitude();
        }

        currentLat = location.getLatitude();
        currentLng = location.getLongitude();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location != null) {
            this.location = location;
            updateLocation(location);
        }
    }

    public Location getLocation() {
        // mLocationClient.getLastLocation();
        return location;
    }

回答by tyczj

According to the docs

根据文档

This method provides a simplified way to get location. It is particularly well suited for applications that do not require an accurate locationand that do not want to maintain extra logic for location updates.

此方法提供了一种获取位置的简化方法。它特别适合不需要准确位置且不想为位置更新维护额外逻辑的应用程序。

so it may or may not return a highly accurate location.

所以它可能会也可能不会返回一个高度准确的位置。

GPS can take a while to lock on so calling getLastLocationmay or may not return a location

GPS 可能需要一段时间才能锁定,因此呼叫getLastLocation可能会也可能不会返回位置

you are better off requesting location updates then after you get a location just stop requesting location updates.

您最好请求位置更新,然后在获得位置后停止请求位置更新。

Also looking at you code you provided are you waiting for the LocationClientto connect before trying to get a location? That will certainly give you a null location since it is not connected to get the location yet.

同时查看您提供的代码是否LocationClient在尝试获取位置之前等待连接?这肯定会给您一个空位置,因为它尚未连接以获取位置。

what you should be doing is in your onConnectedget the last location there, example

您应该做的是onConnected获取那里的最后一个位置,例如

public void onConnected(Bundle connectionHint) {
    Location location = mLocationClient.getLastLocation();
}

as it says in that example onConnected is Called by Location Services when the request to connect the client finishes successfully. At this point, you can request the current location or start periodic updates

正如它在那个例子中所说的 onConnected 是 Called by Location Services when the request to connect the client finishes successfully. At this point, you can request the current location or start periodic updates

回答by M. le Rutte

Without a sim card the coarse location providerhas no way to know the coarse position, unless it is able to find a WiFi network that has been mapped by Google.

没有 SIM 卡coarse location provider就无法知道粗略位置,除非它能够找到已被 Google 映射的 WiFi 网络。

Requesting the last known location may result in a outdated location, and as such is rather useless. I guess this is the position that was recorded the last time some app requested a location update.

请求最后一个已知位置可能会导致位置过时,因此相当无用。我猜这是上次某个应用请求位置更新时记录的位置。

I use the following code to get a recent location:

我使用以下代码获取最近的位置:

    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_COARSE);
    criteria.setAltitudeRequired(false);
    criteria.setSpeedRequired(false);
    criteria.setBearingRequired(false);
    criteria.setCostAllowed(false);

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

    ....
    LocationListener listener = new LocationListener() {
        @Override
        public void onLocationChanged(Location lastKnownLocation) {
                     ....
        }
        // rest of interface
     }


     manager.requestSingleUpdate(criteria, listener, null);

The last call ensures that we request the currentlocation, not the location it was able to find an unknown amount of time before.

最后一次调用确保我们请求当前位置,而不是它在未知时间之前能够找到的位置。

You might try to change it to Criteria.ACCURACY_FINEin order to get the GPS fired up. Be aware that if the GPS didn't have a fix for quite some while it may take more than several minutes before it is actually capable of getting a fix. I'd expect in the mean time that you'd see the GPS icon indicating it is waiting for a fix.

您可能会尝试将其更改Criteria.ACCURACY_FINE为以启动 GPS。请注意,如果 GPS 在很长一段时间内都没有修复,则可能需要几分钟以上的时间才能真正获得修复。我希望同时您会看到 GPS 图标,表明它正在等待修复。

回答by Sujal Mandal

All you need to do is to add a priority property to the request object like this.

您需要做的就是像这样向请求对象添加一个优先级属性。

public void onConnected(Bundle arg0)
{
    locationrequest = LocationRequest.create();
    locationrequest.setInterval(1000);
    locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationclient.requestLocationUpdates(locationrequest, this);
}

Maybe use a boolean variable to let the user have options to force GPS like

也许使用布尔变量让用户可以选择强制使用 GPS,例如

boolean forceGPS=false;
.
.
.//make the user choose to change the value of the boolean
.
.
public void onConnected(Bundle arg0)
    {
        locationrequest = LocationRequest.create();
        locationrequest.setInterval(1000);
        if(forceGPS==true)
        {
        locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        }
        locationclient.requestLocationUpdates(locationrequest, this);
    }

回答by dan_flo10

What's in the OnConnectedmethod?

什么是在OnConnected方法?

In this method you should create the LocationRequestobject with PRIORITY_HIGH_ACCURACYpriority.

在此方法中,您应该优先创建LocationRequest对象PRIORITY_HIGH_ACCURACY

@Override
public void onConnected(Bundle dataBundle) {
    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationClient.requestLocationUpdates(mLocationRequest, fusedListener);
}

回答by Sean Barbeau

There are two things going on here that are causing you to sometimes get null, and sometimes get a location.

这里发生的两件事导致您有时获取null,有时获取位置。

First, you're creating a new instance of the LocationClientin the onClickmethod, which is not the same instance you're calling connect()on in onStart(). This will create unpredictable behavior where sometimes the client will have enough time to connect before returning a result from LocationClient.getLastLocation(), and sometimes it won't.

首先,你要创建的新实例LocationClientonClick方法,这是不是你叫同一个实例connect()onStart()。这将产生不可预测的行为,有时客户端在从 返回结果之前有足够的时间进行连接LocationClient.getLastLocation(),有时则不会。

Second, you should guard the call to LocationClient.getLastLocation()with a call to LocationClient.isConnected(). It says the following in the LocationClient.isConnected()docs: https://developer.android.com/reference/com/google/android/gms/location/LocationClient.html#isConnected()

其次,您应该通过调用 to 来保护LocationClient.getLastLocation()调用LocationClient.isConnected()。它在LocationClient.isConnected()文档中说以下内容:https: //developer.android.com/reference/com/google/android/gms/location/LocationClient.html#isConnected()

Checks if the client is currently connected to the service, so that requests to other methods will succeed. Applications should guard client actions caused by the user with a call to this method.

检查客户端当前是否连接到服务,以便对其他方法的请求将成功。应用程序应保护由用户调用此方法引起的客户端操作。

Since the user is triggering the onClick()code by tapping on the button, you should call this method before trying to get the last location.

由于用户是onClick()通过点击按钮来触发代码的,因此您应该在尝试获取最后一个位置之前调用此方法。

So, your code should look like this:

所以,你的代码应该是这样的:

LocationClient mLocationClient;
Location mCurrentLocation;

@Override
protected void onCreate() {
    ...
    mLocationClient = new LocationClient(this, this, this);
    ...
}

@Override
protected void onStart() {
    mLocationClient.connect();
    super.onStart();
}

@Override
protected void onStop() {
    mLocationClient.disconnect();
    super.onStop();
}

public void onClick(View v) {
    ...
    if (mLocationClient.isConnected()) {
        // You should get a valid location here
        mCurrentLocation = mLocationClient.getLastLocation();
    }
}

This should give the LocationClienta long enough time to connect and give you a valid location, which should hopefully include GPS data.

这应该LocationClient为连接提供足够长的时间并为您提供有效位置,其中应该包括 GPS 数据。

回答by Eric Woodruff

Since no one has shared this link, I found this to be the most helpful documentation http://developer.android.com/guide/topics/location/strategies.html

由于没有人分享此链接,我发现这是最有用的文档http://developer.android.com/guide/topics/location/strategies.html

"You might expect that the most recent location fix is the most accurate. However, because the accuracy of a location fix varies, the most recent fix is not always the best. You should include logic for choosing location fixes based on several criteria. The criteria also varies depending on the use-cases of the application and field testing."

“您可能期望最近的位置修复是最准确的。但是,由于位置修复的准确性各不相同,因此最近的修复并不总是最好的。您应该包括根据多个标准选择位置修复的逻辑。标准也因应用程序和现场测试的用例而异。”

Your best bet is to keep a location listener going as long as the activity is in the foreground and select the most accurate cached location when the button is pressed. You may need to show a spinner or something and disable the button while it waits for an accurate measurement to show up.

最好的办法是,只要 Activity 处于前台,就让位置侦听器一直运行,并在按下按钮时选择最准确的缓存位置。您可能需要显示微调器或其他东西,并在等待准确测量显示时禁用该按钮。