热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ADeepDiveIntoLocation

2019独角兽企业重金招聘Python工程师标准项目主页:http:code.google.compandroid-protips-location[Thispostisby

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

项目主页:http://code.google.com/p/android-protips-location/

[This post is by Reto Meier, Tech Lead for Android Developer Relations, who wrote the book on Android App development. — Tim Bray]


I'm a big fan of location-based apps, but not their seemingly-inevitable startup latency.

Whether it's finding a place to eat or searching for the nearest Boris Bike, I find the delay while waiting for the GPS to get a fix, and then for the results list to populate, to be interminable. Once I’m in a venue and ready to get some tips, check-in, or review the food, I’m frequently thwarted by a lack of data connection.

Rather than shaking my fist at the sky, I’ve written an open-source reference app that incorporates all of the tips, tricks, and cheats I know to reduce the time between opening an app and seeing an up-to-date list of nearby venues - as well as providing a reasonable level of offline support — all while keeping the impact on battery life to a minimum.

Show Me the Code

You can check-out the Android Protips for Location open source project from Google Code. Don’t forget to read the Readme.txt for the steps required to make it compile and run successfully.

What Does it Actually Do?

It uses the Google Places API to implement the core functionality of apps that use location to provide a list of nearby points of interest, drill down into their details, and then check-in/rate/review them.

The code implements many of the best-practices I detailed in my Google I/O 2011 session, Android Protips: Advanced Topics for Expert Android Developers (video), including using Intents to receive location updates, using the Passive Location Provider, using and monitoring device state to vary refresh rates, toggling your manifest Receivers at runtime, and using the Cursor Loader.

The app targets Honeycomb but supports Android platforms from 1.6 and up.

Nothing would make me happier than for you to cut/copy/borrow / steal this code to build better location-based apps. If you do, I’d love it if you told me about it!

Now that you’ve got the code, let’s take a closer look at it

My top priority was freshness: Minimize the latency between opening the app and being able to check in to a desired location, while still minimizing the impact of the app on battery life.

Related requirements:

  • The current location has to be found as quickly as possible.

  • The list of venues should update when the location changes.

  • The list of nearby locations and their details must be available when we’re offline.

  • Check-ins must be possible while we’re offline.

  • Location data and other user data must be handled properly (see our prior blog post on best practices).

Freshness means never having to wait

You can significantly reduce the latency for getting your first location fix by retrieving the last known location from the Location Manager each time the app is resumed.

In this snippet taken from the GingerbreadLastLocationFinder, we iterate through each location provider on the device — including those that aren't currently available — to find the most timely and accurate last known location.

List matchingProviders = locationManager.getAllProviders();
for (String provider: matchingProviders) {Location location = locationManager.getLastKnownLocation(provider);if (location != null) {float accuracy = location.getAccuracy();long time = location.getTime();if ((time > minTime && accuracy bestTime){bestResult = location;bestTime = time;}}
}

If there is one or more locations available from within the allowed latency, we return the most accurate one. If not, we simply return the most recent result.

In the latter case (where it’s determined that the last location update isn't recent enough) this newest result is still returned, but we also request a single location update using that fastest location provider available.

if (locationListener != null &&(bestTime maxDistance)) { IntentFilter locIntentFilter = new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION);context.registerReceiver(singleUpdateReceiver, locIntentFilter); locationManager.requestSingleUpdate(criteria, singleUpatePI);
}

Unfortunately we can’t specify “fastest” when using Criteria to choose a location provider, but in practice we know that coarser providers — particularly the network location provider — tend to return results faster than the more accurate options. In this case I’ve requested coarse accuracy and low power in order to select the Network Provider when it’s available.

Note also that this code snippet shows the GingerbreadLastLocationFinder which uses the requestSingleUpdate method to receive a one-shot location update. This wasn’t available prior to Gingerbread - check out the LegacyLastLocationFinder to see how I have implemented the same functionality for devices running earlier platform versions.

The singleUpdateReceiver passes the received update back to the calling class through a registered Location Listener.

protected BroadcastReceiver singleUpdateReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {context.unregisterReceiver(singleUpdateReceiver);String key = LocationManager.KEY_LOCATION_CHANGED;Location location = (Location)intent.getExtras().get(key);if (locationListener != null && location != null)locationListener.onLocationChanged(location);locationManager.removeUpdates(singleUpatePI);}
};

Use Intents to receive location updates

Having obtained the most accurate/timely estimate of our current location, we also want to receive location updates.

The PlacesConstants class includes a number of values that determine the frequency of location updates (and the associated server polling). Tweak them to ensure that updates occur exactly as often as required.

// The default search radius when searching for places nearby.
public static int DEFAULT_RADIUS = 150;
// The maximum distance the user should travel between location updates.
public static int MAX_DISTANCE = DEFAULT_RADIUS/2;
// The maximum time that should pass before the user gets a location update.
public static long MAX_TIME = AlarmManager.INTERVAL_FIFTEEN_MINUTES;

The next step is to request the location updates from the Location Manager. In this snippet taken from the GingerbreadLocationUpdateRequester we can pass the Criteria used to determine which Location Provider to request updates from directly into the requestLocationUpdates call.

public void requestLocationUpdates(long minTime, long minDistance, Criteria criteria, PendingIntent pendingIntent) {locationManager.requestLocationUpdates(minTime, minDistance, criteria, pendingIntent);
}

Note that we're passing in a Pending Intent rather than a Location Listener.

Intent activeIntent = new Intent(this, LocationChangedReceiver.class);
locationListenerPendingIntent = PendingIntent.getBroadcast(this, 0, activeIntent, PendingIntent.FLAG_UPDATE_CURRENT);

I generally prefer this over using Location Listeners as it offers the flexibility of registering receivers in multiple Activities or Services, or directly in the manifest.

In this app, a new location means an updated list of nearby venues. This happens via a Service that makes a server query and updates the Content Provider that populates the place list.

Because the location change isn’t directly updating the UI, it makes sense to create and register the associated LocationChangedReceiver in the manifest rather than the main Activity.

The Location Changed Receiver extracts the location from each update and starts the PlaceUpdateService to refresh the database of nearby locations.

if (intent.hasExtra(locationKey)) {Location location = (Location)intent.getExtras().get(locationKey);Log.d(TAG, "Actively Updating place list");Intent updateServiceIntent = new Intent(context, PlacesConstants.SUPPORTS_ECLAIR ? EclairPlacesUpdateService.class : PlacesUpdateService.class);updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_LOCATION, location);updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS);updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, true);context.startService(updateServiceIntent);
}

