【Android Developers Training】 106. 创建并

系统 1631 0

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接: http://developer.android.com/training/location/geofencing.html


地理围栏可以将用户当前地点信息和周围的地点信息相结合,它其实是用户接近潜在的感兴趣的地点的程度。要标记一个感兴趣的地点,你需要指定它的经纬度。要调整接近的位置,你还需要添加一个半径。经纬度和半径加起来就成为了一个地理围栏。你可以同一时间有多个激活的地理围栏。

定位服务将一个地理围栏看做是一块面积而不是点和距离。这就可以当用户进入或离开地理围栏时检测到。对于每一个地理围栏,你可以让定位服务向你发送进入事件或离开事件或者都发送。你还可以限制地理围栏的持续时间,方法是定义一个有效期(以毫秒为单位)。当地理围栏过期后,定位服务会自动移除它。


一). 请求地理围栏监测

请求地理围栏监测的第一步是申请必需的权限。要使用地理围栏,你的应用必须申请 ACCESS_FINE_LOCATION 。要申请这一权限,将下列元素添加为 <manifest> 标签的子标签:

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

检查Google Play服务

位置服务是Google Play服务APK的其中一部分。由于用户设备的状态时难以预料的,你应该一直在你尝试连接定位服务之前,检查APK是否已经安装。要检查APK是否安装,可以调用 GooglePlayServicesUtil.isGooglePlayServicesAvailable() ,它会返回一个整形的结果码,其含义可以参阅: ConnectionResult 。如果你遇到了一个错误,可以调用 GooglePlayServicesUtil.getErrorDialog() ,来获取一个本地的对话框,引导用户执行正确地行为,之后将这一对话框显示在一个 DialogFragment 上。这一对话框可能允许用户解决当前的问题,此时Google Play服务会发回一个结果到你的activity中。要处理这一结果,需要覆写 onActivityResult() 方法。

Note:

要使你的应用可以兼容1.6及以后版本的系统,显示 DialogFragment 的activity必须是 FragmentActivity 的子类,而非 Activity 。使用 FragmentActivity 还可以允许你调用 getSupportFragmentManager() 方法来显示 DialogFragment

由于你一直需要在你的代码多个地方检查Google Play服务,所以应该定义一个方法将检查行为进行封装,之后在每次连接尝试之前进行检查。下面的代码片段包含了检查Google Play服务所需要的代码:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
      
         FragmentActivity {

    ...

    
      
      
        //
      
      
         Global constants
      
      
        /*
      
      
        

     * Define a request code to send to Google Play services

     * This code is returned in Activity.onActivityResult

     
      
      
        */
      
      
        private
      
      
        final
      
      
        static
      
      
        int
      
      
        

            CONNECTION_FAILURE_RESOLUTION_REQUEST 
      
      = 9000
      
        ;

    ...

    
      
      
        //
      
      
         Define a DialogFragment that displays the error dialog
      
      
        public
      
      
        static
      
      
        class
      
       ErrorDialogFragment 
      
        extends
      
      
         DialogFragment {

        
      
      
        //
      
      
         Global field to contain the error dialog
      
      
        private
      
      
         Dialog mDialog;

        ...

        
      
      
        //
      
      
         Default constructor. Sets the dialog field to null
      
      
        public
      
      
         ErrorDialogFragment() {

            
      
      
        super
      
      
        ();

            mDialog 
      
      = 
      
        null
      
      
        ;

        }

        ...

        
      
      
        //
      
      
         Set the dialog to display
      
      
        public
      
      
        void
      
      
         setDialog(Dialog dialog) {

            mDialog 
      
      =
      
         dialog;

        }

        ...

        
      
      
        //
      
      
         Return a Dialog to the DialogFragment.
      
      
                @Override

        
      
      
        public
      
      
         Dialog onCreateDialog(Bundle savedInstanceState) {

            
      
      
        return
      
      
         mDialog;

        }

        ...

    }

    ...

    
      
      
        /*
      
      
        

     * Handle results returned to the FragmentActivity

     * by Google Play services

     
      
      
        */
      
      
        

     @Override

    
      
      
        protected
      
      
        void
      
      
         onActivityResult(

            
      
      
        int
      
       requestCode, 
      
        int
      
      
         resultCode, Intent data) {

        
      
      
        //
      
      
         Decide what to do based on the original request code
      
      
        switch
      
      
         (requestCode) {

            ...

            
      
      
        case
      
      
         CONNECTION_FAILURE_RESOLUTION_REQUEST :

            
      
      
        /*
      
      
        

             * If the result code is Activity.RESULT_OK, try

             * to connect again

             
      
      
        */
      
      
        switch
      
      
         (resultCode) {

                    ...

                    
      
      
        case
      
      
         Activity.RESULT_OK :

                    
      
      
        /*
      
      
        

                     * Try the request again

                     
      
      
        */
      
      
        

                    ...

                    
      
      
        break
      
      
        ;

                }

            ...

        }

        ...

    }

    ...

    
      
      
        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("Geofence Detection"
      
        ,

                    
      
      "Google Play services is available."
      
        );

            
      
      
        //
      
      
         Continue
      
      
        return
      
      
        true
      
      
        ;

        
      
      
        //
      
      
         Google Play services was not available for some reason
      
      

        } 
      
        else
      
      
         {

            
      
      
        //
      
      
         Get the error code
      
      
        int
      
       errorCode =
      
         connectionResult.getErrorCode();

            
      
      
        //
      
      
         Get the error dialog from Google Play services
      
      

            Dialog errorDialog =
      
         GooglePlayServicesUtil.getErrorDialog(

                    errorCode,

                    
      
      
        this
      
      
        ,

                    CONNECTION_FAILURE_RESOLUTION_REQUEST);



            
      
      
        //
      
      
         If Google Play services can provide an error dialog
      
      
        if
      
       (errorDialog != 
      
        null
      
      
        ) {

                
      
      
        //
      
      
         Create a new DialogFragment for the error dialog
      
      

                ErrorDialogFragment errorFragment =

                        
      
        new
      
      
         ErrorDialogFragment();

                
      
      
        //
      
      
         Set the dialog in the DialogFragment
      
      
                        errorFragment.setDialog(errorDialog);

                
      
      
        //
      
      
         Show the error dialog in the DialogFragment
      
      
                        errorFragment.show(

                        getSupportFragmentManager(),

                        
      
      "Geofence Detection"
      
        );

            }

        }

    }

    ...

}
      
    

在后续章节的代码片段中,都会调用这一方法来验证是否可获取Google Play服务。

要使用地理围栏,首先定义你想要监测的地理围栏。虽然你经常要将地理围栏信息保存到一个本地的数据库或者从网络上下载下来,你需要将一个地理围栏发送给定位服务作为一个 Geofence 的实例(通过 Geofence.Builder 创建的)。每一个对象包含下列信息:

