Android学习系列(19)--App离线下载
1.下载管理(开始、取消下载)。
2.网络判断(Wi-Fi,3G)。
3.独立进程。
4.定时和手机催醒。
5.自启动。
这里不便关注下载的细节方法,网络下载的方法很多,大概如下:
- /**
- * 下载文件
- * @param url 下载地址
- * @param dest 下载存放的本地文件
- * @param append 断点续传
- * @return
- * @throws Exception
- */
- public long download(String url, File dest, boolean append) throws Exception{
- //初始化变量
- //准备工作
- // ... ...
- try {
- // ... ...
- while((readSize = is.read(buffer)) > 0){
- //网络判断
- os.write(buffer, 0, readSize);
- os.flush();
- //如果需要停止下载,如取消,跳出当前下载
- }
- }
- } finally {
- // ... ...
- }
- // ... ...
- }
复制代码
(1).在下载的时候,我们希望能及时检测到网络状况,比如由Wi-Fi切换到3G网络下,我们应该能及时停止下载。
(2).当用户选择取消下载的时候,我们也能停止当前下载。
获取当前网络状态,主要分为Wi-Fi和Mobile(包括3G,GPRS)两种,我们写一个工具类如下:
- public class NetworkUtils {
- public final static int NONE = 0;//无网络
- public final static int WIFI = 1;//Wi-Fi
- public final static int MOBILE = 2;//3G,GPRS
- /**
- * 获取当前网络状态
- * @param context
- * @return
- */
- public static int getNetworkState(Context context){
- ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- //手机网络判断
- State state = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
- if(state == State.CONNECTED||state == State.CONNECTING){
- return MOBILE;
- }
- //Wifi网络判断
- state = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
- if(state == State.CONNECTED||state == State.CONNECTING){
- return WIFI;
- }
- return NONE;
- }
- }
复制代码
根据网络状态,我们能够控制下载方式:
(1).下载量很大的情况下,我们不大可能在3G情况下进行下载,容易引起用户的反感和担忧。
(2).当客户十分确认可以在3G情况下进行下载,那么也是允许的。
所以,这里提出一个需求,我们要为下载方式设置一个灵活的等级,结合离线下载的特点,我们给出3中方案由用户选择:
(1).移动数据情况下自动下载
(2).只允许Wi-Fi情况下自动下载
(3).关闭下载
这里只列出了自动下载,是因为如果不是自动下载,手动下载用户可以随意控制,无需设置,当然设计到丢流量情况下,如3G下手动下载,提示用户会消耗较大的数据流量,慎用即可。
- public class Constant {
- //离线下载网络设置
- public final static int OFFLINE_MOBILE = 0;
- public final static int OFFLINE_WIFI = 1;
- public final static int OFFLINE_OFF = 3;
- }
- public class Global {
- //设置默认关闭状态,
- //为了应用程序下次启动能够记住用户选择,在第一次启动应用的时候,这个值最终应该存放到数据库中,
- public static int OfflineNetworkSetting = Constant.OFFLINE_OFF;
- }
复制代码
离线下载,无论何时何地,只要适宜进行,则当进行,目前主流的做法是建立后台服务。
- public class OfflineSerivice extends Service {
- // ... ...
- }
复制代码
(1).OfflineService的进程如果默认和应用程序一致,则在应用进程kill的时候,会重启一次(网易新闻在离线下载的时候,退出应用,下载会停顿一小会儿就是这个原因),如果影响不大,这个方案也是可选的。
(2).OfflineService的进程和应用程序分开,如应用程序进程为"cn.cnblogs.tianxia.download",则离线下载服务的进程设置为"cn.cnblogs.tianxia.download.offline",撇清和应用程序的进程的关系。当然,这个会带来一个新的问题,进程间通信,当然因为离线下载和应用程序间的模块比较独立,这个问题还算比较好规避。
(3).OfflineService的进程如果默认和应用程序一致,但是OfflineService继承IntentService,可避免重启的问题,这个是《Pro Android 3》书中提到的方法,非常的好用,但是非常遗憾,本人最近才看到,暂时没有亲手测验,不敢在工作中试用。
按理说,方案3是最佳方案, 但是个人原因,选择了方案2.
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.cnblogs.download">
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <!--省略其他-->
- <service android:name="cn.cnblogs.download.OfflineService" android:process="cn.cnblogs.download.offline"/>
- </application>
- </manifest>
复制代码
4.定时下载和手机催醒
根据用户设置,在wifi的情况下自动下载,但是自动下载的方案有很多种,频繁的更新下载,定点下载(早上8点,下午4点),间隔下载(每隔6小时)。
这里,我们选择每隔6个小时下载。
(1).这里介绍一种错误的方案。一看到每隔6小时,很容易想到开启一个子线程计时,累计到6个小时,子线程通知下载服务开始新一轮下载。这个方案的思路是没有错的,但是却忽略了手机处于休眠状态,这个子线程其实是停止执行的,那么所谓的6个小时的效果就又可能永远达不到,而且必然不正确或者不准确。
(2).所以,需要使用到一种不休眠的办法:定时器和广播接收器。每隔6小时我们发送一个广播,广播接收器通知开始离线下载。(可参考newsrob源码和书籍《Pro Android 3》):
- public class OfflineSerivice extends Service {
- //上次成功下载的时间
- private long lastDownloadTime;
- // 省略代码... ...
- public static void startAlarm(Context context){
- AlarmManager alarmManager = (AlarmManager) context.getSystemService("alarm");
- //每隔6个小时发送广播到OfflineAlarmReceiver
- //也可以设置为10分钟检测一下下载条件,而在OfflineAlarmRecrive中判断开始下载,避免6小时下载失败需再等待6小时过长时间的问题
- Intent intent = new Intent(context,OfflineAlarmReceiver.class);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, intent, 0);
- alarmManager.cancel(pendingIntent);
- alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(), 3600000*6, pendingIntent);
- }
- }
复制代码
OfflineAlarmRecriver中处理开始下载条件,并通知开始下载:
- public class OfflineAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent arg1) {
- // 省略代码...,初始化变量,准备工作...
- if(System.currentTimeMillis()-OfflineService.lastDownloadTime>3600000*60&&其他条件){
- //打开离线下载服务
- Intent alarmIntent = new Intent(context, OfflineService.class);
- context.startService(alarmIntent);
- }
- }
- }
复制代码
前面我们提到了线程休眠的问题,需要在下载的时候能够唤醒手机,下载完成后能回到休眠状态,下面是两个工具方法:
- public static PowerManager.WakeLock wakeLock;
- /**
- * 唤醒服务
- */
- public static void acquireWakeLock(Context context){
- if(wakeLock!=null){
- return;
- }
- PowerManager powerManager = (PowerManager)(context.getSystemService(Context.POWER_SERVICE));
- wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "com.cnblogs.download.OfflineService");
- wakeLock.acquire();
- }
- /**
- * 释放唤醒服务,返回休眠状态
- */
- public static void releaseWakeLock(){
- if(wakeLock!=null&&wakeLock.isHeld()){
- wakeLock.release();
- wakeLock = null;
- }
- }
复制代码
其中PowerManager.PARTIAL_WAKE_LOCK意思是仅唤醒CPU方式,此时能自动主动检测网络状态,从而保证网络正常。
需要在Mainifest.xml中设置权限:
- <uses-permission android:name="android.permission.WAKE_LOCK" />
复制代码
然后在下载服务的onStartConmmand()激活催醒状态,然后在下载完成后释放催醒状态:
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- acquireWakeLock(OfflineService.this);
- //省略代码... ...
- return super.onStartCommand(intent, flags, startId);
- }
复制代码
5.自启动
为了代码清晰,我们再定义一个自启动的receiver:
- /**
- * 自启动离线下载服务
- * @author user
- */
- public class OfflineReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent arg1) {
- //启动定时器
- OfflineService.startAlarm(context);
- }
- }
复制代码
在AndroidManifest.xml注册此接收器,如下:
- <receiver android:name="cn.cnblogs.download.OfflineReceiver">
- <intent-filter>
- <!--自启动-->
- <action android:name="android.intent.action.BOOT_COMPLETED" />
- <category android:name="android.intent.category.HOME" />
- </intent-filter>
- </receiver>
复制代码
为了简洁明晰,开门见山,本文仅针对离线下载的最重要的关联点列举说明,而对于清理策略,手动和自动模式,界面跳转,UI设计和业务要求没有过多的涉及,但是往往这些东西才是花费你大量的时间,需要大量细节的积累和耐心的调试,我们唯一要做的事情就是不断的完善!
Android学习系列(19)--App离线下载的更多相关文章
- Android学习系列(7)--App轮询服务器消息
这篇文章是android开发人员的必备知识. 1.轮询服务器 一般的应用,定时通知消息可以采用轮询的方法从服务器拿取消息,当然实时消息通知的话,建议采用推送服务. 其中需要注意轮询的频率 ...
- Android学习系列(15)--App列表之游标ListView(索引ListView)
游标ListView,提供索引标签,使用户能够快速定位列表项. 也可以叫索引ListView,有的人称也为Tweaked ListView,可能更形象些吧. 一看图啥都懂了: 1. ...
- Android学习系列(23)--App主界面实现
在上篇文章<Android学习系列(22)--App主界面比较>中我们浅略的分析了几个主界面布局,选了一个最大众化的经典布局.今天我们就这个经典布局,用代码具体的实现它. 1.预览图先看下 ...
- Android学习系列(17)--App列表之圆角ListView(续)
http://www.cnblogs.com/qianxudetianxia/archive/2011/09/19/2068760.html 本来这篇文章想并到上篇Android学习系列(16)- ...
- Android学习系列(18)--App工程结构搭建
本文算是一篇漫谈,谈一谈关于Android开发中工程初始化的时候如何在初期我们就能搭建一个好的架构. 关于android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的 ...
- Android学习系列(3)--App自动更新之自定义进度视图和内部存储
友好的视觉感知和稳定的不出错表现,来自于我们追求美感和考虑的全面性,博客园从技术的角度,一直我都很欣赏.这篇文章是android开发人员的必备知识,是我特别为大家整理和总结的,不求完美,但是有用. 这 ...
- Android学习系列(37)--App调试内存泄露之Context篇(下)
接着<Android学习系列(36)--App调试内存泄露之Context篇(上)>继续分析. 5. AsyncTask对象 我N年前去盛大面过一次试,当时面试官极力推荐我使用AsyncT ...
- Android学习系列(11)--App列表之拖拽ListView(下)
接着上篇Android学习系列(10)--App列表之拖拽ListView(上)我们继续实现ListView的拖拽效果. 7.重写onTouchEvent()方法. 在这个方法中我们主要是处理 ...
- Android学习系列(10)--App列表之拖拽ListView(上)
研究了很久的拖拽ListView的实现,受益良多,特此与尔共飨. 鉴于这部分内容网上的资料少而简陋,而具体的实现过程或许对大家才有帮助,为了详尽而不失真,我们一步一步分析,分成两篇文章. ...
随机推荐
- 学习MongoDB-应用举例(利用java操作MongoDB)
Java驱动程序是MongoDB最早的驱动,已用于生产环境很长时间,十分稳定.可以到MongoDB官方网站下载驱动,驱动版本为2.9.0. 这个jar文件中,有两个包,我们会经常用到com.mongo ...
- 牛气冲天的Iframe应用笔记
纵观时下网站,本来网速就有些慢,可是几乎每页都要放什么Banner,栏目图片,版权等一大堆雷同的东西,当然,出于网站风格统一.广告效应的需要,本无可厚非,可毕竟让用户的钱包为这些“点缀“的东西”日益消 ...
- QlikView图表显示同比数据
数据准备例如以下: SalesData: LOAD Num(ID) as ID, Date(Date) as Date, Month, Num(Year) as Year, Num(Sales) as ...
- json字符串和dict互转
json字符串和dict互转 import json str = '{"params":[{"id":222,"offset":0},{&q ...
- 微信小程序:字体保持大小
小程序和网页差不多,前台用wxml把内容摆好,然后用css调整样式.所以和web一样,必须要能够精确控制每一个元素的大小.在Web中,通过CSS基本达到了像素级的控制.但在小程序中,情况有所不同.下面 ...
- C# Lambda表达式 基础
什么是Lambda 表达式? "Lambda表达式"实际上是一个方法,只不过该方法是一个匿名方法(就是没有名字的方法(函数),就是说只有在定义的时候能调用,在其他地方就不能调用了) ...
- Office EXCEL 不用VB,你也可以制作自己的Excel菜单!
还记得这个讨厌的VB吗?为了做一个COM插件,生成一个DLL,麻烦一大堆.其实我们想要的仅仅是把自己写的宏封装一下,更好的调用而已. 打开工具,自定义,在命令菜单中选择新菜单,然后拖放右侧的新菜单到顶 ...
- Discuz常见小问题-如何实现word文档转成帖子
有一些网站比如QQ空间是可以直接导入Word文件生成网页版本的,但是效果不理想 可以发现图片进来之后都是变形了的 最笨的方法是一个一个复制粘贴(当然也不需要这么麻烦,你可以打开一个word文档之后,保 ...
- 【解决】缺少libstdc++.so.6库的原因及解决的方法
问题原因: 系统是64bit,该库是32bit的,在64bit系统上安装32bit库 解决的方法: 1. 查看哪个安装包包括该库:yum provides libstdc++.so.6 libs ...
- jasperreport 通过javabean datasoource实现chart的报表
继上次report的demo后,还在继续做着report方面的research,今天主要是实现了通过javabean datasource填充chart图表,通过webservice下载pdf格式的报 ...