• Android Video Tutorials

Android - 基于位置的服务



Android 位置 API 使您能够轻松构建位置感知应用程序,而无需关注底层位置技术的细节。

这得益于 **Google Play 服务**,它通过自动位置跟踪、地理围栏和活动识别来简化在您的应用中添加位置感知功能。

本教程将向您展示如何在您的 APP 中使用位置服务来获取当前位置、获取定期位置更新、查找地址等。

位置对象

**Location** 对象表示一个地理位置,它可以包含纬度、经度、时间戳以及其他信息,如方位角、海拔和速度。您可以使用以下重要方法来获取位置特定信息:

序号 方法及描述
1

float distanceTo(Location dest)

返回此位置与给定位置之间的近似距离(单位:米)。

2

float getAccuracy()

获取此位置的估计精度(单位:米)。

3

double getAltitude()

获取海拔(如果可用),单位为海平面以上米数。

4

float getBearing()

获取方位角(单位:度)。

5

double getLatitude()

获取纬度(单位:度)。

6

double getLongitude()

获取经度(单位:度)。

7

float getSpeed()

获取速度(如果可用),单位为地面上的米/秒。

8

boolean hasAccuracy()

如果此位置具有精度,则返回 true。

9

boolean hasAltitude()

如果此位置具有海拔,则返回 true。

10

boolean hasBearing()

如果此位置具有方位角,则返回 true。

11

boolean hasSpeed()

如果此位置具有速度,则返回 true。

12

void reset()

清除位置内容。

13

void setAccuracy(float accuracy)

设置此位置的估计精度(单位:米)。

14

void setAltitude(double altitude)

设置海拔(单位:海平面以上米数)。

15

void setBearing(float bearing)

设置方位角(单位:度)。

16

void setLatitude(double latitude)

设置纬度(单位:度)。

17

void setLongitude(double longitude)

设置经度(单位:度)。

18

void setSpeed(float speed)

设置速度(单位:地面上的米/秒)。

19

String toString()

返回一个包含此对象的简洁、易于理解的描述的字符串。

获取当前位置

要获取当前位置,请创建一个位置客户端,即 **LocationClient** 对象,使用 **connect()** 方法将其连接到位置服务,然后调用其 **getLastLocation()** 方法。此方法返回最近的位置,以 **Location** 对象的形式,其中包含纬度和经度坐标以及如上所述的其他信息。要在您的活动中使用基于位置的功能,您必须实现两个接口:

  • GooglePlayServicesClient.ConnectionCallbacks
  • GooglePlayServicesClient.OnConnectionFailedListener

这些接口提供以下重要的回调方法,您需要在您的活动类中实现它们:

序号 回调方法及描述
1

abstract void onConnected(Bundle connectionHint)

当位置服务成功连接到位置客户端时,将调用此回调方法。您将使用 **connect()** 方法连接到位置客户端。

2

abstract void onDisconnected()

当客户端断开连接时,将调用此回调方法。您将使用 **disconnect()** 方法断开与位置客户端的连接。

3

abstract void onConnectionFailed(ConnectionResult result)

当客户端连接到服务时发生错误时,将调用此回调方法。

您应该在活动的 **onCreate()** 方法中创建位置客户端,然后在 **onStart()** 中连接它,以便位置服务在您的活动完全可见时维护当前位置。您应该在 **onStop()** 方法中断开客户端的连接,以便当您的应用不可见时,位置服务不会维护当前位置。这有助于在很大程度上节省电池电量。

获取更新的位置

如果您希望获得位置更新,那么除了上面提到的接口之外,您还需要实现 **LocationListener** 接口。此接口提供以下回调方法,您需要在您的活动类中实现它:

序号 回调方法及描述
1

abstract void onLocationChanged(Location location)

此回调方法用于接收来自 LocationClient 的通知,通知位置已更改。

位置服务质量

**LocationRequest** 对象用于从 **LocationClient** 请求位置更新的服务质量 (QoS)。以下是一些可用于处理 QoS 的有用 setter 方法。在 Android 官方文档中,您可以查看相应的 getter 方法。

序号 方法及描述
1

setExpirationDuration(long millis)

设置此请求的持续时间(单位:毫秒)。

2

setExpirationTime(long millis)

设置请求过期时间(单位:自启动以来的毫秒数)。

3

setFastestInterval(long millis)

显式设置位置更新的最短间隔(单位:毫秒)。

4

setInterval(long millis)

设置活动位置更新的所需间隔(单位:毫秒)。

5

setNumUpdates(int numUpdates)

设置位置更新次数。

6

setPriority(int priority)

设置请求的优先级。