经纬度和半径:

给地理围栏定义一个圆形区域。使用经纬度标记一个感兴趣的地点,并且使用半径来调整当用户具体该地点多近后地理围栏会被检测到。半径越大,用户接近地理围栏时,激活它的可能性就越高。例如,如果一个应用提供了一个大半径的地理围栏,当用户回家时可以自动打开房间里的灯。由于半径设的太大,很有可能用户离开之后灯还是亮着的。

有效期:

设置地理围栏的有效期。一旦超过了有效期,定位服务将会删除该地理围栏。在大多数情况下,你应该指定一个有效期,但你也可能希望为用户的屋子或者工作地点的地理围栏长期保留。

过度类型:

当用户进入了地理围栏的范围(“ 进入 ”)以及当用于离开了此范围(“ 离开 ”),定位服务可以检测到这两个类型之一,或者两者都检测到。

地理围栏ID:

一个和地理围栏一起保存的字符串。你应该让这个值保持唯一,所以你可以使用它从定位服务中移除一个地理围栏。

定义一个地理围栏存储

一个地理围栏应用需要读写地理围栏数据以持久化数据。你不应该使用 Geofence 对象来做这件事情;相反的,使用诸如数据库等存储技术来保存相关的数据是比较好的。

作为一个存储数据的例子,下面的代码片段定义了两个类,它们使用应用的 SharedPreferences 实例持久化数据。类 SimpleGeofence ,是一个类似于数据库记录的类,它以一个“ 稀疏 ”的形式保存一个单一的 Geofence 对象。类 SimpleGeofenceStore 类似于一个数据库,它向 SharedPreferences 实例读写 SimpleGeofence 数据。

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
      
         FragmentActivity {

    ...

    
      
      
        /**
      
      
        

     * A single Geofence object, defined by its center and radius.

     
      
      
        */
      
      
        public
      
      
        class
      
      
         SimpleGeofence {

            
      
      
        //
      
      
         Instance variables
      
      
        private
      
      
        final
      
      
         String mId;

            
      
      
        private
      
      
        final
      
      
        double
      
      
         mLatitude;

            
      
      
        private
      
      
        final
      
      
        double
      
      
         mLongitude;

            
      
      
        private
      
      
        final
      
      
        float
      
      
         mRadius;

            
      
      
        private
      
      
        long
      
      
         mExpirationDuration;

            
      
      
        private
      
      
        int
      
      
         mTransitionType;



        
      
      
        /**
      
      
        

         * 
      
      
        @param
      
      
         geofenceId The Geofence's request ID

         * 
      
      
        @param
      
      
         latitude Latitude of the Geofence's center.

         * 
      
      
        @param
      
      
         longitude Longitude of the Geofence's center.

         * 
      
      
        @param
      
      
         radius Radius of the geofence circle.

         * 
      
      
        @param
      
      
         expiration Geofence expiration duration

         * 
      
      
        @param
      
      
         transition Type of Geofence transition.

         
      
      
        */
      
      
        public
      
      
         SimpleGeofence(

                String geofenceId,

                
      
      
        double
      
      
         latitude,

                
      
      
        double
      
      
         longitude,

                
      
      
        float
      
      
         radius,

                
      
      
        long
      
      
         expiration,

                
      
      
        int
      
      
         transition) {

            
      
      
        //
      
      
         Set the instance fields from the constructor
      
      
        this
      
      .mId =
      
         geofenceId;

            
      
      
        this
      
      .mLatitude =
      
         latitude;

            
      
      
        this
      
      .mLongitude =
      
         longitude;

            
      
      
        this
      
      .mRadius =
      
         radius;

            
      
      
        this
      
      .mExpirationDuration =
      
         expiration;

            
      
      
        this
      
      .mTransitionType =
      
         transition;

        }

        
      
      
        //
      
      
         Instance field getters
      
      
        public
      
      
         String getId() {

            
      
      
        return
      
      
         mId;

        }

        
      
      
        public
      
      
        double
      
      
         getLatitude() {

            
      
      
        return
      
      
         mLatitude;

        }

        
      
      
        public
      
      
        double
      
      
         getLongitude() {

            
      
      
        return
      
      
         mLongitude;

        }

        
      
      
        public
      
      
        float
      
      
         getRadius() {

            
      
      
        return
      
      
         mRadius;

        }

        
      
      
        public
      
      
        long
      
      
         getExpirationDuration() {

            
      
      
        return
      
      
         mExpirationDuration;

        }

        
      
      
        public
      
      
        int
      
      
         getTransitionType() {

            
      
      
        return
      
      
         mTransitionType;

        }

        
      
      
        /**
      
      
        

         * Creates a Location Services Geofence object from a

         * SimpleGeofence.

         *

         * 
      
      
        @return
      
      
         A Geofence object

         
      
      
        */
      
      
        public
      
      
         Geofence toGeofence() {

            
      
      
        //
      
      
         Build a new Geofence object
      
      
        return
      
      
        new
      
      
         Geofence.Builder()

                    .setRequestId(getId())

                    .setTransitionTypes(mTransitionType)

                    .setCircularRegion(

                            getLatitude(), getLongitude(), getRadius())

                    .setExpirationDuration(mExpirationDuration)

                    .build();

        }

    }

    ...

    
      
      
        /**
      
      
        

     * Storage for geofence values, implemented in SharedPreferences.

     
      
      
        */
      
      
        public
      
      
        class
      
      
         SimpleGeofenceStore {

        
      
      
        //
      
      
         Keys for flattened geofences stored in SharedPreferences
      
      
        public
      
      
        static
      
      
        final
      
       String KEY_LATITUDE =

                "com.example.android.geofence.KEY_LATITUDE"
      
        ;

        
      
      
        public
      
      
        static
      
      
        final
      
       String KEY_LONGITUDE =

                "com.example.android.geofence.KEY_LONGITUDE"
      
        ;

        
      
      
        public
      
      
        static
      
      
        final
      
       String KEY_RADIUS =

                "com.example.android.geofence.KEY_RADIUS"
      
        ;

        
      
      
        public
      
      
        static
      
      
        final
      
       String KEY_EXPIRATION_DURATION =

                "com.example.android.geofence.KEY_EXPIRATION_DURATION"
      
        ;

        
      
      
        public
      
      
        static
      
      
        final
      
       String KEY_TRANSITION_TYPE =

                "com.example.android.geofence.KEY_TRANSITION_TYPE"
      
        ;

        
      
      
        //
      
      
         The prefix for flattened geofence keys
      
      
        public
      
      
        static
      
      
        final
      
       String KEY_PREFIX =

                "com.example.android.geofence.KEY"
      
        ;

        
      
      
        /*
      
      
        

         * Invalid values, used to test geofence storage when

         * retrieving geofences

         
      
      
        */
      
      
        public
      
      
        static
      
      
        final
      
      
        long
      
       INVALID_LONG_VALUE = -999l
      
        ;

        
      
      
        public
      
      
        static
      
      
        final
      
      
        float
      
       INVALID_FLOAT_VALUE = -999.0f
      
        ;

        
      
      
        public
      
      
        static
      
      
        final
      
      
        int
      
       INVALID_INT_VALUE = -999
      
        ;

        
      
      
        //
      
      
         The SharedPreferences object in which geofences are stored
      
      
        private
      
      
        final
      
      
         SharedPreferences mPrefs;

        
      
      
        //
      
      
         The name of the SharedPreferences
      
      
        private
      
      
        static
      
      
        final
      
       String SHARED_PREFERENCES =

                "SharedPreferences"
      
        ;

        
      
      
        //
      
      
         Create the SharedPreferences storage with private access only
      
      
        public
      
      
         SimpleGeofenceStore(Context context) {

            mPrefs 
      
      =
      
        

                    context.getSharedPreferences(

                            SHARED_PREFERENCES,

                            Context.MODE_PRIVATE);

        }

        
      
      
        /**
      
      
        

         * Returns a stored geofence by its id, or returns null

         * if it's not found.

         *

         * 
      
      
        @param
      
      
         id The ID of a stored geofence

         * 
      
      
        @return
      
      
         A geofence defined by its center and radius. See

         
      
      
        */
      
      
        public
      
      
         SimpleGeofence getGeofence(String id) {

            
      
      
        /*
      
      
        

             * Get the latitude for the geofence identified by id, or

             * INVALID_FLOAT_VALUE if it doesn't exist

             
      
      
        */
      
      
        double
      
       lat =
      
         mPrefs.getFloat(

                    getGeofenceFieldKey(id, KEY_LATITUDE),

                    INVALID_FLOAT_VALUE);

            
      
      
        /*
      
      
        

             * Get the longitude for the geofence identified by id, or

             * INVALID_FLOAT_VALUE if it doesn't exist

             
      
      
        */
      
      
        double
      
       lng =
      
         mPrefs.getFloat(

                    getGeofenceFieldKey(id, KEY_LONGITUDE),

                    INVALID_FLOAT_VALUE);

            
      
      
        /*
      
      
        

             * Get the radius for the geofence identified by id, or

             * INVALID_FLOAT_VALUE if it doesn't exist

             
      
      
        */
      
      
        float
      
       radius =
      
         mPrefs.getFloat(

                    getGeofenceFieldKey(id, KEY_RADIUS),

                    INVALID_FLOAT_VALUE);

            
      
      
        /*
      
      
        

             * Get the expiration duration for the geofence identified

             * by id, or INVALID_LONG_VALUE if it doesn't exist

             
      
      
        */
      
      
        long
      
       expirationDuration =
      
         mPrefs.getLong(

                    getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),

                    INVALID_LONG_VALUE);

            
      
      
        /*
      
      
        

             * Get the transition type for the geofence identified by

             * id, or INVALID_INT_VALUE if it doesn't exist

             
      
      
        */
      
      
        int
      
       transitionType =
      
         mPrefs.getInt(

                    getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),

                    INVALID_INT_VALUE);

            
      
      
        //
      
      
         If none of the values is incorrect, return the object
      
      
        if
      
      
         (

                lat 
      
      != GeofenceUtils.INVALID_FLOAT_VALUE &&
      
        

                lng 
      
      != GeofenceUtils.INVALID_FLOAT_VALUE &&
      
        

                radius 
      
      != GeofenceUtils.INVALID_FLOAT_VALUE &&
      
        

                expirationDuration 
      
      !=
      
        

                        GeofenceUtils.INVALID_LONG_VALUE 
      
      &&
      
        

                transitionType 
      
      !=
      
         GeofenceUtils.INVALID_INT_VALUE) {



                
      
      
        //
      
      
         Return a true Geofence object
      
      
        return
      
      
        new
      
      
         SimpleGeofence(

                        id, lat, lng, radius, expirationDuration,

                        transitionType);

            
      
      
        //
      
      
         Otherwise, return null.
      
      

            } 
      
        else
      
      
         {

                
      
      
        return
      
      
        null
      
      
        ;

            }

        }

        
      
      
        /**
      
      
        

         * Save a geofence.

         * 
      
      
        @param
      
      
         geofence The SimpleGeofence containing the

         * values you want to save in SharedPreferences

         
      
      
        */
      
      
        public
      
      
        void
      
      
         setGeofence(String id, SimpleGeofence geofence) {

            
      
      
        /*
      
      
        

             * Get a SharedPreferences editor instance. Among other

             * things, SharedPreferences ensures that updates are atomic

             * and non-concurrent

             
      
      
        */
      
      
        

            Editor editor 
      
      =
      
         mPrefs.edit();

            
      
      
        //
      
      
         Write the Geofence values to SharedPreferences
      
      
                    editor.putFloat(

                    getGeofenceFieldKey(id, KEY_LATITUDE),

                    (
      
      
        float
      
      
        ) geofence.getLatitude());

            editor.putFloat(

                    getGeofenceFieldKey(id, KEY_LONGITUDE),

                    (
      
      
        float
      
      
        ) geofence.getLongitude());

            editor.putFloat(

                    getGeofenceFieldKey(id, KEY_RADIUS),

                    geofence.getRadius());

            editor.putLong(

                    getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),

                    geofence.getExpirationDuration());

            editor.putInt(

                    getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),

                    geofence.getTransitionType());

            
      
      
        //
      
      
         Commit the changes
      
      
                    editor.commit();

        }

        
      
      
        public
      
      
        void
      
      
         clearGeofence(String id) {

            
      
      
        /*
      
      
        

             * Remove a flattened geofence object from storage by

             * removing all of its keys

             
      
      
        */
      
      
        

            Editor editor 
      
      =
      
         mPrefs.edit();

            editor.remove(getGeofenceFieldKey(id, KEY_LATITUDE));

            editor.remove(getGeofenceFieldKey(id, KEY_LONGITUDE));

            editor.remove(getGeofenceFieldKey(id, KEY_RADIUS));

            editor.remove(getGeofenceFieldKey(id,

                    KEY_EXPIRATION_DURATION));

            editor.remove(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE));

            editor.commit();

        }

        
      
      
        /**
      
      
        

         * Given a Geofence object's ID and the name of a field

         * (for example, KEY_LATITUDE), return the key name of the

         * object's values in SharedPreferences.

         *

         * 
      
      
        @param
      
      
         id The ID of a Geofence object

         * 
      
      
        @param
      
      
         fieldName The field represented by the key

         * 
      
      
        @return
      
      
         The full key name of a value in SharedPreferences

         
      
      
        */
      
      
        private
      
      
         String getGeofenceFieldKey(String id,

                String fieldName) {

            
      
      
        return
      
       KEY_PREFIX + "_" + id + "_" +
      
         fieldName;

        }

    }

    ...

}
      
    

