使用Google Play服务的Android定位
欢迎使用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秒更新一次。