例如,如果您的应用程序需要高精度位置,则应创建一个位置请求,将 **setPriority(int)** 设置为 PRIORITY_HIGH_ACCURACY,并将 **setInterval(long)** 设置为 5 秒。您还可以使用更大的间隔和/或其他优先级(如 PRIORITY_LOW_POWER 以请求“城市”级别的精度或 PRIORITY_BALANCED_POWER_ACCURACY 以请求“街区”级别的精度)。

活动应认真考虑在进入后台时(例如在 onPause() 中)删除所有位置请求,或者至少将请求切换到更大的间隔和更低的质量以节省电量。

显示位置地址

获得 **Location** 对象后,您可以使用 **Geocoder.getFromLocation()** 方法获取给定纬度和经度的地址。此方法是同步的,可能需要很长时间才能完成其工作,因此您应该从 **AsyncTask** 类的 **doInBackground()** 方法中调用此方法。

**AsyncTask** 必须进行子类化才能使用,子类将覆盖 **doInBackground(Params...)** 方法在后台执行任务,并且 **onPostExecute(Result)** 方法在后台计算完成后以及在显示结果时在 UI 线程上被调用。AsyncTask 中还有一个重要的可用方法,即 **execute(Params... params)**,此方法使用指定的参数执行任务。

示例

以下示例将向您展示如何在实践中使用应用中的位置服务来获取当前位置及其相应的地址等。

要体验此示例,您需要配备最新 Android 操作系统的实际移动设备,否则您将不得不与可能无法正常工作的模拟器作斗争。

创建 Android 应用

步骤 描述
1 您将使用 Android Studio IDE 创建一个 Android 应用,并将其命名为 *Tutorialspoint*,位于包 *com.example.tutorialspoint7.myapplication* 下。
2 添加 *src/GPSTracker.java* 文件并添加所需的代码。
3 修改 *src/MainActivity.java* 文件,并添加如下所示的所需代码,以处理获取当前位置及其相应地址。
4 修改布局 XML 文件 *res/layout/activity_main.xml*,添加所有 GUI 组件,包括三个按钮和两个文本视图以显示位置/地址。
5 修改 *res/values/strings.xml* 以定义所需的常量值
6 修改 *AndroidManifest.xml*,如下所示
7 运行应用以启动 Android 模拟器并验证对应用所做的更改的结果。

以下是修改后的主活动文件 **MainActivity.java** 的内容。

package com.example.tutorialspoint7.myapplication;

import android.Manifest;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.test.mock.MockPackageManager;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

   Button btnShowLocation;
   private static final int REQUEST_CODE_PERMISSION = 2;
   String mPermission = Manifest.permission.ACCESS_FINE_LOCATION;

   // GPSTracker class
   GPSTracker gps;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
		
      try {
         if (ActivityCompat.checkSelfPermission(this, mPermission)
            != MockPackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(this, new String[]{mPermission}, 
               REQUEST_CODE_PERMISSION);

            // If any permission above not allowed by user, this condition will
               execute every time, else your else part will work
         }
      } catch (Exception e) {
         e.printStackTrace();
      }

      btnShowLocation = (Button) findViewById(R.id.button);

      // show location button click event
      btnShowLocation.setOnClickListener(new View.OnClickListener() {

         @Override
         public void onClick(View arg0) {
            // create class object
            gps = new GPSTracker(MainActivity.this);

            // check if GPS enabled
            if(gps.canGetLocation()){

               double latitude = gps.getLatitude();
               double longitude = gps.getLongitude();

               // \n is for new line
               Toast.makeText(getApplicationContext(), "Your Location is - \nLat: "
                  + latitude + "\nLong: " + longitude, Toast.LENGTH_LONG).show();
            }else{
               // can't get location
               // GPS or Network is not enabled
               // Ask user to enable GPS/network in settings
               gps.showSettingsAlert();
            }

         }
      });
   }
}

以下是修改后的主活动文件 **GPSTracker.java** 的内容。

package com.example.tutorialspoint7.myapplication;

import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;

public class GPSTracker extends Service implements LocationListener {

   private final Context mContext;

   // flag for GPS status
   boolean isGPSEnabled = false;

   // flag for network status
   boolean isNetworkEnabled = false;

   // flag for GPS status
   boolean canGetLocation = false;

   Location location; // location
   double latitude; // latitude
   double longitude; // longitude

   // The minimum distance to change Updates in meters
   private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

   // The minimum time between updates in milliseconds
   private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

   // Declaring a Location Manager
   protected LocationManager locationManager;

   public GPSTracker(Context context) {
      this.mContext = context;
      getLocation();
   }