创建地理围栏对象

下面的代码片段使用 SimpleGeofence SimpleGeofenceStore 类从UI中获取地理围栏数据,把这些对象存储在一个 SimpleGeofenceStore 对象中,之后创建 Geofence 对象:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
      
         FragmentActivity {

    ...

    
      
      
        /*
      
      
        

     * Use to set an expiration time for a geofence. After this amount

     * of time Location Services will stop tracking the geofence.

     
      
      
        */
      
      
        private
      
      
        static
      
      
        final
      
      
        long
      
       SECONDS_PER_HOUR = 60
      
        ;

    
      
      
        private
      
      
        static
      
      
        final
      
      
        long
      
       MILLISECONDS_PER_SECOND = 1000
      
        ;

    
      
      
        private
      
      
        static
      
      
        final
      
      
        long
      
       GEOFENCE_EXPIRATION_IN_HOURS = 12
      
        ;

    
      
      
        private
      
      
        static
      
      
        final
      
      
        long
      
       GEOFENCE_EXPIRATION_TIME =
      
        

            GEOFENCE_EXPIRATION_IN_HOURS 
      
      *
      
        

            SECONDS_PER_HOUR 
      
      *
      
        

            MILLISECONDS_PER_SECOND;

    ...

    
      
      
        /*
      
      
        

     * Handles to UI views containing geofence data

     
      
      
        */
      
      
        //
      
      
         Handle to geofence 1 latitude in the UI
      
      
        private
      
      
         EditText mLatitude1;

    
      
      
        //
      
      
         Handle to geofence 1 longitude in the UI
      
      
        private
      
      
         EditText mLongitude1;

    
      
      
        //
      
      
         Handle to geofence 1 radius in the UI
      
      
        private
      
      
         EditText mRadius1;

    
      
      
        //
      
      
         Handle to geofence 2 latitude in the UI
      
      
        private
      
      
         EditText mLatitude2;

    
      
      
        //
      
      
         Handle to geofence 2 longitude in the UI
      
      
        private
      
      
         EditText mLongitude2;

    
      
      
        //
      
      
         Handle to geofence 2 radius in the UI
      
      
        private
      
      
         EditText mRadius2;

    
      
      
        /*
      
      
        

     * Internal geofence objects for geofence 1 and 2

     
      
      
        */
      
      
        private
      
      
         SimpleGeofence mUIGeofence1;

    
      
      
        private
      
      
         SimpleGeofence mUIGeofence2;

    ...

    
      
      
        //
      
      
         Internal List of Geofence objects
      
      

    List<Geofence>
      
         mGeofenceList;

    
      
      
        //
      
      
         Persistent storage for geofences
      
      
        private
      
      
         SimpleGeofenceStore mGeofenceStorage;

    ...

    @Override

    
      
      
        protected
      
      
        void
      
      
         onCreate(Bundle savedInstanceState) {

        
      
      
        super
      
      
        .onCreate(savedInstanceState);

        ...

        
      
      
        //
      
      
         Instantiate a new geofence storage area
      
      

        mGeofenceStorage = 
      
        new
      
       SimpleGeofenceStore(
      
        this
      
      
        );



        
      
      
        //
      
      
         Instantiate the current List of geofences
      
      

        mCurrentGeofences = 
      
        new
      
       ArrayList<Geofence>
      
        ();

    }

    ...

    
      
      
        /**
      
      
        

     * Get the geofence parameters for each geofence from the UI

     * and add them to a List.

     
      
      
        */
      
      
        public
      
      
        void
      
      
         createGeofences() {

        
      
      
        /*
      
      
        

         * Create an internal object to store the data. Set its

         * ID to "1". This is a "flattened" object that contains

         * a set of strings

         
      
      
        */
      
      
        

        mUIGeofence1 
      
      = 
      
        new
      
      
         SimpleGeofence(

                
      
      "1"
      
        ,

                Double.valueOf(mLatitude1.getText().toString()),

                Double.valueOf(mLongitude1.getText().toString()),

                Float.valueOf(mRadius1.getText().toString()),

                GEOFENCE_EXPIRATION_TIME,

                
      
      
        //
      
      
         This geofence records only entry transitions
      
      
                        Geofence.GEOFENCE_TRANSITION_ENTER);

        
      
      
        //
      
      
         Store this flat version
      
      

        mGeofenceStorage.setGeofence("1"
      
        , mUIGeofence1);

        
      
      
        //
      
      
         Create another internal object. Set its ID to "2"
      
      

        mUIGeofence2 = 
      
        new
      
      
         SimpleGeofence(

                
      
      "2"
      
        ,

                Double.valueOf(mLatitude2.getText().toString()),

                Double.valueOf(mLongitude2.getText().toString()),

                Float.valueOf(mRadius2.getText().toString()),

                GEOFENCE_EXPIRATION_TIME,

                
      
      
        //
      
      
         This geofence records both entry and exit transitions
      
      

                Geofence.GEOFENCE_TRANSITION_ENTER |
      
        

                Geofence.GEOFENCE_TRANSITION_EXIT);

        
      
      
        //
      
      
         Store this flat version
      
      

        mGeofenceStorage.setGeofence(2
      
        , mUIGeofence2);

        mGeofenceList.add(mUIGeofence1.toGeofence());

        mGeofenceList.add(mUIGeofence2.toGeofence());

    }

    ...

}
      
    

