以最小用户影响后台发送从Android设备数据到服务器 [英] Sending data from Android device to server in the background with minimal user impact

查看:236
本文介绍了以最小用户影响后台发送从Android设备数据到服务器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序,生成需要发送到我的服务器最终(几个小时的延迟是没有问题的)数据。我想以最小的影响到用户做到这一点,这对我意味着什么:

  • 在最小的电池使用
  • 在没有额外的权限,尤其不能粗略的人
  • 在没有其他奇异的变化对系统(参见后面的示例)

到目前为止,我已经找到了三种方法,即所有有一些严重的问题:

  1. 使用 SyncAdapter触发通过网络讯息
    从技术上讲,这听起来像是一个完美的答案的问题:有没有系统调用我的应用程序服务,只要它做的东西在网络上反正。这样一来,网络是保证可用,有几乎没有任何额外的电池使用,因为无线功能已经打开,整个事情发生的背景...
    然后我尝试实际执行它...
    不幸的是,这种方法需要一些怪异的修改系统:我的应用程序需要创建一个新的虚拟帐户的同步过程中,即使我的应用程序不以其他方式使用任何帐户。不幸的是,<一个href="http://stackoverflow.com/questions/19562432/hide-dummy-account-for-sync-adapter-from-settings">this帐户无法从用户隐藏的,所以现在当用户点击添加帐户,在系统设置我的应用程序上市,但在​​应用程序中,没有迹象表明用户账户的任何地方......另外,我需要申请各种奇怪的权限,如创建帐户和设置密码,打开或关闭同步和读取同步设置。为什么一个应用程序,甚至没有为我提供一个用户帐户需要的权限设置我的设备上的密码???不冷静...删除!

  2. 使用的BroadcastReceiver 和<一HREF =htt​​p://stackoverflow.com/a/10675746/1217178>检查连接火灾时:
    这需要许可查看网络连接,甚至可能关于Wi-Fi网络查看信息,如Wi-Fi无线是否启用和连接Wi-Fi设备的名称......为什么会这个程序关心我的Wi-Fi或网上邻居?令人毛骨悚然......没有得到安装在我的设备上...
    这种方法的好处是,网络连通性的变化,而无线电已打开(至少发生改变的可用状态)可能只发生。所以,如果我用这个事件来触发上传(尝试),电池的影响将是最小的,因为我不会导致打开收音机只是上传。不幸的是,这个事件可能不会被经常调用。而更糟糕的是,从它听起来像的BroadcastReceiver的文档才会被调用,而我的应用程序是在恢复的通态。所以,我不会甚至可以利用的事件,而我的应用程序暂停...即使用这些广播事件只会比选择3稍好:

  3. 只是一味的启动上载定期,如果失败,重试一遍后:
    这种方法不需要任何奇怪的权限和不惹任何系统设置。但是,很显然,这是最糟糕的,我可以使用电池做。所以,虽然这是可行的,我宁愿找一个更聪明的替代...

有没有办法解决前两种方法中的一个问题?最佳的解决办法是未经虚设用户帐户SyncAdapter,或者,至少一个隐藏的一个,或一个已经存在于系统中。但是许多小时搜索没有取得任何可用答案...

还是有另一种更好的方法完全?

解决方案

@CommonsWare指出另外两个方法:

