转载请注明:http://blog.csdn.net/btyh17mxy/article/details/9038443 

官方给出了一个demo:http://developer.android.com/shareables/training/GeofenceDetection.zip

Geofence是一个基于Google Play Services的虚拟地理区域,是一个由中心点经纬度和半径描述的圆形区域。Location Service会以低功耗的方式获取用户的位置,当用户进入或退出Geofence范围时会通知应用,应用接受到通知后可采取相应的操作,例如在通知栏显示这样的通知:


一、方法

1、检查Google Play Services是否可用



/** * 驗證Google Play Services是否可用 *  * @return 可用返回真否則返回假 */private boolean servicesConnected() {// 檢查服務是否可用int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);// 如果可用if (ConnectionResult.SUCCESS == resultCode) {// Log狀態Log.d(GeofenceUtils.APPTAG,getString(R.string.play_services_available));return true;// 如果不可用} else {// 顯示錯誤提示對話框Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode,this, 0);if (dialog != null) {ErrorDialogFragment errorFragment = new ErrorDialogFragment();errorFragment.setDialog(dialog);errorFragment.show(getSupportFragmentManager(),GeofenceUtils.APPTAG);}return false;}}

在应用启动的时候就应该检查一下GoogleServices是否可用,如果不可用的话是不能使用Geofence的。

PS:可能是输入法抽风,在Eclipse里面总是打出繁体字来

2、连接和断开LocationClient、添加和移除Geofence(s)、处理连接异常

LocationClient的原型是:

com.google.android.gms.location.LocationClient.LocationClient(Context context, ConnectionCallbacks connectionCallbacks, OnConnectionFailedListener connectionFailedListener)
在官方的例子中是写了一个持有主调Activity强引用并导入上述几个回调接口的类来处理添加Geofence请求。像这样:

/** * 连接Google Play Services和请求Geofence *  *  * Note: 必须首先验证Google Play Services可用 *  Use GooglePlayServicesUtil.isGooglePlayServicesAvailable() to check. * * 调用 AddGeofence()方法添加Geofence * */public class GeofenceRequester                implements                    OnAddGeofencesResultListener,                    ConnectionCallbacks,                    OnConnectionFailedListener {    // 主调Activity的强引用    private final Activity mActivity;        // Stores the PendingIntent used to send geofence transitions back to the app    private PendingIntent mGeofencePendingIntent;    // Stores the current list of geofences    private ArrayList mCurrentGeofences;    // Stores the current instantiation of the location client    private LocationClient mLocationClient;    /*     * Flag that indicates whether an add or remove request is underway. Check this     * flag before attempting to start a new request.     */    private boolean mInProgress;    public GeofenceRequester(Activity activityContext) {        // Save the context        mActivity = activityContext;        // Initialize the globals to null        mGeofencePendingIntent = null;        mLocationClient = null;        mInProgress = false;    }    /**     * Set the "in progress" flag from a caller. This allows callers to re-set a     * request that failed but was later fixed.     *     * @param flag Turn the in progress flag on or off.     */    public void setInProgressFlag(boolean flag) {        // Set the "In Progress" flag.        mInProgress = flag;    }    /**     * Get the current in progress status.     *     * @return The current value of the in progress flag.     */    public boolean getInProgressFlag() {        return mInProgress;    }    /**     * Returns the current PendingIntent to the caller.     *     * @return The PendingIntent used to create the current set of geofences     */    public PendingIntent getRequestPendingIntent() {        return createRequestPendingIntent();    }    /**     * Start adding geofences. Save the geofences, then start adding them by requesting a     * connection     *     * @param geofences A List of one or more geofences to add     */    public void addGeofences(List geofences) throws UnsupportedOperationException {        /*         * Save the geofences so that they can be sent to Location Services once the         * connection is available.         */        mCurrentGeofences = (ArrayList) geofences;        // If a request is not already in progress        if (!mInProgress) {            // Toggle the flag and continue            mInProgress = true;            // Request a connection to Location Services            requestConnection();        // If a request is in progress        } else {            // Throw an exception and stop the request            throw new UnsupportedOperationException();        }    }    /**     * 请求链接Location Service     *      */    private void requestConnection() {        getLocationClient().connect();    }    /**     * 返回已经存在的Location Client 或者创建一个新的     *     * @return A LocationClient object     */    private GooglePlayServicesClient getLocationClient() {        if (mLocationClient == null) {            mLocationClient = new LocationClient(mActivity, this, this);        }        return mLocationClient;    }    /**     * 一旦连接可用,发送请求添加Goefence     */    private void continueAddGeofences() {        // Get a PendingIntent that Location Services issues when a geofence transition occurs        mGeofencePendingIntent = createRequestPendingIntent();        // Send a request to add the current geofences        mLocationClient.addGeofences(mCurrentGeofences, mGeofencePendingIntent, this);    }    /**     * 处理添加Geofence结果     */    public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) {        // Create a broadcast Intent that notifies other components of success or failure        Intent broadcastIntent = new Intent();        // Temp storage for messages        String msg;        // If adding the geocodes was successful        if (LocationStatusCodes.SUCCESS == statusCode) {            // Create a message containing all the geofence IDs added.            msg = mActivity.getString(R.string.add_geofences_result_success,                    Arrays.toString(geofenceRequestIds));            // In debug mode, log the result            Log.d(GeofenceUtils.APPTAG, msg);            // Create an Intent to broadcast to the app            broadcastIntent.setAction(GeofenceUtils.ACTION_GEOFENCES_ADDED)                           .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)                           .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, msg);        // If adding the geofences failed        } else {            /*             * Create a message containing the error code and the list             * of geofence IDs you tried to add             */            msg = mActivity.getString(                    R.string.add_geofences_result_failure,                    statusCode,                    Arrays.toString(geofenceRequestIds)            );            // Log an error            Log.e(GeofenceUtils.APPTAG, msg);            // Create an Intent to broadcast to the app            broadcastIntent.setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR)                           .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)                           .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, msg);        }        // Broadcast whichever result occurred        LocalBroadcastManager.getInstance(mActivity).sendBroadcast(broadcastIntent);        // Disconnect the location client        requestDisconnection();    }    /**     * Get a location client and disconnect from Location Services     */    private void requestDisconnection() {        // A request is no longer in progress        mInProgress = false;        getLocationClient().disconnect();    }    /**     * Called by Location Services once the location client is connected.     *     * Continue by adding the requested geofences.     */    public void onConnected(Bundle arg0) {        // If debugging, log the connection        Log.d(GeofenceUtils.APPTAG, mActivity.getString(R.string.connected));        // Continue adding the geofences        continueAddGeofences();    }    /**     * Called by Location Services once the location client is disconnected.     */    public void onDisconnected() {        // Turn off the request flag        mInProgress = false;        // In debug mode, log the disconnection        Log.d(GeofenceUtils.APPTAG, mActivity.getString(R.string.disconnected));        // Destroy the current location client        mLocationClient = null;    }    /**     * 获取一个发送给Location services的PendingIntent.Location Services检测到Geofence过度的时     * 候会调用其中的Intent     *     * @return A PendingIntent for the IntentService that handles geofence transitions.     */    private PendingIntent createRequestPendingIntent() {        // 如果PendingIntent已經存在        if (null != mGeofencePendingIntent) {            // 返回存在的PendingIntent            return mGeofencePendingIntent;        // 如果PendingIntent不存在        } else {            // 創建一個新的            Intent intent = new Intent(mActivity, ReceiveTransitionsIntentService.class);            /*             * 要使用PendingIntent.FLAG_UPDATE_CURRENT標籤創建,否則Location Services無法匹配             */            return PendingIntent.getService(                    mActivity,                    0,                    intent,                    PendingIntent.FLAG_UPDATE_CURRENT);        }    }    /**     * 實現OnConnectionFailedListener.onConnectionFailed接口     * 如果鏈接或斷開請求失敗,會調用該方法     */    public void onConnectionFailed(ConnectionResult connectionResult) {        // 關閉請求標籤        mInProgress = false;        /*         * Google Play services 能夠解析一部分錯誤。如果錯誤能夠被解析,嘗試         * 發送Inten來啓動能夠解析該錯誤的Google Play services activity         */        if (connectionResult.hasResolution()) {            try {                // 啓動嘗試解析該錯誤的Activity                connectionResult.startResolutionForResult(mActivity,                    GeofenceUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);            /*             * Thrown if Google Play services canceled the original             * PendingIntent             */            } catch (SendIntentException e) {                // Log the error            Log.e(GeofenceUtils.APPTAG,e.toString());                e.printStackTrace();            }        /*         * If no resolution is available, put the error code in         * an error Intent and broadcast it back to the main Activity.         * The Activity then displays an error dialog.         * is out of date.         */        } else {            Intent errorBroadcastIntent = new Intent(GeofenceUtils.ACTION_CONNECTION_ERROR);            errorBroadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)                                .putExtra(GeofenceUtils.EXTRA_CONNECTION_ERROR_CODE,                                        connectionResult.getErrorCode());            LocalBroadcastManager.getInstance(mActivity).sendBroadcast(errorBroadcastIntent);        }    }}

移除的操作跟这个类似,所以不再啰嗦。

3、接收并处理Notification

在创建的时候就需要指定一个用于接收Notification的IntentServeice,像这样

// 創建一個新的        // 使用ReceiveTransitionsIntentService处理Notification            Intent intent = new Intent(mActivity, ReceiveTransitionsIntentService.class);            /*             * 要使用PendingIntent.FLAG_UPDATE_CURRENT標籤創建,否則Location Services無法匹配             */            return PendingIntent.getService(                    mActivity,                    0,                    intent,                    PendingIntent.FLAG_UPDATE_CURRENT);

下面是该类的具体实现:

/** * 本类以接受包含触发事件的Geofence(s)的过度类型和GeofenceID的Intent的形式,接受Location Services *  发来的Geofence过度事件。 */public class ReceiveTransitionsIntentService extends IntentService {    /**     * Sets an identifier for this class' background thread     */    public ReceiveTransitionsIntentService() {        super("ReceiveTransitionsIntentService");    }    /**     * 处理发送来的Intent     * @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) {    Log.d(GeofenceUtils.APPTAG, "触发");        // Create a local broadcast Intent        Intent broadcastIntent = new Intent();        // Give it the category for all intents sent by the Intent Service        broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);        // First check for errors        if (LocationClient.hasError(intent)) {            // Get the error code            int errorCode = LocationClient.getErrorCode(intent);            // Get the error message            String errorMessage = LocationServiceErrorMessages.getErrorString(this, errorCode);            // Log the error            Log.e(GeofenceUtils.APPTAG,                    getString(R.string.geofence_transition_error_detail, errorMessage)            );            // Set the action and error message for the broadcast intent            broadcastIntent.setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR)                           .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, errorMessage);            // Broadcast the error *locally* to other components in this app            LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);        // If there's no error, get the transition type and create a notification        } else {            // Get the type of transition (entry or exit)            int transition = LocationClient.getGeofenceTransition(intent);            // Test that a valid transition was reported            if (                    (transition == Geofence.GEOFENCE_TRANSITION_ENTER)                    ||                    (transition == Geofence.GEOFENCE_TRANSITION_EXIT)               ) {                // Post a notification                List geofences = LocationClient.getTriggeringGeofences(intent);                String[] geofenceIds = new String[geofences.size()];                for (int index = 0; index < geofences.size() ; index++) {                    geofenceIds[index] = geofences.get(index).getRequestId();                }                String ids = TextUtils.join(GeofenceUtils.GEOFENCE_ID_DELIMITER,geofenceIds);                String transitionType = getTransitionString(transition);                sendNotification(transitionType, ids);                // Log the transition type and a message                Log.d(GeofenceUtils.APPTAG,                        getString(                                R.string.geofence_transition_notification_title,                                transitionType,                                ids));                Log.d(GeofenceUtils.APPTAG,                        getString(R.string.geofence_transition_notification_text));            // An invalid transition was reported            } else {                // Always log as an error                Log.e(GeofenceUtils.APPTAG,                        getString(R.string.geofence_transition_invalid_type, transition));            }        }    }    /**     * 当检测到过度事件的时候向通知栏发送一个通知     * 如果用户点击这个通知,就打开主Activity     * @param transitionType The type of transition that occurred.     */    private void sendNotification(String transitionType, String ids) {        // Create an explicit content Intent that starts the main Activity        Intent notificationIntent =                new Intent(getApplicationContext(),MainActivity.class);        // Construct a task stack        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);        // Adds the main Activity to the task stack as the parent        stackBuilder.addParentStack(MainActivity.class);        // Push the content Intent onto the stack        stackBuilder.addNextIntent(notificationIntent);        // Get a PendingIntent containing the entire back stack        PendingIntent notificationPendingIntent =                stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);        // Get a notification builder that's compatible with platform versions >= 4        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);        // Set the notification contents        builder.setSmallIcon(R.drawable.ic_notification)               .setContentTitle(                       getString(R.string.geofence_transition_notification_title,                               transitionType, ids))               .setContentText(getString(R.string.geofence_transition_notification_text))               .setContentIntent(notificationPendingIntent);        // Get an instance of the Notification manager        NotificationManager mNotificationManager =            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);        // Issue the notification        mNotificationManager.notify(0, builder.build());    }    /**     * 将过度类型代码转换成文字     * @param transitionType A transition type constant defined in Geofence     * @return A String indicating the type of transition     */    private String getTransitionString(int transitionType) {        switch (transitionType) {            case Geofence.GEOFENCE_TRANSITION_ENTER:                return getString(R.string.geofence_transition_entered);            case Geofence.GEOFENCE_TRANSITION_EXIT:                return getString(R.string.geofence_transition_exited);            default:                return getString(R.string.geofence_transition_unknown);        }    }}

二、问题

1、精确度问题    

    由于Location Services并不是用GPS,所以精确度不高,虽然根据测试的结果精确度在50M左右(如下图是通过LocationClient.getLastLocation()获取到的GPS坐标),但我觉得它远没有那么精确。昨天之所以一直没能触发警报就是因为半径设的太小了,今天Google一下发现stackoverflow上有人说半径不宜小于1000M,改过之后就没有问题了。实际中还应该根据项目需求调整精确度。       

2、Geofence范围问题  

    Geofence只能描述一个圆形区域,这点似乎很头疼,还应该注意半径的大小,太小(小于1000M)不容易触发。

3、国内阉割Google服务问题      

由于国内许多手机阉割了Google服务,所以不很可能不能使用该服务。不知道有没有相应的办法呢?

4、虚拟机不支持GooglePlayServices的问题

    虽然有办法在虚拟机中装入GooglePlayServices,但是根据Google官方的解释,虚拟机是不支持的(我也测试过,的确不行)。

三、源码下载

源码地址http://download.csdn.net/detail/btyh17mxy/5532345

更多相关文章

  1. 解决通知关闭Toast失效问题
  2. 在命令行中通过adb shell am broadcast发送广播通知
  3. android 通知栏背景颜色跟随app导航栏背景颜色变化而变化
  4. Android(安卓)学习记录 之 notification
  5. Android(安卓)之 Shape (圆角输入框)
  6. Android中View和SurfaceView
  7. 在android状态栏上添加多个图标
  8. Android(安卓)Shell命令dumpsys
  9. android 保存图片到相册并正常显示

随机推荐

  1. Android 之采用execSQL和rawQuery方法完
  2. Android应用安全学习笔记前言
  3. Flutter基础—你好,Flutter!
  4. [置顶] Android中View的加载过程
  5. Android主流屏幕分辨率介绍
  6. ListView有背景图片或背景颜色,那么在滑动
  7. android openGL ES2 一切从绘制纹理开始
  8. Android(安卓)ViewPager实现循环轮播图
  9. android 7种网络连接方式《IT蓝豹》
  10. Android之Handler非主线程更新UI