除了你希望监测的存储 Geofence 对象的 List ,你还需要向定位服务提供一个 Intent ,当监测到地理围栏转换的时候会将它发送给你的应用。

为地理围栏转换定义一个Intent

从定位服务发送的 Intent 可以激活你应用中的多个行为,但是你不应该让它启动一个activity或者fragment,因为组件只有在用户行为的出发条件下变的向用户可见才行。在很多情况下,用一个 IntentService 来处理intent是一个不错的方式。一个 IntentService 可以发布一个通知,在后台执行一个长时间运作的任务,将intent发送给其它服务,或者发送一个广播intent。下面的代码片段展示了如何定义一个 PendingIntent 来启动一个 IntentService

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
      
         FragmentActivity {

    ...

    
      
      
        /*
      
      
        

     * Create a PendingIntent that triggers an IntentService in your

     * app when a geofence transition occurs.

     
      
      
        */
      
      
        private
      
      
         PendingIntent getTransitionPendingIntent() {

        
      
      
        //
      
      
         Create an explicit Intent
      
      

        Intent intent = 
      
        new
      
       Intent(
      
        this
      
      
        ,

                ReceiveTransitionsIntentService.
      
      
        class
      
      
        );

        
      
      
        /*
      
      
        

         * Return the PendingIntent

         
      
      
        */
      
      
        return
      
      
         PendingIntent.getService(

                
      
      
        this
      
      
        ,

                
      
      0
      
        ,

                intent,

                PendingIntent.FLAG_UPDATE_CURRENT);

    }

    ...

}
      
    