   public Location getLocation() {
      try {
         locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);

         // getting GPS status
         isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

         // getting network status
         isNetworkEnabled = locationManager
            .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

         if (!isGPSEnabled && !isNetworkEnabled) {
            // no network provider is enabled
         } else {
            this.canGetLocation = true;
            // First get location from Network Provider
            if (isNetworkEnabled) {
               locationManager.requestLocationUpdates(
                  LocationManager.NETWORK_PROVIDER,
                  MIN_TIME_BW_UPDATES,
                  MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
						
               Log.d("Network", "Network");
               if (locationManager != null) {
                  location = locationManager
                     .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
							
                  if (location != null) {
                     latitude = location.getLatitude();
                     longitude = location.getLongitude();
                  }
               }
            }
				
            // if GPS Enabled get lat/long using GPS Services
            if (isGPSEnabled) {
               if (location == null) {
                  locationManager.requestLocationUpdates(
                     LocationManager.GPS_PROVIDER,
                     MIN_TIME_BW_UPDATES,
                     MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
							
                  Log.d("GPS Enabled", "GPS Enabled");
                  if (locationManager != null) {
                     location = locationManager
                        .getLastKnownLocation(LocationManager.GPS_PROVIDER);
								
                     if (location != null) {
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
                     }
                  }
               }
            }
         }

      } catch (Exception e) {
         e.printStackTrace();
      }

      return location;
   }

   /**
      * Stop using GPS listener
      * Calling this function will stop using GPS in your app
   * */
	
   public void stopUsingGPS(){
      if(locationManager != null){
         locationManager.removeUpdates(GPSTracker.this);
      }
   }

   /**
      * Function to get latitude
   * */
	
   public double getLatitude(){
      if(location != null){
         latitude = location.getLatitude();
      }

      // return latitude
      return latitude;
   }

   /**
      * Function to get longitude
   * */
	
   public double getLongitude(){
      if(location != null){
         longitude = location.getLongitude();
      }

      // return longitude
      return longitude;
   }

   /**
      * Function to check GPS/wifi enabled
      * @return boolean
   * */
	
   public boolean canGetLocation() {
      return this.canGetLocation;
   }

   /**
      * Function to show settings alert dialog
      * On pressing Settings button will lauch Settings Options
   * */
	
   public void showSettingsAlert(){
      AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

      // Setting Dialog Title
      alertDialog.setTitle("GPS is settings");

      // Setting Dialog Message
      alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");

      // On pressing Settings button
      alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog,int which) {
            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            mContext.startActivity(intent);
         }
      });

      // on pressing cancel button
      alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
            dialog.cancel();
         }
      });

      // Showing Alert Message
      alertDialog.show();
   }

   @Override
   public void onLocationChanged(Location location) {
   }

   @Override
   public void onProviderDisabled(String provider) {
   }

   @Override
   public void onProviderEnabled(String provider) {
   }

   @Override
   public void onStatusChanged(String provider, int status, Bundle extras) {
   }

   @Override
   public IBinder onBind(Intent arg0) {
      return null;
   }
}

**res/layout/activity_main.xml** 文件的内容如下:

<?xml version = "1.0" encoding = "utf-8"?>
<LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent"
   android:orientation = "vertical" >


   <Button
      android:id = "@+id/button"
      android:layout_width = "fill_parent"
      android:layout_height = "wrap_content"
      android:text = "getlocation"/>

</LinearLayout>

**res/values/strings.xml** 文件的内容如下,用于定义两个新的常量:

<?xml version = "1.0" encoding = "utf-8"?>
<resources>
   <string name = "app_name">Tutorialspoint</string>
</resources>

**AndroidManifest.xml** 文件的默认内容如下:

<?xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
   package = "com.example.tutorialspoint7.myapplication">
   <uses-permission android:name = "android.permission.ACCESS_FINE_LOCATION" />
   <uses-permission android:name = "android.permission.INTERNET" />
   <application
      android:allowBackup = "true"
      android:icon = "@mipmap/ic_launcher"
      android:label = "@string/app_name"
      android:supportsRtl = "true"
      android:theme = "@style/AppTheme">
		
      <activity android:name = ".MainActivity">
         <intent-filter>
            <action android:name = "android.intent.action.MAIN" />

            <category android:name = "android.intent.category.LAUNCHER" />
         </intent-filter>
      </activity>
   </application>

</manifest>

让我们尝试运行您的Tutorialspoint应用程序。我假设您已将您的实际Android移动设备连接到您的电脑。要从Android Studio运行应用程序,请打开项目中的一个活动文件,然后点击工具栏中的运行Eclipse Run 图标图标。在启动应用程序之前,Android Studio安装程序将显示以下窗口,供您选择要运行Android应用程序的位置。

Android Mobile Device

现在要查看位置,请选择“获取位置”按钮,它将显示如下位置信息:

Android Mobile Location Info
广告