播放位置服务getLastLocation返回null

时间:2014-07-29 14:23:49

标签: java android google-play-services

我正在尝试收听位置更改,但有时会调用onLocationChanged回调,而getLastLocation会返回null,而Google地图始终可以正常运行。

注意

如果我重启设备,位置服务只能工作约2天;在此之后,我的应用程序和the SDK example都无法正常运行。

这是我的服务代码:

public class VisitService extends Service implements
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener, 
        LocationListener {

    private final IBinder mBinder = new VisitBinder();


    // A request to connect to Location Services
    private LocationRequest mLocationRequest;

    // Stores the current instantiation of the location client in this object
    private LocationClient mLocationClient;


    private boolean mLocationServiceConnected = false;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        initLocation();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        initLocation();
        return mBinder;
    }

    public void startVisit() {
        if (!servicesConnected()) {
            listener.onVisitStartError();
            return;
        }

        if (mLocationServiceConnected) {
            if (isAcceptableLocation(mLocationClient.getLastLocation())) {
                Toast.makeText(this, "You have arrived!", Toast.LENGTH_LONG);
            } else {
                mVisitListener.onVisitStartError();
            }
        }    
    }

    private boolean isAcceptableLocation(Location location) {
        if (location == null) {
            Toast.makeText(this, "Location is null", Toast.LENGTH_LONG).show();
            return false;
        }

        return true;
    }


    private void initLocation() {

        if (mLocationRequest == null) {
            // Create a new global location parameters object
            mLocationRequest = LocationRequest.create();
            /*
             * Set the update interval
             */
            mLocationRequest
                    .setInterval(LocationUtils.UPDATE_INTERVAL_IN_MILLISECONDS);

            // Use high accuracy
            mLocationRequest
                    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

            // Set the interval ceiling to one minute
            mLocationRequest
                    .setFastestInterval(LocationUtils.FAST_INTERVAL_CEILING_IN_MILLISECONDS);

        }

        if (mLocationClient == null) {
            mLocationClient = new LocationClient(this, this, this);
            mLocationClient.connect();
        }
    }

    /**
     * Verify that Google Play services is available before making a request.
     * 
     * @return true if Google Play services is available, otherwise false
     */
    private boolean servicesConnected() {

        // Check that Google Play services is available
        int resultCode = GooglePlayServicesUtil
                .isGooglePlayServicesAvailable(this);

        // If Google Play services is available
        if (ConnectionResult.SUCCESS == resultCode) {
            // In debug mode, log the status
            Log.d(LocationUtils.APPTAG,
                    getString(R.string.play_services_available));

            return true;
        } else {

            return false;
        }
    }

    public class VisitBinder extends Binder {
        public VisitService getService() {
            return VisitService.this;
        }
    }


    /**
     * In response to a request to start updates, send a request to Location
     * Services
     */
    private void startPeriodicUpdates() {

        mLocationClient.requestLocationUpdates(mLocationRequest, this);
        Toast.makeText(this, "Location Update Requested", Toast.LENGTH_LONG).show();
    }

    /**
     * In response to a request to stop updates, send a request to Location
     * Services
     */
    private void stopPeriodicUpdates() {
        mLocationClient.removeLocationUpdates(this);

    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.d(LocationUtils.APPTAG, "Location Services Connection faild");


    }

    @Override
    public void onConnected(Bundle bundle) {
        Log.d(LocationUtils.APPTAG, "Location Services Connected");
        mLocationServiceConnected = true;
        startPeriodicUpdates();
    }

    @Override
    public void onDisconnected() {
        mLocationServiceConnected = false;
    }

    @Override
    public void onLocationChanged(Location location) {
        Toast.makeText(this, "Location has been changed", Toast.LENGTH_LONG).show();
    }
}

Android清单

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

8 个答案:

答案 0 :(得分:7)

首先是一个错误。我也遇到了这个问题。

您可以尝试一种简单的解决方法。

在尝试获取最后一个位置之前请求位置更新,即在尝试使用getLastLocation()找到最后位置之前请求位置更新;

就像:

LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Criteria crta = new Criteria();
crta.setAccuracy(Criteria.ACCURACY_FINE);
crta.setAltitudeRequired(true);
crta.setBearingRequired(true);
crta.setCostAllowed(true);
crta.setPowerRequirement(Criteria.POWER_LOW); 
String provider = locationManager.getBestProvider(crta, true);
Log.d("","provider : "+provider);
// String provider = LocationManager.GPS_PROVIDER; 
locationManager.requestLocationUpdates(provider, 1000, 0, locationListener);
Location location = locationManager.getLastKnownLocation(provider); 

此实现基于LocationManager。请选择位置客户端。

希望它有所帮助!

<强>更新

在这里你也可以使用onLocationChanged()方法,因为它会在每次更改位置时更新你,而getLastLocation()方法返回可能为NULL的最佳已知位置。