要向定位服务请求监测地理围栏,所需的代码现在你已经都有了。

发送监测请求

发送监测请求需要两种异步操作。第一种操作为请求获取一个定位客户端,第二个操作使用客户端发送请求。在这两个情况中,定位服务会在它完成了操作后调用一个回调函数。要处理这些操作的最佳方法是将这些函数调用串联起来。下面的代码片段将演示如何设置一个acitvity,定义方法,并以正确地顺序调用他们。

首先,修改activity类定义来实现必要的回调接口。添加下列接口:

ConnectionCallbacks

当一个定位客户端连接或者断开连接后,定位服务需要调用的方法。

OnConnectionFailedListener

当尝试连接定位客户端失败或发生错误后,定位服务需要调用的方法。

OnAddGeofencesResultListener

一旦添加了地理围栏,定位服务调用的方法。

例如:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

}
      
    

开始请求过程

接下来,定义一个方法,它通过连接定位服务来开始请求的过程。通过设置一个全局变量来标记它是一个添加地理围栏的请求。这将允许你使用 ConnectionCallbacks.onConnected() 这一回调函数来添加地理围栏或者移除它们,这些细节将在下面的章节展开。

为了防止竞争场景的发生(比如你的应用在第一个请求结束之前又发出了第二个请求),定义一个布尔变量,用来标记当前请求的状态:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        //
      
      
         Holds the location client
      
      
        private
      
      
         LocationClient mLocationClient;

    
      
      
        //
      
      
         Stores the PendingIntent used to request geofence monitoring
      
      
        private
      
      
         PendingIntent mGeofenceRequestIntent;

    
      
      
        //
      
      
         Defines the allowable request types.
      
      
        public
      
      
        enum
      
       REQUEST_TYPE =
      
         {ADD}

    
      
      
        private
      
      
         REQUEST_TYPE mRequestType;

    
      
      
        //
      
      
         Flag that indicates if a request is underway.
      
      
        private
      
      
        boolean
      
      
         mInProgress;

    ...

    @Override

    
      
      
        protected
      
      
        void
      
      
         onCreate(Bundle savedInstanceState) {

        ...

        
      
      
        //
      
      
         Start with the request flag set to false
      
      

        mInProgress = 
      
        false
      
      
        ;

        ...

    }

    ...

    
      
      
        /**
      
      
        

     * Start a request for geofence monitoring by calling

     * LocationClient.connect().

     
      
      
        */
      
      
        public
      
      
        void
      
      
         addGeofences() {

        
      
      
        //
      
      
         Start a request to add geofences
      
      

        mRequestType =
      
         ADD;

        
      
      
        /*
      
      
        

         * Test for Google Play services after setting the request type.

         * If Google Play services isn't present, the proper request

         * can be restarted.

         
      
      
        */
      
      
        if
      
       (!
      
        servicesConnected()) {

            
      
      
        return
      
      
        ;

        }

        
      
      
        /*
      
      
        

         * Create a new location client object. Since the current

         * activity class implements ConnectionCallbacks and

         * OnConnectionFailedListener, pass the current activity object

         * as the listener for both parameters

         
      
      
        */
      
      
        

        mLocationClient 
      
      = 
      
        new
      
       LocationClient(
      
        this
      
      , 
      
        this
      
      , 
      
        this
      
      
        )

        
      
      
        //
      
      
         If a request is not already underway
      
      
        if
      
       (!
      
        mInProgress) {

            
      
      
        //
      
      
         Indicate that a request is underway
      
      

            mInProgress = 
      
        true
      
      
        ;

            
      
      
        //
      
      
         Request a connection from the client to Location Services
      
      
                    mLocationClient.connect();

        } 
      
      
        else
      
      
         {

            
      
      
        /*
      
      
        

             * A request is already underway. You can handle

             * this situation by disconnecting the client,

             * re-setting the flag, and then re-trying the

             * request.

             
      
      
        */
      
      
        

        }

    }

    ...

}
      
    

发送请求来添加地理围栏

在你的 ConnectionCallbacks.onConnected() 实现中,调用 LocationClient.addGeofences() 。注意,如果连接失败了, onConnected() 不会被调用,请求被中止。

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        /*
      
      
        

     * Provide the implementation of ConnectionCallbacks.onConnected()

     * Once the connection is available, send a request to add the

     * Geofences

     
      
      
        */
      
      
        

    @Override

    
      
      
        private
      
      
        void
      
      
         onConnected(Bundle dataBundle) {

        ...

        
      
      
        switch
      
      
         (mRequestType) {

            
      
      
        case
      
      
         ADD :

                
      
      
        //
      
      
         Get the PendingIntent for the request
      
      

                mTransitionPendingIntent =
      
        

                        getTransitionPendingIntent();

                
      
      
        //
      
      
         Send a request to add the current geofences
      
      
                        mLocationClient.addGeofences(

                        mCurrentGeofences, pendingIntent, 
      
      
        this
      
      
        );

            ...

        }

    }

    ...

}
      
    

注意 addGeofences() 会迅速返回,但是请求的状态在定位服务调用 onAddGeofencesResult() 之前是不定的。一旦这一方法被调用,你就能够确定请求是否成功。

检查定位服务返回的结果

当定位服务调用了你的回调函数 onAddGeofencesResult() 的实现,这就代表请求完成了,之后检查传入的状态码。如果请求成功,那么你所请求的地理围栏将被激活。否则,地理围栏不会被激活,你需要继续尝试请求或者报告错误。例如:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

        ...

    
      
      
        /*
      
      
        

     * Provide the implementation of

     * OnAddGeofencesResultListener.onAddGeofencesResult.

     * Handle the result of adding the geofences

     *

     
      
      
        */
      
      
        

    @Override

    
      
      
        public
      
      
        void
      
      
         onAddGeofencesResult(

            
      
      
        int
      
      
         statusCode, String[] geofenceRequestIds) {

        
      
      
        //
      
      
         If adding the geofences was successful
      
      
        if
      
       (LocationStatusCodes.SUCCESS ==
      
         statusCode) {

            
      
      
        /*
      
      
        

             * Handle successful addition of geofences here.

             * You can send out a broadcast intent or update the UI.

             * geofences into the Intent's extended data.

             
      
      
        */
      
      
        

        } 
      
      
        else
      
      
         {

        
      
      
        //
      
      
         If adding the geofences failed
      
      
        /*
      
      
        

             * Report errors here.

             * You can log the error using Log.e() or update

             * the UI.

             
      
      
        */
      
      
        

        }

        
      
      
        //
      
      
         Turn off the in progress flag and disconnect the client
      
      

        mInProgress = 
      
        false
      
      
        ;

        mLocationClient.disconnect();

    }

    ...

}
      
    

