使用 google play 服务和 FusedLocationProviderClient 进行位置更新

Posted

技术标签:

【中文标题】使用 google play 服务和 FusedLocationProviderClient 进行位置更新【英文标题】:Location Updates using google play services and FusedLocationProviderClient 【发布时间】:2018-02-08 22:57:56 【问题描述】:

我想使用最新的融合位置提供程序客户端在“后台服务”上获取位置更新。我不想使用所有正在使用的位置侦听器和 Google API 客户端。 我还需要使用 google play services 提供的位置设置 Api 来检查该“后台服务”上的位置设置是否被禁用或启用。请帮助。

【问题讨论】:

到目前为止您尝试了哪些方法,您遇到了哪些问题? 请同时提及您为解决此问题所做的研究 我已经使用位置监听器 -LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, locationListener) 获得了位置更新;但我想按照 google -mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, callback) 的建议使用它 但为此我需要使用 SettingsApi 检查位置设置,其结果在“onActivityResult”上提供。 这里的实际问题是什么?你试过什么?如果您将应用程序定位到 marshmellow+ ,则需要显示权限屏幕,别无他法,这是 android 的权限策略 【参考方案1】:

如果我们需要使用融合位置api,那么我们需要使用Google API客户端,对于后台服务使用它不是问题,以下是我用于在后台获取位置更新的示例,但还有一个Google 从 6.0 开始引入“打瞌睡”模式,所以当您的设备处于“打瞌睡”模式时,我无法保证提供更新,

import android.Manifest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

/**
 * Created By Dhaval Solanki
 */

public class ConnectionService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener 
    String TAG = "ConnectionService";


    private GoogleApiClient mGoogleApiClient;
    private LocationRequest mLocationRequest;
    private Location previousLocation;
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 30000;
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
            UPDATE_INTERVAL_IN_MILLISECONDS / 2;


    private Location mLastLocation;
    private Context context;

    @Override
    public IBinder onBind(Intent intent) 
        Log.i(TAG, "onBind");
        return null;
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        context = getApplicationContext();
        Log.i(TAG, "onStartCommand");
        if (checkPermsion(context)) 
            setupLocationService(context);
        
        return Service.START_STICKY;
    

    @Override
    public void onCreate() 
        super.onCreate();
        ApplicationCLass.isServiceRunning = true;
    

    @Override
    public boolean onUnbind(Intent intent) 
        Log.i(TAG, "onUnbind");
        ApplicationCLass.isServiceRunning = false;
        return super.onUnbind(intent);
    

    @Override
    public void onDestroy() 
        ApplicationCLass.isServiceRunning = false;
        super.onDestroy();
    

    private void setupLocationService(Context context) 
        if (checkPlayServices()) 
            mGoogleApiClient = new GoogleApiClient.Builder(context)
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();
            createLocationRequest();
        
    

    protected void createLocationRequest() 
        mLocationRequest = new LocationRequest().create();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        mGoogleApiClient.connect();
    

    public boolean checkPermsion(Context context) 
        int MyVersion = Build.VERSION.SDK_INT;
        if (MyVersion > Build.VERSION_CODES.LOLLIPOP_MR1) 
            if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
                return false;
             else if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
                return false;
             else 
                return true;
            
         else 
            return true;
        
    

    private boolean checkPlayServices() 
        GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
        int result = googleAPI.isGooglePlayServicesAvailable(this);
        if (result != ConnectionResult.SUCCESS) 
            return false;
        
        return true;
    


    private void startLocationUpdates() 
        if (mGoogleApiClient.isConnected()) 
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
                return;
            
            if (Prefs.getUserLat(context).trim().length() > 0 && Prefs.getUserLong(context).trim().length() > 0) 
             else 
                mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
                        mGoogleApiClient);
                if (mLastLocation != null) 
                    Prefs.setUserLat(context, String.valueOf(mLastLocation.getLatitude()));
                    Prefs.setUserLong(context, String.valueOf(mLastLocation.getLongitude()));
                
            
            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);
        
    

    @Override
    public void onConnected(@Nullable Bundle bundle) 
        Log.i(TAG, "Connected to onConnected");
        startLocationUpdates();
    

    @Override
    public void onConnectionSuspended(int i) 
        Log.i(TAG, "Connected to onConnectionSuspended");
    

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) 
        Log.i(TAG, "Connected to onConnectionFailed");
    

    @Override
    public void onLocationChanged(Location location) 
        try 
            Logger.print("onLocationChanged", "latitued :" + location.getLatitude() + " ,, Longitude " + location.getLongitude());
         catch (Exception e) 
            e.printStackTrace();
        

    