Monitor inactive providers for a better option

The snippet from PlacesActivity below shows how to monitor two important conditions:

  • The Location Provider we are using being deactivated.

  • A better Location Provider becoming available.

In either case, we simply re-run the process used to determine the best available provider and request location updates.

// Register a receiver that listens for when the provider I'm using has been disabled.
IntentFilter intentFilter = new IntentFilter(PlacesConstants.ACTIVE_LOCATION_UPDATE_PROVIDER_DISABLED);
registerReceiver(locProviderDisabledReceiver, intentFilter);// Listen for a better provider becoming available.
String bestProvider = locationManager.getBestProvider(criteria, false);
String bestAvailableProvider = locationManager.getBestProvider(criteria, true);
if (bestProvider != null && !bestProvider.equals(bestAvailableProvider))locationManager.requestLocationUpdates(bestProvider, 0, 0, bestInactiveLocationProviderListener, getMainLooper());

Freshness means always being up to date. What if we could reduce startup latency to zero?

You can start the PlacesUpdateService in the background to refresh the list of nearby locations while your app is in the background. Done correctly, a relevant list of venues can be immediately available when you open the app.

Done poorly, your users will never find this out as you’ll have drained their battery too quickly.

Requesting location updates (particularly using the GPS) while your app isn’t in the foreground is poor practice, as it can significantly impact battery life. Instead, you can use the Passive Location Provider to receive location updates alongside other apps that have already requested them.

This extract from the FroyoLocationUpdateRequester enables passive updates on Froyo+ platforms.

public void requestPassiveLocationUpdates(long minTime, long minDistance, PendingIntent pendingIntent) {locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,PlacesConstants.MAX_TIME, PlacesConstants.MAX_DISTANCE, pendingIntent);
}