处理连接中断

在有些情况下,定位服务可能会在你调用了 disconnect() 之前就中断连接了。要处理这种情况,需要实现 onDisconnected() 方法。在这个方法中,设置请求标识,以表明当前没有进行中的请求,并将客户端移除:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        /*
      
      
        

     * Implement ConnectionCallbacks.onDisconnected()

     * Called by Location Services once the location client is

     * disconnected.

     
      
      
        */
      
      
        

    @Override

    
      
      
        public
      
      
        void
      
      
         onDisconnected() {

        
      
      
        //
      
      
         Turn off the request flag
      
      

        mInProgress = 
      
        false
      
      
        ;

        
      
      
        //
      
      
         Destroy the current location client
      
      

        mLocationClient = 
      
        null
      
      
        ;

    }

    ...

}
      
    

处理连接错误

除了处理定位服务的常规回调函数外,你还需要提供一个回调函数,该函数会在连接错误发生的时候被定为服务调用。该回调函数可以重用 DialogFragment 类(你在检查Google Play服务时所定义的类)。同时它也可以重用当用户与错误对话框交互时,接收任何由Google Play服务返回的结果的 onActivityResult() 函数。下面的代码片段展示了该回调函数的一个例子:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        //
      
      
         Implementation of OnConnectionFailedListener.onConnectionFailed
      
      
            @Override

    
      
      
        public
      
      
        void
      
      
         onConnectionFailed(ConnectionResult connectionResult) {

        
      
      
        //
      
      
         Turn off the request flag
      
      

        mInProgress = 
      
        false
      
      
        ;

        
      
      
        /*
      
      
        

         * If the error has a resolution, start a Google Play services

         * activity to resolve it.

         
      
      
        */
      
      
        if
      
      
         (connectionResult.hasResolution()) {

            
      
      
        try
      
      
         {

                connectionResult.startResolutionForResult(

                        
      
      
        this
      
      
        ,

                        CONNECTION_FAILURE_RESOLUTION_REQUEST);

            } 
      
      
        catch
      
      
         (SendIntentException e) {

                
      
      
        //
      
      
         Log the error
      
      
                        e.printStackTrace();

            }

        
      
      
        //
      
      
         If no resolution is available, display an error dialog
      
      

        } 
      
        else
      
      
         {

            
      
      
        //
      
      
         Get the error code
      
      
        int
      
       errorCode =
      
         connectionResult.getErrorCode();

            
      
      
        //
      
      
         Get the error dialog from Google Play services
      
      

            Dialog errorDialog =
      
         GooglePlayServicesUtil.getErrorDialog(

                    errorCode,

                    
      
      
        this
      
      
        ,

                    CONNECTION_FAILURE_RESOLUTION_REQUEST);

            
      
      
        //
      
      
         If Google Play services can provide an error dialog
      
      
        if
      
       (errorDialog != 
      
        null
      
      
        ) {

                
      
      
        //
      
      
         Create a new DialogFragment for the error dialog
      
      

                ErrorDialogFragment errorFragment =

                        
      
        new
      
      
         ErrorDialogFragment();

                
      
      
        //
      
      
         Set the dialog in the DialogFragment
      
      
                        errorFragment.setDialog(errorDialog);

                
      
      
        //
      
      
         Show the error dialog in the DialogFragment
      
      
                        errorFragment.show(

                        getSupportFragmentManager(),

                        
      
      "Geofence Detection"
      
        );

            }

        }

    }

    ...

}
      
    

二). 处理地理围栏转换

当定位服务检测到了用户进入或者离开了一个地理围栏,它会发送一个 Intent ,该 Intent 来自于你请求添加地理围栏时所用到的 PendingIntent

定义一个IntentService

下面的代码片段展示了当一个地理围栏转换发生的时候, 如何定义一个 IntentService 。当用户点击通知时, 显示 应用的主activity:

      
        public
      
      
        class
      
       ReceiveTransitionsIntentService 
      
        extends
      
      
         IntentService {

    ...

    
      
      
        /**
      
      
        

     * Sets an identifier for the service

     
      
      
        */
      
      
        public
      
      
         ReceiveTransitionsIntentService() {

        
      
      
        super
      
      ("ReceiveTransitionsIntentService"
      
        );

    }

    
      
      
        /**
      
      
        

     * Handles incoming intents

     *
      
      
        @param
      
      
         intent The Intent sent by Location Services. This

     * Intent is provided

     * to Location Services (inside a PendingIntent) when you call

     * addGeofences()

     
      
      
        */
      
      
        

    @Override

    
      
      
        protected
      
      
        void
      
      
         onHandleIntent(Intent intent) {

        
      
      
        //
      
      
         First check for errors
      
      
        if
      
      
         (LocationClient.hasError(intent)) {

            
      
      
        //
      
      
         Get the error code with a static method
      
      
        int
      
       errorCode =
      
         LocationClient.getErrorCode(intent);

            
      
      
        //
      
      
         Log the error
      
      

            Log.e("ReceiveTransitionsIntentService"
      
        ,

                    
      
      "Location Services error: " +
      
        

                    Integer.toString(errorCode));

            
      
      
        /*
      
      
        

             * You can also send the error code to an Activity or

             * Fragment with a broadcast Intent

             
      
      
        */
      
      
        /*
      
      
        

         * If there's no error, get the transition type and the IDs

         * of the geofence or geofences that triggered the transition

         
      
      
        */
      
      
        

        } 
      
      
        else
      
      
         {

            
      
      
        //
      
      
         Get the type of transition (entry or exit)
      
      
        int
      
       transitionType =
      
        

                    LocationClient.getGeofenceTransition(intent);

            
      
      
        //
      
      
         Test that a valid transition was reported
      
      
        if
      
      
         (

                (transitionType 
      
      ==
      
         Geofence.GEOFENCE_TRANSITION_ENTER)

                 
      
      ||
      
        

                (transitionType 
      
      ==
      
         Geofence.GEOFENCE_TRANSITION_EXIT)

               ) {

                List 
      
      <Geofence> triggerList =
      
        

                        getTriggeringGeofences(intent);



                String[] triggerIds 
      
      = 
      
        new
      
      
         String[geofenceList.size()];



                
      
      
        for
      
       (
      
        int
      
       i = 0; i < triggerIds.length; i++
      
        ) {

                    
      
      
        //
      
      
         Store the Id of each geofence
      
      

                    triggerIds[i] =
      
         triggerList.get(i).getRequestId();

                }

                
      
      
        /*
      
      
        

                 * At this point, you can store the IDs for further use

                 * display them, or display the details associated with

                 * them.

                 
      
      
        */
      
      
        

            }

        
      
      
        //
      
      
         An invalid transition was reported
      
      

        } 
      
        else
      
      
         {

            Log.e(
      
      "ReceiveTransitionsIntentService"
      
        ,

                    
      
      "Geofence transition error: " +
      
        

                    Integer.toString()transitionType));

        }

    }

    ...
        
}

