使用Google Play服务的Android定位

时间:2020-02-23 14:29:03  来源:igfitidea点击:

欢迎使用Google Play服务示例访问Android位置。
今天,我们将学习如何通过示例应用程序使用Google Play服务API来检索您的移动位置。

Android Location API概述

在上一教程中,我们使用了Android的API(自Android的API 1开始提供)来检索用户的位置。
那么为什么需要引入Google Play定位服务? Google为什么没有增强Android的Location API?与默认的Android Location API相比,Google Play定位服务有哪些优势?让我们讨论这些问题以获得清晰的想法。

需要引入Google Play定位服务

作为Google Play服务的一部分的Google Location Services API提供了更强大的高级框架,该框架可自动执行诸如位置提供商选择和电源管理之类的任务。
此外,它提供了新功能,例如Android Framework的Location API中未提供的用户活动检测。
目前,Google提供了5种用户状态,分别是"车内","骑行","步行","静止"和"倾斜",它们足以检测用户的活动并根据用户的状态提供正确的内容。
它提供的另一个功能是Geofencing API,用于通知用户进入或者离开特定区域。
上述优势清楚地说明了为什么Google推荐使用Google Location Services API(也称为FusedLocationProviderApi)来获取用户位置。
它可以根据我们的需求提供最佳的准确性。

Google为什么没有增强Android的Location API?

从技术角度来看,由于Android具有智能手机制造商掌握的独立更新推出功能,因此Google并未改进Android的Location API。
Google对它的控制较少,因此决定改用新的API。

有几个重要的类可用于获取位置:

  • LocationRequest:一个数据对象,其中包含对FusedLocationProviderApi的请求的服务质量参数。
    LocationRequest对象用于从FusedLocationProviderApi请求位置更新的服务质量。

  • FusedLocationProviderApi:与融合的位置提供程序进行交互的主要入口点。
    这些方法必须与GoogleApiClient客户端结合使用,我们将在稍后介绍。

  • com.google.android.gms.location.LocationListener:位置更改后,LocationListener接口用于从FusedLocationProviderApi接收通知。
    如果使用" requestLocationUpdates(GoogleApiClient,LocationRequest,LocationListener)"或者" requestLocationUpdates(GoogleApiClient,LocationRequest,LocationListener,Looper)"方法向位置客户端注册了LocationListener,则调用onLocationChanged方法。

要使用Google Play的位置服务API,我们需要先调用GoogleAPIClient。

GoogleAPIClient

GoogleAPIClient允许我们使用一个调用来调用多个Google API。
以下是使用两个API调用GoogleAPIClient的示例代码段:Location Services和Drive API。

GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
                  .addApi(LocationServices.API)
                  .addApi(Drive.API)
                  .addScope(Drive.SCOPE_FILE)
                  .addConnectionCallbacks(this)
                  .addOnConnectionFailedListener(this).build();

mGoogleApiClient.connect();

为addConnectionCallbacks和addOnConnectionFailedListener实现了GoogleApiClient.ConnectionCallbacks和GoogleApiClient.OnConnectionFailedListener。
建立与所有API的连接后,将调用属于GoogleApiClient.ConnectionCallbacks接口的onConnected()方法。
连接失败时,将调用属于GoogleApiClient.OnConnectionFailedListener接口的onConnectionFailed()。

代码

在build.gradle文件中添加以下依赖项。

compile'com.google.android.gms:play-services:9.8.0

在AndroidManifest.xml文件中添加以下权限。

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

下面给出activity_main.xml。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:tools="https://schemas.android.com/tools"
  android:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  tools:context="com.theitroad.fusedlocationprovider.MainActivity">

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Current Location"
      android:textAppearance="@style/TextAppearance.AppCompat.Display1"
      android:layout_centerVertical="true"
      android:id="@+id/current_location"
      android:layout_centerHorizontal="true" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerHorizontal="true"
      android:textSize="18sp"
      android:id="@+id/latLng"
      android:layout_below="@+id/current_location"
      

</RelativeLayout>