As a result receiving background location updates is effectively free! Unfortunately the battery cost of your server downloads aren’t, so you’ll still need to carefully balance how often you act on passive location updates with battery life.

You can achieve a similar effect in pre-Froyo devices using inexact repeating non-wake alarms as shown in the LegacyLocationUpdateRequester.  

public void requestPassiveLocationUpdates(long minTime, long minDistance, PendingIntent pendingIntent) {alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, System.currentTimeMillis()+PlacesConstants.MAX_TIME, PlacesConstants.MAX_TIME, pendingIntent);
}

Rather than receiving updates from the Location Manager, this technique manually checks the last known location at a frequency determined by the maximum location update latency.

This legacy technique is significantly less efficient, so you may choose to simply disable background updates on pre-Froyo devices.

We handle updates themselves within the PassiveLocationChangedReceiver which determines the current location and starts the PlaceUpdateService.

if (location != null) {Intent updateServiceIntent = new Intent(context, PlacesConstants.SUPPORTS_ECLAIR ? EclairPlacesUpdateService.class : PlacesUpdateService.class);updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_LOCATION, location);updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS);updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, false);context.startService(updateServiceIntent);
}

Using Intents to passively receive location updates when your app isn't active

You’ll note that we registered the Passive Location Changed Receiver in the application manifest.

As a result we can continue to receive these background updates even when the application has been killed by the system to free resources.

This offers the significant advantage of allowing the system to reclaim the resources used by your app, while still retaining the advantages of a zero latency startup.

If your app recognizes the concept of “exiting” (typically when the user clicks the back button on your home screen), it’s good form to turn off passive location updates - including disabling your passive manifest Receiver.

Being fresh means working offline

To add offline support we start by caching all our lookup results to the PlacesContentProvider and PlaceDetailsContentProvider.

Under certain circumstances we will also pre-fetch location details. This snippet from the PlacesUpdateService shows how pre-fetching is enabled for a limited number of locations.

Note that pre-fetching is also potentially disabled while on mobile data networks or when the battery is low.

if ((prefetchCount }

We use a similar technique to provide support for offline checkins. The PlaceCheckinService queues failed checkins, and checkins attempted while offline, to be retried (in order) when the ConnectivityChangedReceiver determines that we’re back online.

Optimizing battery life: Smart Services and using device state to toggle your manifest Receivers

There's no point running update services when we aren’t online, so the PlaceUpdateService checks for connectivity before attempting an update.

NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&activeNetwork.isConnectedOrConnecting();

If we’re not connected, the Passive and Active Location Changed Receivers are disabled and the the ConnectivityChangedReceiver is turned on.

ComponentName connectivityReceiver = new ComponentName(this, ConnectivityChangedReceiver.class);
ComponentName locationReceiver = new ComponentName(this, LocationChangedReceiver.class);
ComponentName passiveLocationReceiver = new ComponentName(this, PassiveLocationChangedReceiver.class);pm.setComponentEnabledSetting(connectivityReceiver,PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);pm.setComponentEnabledSetting(locationReceiver,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);pm.setComponentEnabledSetting(passiveLocationReceiver,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

The ConnectivityChangedReceiver listens for connectivity changes. When a new connection is made, it simply disables itself and re-enables the location listeners.

Monitoring battery state to reduce functionality and save power

When your phone is on its last 15%, most apps are firmly in the back seat to conserving what watts you have remaining. We can register manifest Receivers to be alerted when the device enters or leaves the low battery state.


This snippet from the PowerStateChangedReceiver disables the PassiveLocationChangedReceiver whenever the device enters a low battery state, and turns it back on once the battery level is okay.

boolean batteryLow = intent.getAction().equals(Intent.ACTION_BATTERY_LOW);pm.setComponentEnabledSetting(passiveLocationReceiver,batteryLow ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED :PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP);

What’s Next?

This is already a monster post, so I’m going to leave it there. I’ll follow up in the next week with a post on my personal blog, The Radioactive Yak, that will go in to more detail on the psychic and smooth elements of this app like using the Backup Manager and the Cursor Loader.

I also plan to build a similar reference app for news apps, so that I can spend more time reading and less time waiting.

In the mean time, happy coding!


转:https://my.oschina.net/dingbuoyi/blog/61876



推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
author-avatar
拍友2502914573
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有