在清单列表中声明IntentService

要在系统中使用 IntentService ,在应用清单文件中添加一个 <service> 标签,例如:

      
        <
      
      
        service

    
      
      
        android:name
      
      
        ="com.example.android.location.ReceiveTransitionsIntentService"
      
      
        

    android:label
      
      
        ="@string/app_name"
      
      
        

    android:exported
      
      
        ="false"
      
      
        >
      
      
        </
      
      
        service
      
      
        >
      
    

注意,你不需要为该服务指定intent过滤器,因为它仅会接收显式的intent。如何创建地理围栏转换intent,可以阅读: Send the monitoring request


停止地理围栏监控

要停止地理围栏监控,你需要将它们移除。你可以通过一个 PendingIntent 将所有地理围栏全部移除,或者只移除一部分。过程与添加地理围栏类似。首先需要为移除请求获取定位客户端,然后使用客户端提出申请。

定位服务在完成移除后所调用的回调函数在 LocationClient.OnRemoveGeofencesResultListener 接口中被定义。将该接口声明为你的类定义的一部分,之后添加其两个方法的定义:

onRemoveGeofencesByPendingIntentResult()

当定位服务使用函数 removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener) 移除了所有地理围栏后被调用。

onRemoveGeofencesByRequestIdsResult(List<String>, LocationClient.OnRemoveGeofencesResultListener)

当定位服务使用函数 removeGeofences(List<String>, LocationClient.OnRemoveGeofencesResultListener) 将给定ID所对应的部分地理围栏移除后被调用。

下面给出这些方法的使用样例:

移除所有地理围栏

由于移除地理围栏会使用一些添加地理围栏时所使用的方法,我们从定义另一个请求类型开始:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        //
      
      
         Enum type for controlling the type of removal requested
      
      
        public
      
      
        enum
      
       REQUEST_TYPE =
      
         {ADD, REMOVE_INTENT}

    ...

}
      
    

通过获取定位服务的连接开始移除请求。如果连接失败了, onConnected() 不会被调用,请求中止。下面的代码片段展示了如何开始请求:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        /**
      
      
        

     * Start a request to remove geofences by calling

     * LocationClient.connect()

     
      
      
        */
      
      
        public
      
      
        void
      
      
         removeGeofences(PendingIntent requestIntent) {

        
      
      
        //
      
      
         Record the type of removal request
      
      

        mRequestType =
      
         REMOVE_INTENT;

        
      
      
        /*
      
      
        

         * Test for Google Play services after setting the request type.

         * If Google Play services isn't present, the request can be

         * restarted.

         
      
      
        */
      
      
        if
      
       (!
      
        servicesConnected()) {

            
      
      
        return
      
      
        ;

        }

        
      
      
        //
      
      
         Store the PendingIntent
      
      

        mGeofenceRequestIntent =
      
         requestIntent;

        
      
      
        /*
      
      
        

         * Create a new location client object. Since the current

         * activity class implements ConnectionCallbacks and

         * OnConnectionFailedListener, pass the current activity object

         * as the listener for both parameters

         
      
      
        */
      
      
        

        mLocationClient 
      
      = 
      
        new
      
       LocationClient(
      
        this
      
      , 
      
        this
      
      , 
      
        this
      
      
        );

        
      
      
        //
      
      
         If a request is not already underway
      
      
        if
      
       (!
      
        mInProgress) {

            
      
      
        //
      
      
         Indicate that a request is underway
      
      

            mInProgress = 
      
        true
      
      
        ;

            
      
      
        //
      
      
         Request a connection from the client to Location Services
      
      
                    mLocationClient.connect();

        } 
      
      
        else
      
      
         {

            
      
      
        /*
      
      
        

             * A request is already underway. You can handle

             * this situation by disconnecting the client,

             * re-setting the flag, and then re-trying the

             * request.

             
      
      
        */
      
      
        

        }

    }

    ...

}
      
    

当定位服务调用了回调函数指明连接已建立,那么就发出移除所有地理围栏的请求。再发出请求后记得关闭连接。例如:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        /**
      
      
        

     * Once the connection is available, send a request to remove the

     * Geofences. The method signature used depends on which type of

     * remove request was originally received.

     
      
      
        */
      
      
        private
      
      
        void
      
      
         onConnected(Bundle dataBundle) {

        
      
      
        /*
      
      
        

         * Choose what to do based on the request type set in

         * removeGeofences

         
      
      
        */
      
      
        switch
      
      
         (mRequestType) {

            ...

            
      
      
        case
      
      
         REMOVE_INTENT :

                mLocationClient.removeGeofences(

                        mGeofenceRequestIntent, 
      
      
        this
      
      
        );

                
      
      
        break
      
      
        ;

            ...

        }

    }

    ...

}
      
    

虽然对 removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener) 的调用后,服务端会马上返回,但移除请求的结果在定位服务调用 onRemoveGeofencesByPendingIntentResult() 之前是不定的。下面的代码片段展示了如何定义这一方法:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        /**
      
      
        

     * When the request to remove geofences by PendingIntent returns,

     * handle the result.

     *

     *
      
      
        @param
      
      
         statusCode the code returned by Location Services

     *
      
      
        @param
      
      
         requestIntent The Intent used to request the removal.

     
      
      
        */
      
      
        

    @Override

    
      
      
        public
      
      
        void
      
       onRemoveGeofencesByPendingIntentResult(
      
        int
      
      
         statusCode,

            PendingIntent requestIntent) {

        
      
      
        //
      
      
         If removing the geofences was successful
      
      
        if
      
       (statusCode ==
      
         LocationStatusCodes.SUCCESS) {

            
      
      
        /*
      
      
        

             * Handle successful removal of geofences here.

             * You can send out a broadcast intent or update the UI.

             * geofences into the Intent's extended data.

             
      
      
        */
      
      
        

        } 
      
      
        else
      
      
         {

        
      
      
        //
      
      
         If adding the geocodes failed
      
      
        /*
      
      
        

             * Report errors here.

             * You can log the error using Log.e() or update

             * the UI.

             
      
      
        */
      
      
        

        }

        
      
      
        /*
      
      
        

         * Disconnect the location client regardless of the

         * request status, and indicate that a request is no

         * longer in progress

         
      
      
        */
      
      
        

        mInProgress 
      
      = 
      
        false
      
      
        ;

        mLocationClient.disconnect();

    }

    ...

}
      
    