根据doc,GetLastLocation()方法将在极少数情况下返回NULL。但这种情况经常发生在我身上。

最新更新

只有一个关于GPS的更新:

首先,我们需要使用GoogleApiClient代替LocationClient类。

其次,我们需要实施GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener

第三,我们需要覆盖其onConnected()onConnectionSuspended()onConnectionFailed()方法。

休息你很高兴。

干杯!

答案 1 :(得分:1)

您正在使用getLastLocation()用于其他目的之外的目的。当位置感知应用程序第一次启动时使用getLastLocation()。两天后你应该有一个非常准确的解决方案。你为什么不遵循位置感知设计指南。

我说你需要回归START_STICKY。但您已对onStartCommand()onBind()进行了编码。您还需要启动该服务,然后绑定到该服务,START_STICKY在客户端解除绑定后才能工作。

请勿在服务中检查GooglePlay。在启动服务之前,该服务需要在活动中对GooglePlay进行轻量级检查。服务越轻,它就越有可能留在记忆中。在单独的过程中运行Service

答案 2 :(得分:1)

我根据以下代码开发了我的应用程序,如果您需要,我可以使用我的应用程序名称是&#34; https://play.google.com/store/apps/details?id=com.deep.profilemaper&#34; 。

这是我的代码:BackgroundLocationService.java

public class BackgroundLocationService extends Service implements
    GooglePlayServicesClient.ConnectionCallbacks,GooglePlayServicesClient.OnConnectionFailedListener,LocationListener  {

    IBinder mBinder = new LocalBinder();

    private LocationClient mLocationClient;
    private LocationRequest mLocationRequest;
   // Flag that indicates if a request is underway.
   private boolean mInProgress;

   private Boolean servicesAvailable = false;

public class LocalBinder extends Binder {
    public BackgroundLocationService getServerInstance() {
        return BackgroundLocationService.this;
    }
}

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


    mInProgress = false;
    // Create the LocationRequest object
    mLocationRequest = LocationRequest.create();
    // Use high accuracy
    mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
    // Set the update interval to 5 seconds
    mLocationRequest.setInterval(Constants.UPDATE_INTERVAL);
    // Set the fastest update interval to 1 second
    mLocationRequest.setFastestInterval(Constants.FASTEST_INTERVAL);

    servicesAvailable = servicesConnected();

    /*
     * Create a new location client, using the enclosing class to
     * handle callbacks.
     */
    mLocationClient = new LocationClient(this, this, this);


}

private boolean servicesConnected() {

    // Check that Google Play services is available
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) {

        return true;
    } else {

        return false;
    }
}

public int onStartCommand (Intent intent, int flags, int startId)
{
    super.onStartCommand(intent, flags, startId);

    if(!servicesAvailable || mLocationClient.isConnected() || mInProgress)
        return START_STICKY;

    setUpLocationClientIfNeeded();
    if(!mLocationClient.isConnected() || !mLocationClient.isConnecting() && !mInProgress)
    {
        appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Started", Constants.LOG_FILE);
        mInProgress = true;
        mLocationClient.connect();
    }

    return START_STICKY;
}

/*
 * Create a new location client, using the enclosing class to
 * handle callbacks.
 */
private void setUpLocationClientIfNeeded()
{
    if(mLocationClient == null) 
        mLocationClient = new LocationClient(this, this, this);
}

// Define the callback method that receives location updates
@Override
public void onLocationChanged(Location location) {
    // Report to the UI that the location was updated
    String msg = Double.toString(location.getLatitude()) + "," +
            Double.toString(location.getLongitude());
    Log.d("debug", msg);
    // Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    appendLog(msg, Constants.LOCATION_FILE);
}

@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

public String getTime() {
    SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return mDateFormat.format(new Date());
}

public void appendLog(String text, String filename)
{       
   File logFile = new File(filename);
   if (!logFile.exists())
   {
      try
      {
         logFile.createNewFile();
      } 
      catch (IOException e)
      {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }
   try
   {
      //BufferedWriter for performance, true to set append to file flag
      BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true)); 
      buf.append(text);
      buf.newLine();
      buf.close();
   }
   catch (IOException e)
   {
      // TODO Auto-generated catch block
      e.printStackTrace();
   }
}

@Override
public void onDestroy(){
    // Turn off the request flag
    mInProgress = false;
    if(servicesAvailable && mLocationClient != null) {
        mLocationClient.removeLocationUpdates(this);
        // Destroy the current location client
        mLocationClient = null;
    }
    // Display the connection status
    // Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ": Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
    appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Stopped", Constants.LOG_FILE);
    super.onDestroy();  
}

/*
 * 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
 */