MainAcitivity.java在下面给出。

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
      GoogleApiClient.OnConnectionFailedListener, LocationListener {

  Location mLocation;
  TextView latLng;
  GoogleApiClient mGoogleApiClient;
  private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;

  private LocationRequest mLocationRequest;
  private long UPDATE_INTERVAL = 15000;  /* 15 secs */
  private long FASTEST_INTERVAL = 5000; /* 5 secs */

  private ArrayList permissionsToRequest;
  private ArrayList permissionsRejected = new ArrayList();
  private ArrayList permissions = new ArrayList();

  private final static int ALL_PERMISSIONS_RESULT = 101;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      latLng = (TextView) findViewById(R.id.latLng);

      permissions.add(ACCESS_FINE_LOCATION);
      permissions.add(ACCESS_COARSE_LOCATION);

      permissionsToRequest = findUnAskedPermissions(permissions);
      //get the permissions we have asked for before but are not granted..
      //we will store this in a global list to access later.

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

          if (permissionsToRequest.size() > 0)
              requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
      }

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

  private ArrayList findUnAskedPermissions(ArrayList wanted) {
      ArrayList result = new ArrayList();

      for (String perm : wanted) {
          if (!hasPermission(perm)) {
              result.add(perm);
          }
      }

      return result;
  }

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

  @Override
  protected void onResume() {
      super.onResume();

      if (!checkPlayServices()) {
          latLng.setText("Please install Google Play services.");
      }
  }

  @Override
  public void onConnected(@Nullable Bundle bundle) {

      if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_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.
          return;
      }
      mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

      if(mLocation!=null)
      {
          latLng.setText("Latitude : "+mLocation.getLatitude()+" , Longitude : "+mLocation.getLongitude());
      }

      startLocationUpdates();

  }

  @Override
  public void onConnectionSuspended(int i) {

  }

  @Override
  public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

  }

  @Override
  public void onLocationChanged(Location location) {

      if(location!=null)
          latLng.setText("Latitude : "+location.getLatitude()+" , Longitude : "+location.getLongitude());

  }

  private boolean checkPlayServices() {
      GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
      int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
      if (resultCode != ConnectionResult.SUCCESS) {
          if (apiAvailability.isUserResolvableError(resultCode)) {
              apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
                      .show();
          } else
              finish();

          return false;
      }
      return true;
  }

  protected void startLocationUpdates() {
      mLocationRequest = new LocationRequest();
      mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
      mLocationRequest.setInterval(UPDATE_INTERVAL);
      mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
      if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
          Toast.makeText(getApplicationContext(), "Enable Permissions", Toast.LENGTH_LONG).show();
      }

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

  }

  private boolean hasPermission(String permission) {
      if (canMakeSmores()) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
              return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
          }
      }
      return true;
  }

  private boolean canMakeSmores() {
      return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
  }

  @TargetApi(Build.VERSION_CODES.M)
  @Override
  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

      switch (requestCode) {

          case ALL_PERMISSIONS_RESULT:
              for (String perms : permissionsToRequest) {
                  if (!hasPermission(perms)) {
                      permissionsRejected.add(perms);
                  }
              }

              if (permissionsRejected.size() > 0) {

                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                      if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
                          showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
                                  new DialogInterface.OnClickListener() {
                                      @Override
                                      public void onClick(DialogInterface dialog, int which) {
                                          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                              requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
                                          }
                                      }
                                  });
                          return;
                      }
                  }

              }

              break;
      }

  }

  private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
      new AlertDialog.Builder(MainActivity.this)
              .setMessage(message)
              .setPositiveButton("OK", okListener)
              .setNegativeButton("Cancel", null)
              .create()
              .show();
  }

  @Override
  protected void onDestroy() {
      super.onDestroy();
      stopLocationUpdates();
  }

  public void stopLocationUpdates()
  {
      if (mGoogleApiClient.isConnected()) {
          LocationServices.FusedLocationApi
                  .removeLocationUpdates(mGoogleApiClient, this);
          mGoogleApiClient.disconnect();
      }
  }
}

" mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);"用于从位置服务获取最近的已知位置。

以下是在genymotion模拟器上运行应用程序时的输出。

由于未安装Google Play服务,因此我们的模拟器无法获取位置。
我们来看看智能手机上的输出是什么样子。
上述应用程序中的纬度和经度文本每5-15秒更新一次。