<醇开始=4>
  • 使用的jobscheduler
    这使得调度作业依赖于特定的网络连接或设备,甚至有一定的充电状态将来执行。它说,它试图批处理作业执行系统上的所有应用程序,它可以节省电池,避免额外的射频激活,如果其他应用程序也有执行该接入网络的工作。但是,如果我的应用程序是唯一一个需要做一个网络请求,我不认为的jobscheduler提供的电池节电不尽相同数额作为网络挠痒痒驱动SyncAdapter会。这仅仅是基于我目前的理解猜测,虽然。
    与使用的jobscheduler最大的问题在于,它需要API级别21,即安卓5.0+。对于某些应用程序,这是可以接受的,但对我来说这不是......

  • 使用谷歌播放服务云Messenger的API,特别是 GcmNetworkManager 这似乎是很多相同的提供pretty的相同的功能(除了控制重试退避策略)认为的jobscheduler做,但它可在有谷歌播放服务V7.5或更高版本的所有设备。谷歌播放服务是供设备早在Android 2.3的,但是V7.5只发布了2015年五月左右结束,而V7.5也可用于Android 2.3,也不能保证它安装。
    要使用它时,(据说非常轻巧)谷歌播放服务API库需要被添加到应用程序,它提供了方法<一href="https://developers.google.com/android/reference/com/google/android/gms/common/GoogleApiAvailability#isGooglePlayServicesAvailable(android.content.Context)"相对=nofollow> GoogleApiAvailability.isGooglePlayServicesAvailable(...),可以检查,如果谷歌游戏服务确实安装和更新,以通过API库所需的版本。为了最大限度地提高的可能性,这是的情况下,API库V7.5可以加入到该应用(参见<一href="http://stackoverflow.com/questions/30556787/how-can-i-use-older-version-of-google-play-services/32859695#32859695">here和<一href="http://stackoverflow.com/questions/20982533/how-to-download-older-google-play-services/26838281#26838281">here如何得到它)。如果校验失败,由于用户可解析的问题,该库甚至提供所需的对话框,提示用户来解决该问题(例如运行的更新,...)。
    这种方法的主要优点是,它不需要用于该应用任何额外的权限和它不依赖于任何其他的变化的系统(模拟用户帐户,...)。因此,它是完全透明的用户

  • 我还发现了一个额外的方法,以上3点有点关系:

    <醇开始=6>
  • 使用<一个href="http://developer.android.com/reference/android/app/AlarmManager.html#setInexactRepeating(int,%20long,%20long,%20android.app.PendingIntent)"相对=nofollow> AlarmManager.setInexactRepeating(...):
    该AlarmManager还试图批量回调在一起,就像的jobscheduler和GcmNetworkManager做。事实上,在寻找LogCat中,有时读的AlarmManager:检查报警...... com.google.android.gms的,似乎GCM,大概也的jobscheduler,使用AlarmManager触发其执行。从AlarmManager缺少的特点是:燮pressing基于网络连接或电池的充电状态,和失败执行与退避时间表的自动重试的回调。从技术上讲,人们可以很容易地添加这些功能,但他们中的一些,比如检查网络连接,可能需要额外的权限
    这种方法的主要优点是:它是适用于几乎所有的设备(从API​​级别3)。
  • 目前我已在GcmNetworkManager方法来实现(5点),并将其按预期工作。

    但实际上,我考虑转移到AlarmManager.setInexactRepeating(...)(6点),以最大限度地提高兼容性。检查设备充电状态似乎是可能不需要额外的权限的而非检查网络连接,我可以断火的HTTP请求,并检查是否失败...我会错过的唯一功能是确定用户是否是一个计量连接或不。当然,这将是一些工作落实重,背取舍,...

    更新:

    好像的jobscheduler实际上并检测现有的网络活动(见本的的jobscheduler介绍),这可能会使其优于只使用AlarmManager,和pretty的多一样好,甚至更好SyncAdapters ...的GCM文件还声称,根据当前网络活动的回调优化(不只是可用性)......所以,我想最佳的解决方案是使用的jobscheduler如果有的话,并回落到GCM它不是并向AlarmManager,其中GCM不可用或者......呸......

    I have an app that generates data that needs to be sent to my server eventually (several hours of delay are no problem at all). I would like to do this with minimal impact to the user, which means to me:

    • Minimal battery use
    • No extra permissions, especially not "sketchy" ones
    • No other strange changes to the system (see example later)

    So far I've found three approaches, that all have some serious problems:

    1. Using a SyncAdapter triggered by a network message:
      Technically, this sounded like the perfect answer to the problem: Have the system call a service in my app whenever it's doing something on the network anyways. That way, the network is guaranteed to be available, there's barely any extra battery use, since the radio is already on, and the whole thing happens in the background...
      And then I tried actually implementing it...
      Unfortunately, this approach requires some weird modifications to the system: My app needs to create a new dummy account for the sync-process, even if my app doesn't otherwise use any accounts. And unfortunately, this account cannot be hidden from the user, so now my app is listed when the user clicks "Add Account" in the System Settings, but inside the app, there is no indication of user accounts anywhere... Also, I need to request all sorts of strange permissions, like "Create accounts and set passwords", "Toggle sync on and off", and "Read sync settings". Why would an app that doesn't even provide me with a user account require a permission to set passwords on my device??? Not cool... Delete!!!

    2. Using a BroadcastReceiver and checking for connectivity when it fires:
      This requires permission to "view network connections" and possibly even "view information about Wi-Fi networking, such as whether Wi-Fi is enabled and names of connected Wi-Fi devices" ... Why would this app care about my wi-fi or network neighborhood? Creepy... Not getting installed on my device...
      The nice thing about this approach is that a network connectivity change likely only happens while the radios are turned on (at least a change to the "available" state). So, if I use this event to trigger an upload (attempt), the battery-impact will be minimal as I won't cause the radio to turn on just for the upload. Unfortunately, this event will probably not be called very often. And to make matters worse, from the docs it sounds like the BroadcastReceiver will only be called while my app is in the resumed-state. So, I won't even be able to make use of events while my app is paused... I.e. using these broadcast events will only be slightly better than option 3:

    3. Just blindly initiating the upload at regular intervals and, if it fails, retrying it again later:
      This approach doesn't require any strange permissions and doesn't mess with any system settings. But, clearly, it is the worst I can do for battery use. So, while it's workable, I'd rather find a smarter alternative...

    Is there a way to fix the issues of one of the first two approaches? The optimal solution would be a SyncAdapter without a dummy user-account, or, at least, with a hidden one, or one that already exists in the system... But many hours of searching didn't yield any usable answers...

    Or is there another better way altogether?

    解决方案

    @CommonsWare pointed out two further approaches:

    1. Using JobScheduler:
      This allows scheduling jobs for future execution that depend on certain network connectivity or even a certain charging status of the device. It says that it tries to batch job executions for all applications on the system, which may save battery by avoiding additional radio activation, if other apps also have jobs executing that access the network. But if my app is the only one needing to do a network request, I don't think the JobScheduler provides quite the same amount of battery-saving as a network-tickle-driven SyncAdapter would. This is just a guess based on my current understanding, though.
      The big issue with using the JobScheduler is that it requires API level 21, i.e. Android 5.0+. For some apps, this might be acceptable, but for me it's not...

    2. Using Google Play Services' Cloud Messenger API, specifically GcmNetworkManager: This seems to provide pretty much identically the same functionality (except for controlling the retry-backoff-strategy) that the JobScheduler does, except it is available on all devices that have Google Play Services v7.5 or higher installed. Google Play Services is available for devices as far back as Android 2.3, but v7.5 was only released end of May 2015. So, while v7.5 is also available for Android 2.3, it is not guaranteed that it is installed.
      To use it, the (supposedly very lightweight) Google Play Services API library needs to be added to the app, which provides the method GoogleApiAvailability.isGooglePlayServicesAvailable(...) that can check if Google Play Services is indeed installed and updated to the version required by the API library. To maximize the chances that this is the case, the API library v7.5 can be added to the app (see here and here for how to get it). If the check fails due to a user-resolvable problem, the library even provides the required dialog to prompt the user to fix the issue (e.g. run an update, ...).
      The major advantage of this approach is that it does not require any extra permissions for the app and it doesn't rely on any other changes to the system (mock user accounts, ...). So, it is entirely transparent to the user.

    I also found one additional approach, somewhat related to point 3 above:

    1. Using AlarmManager.setInexactRepeating(...):
      The AlarmManager also tries to batch callbacks together, just like the JobScheduler and GcmNetworkManager do. In fact, looking at LogCat that sometimes reads "AlarmManager: Checking for alarms... com.google.android.gms", it seems that GCM, and probably also JobScheduler, use the AlarmManager to trigger their execution. The features missing from the AlarmManager are: Suppressing a callback based on network connectivity or battery charging status, and automatic retries of failed executions with a back-off schedule. Technically, one can easily add these features, but some of them, like checking for network connectivity, might require additional permissions.
      The major advantage of this approach is: It's available on almost all devices (starting API level 3).

    Currently I have the GcmNetworkManager approach implemented (point 5) and it works as expected.

    But I'm actually considering moving to AlarmManager.setInexactRepeating(...) (point 6) to maximize compatibility. Checking for the device charging state seems to be possible without extra permissions and rather than checking for network connectivity, I can just fire off the http request and check whether it failed... The only feature I would be missing is determining whether the user is on a metered connection or not. And, of course, it will be a bit of work to implement retries, back-offs, ...

    Update:

    It seems like JobScheduler actually does detect existing network activity (see this JobScheduler introduction), which might make it superior to just using the AlarmManager, and pretty much as good or better than SyncAdapters... The GCM documentation also claims callback optimization based on current network activity (not just availability)... So, I guess the optimal solution would be to use the JobScheduler where available and fall back to GCM where it's not and to the AlarmManager where GCM isn't available either... Yuck...

    这篇关于以最小用户影响后台发送从Android设备数据到服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