原文:http://www.cnblogs.com/qianxudetianxia/archive/2011/07/20/2108965.html

宜未雨而绸缪,毋临渴而掘井。----朱用纯《治家格言》

离线下载,在有网络的情况下下载服务器数据,以便无网络时也能阅读,就是离线阅读。

离线下载的功能点如下:
      1.下载管理(开始、取消下载)。
      2.网络判断(Wi-Fi,3G)。
      3.独立进程。
      4.定时和手机催醒。
      5.自启动。

1.下载管理
       这里不便关注下载的细节方法,网络下载的方法很多,大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 下载文件
 * @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).当用户选择取消下载的时候,我们也能停止当前下载。

2.网络判断
      获取当前网络状态,主要分为Wi-Fi和Mobile(包括3G,GPRS)两种,我们写一个工具类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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下手动下载,提示用户会消耗较大的数据流量,慎用即可。

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

现在可以根据规则比较当前网络和离线网络设置,判定离线下载服务的开启。

3.独立进程
      离线下载,无论何时何地,只要适宜进行,则当进行,目前主流的做法是建立后台服务。

1
2
3
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>

这样,在启动的时候,能够接受启动广播,并执行启动定时器操作。

6.小结
      为了简洁明晰,开门见山,本文仅针对离线下载的最重要的关联点列举说明,而对于清理策略,手动和自动模式,界面跳转,UI设计和业务要求没有过多的涉及,但是往往这些东西才是花费你大量的时间,需要大量细节的积累和耐心的调试,我们唯一要做的事情就是不断的完善!

【转】Android学习系列–App离线下载功能实现的更多相关文章

  1. Android学习系列--App列表之拖拽ListView(下)

    接着上篇Android学习系列(10)--App列表之拖拽ListView(上)我们继续实现ListView的拖拽效果. 7.重写onTouchEvent()方法.     在这个方法中我们主要是处理 ...

  2. Android学习系列--App列表之拖拽ListView(上)

    研究了很久的拖拽ListView的实现,受益良多,特此与尔共飨.      鉴于这部分内容网上的资料少而简陋,而具体的实现过程或许对大家才有帮助,为了详尽而不失真,我们一步一步分析,分成两篇文章. 一 ...

  3. Android学习之APP点击功能闪退问题的处理一

    java代码如下: activity_main.xml代码如下: APP测试问题:安装好APP后,点击“点击事件”,APP会闪退 检查方向: 1.查看java代码中的成员变量textview是否初始化 ...

  4. Android学习系列(18)--App工程结构搭建

     本文算是一篇漫谈,谈一谈关于Android开发中工程初始化的时候如何在初期我们就能搭建一个好的架构.      关于android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的 ...

  5. Android学习系列(3)--App自动更新之自定义进度视图和内部存储

    友好的视觉感知和稳定的不出错表现,来自于我们追求美感和考虑的全面性,博客园从技术的角度,一直我都很欣赏.这篇文章是android开发人员的必备知识,是我特别为大家整理和总结的,不求完美,但是有用. 这 ...

  6. Android学习系列(37)--App调试内存泄露之Context篇(下)

    接着<Android学习系列(36)--App调试内存泄露之Context篇(上)>继续分析. 5. AsyncTask对象 我N年前去盛大面过一次试,当时面试官极力推荐我使用AsyncT ...

  7. Android学习系列(7)--App轮询服务器消息

    这篇文章是android开发人员的必备知识. 1.轮询服务器     一般的应用,定时通知消息可以采用轮询的方法从服务器拿取消息,当然实时消息通知的话,建议采用推送服务.    其中需要注意轮询的频率 ...

  8. Android学习系列(15)--App列表之游标ListView(索引ListView)

    游标ListView,提供索引标签,使用户能够快速定位列表项.      也可以叫索引ListView,有的人称也为Tweaked ListView,可能更形象些吧.      一看图啥都懂了: 1. ...

  9. Android学习系列(23)--App主界面实现

    在上篇文章<Android学习系列(22)--App主界面比较>中我们浅略的分析了几个主界面布局,选了一个最大众化的经典布局.今天我们就这个经典布局,用代码具体的实现它. 1.预览图先看下 ...

随机推荐

  1. AndroidUI组件之TabHost

    package com.gc.tabhost; /** * @author Android将军 * * * * 1.TabHost是一种非常有用的组件,TabHost能够非常方便地在窗体上放置 * 多 ...

  2. iis7、iis8配置备份还原

    原文 iis7.iis8配置备份还原 方法1: 1.打开我们的IIS管理器,在功能视图里找到“共享的配置”这个功能然后双击进入. 2.进入“共享的配置”后单机右上方的“导出配置”选项,选择导出配置文件 ...

  3. hdu4758 Walk Through Squares 自动机+DP

    题意:给n*m的地图,在地图的点上走,(n+1)*(m+1)个点,两种操作:往下走D和往右走R.现在要从左上角走到右下角,给定两个操作串,问包含这两个串的走法总共有多少种. 做法:用这两个串构建自动机 ...

  4. 酒旗少年狂暖风,至0基本的前端开发project教师们学习计划

    酒旗风暖少年狂,为0基础前端开发project师做学习计划 夜幕降暂时.走到一张废弃已久的书桌前,打开台灯,看到书桌上已经布满灰尘,而桌上的那盆羸弱的文竹已经枝繁叶茂.我擦干净了桌面,坐了下来,把买回 ...

  5. 在 Windows 7 Professional、Enterprise 或 Ultimate 上安装 IIS 7.5

    原文 在 Windows 7 Professional.Enterprise 或 Ultimate 上安装 IIS 7.5 应用到: Windows Server 2008 R2 默认情况下,Wind ...

  6. PYTHON单元测试

    PYTHON开发入门与实战11-单元测试 1. 单元测试 本章节我们来讲讲django工程中如何实现单元测试,单元测试如何编写以及在可持续项目中单元测试的重要性. 下面是单元测试的定义: 单元测试是开 ...

  7. nodejs安装:nodejs入门

    nodejs开篇 前几天看到好多关于node 的帖子没有单独说明node安装的文章~ 特发此篇 总结一下平时在windows上nodejs的安装... 1.js来搞前后端分离是nodejs的一大特点, ...

  8. HTTPS背后的加密算法(转)

    当你在浏览器的地址栏上输入https开头的网址后,浏览器和服务器之间会在接下来的几百毫秒内进行大量的通信.InfoQ的这篇文章对此有非常详细的描述.这些复杂的步骤的第一步,就是浏览器与服务器之间协商一 ...

  9. from声明

    在整个应用程序,只有三行声明.这是最短单WIN32应用,但它的功能是非常有限,简单地显示一个消息框,示出来,其他什么事情也没有做.以下就来分析这三行语句了.别小看这三行语句.其实是隐藏着非常多知识点在 ...

  10. Intel 80x86 寻址模式

    随着进一步的研究,我们会逐渐接触到你的电脑配置模式的内在联系,和设计原则.在这些,解决计算机是一个非常重要的概念,我们需要很好地理解了什么. 一.预赛: (1)作数 在接触寻址方式之前,我们还要先了解 ...