移除单个地理围栏

移除单个地理围栏或者部分地理围栏的过程同删除全部地理围栏相似。要指定你想要移除的地理围栏,需要把地理围栏的ID添加到一个String的 List 对象中。将这个 List 传递给 removeGeofences,该方法之后便开始移除。

通过添加一个移除地理围栏请求类型的list,然后添加一个全局变量来存储地理围栏的list:

      
            ...

    
      
      
        //
      
      
         Enum type for controlling the type of removal requested
      
      
        public
      
      
        enum
      
       REQUEST_TYPE =
      
         {ADD, REMOVE_INTENT, REMOVE_LIST}

    
      
      
        //
      
      
         Store the list of geofence Ids to remove
      
      

    String<List> mGeofencesToRemove;
    

之后定义你想要移除的地理围栏list。例如,在下面的例子中,要移除的 Geofence 的ID为“1”:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

        List
      
      <String> listOfGeofences =
      
        

                Collections.singletonList(
      
      "1"
      
        );

        removeGeofences(listOfGeofences);

    ...

}
      
    

下面的代码片段定义了removeGeofences()方法:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        /**
      
      
        

     * Start a request to remove monitoring by

     * calling LocationClient.connect()

     *

     
      
      
        */
      
      
        public
      
      
        void
      
       removeGeofences(List<String>
      
         geofenceIds) {

        
      
      
        //
      
      
         If Google Play services is unavailable, exit

        
      
      
        //
      
      
         Record the type of removal request
      
      

        mRequestType =
      
         REMOVE_LIST;

        
      
      
        /*
      
      
        

         * Test for Google Play services after setting the request type.

         * If Google Play services isn't present, the request can be

         * restarted.

         
      
      
        */
      
      
        if
      
       (!
      
        servicesConnected()) {

            
      
      
        return
      
      
        ;

        }

        
      
      
        //
      
      
         Store the list of geofences to remove
      
      

        mGeofencesToRemove =
      
         geofenceIds;

        
      
      
        /*
      
      
        

         * Create a new location client object. Since the current

         * activity class implements ConnectionCallbacks and

         * OnConnectionFailedListener, pass the current activity object

         * as the listener for both parameters

         
      
      
        */
      
      
        

        mLocationClient 
      
      = 
      
        new
      
       LocationClient(
      
        this
      
      , 
      
        this
      
      , 
      
        this
      
      
        );

        
      
      
        //
      
      
         If a request is not already underway
      
      
        if
      
       (!
      
        mInProgress) {

            
      
      
        //
      
      
         Indicate that a request is underway
      
      

            mInProgress = 
      
        true
      
      
        ;

            
      
      
        //
      
      
         Request a connection from the client to Location Services
      
      
                    mLocationClient.connect();

        } 
      
      
        else
      
      
         {

            
      
      
        /*
      
      
        

             * A request is already underway. You can handle

             * this situation by disconnecting the client,

             * re-setting the flag, and then re-trying the

             * request.

             
      
      
        */
      
      
        

        }

    }

    ...

}
      
    

当定位服务激活了回调函数表明这个链接已经建立以后,发出该请求来移除列表中的地理围栏。在发出请求之后关闭连接。例如:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        private
      
      
        void
      
      
         onConnected(Bundle dataBundle) {

        ...

        
      
      
        switch
      
      
         (mRequestType) {

        ...

        
      
      
        //
      
      
         If removeGeofencesById was called
      
      
        case
      
      
         REMOVE_LIST :

                mLocationClient.removeGeofences(

                        mGeofencesToRemove, 
      
      
        this
      
      
        );

                
      
      
        break
      
      
        ;

        ...

        }

        ...

    }

    ...

}
      
      
         
      
    

定义 onRemoveGeofencesByRequestIdsResult() 的实现。定位服务会激活该回调函数来指出这个移除地理围栏的请求已经完成。在该方法中,检查传入的状态码然后采取对应的措施:

      
        public
      
      
        class
      
       MainActivity 
      
        extends
      
       FragmentActivity 
      
        implements
      
      
        

        ConnectionCallbacks,

        OnConnectionFailedListener,

        OnAddGeofencesResultListener {

    ...

    
      
      
        /**
      
      
        

     * When the request to remove geofences by IDs returns, handle the

     * result.

     *

     * 
      
      
        @param
      
      
         statusCode The code returned by Location Services

     * 
      
      
        @param
      
      
         geofenceRequestIds The IDs removed

     
      
      
        */
      
      
        

    @Override

    
      
      
        public
      
      
        void
      
      
         onRemoveGeofencesByRequestIdsResult(

            
      
      
        int
      
      
         statusCode, String[] geofenceRequestIds) {

        
      
      
        //
      
      
         If removing the geocodes was successful
      
      
        if
      
       (LocationStatusCodes.SUCCESS ==
      
         statusCode) {

            
      
      
        /*
      
      
        

             * Handle successful removal of geofences here.

             * You can send out a broadcast intent or update the UI.

             * geofences into the Intent's extended data.

             
      
      
        */
      
      
        

        } 
      
      
        else
      
      
         {

        
      
      
        //
      
      
         If removing the geofences failed
      
      
        /*
      
      
        

             * Report errors here.

             * You can log the error using Log.e() or update

             * the UI.

             
      
      
        */
      
      
        

        }

        
      
      
        //
      
      
         Indicate that a request is no longer in progress
      
      

        mInProgress = 
      
        false
      
      
        ;

        
      
      
        //
      
      
         Disconnect the location client
      
      
                mLocationClient.disconnect();

    }

    ...

}
      
    

你可以将地理围栏和其它地点感知的功能结合起来,比如定期的地点更新或者行为认知等,这些会在该系列课程中的后续课程中展开。

在下一节课程中,会向你展示请求和接收activity更新。在定期的间隔中,定位服务可以给你发送有关用户当前物理行为的信息。基于这一信息,你可以改变你的应用行为,例如,如果你检测到用户在步行而不在开车,你可以增加定期更新的间隔。

【Android Developers Training】 106. 创建并检测地理围栏


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论