另一个警报服务是否继续运行

import android.app.ActivityManager;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class AlarmService extends IntentService 
    @Override
    protected void onHandleIntent(Intent intent) 
        boolean isServiceRunning = false;
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) 
            if (ConnectionService.class.getName().equals(service.service.getClassName())) 
                isServiceRunning = true;
                break;
            
        
        Log.i("AlarmService", "Service is running " + isServiceRunning);
        if(!isServiceRunning) 
            startService(new Intent(getApplicationContext(),ConnectionService.class));
         else 
            ConnectionService.checkConnectionAndConnect(getApplicationContext());
        
    

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     */
    public AlarmService() 
        super("AlaramService");
    

最后启动服务

private void checkAndStartService() 
        final ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        new AsyncTask<Void, Void, Boolean>() 
            boolean isServiceRunning = false;

            @Override
            protected Boolean doInBackground(Void... params) 
                for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) 
                    if (ConnectionService.class.getName().equals(service.service.getClassName())) 
                        isServiceRunning = true;
                        break;
                    
                
                return isServiceRunning;
            

            @Override
            protected void onPostExecute(Boolean aBoolean) 
                super.onPostExecute(aBoolean);
                Log.i("onPostExecute", "Service running = " + aBoolean);
                if (!aBoolean) 
                    startService(new Intent(ActivitySplash.this, ConnectionService.class));
                    Intent i = new Intent(ActivitySplash.this, AlarmService.class);
                    PendingIntent pi = PendingIntent.getService(ActivitySplash.this, 0, i, 0);
                    AlarmManager am = (AlarmManager) ActivitySplash.this.getSystemService(Context.ALARM_SERVICE);
                    am.cancel(pi); // cancel any existing alarms
                    am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 60000, pi);
                
            
        .execute();
    

【讨论】:

只有一个问题,如果用户在您的服务运行时关闭 gps,那么您如何获取位置更新@Dhaval Solanki 在切换后不可能我说的是待机模式意味着锁定手机而已。 如果你关闭并启动开关,那么你需要重新启动服务,你可以使用启动完成接收器来实现。 然后我们如何向用户请求位置设置并从服务中获取该位置设置的回调。 答案中引用的“Prefs”类是什么?【参考方案2】:

您可以为 intentService 编写代码,如下所示。考虑到因为它是在后台运行的,所以我们不会显示权限对话框。

public class GetLocationService extends IntentService
    implements
    GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener, LocationListener 
public static String TAG = GetLocationService.class.getSimpleName();
private GoogleApiClient mGoogleApiClient;


public GetLocationService() 
    super(TAG);


/**
 * Creates an IntentService.  Invoked by your subclass's constructor.
 *
 * @param name Used to name the worker thread, important only for debugging.
 */
public GetLocationService(String name) 
    super(name);


@Override
protected void onHandleIntent(Intent intent) 
    if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) 
        Log.d(TAG, "disconnecting");
        mGoogleApiClient.disconnect();
    
    Log.d(TAG, "onHandleIntent");
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();
    mGoogleApiClient.connect();


@Override
public void onConnected(@Nullable Bundle bundle) 
    LocationRequest locationRequest = LocationRequest.create();
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    LocationRequest locationRequestLow = LocationRequest.create();
    locationRequestLow.setPriority(LocationRequest.PRIORITY_LOW_POWER);
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==
            PackageManager.PERMISSION_GRANTED) 
        // TODO: Consider calling
        //    ActivityCompat#requestPermissions
        // here to request the missing permissions, and then overriding
        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
        //                                          int[] grantResults)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, this);
     else 
        Log.d(TAG, "permission denied");
    


@Override
public void onConnectionSuspended(int i) 



@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) 



@Override
public void onLocationChanged(Location location) 
    Log.d(TAG, location.toString());    

【讨论】:

【参考方案3】:

你可以像这样扩展服务类

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

你可以在这里查看完整的源代码BackgroundLocationService.java