@Override
public void onConnected(Bundle bundle) {

    // Request location updates using static settings
    mLocationClient.requestLocationUpdates(mLocationRequest, this);
    appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Connected", Constants.LOG_FILE);

}

/*
 * Called by Location Services if the connection to the
 * location client drops because of an error.
 */
@Override
public void onDisconnected() {
    // Turn off the request flag
    mInProgress = false;
    // Destroy the current location client
    mLocationClient = null;
    // Display the connection status
    // Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ": Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
    appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Disconnected", Constants.LOG_FILE);
}

/*
 * Called by Location Services if the attempt to
 * Location Services fails.
 */
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    mInProgress = false;

    /*
     * 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()) {

    // If no resolution is available, display an error dialog
    } else {

      }
    }

}

<强> *** *** Constants.java

public final class Constants {

// Milliseconds per second
private static final int MILLISECONDS_PER_SECOND = 1000;
// Update frequency in seconds
private static final int UPDATE_INTERVAL_IN_SECONDS = 30;
// Update frequency in milliseconds
public static final long UPDATE_INTERVAL = MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
// The fastest update frequency, in seconds
private static final int FASTEST_INTERVAL_IN_SECONDS = 30;
// A fast frequency ceiling in milliseconds
public static final long FASTEST_INTERVAL = MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
// Stores the lat / long pairs in a text file
public static final String LOCATION_FILE = "sdcard/location.txt";
// Stores the connect / disconnect data in a text file
public static final String LOG_FILE = "sdcard/log.txt";


/**
 * Suppress default constructor for noninstantiability
 */
private Constants() {
    throw new AssertionError();
    }
}

此代码包含一些变量,如当前日期和时间以及LOG_File等常量,仅用于记录目的。所以,我已经排除了该代码。 此代码经过完美测试和运行。试一试。

感谢。

答案 3 :(得分:1)

<强>第一

这是旧Android设备上的一种错误。

显然,位置API在不同的Android版本上有不同的行为(LocationManager在Android&gt; = 4.1上有问题,而Play Services在Android 2.3上有问题),请参阅here

所以我最终得到的是:

尝试通过播放服务检索最后一个位置,如果尝试失败,请尝试使用LocationManager。

<强>第二

我想感谢大家提出的宝贵意见,我得到提示,使用AndroidHacker发布的先前解决方法,所以我认为他应该获得赏金。

答案 4 :(得分:1)

如果Google地图尚未确定您设备的位置,则可能会出现此问题。尝试启动Google地图。如果无法在设备的设置中解决此问题(例如启用WLAN + GPS)。

答案 5 :(得分:0)

the doc开始,很明显有时候getLastLocation()会返回null。因此,根据“我无法接收onLocationChanged回调”的含义,您可以延迟startVisit重要的呼叫(此时立即显示您的吐司),直到您收到此onLocationChanged回调。这样,如果您可以直接访问上一个已知位置,这将是直接访问,否则您将等待onLocationChanged回调

public class VisitService extends Service implements
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener, 
        LocationListener {
    //...
    boolean mIsInitialized=false;
    Location mCurrentLocation;
    boolean mStartVisitsCalled=false;
    //...

    public void startVisit() {
        if (!servicesConnected()) {
            listener.onVisitStartError();
            return;
        }

        if (mLocationServiceConnected) {
        if (((mLocationClient.getLastLocation() != null && isAcceptableLocation(mLocationClient.getLastLocation()))
                || (isInitialized && isAcceptableLocation(mCurrentLocation))) {
                //doNiceStuff();
            } else
                mStartVisitsCalled=true;
                //Wait...
            }
        }    
    }
    //...

    @Override
    public void onLocationChanged(Location location) {
        this.mIsInitialized=true;
        this.mCurrrentLocation = location;
        if(mStartVisitsCalled) {
            //delayed doNiceStuff();
        }

    }
}

答案 6 :(得分:0)

你的服务可能被杀,可能会发生。 尝试将onStartCommand()返回的值更改为

return STICKY_START;

一旦资源可用,这将重新启动服务。

以下是API Reference

中的相关部分

public static final int START_STICKY

在API级别5中添加 从onStartCommand(Intent,int,int)返回的常量:如果此服务的进程在启动时被终止(从onStartCommand(Intent,int,int)返回后),则将其保留在启动状态但不保留此状态交付意图。稍后系统将尝试重新创建服务。因为它处于启动状态,所以它将保证在创建新服务实例后调用onStartCommand(Intent,int,int);如果没有任何挂起的启动命令要传递给服务,它将使用null intent对象调用,因此您必须注意检查这一点。

此模式适用于将在任意时间段内显式启动和停止运行的内容,例如执行背景音乐播放的服务。

常数值:1(0x00000001)

答案 7 :(得分:0)

谷歌地图必须正常工作,因为它必须缓存以前的位置信息或直接使用位置API - 而不是谷歌播放服务位置api ..