用于启用位置设置

   private void displayLocationSettingsRequest() 
    GoogleApiClient googleApiClient = new GoogleApiClient.Builder(context)
            .addApi(LocationServices.API).build();
    googleApiClient.connect();

    LocationRequest locationRequest = LocationRequest.create();
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationRequest.setInterval(10000);
    locationRequest.setFastestInterval(10000 / 2);

    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
    builder.setAlwaysShow(true);

    PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build());
    result.setResultCallback(new ResultCallback<LocationSettingsResult>() 
        @Override
        public void onResult(@NonNull LocationSettingsResult result) 
            final Status status = result.getStatus();
            switch (status.getStatusCode()) 
                case LocationSettingsStatusCodes.SUCCESS:
                    break;
                case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:

                    try 
                        // Show the dialog by calling startResolutionForResult(), and check the result
                        // in onActivityResult().
                        status.startResolutionForResult(context, REQUEST_CHECK_SETTINGS);
                     catch (IntentSender.SendIntentException ignored) 
                    
                    break;
                case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                    break;
            
        
    );


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    switch (requestCode) 
   // Check for the integer request code originally supplied to 
    startResolutionForResult().
        case REQUEST_CHECK_SETTINGS:
            switch (resultCode) 
                case Activity.RESULT_OK:
                    //startLocationUpdates();
                    break;
                case Activity.RESULT_CANCELED:
                    displayLocationSettingsRequest();//keep asking
                    showToast("Location permission needed");
                    break;
            
            break;
    

您可以从服务中检查位置设置,如果位置已关闭,

LocationManager lm = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
  boolean gps_enabled = false;
      boolean network_enabled = false;

   try 
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
  catch(Exception ex) 

   try 
network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
   catch(Exception ex) 

   if(!gps_enabled && !network_enabled) 
// notify user
 // Either you can display a Notification(Recommended way) or you can show dialog with 

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
//It needs SYSTEM_ALERT_WINDOW permission. Add this permission in Manifest.

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

 

【讨论】:

您正在检查活动的设置,我的问题是当没有前台活动时如何检查服务的设置。 从哪里获得该位置设置窗口的结果,以便在位置设置打开时请求位置更新@Ameer【参考方案4】:

您好,我非常了解位置服务以及我们如何以完美的方式使用它。看看我会给你一些关于融合 api 的想法。

请务必按照以下步骤操作。这是非常好的和简单的。 第 1 步。制作此类 GoogleLocationService.java

public class GoogleLocationService 
private GoogleServicesCallbacks callbacks = new GoogleServicesCallbacks();
LocationUpdateListener locationUpdateListener;
Context activity;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;

public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 30000;


public GoogleLocationService(Context activity, LocationUpdateListener locationUpdateListener) 
    this.locationUpdateListener = locationUpdateListener;
    this.activity = activity;
    buildGoogleApiClient();


protected synchronized void buildGoogleApiClient() 
    //Log.i(TAG, "Building GoogleApiClient");
    mGoogleApiClient = new GoogleApiClient.Builder(activity)
            .addConnectionCallbacks(callbacks)
            .addOnConnectionFailedListener(callbacks)
            .addApi(LocationServices.API)
            .build();
    createLocationRequest();
    mGoogleApiClient.connect();


protected void createLocationRequest() 
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);



private class GoogleServicesCallbacks implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener 

    @Override
    public void onConnected(Bundle bundle) 
        startLocationUpdates();
    

    @Override
    public void onConnectionSuspended(int i) 
        mGoogleApiClient.connect();
    

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) 

        if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) 
            Toast.makeText(activity, "Google play service not updated", Toast.LENGTH_LONG).show();

        
        locationUpdateListener.cannotReceiveLocationUpdates();
    

    @Override
    public void onLocationChanged(Location location) 
        if (location.hasAccuracy()) 
            if (location.getAccuracy() < 30) 
                locationUpdateListener.updateLocation(location);
            
        
    


private static boolean locationEnabled(Context context) 
    boolean gps_enabled = false;
    LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    try 
        gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
     catch (Exception ex) 
        ex.printStackTrace();
    
    return gps_enabled;


private boolean servicesConnected(Context context) 
    return isPackageInstalled(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, context);


private boolean isPackageInstalled(String packagename, Context context) 
    PackageManager pm = context.getPackageManager();
    try 
        pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
        return true;
     catch (PackageManager.NameNotFoundException e) 
        e.printStackTrace();
        return false;
    



public void startUpdates() 
    /*
     * Connect the client. Don't re-start any requests here; instead, wait
     * for onResume()
     */
    if (servicesConnected(activity)) 
        if (locationEnabled(activity)) 
            locationUpdateListener.canReceiveLocationUpdates();
            startLocationUpdates();
         else 
            locationUpdateListener.cannotReceiveLocationUpdates();
            Toast.makeText(activity, "Unable to get your location.Please turn on your device Gps", Toast.LENGTH_LONG).show();
        
     else 
        locationUpdateListener.cannotReceiveLocationUpdates();
        Toast.makeText(activity, "Google play service not available", Toast.LENGTH_LONG).show();
    


//stop location updates
public void stopUpdates() 
    stopLocationUpdates();


//start location updates
private void startLocationUpdates() 

    if (checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(activity, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
        return;
    
    if (mGoogleApiClient.isConnected()) 
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, callbacks);
    


public void stopLocationUpdates() 
    if (mGoogleApiClient.isConnected()) 
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, callbacks);
    


public void startGoogleApi() 
    mGoogleApiClient.connect();


public void closeGoogleApi() 
    mGoogleApiClient.disconnect();


 

第二步。制作这个界面 LocationUpdateListener.java

 public interface LocationUpdateListener 

/**
 * Called immediately the service starts if the service can obtain location
 */
void canReceiveLocationUpdates();

/**
 * Called immediately the service tries to start if it cannot obtain location - eg the user has disabled wireless and
 */
void cannotReceiveLocationUpdates();

/**
 * Called whenever the location has changed (at least non-trivially)
 * @param location
 */
void updateLocation(Location location);

/**
 * Called when GoogleLocationServices detects that the device has moved to a new location.
 * @param localityName The name of the locality (somewhere below street but above area).
 */
void updateLocationName(String localityName, Location location);

第 3 步。创建此位置服务类 LocationService.java

public class LocationService extends Service 

private GoogleLocationService googleLocationService;

@Override
public void onCreate() 
    super.onCreate();
    //start the handler for getting locations
    //create component

    updateLocation(getApplicationContext());


@Override
public int onStartCommand(Intent intent, int flags, int startId) 
    return Service.START_STICKY;


//get current location os user
private void updateLocation(Context context) 
    googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() 
        @Override
        public void canReceiveLocationUpdates() 
        

        @Override
        public void cannotReceiveLocationUpdates() 
        

        //update location to our servers for tracking purpose
        @Override
        public void updateLocation(Location location) 
            if (location != null ) 
                Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());

            
        

        @Override
        public void updateLocationName(String localityName, Location location) 

            googleLocationService.stopLocationUpdates();
        
    );
    googleLocationService.startUpdates();



IBinder mBinder = new LocalBinder();


public class LocalBinder extends Binder 
    public LocationService getServerInstance() 
        return LocationService.this;
    


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


//stop location updates on stopping the service
@Override
public void onDestroy() 
    super.onDestroy();
    if (googleLocationService != null) 
        googleLocationService.stopLocationUpdates();
    


修改后的答案:

如何使用服务,

在你的主要活动中启动服务,像这样

startService(new Intent(context, LocationService.class));

停止,

stopService(new Intent(context, LocationService.class));

并像这样在清单中声明,

<service
 android:name=".service.location.LocationService"
 android:enabled="true"></service>

注意:我这样做是因为我在杀死应用程序后需要位置。如果您不需要服务。然后,您可以在需要更新位置的类中直接调用下面的代码并删除位置服务.

 private GoogleLocationService googleLocationService;

 googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() 
    @Override
    public void canReceiveLocationUpdates() 
    

    @Override
    public void cannotReceiveLocationUpdates() 
    

    //update location to our servers for tracking purpose
    @Override
    public void updateLocation(Location location) 
        if (location != null ) 
            Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());

        
    

    @Override
    public void updateLocationName(String localityName, Location location) 

        googleLocationService.stopLocationUpdates();
    
);
googleLocationService.startUpdates();


and call this onDestroy 
if (googleLocationService != null) 
    googleLocationService.stopLocationUpdates();

记住 Locationservice 也应该在 manifest 中声明。 在我看来,这是最好的解决方案。 谢谢希望对你有帮助

【讨论】:

【参考方案5】:

这个问题似乎不是很具体。您在开始使用定位服务时似乎遇到了问题。

最好的办法可能是查看 Google 示例 (https://github.com/googlesamples/android-play-location) 以更好地了解机制。 我可能会从实现最简单的示例(LocationUpdates)开始。它使用 FusedLocationProvider 并检查设置是否已启用,因此它应该满足您的需求,或者至少可以帮助您入门。

如果您确实需要使用服务,可以查看LocationUpdatesForegroundService 或LocationUpdatesPendingIntent。但我强烈建议您在执行此操作之前阅读并理解第一个示例。

【讨论】:

以上是关于使用 google play 服务和 FusedLocationProviderClient 进行位置更新的主要内容,如果未能解决你的问题,请参考以下文章

Google play是啥?_?

如何使用“google.gcm”服务?

UnknownPluginException 使用 Google Play 服务和插件 DSL

google play商店怎么使用

使用 google play 服务和 FusedLocationProviderClient 进行位置更新

为啥打开google play就闪退