App离线下载
来源:互联网 发布:淘宝阳光房隔热材料 编辑:程序博客网 时间:2024/04/28 22:40
原文:http://www.cnblogs.com/qianxudetianxia/archive/2011/07/20/2108965.html
宜未雨而绸缪,毋临渴而掘井。----朱用纯《治家格言》
离线下载,在有网络的情况下下载服务器数据,以便无网络时也能阅读,就是离线阅读。
离线下载的功能点如下:
1.下载管理(开始、取消下载)。
2.网络判断(Wi-Fi,3G)。
3.独立进程。
4.定时和手机催醒。
5.自启动。
1.下载管理
这里不便关注下载的细节方法,网络下载的方法很多,大概如下:
01
/**
02
* 下载文件
03
* @param url 下载地址
04
* @param dest 下载存放的本地文件
05
* @param append 断点续传
06
* @return
07
* @throws Exception
08
*/
09
public
long
download(String url, File dest,
boolean
append)
throws
Exception{
10
//初始化变量
11
//准备工作
12
// ... ...
13
14
try
{
15
// ... ...
16
while
((readSize = is.read(buffer)) >
0
){
17
//网络判断
18
19
os.write(buffer,
0
, readSize);
20
os.flush();
21
22
//如果需要停止下载,如取消,跳出当前下载
23
}
24
}
25
}
finally
{
26
// ... ...
27
}
28
// ... ...
29
}
这里要注意几点:
(1).在下载的时候,我们希望能及时检测到网络状况,比如由Wi-Fi切换到3G网络下,我们应该能及时停止下载。
(2).当用户选择取消下载的时候,我们也能停止当前下载。
2.网络判断
获取当前网络状态,主要分为Wi-Fi和Mobile(包括3G,GPRS)两种,我们写一个工具类如下:
01
public
class
NetworkUtils {
02
03
public
final
static
int
NONE =
0
;
//无网络
04
public
final
static
int
WIFI =
1
;
//Wi-Fi
05
public
final
static
int
MOBILE =
2
;
//3G,GPRS
06
07
/**
08
* 获取当前网络状态
09
* @param context
10
* @return
11
*/
12
public
static
int
getNetworkState(Context context){
13
ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
14
15
//手机网络判断
16
State state = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
17
if
(state == State.CONNECTED||state == State.CONNECTING){
18
return
MOBILE;
19
}
20
21
//Wifi网络判断
22
state = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
23
if
(state == State.CONNECTED||state == State.CONNECTING){
24
return
WIFI;
25
}
26
return
NONE;
27
}
28
}
根据网络状态,我们能够控制下载方式:
(1).下载量很大的情况下,我们不大可能在3G情况下进行下载,容易引起用户的反感和担忧。
(2).当客户十分确认可以在3G情况下进行下载,那么也是允许的。
所以,这里提出一个需求,我们要为下载方式设置一个灵活的等级,结合离线下载的特点,我们给出3中方案由用户选择:
(1).移动数据情况下自动下载
(2).只允许Wi-Fi情况下自动下载
(3).关闭下载
这里只列出了自动下载,是因为如果不是自动下载,手动下载用户可以随意控制,无需设置,当然设计到丢流量情况下,如3G下手动下载,提示用户会消耗较大的数据流量,慎用即可。
01
public
class
Constant {
02
//离线下载网络设置
03
public
final
static
int
OFFLINE_MOBILE =
0
;
04
public
final
static
int
OFFLINE_WIFI =
1
;
05
public
final
static
int
OFFLINE_OFF =
3
;
06
}
07
08
public
class
Global {
09
//设置默认关闭状态,
10
//为了应用程序下次启动能够记住用户选择,在第一次启动应用的时候,这个值最终应该存放到数据库中,
11
public
static
int
OfflineNetworkSetting = Constant.OFFLINE_OFF;
12
}
现在可以根据规则比较当前网络和离线网络设置,判定离线下载服务的开启。
3.独立进程
离线下载,无论何时何地,只要适宜进行,则当进行,目前主流的做法是建立后台服务。
1
public
class
OfflineSerivice
extends
Service {
2
// ... ...
3
}
(1).OfflineService的进程如果默认和应用程序一致,则在应用进程kill的时候,会重启一次(网易新闻在离线下载的时候,退出应用,下载会停顿一小会儿就是这个原因),如果影响不大,这个方案也是可选的。
(2).OfflineService的进程和应用程序分开,如应用程序进程为"cn.cnblogs.tianxia.download",则离线下载服务的进程设置为"cn.cnblogs.tianxia.download.offline",撇清和应用程序的进程的关系。当然,这个会带来一个新的问题,进程间通信,当然因为离线下载和应用程序间的模块比较独立,这个问题还算比较好规避。
(3).OfflineService的进程如果默认和应用程序一致,但是OfflineService继承IntentService,可避免重启的问题,这个是《Pro Android 3》书中提到的方法,非常的好用,但是非常遗憾,本人最近才看到,暂时没有亲手测验,不敢在工作中试用。
按理说,方案3是最佳方案, 但是个人原因,选择了方案2.
1
<
manifest
xmlns:android
=
"http://schemas.android.com/apk/res/android"
2
package
=
"com.cnblogs.download"
>
3
<
application
android:icon
=
"@drawable/icon"
android:label
=
"@string/app_name"
>
4
<!--省略其他-->
5
<
service
android:name
=
"cn.cnblogs.download.OfflineService"
android:process
=
"cn.cnblogs.download.offline"
/>
6
</
application
>
7
</
manifest
>
4.定时下载和手机催醒
根据用户设置,在wifi的情况下自动下载,但是自动下载的方案有很多种,频繁的更新下载,定点下载(早上8点,下午4点),间隔下载(每隔6小时)。
这里,我们选择每隔6个小时下载。
(1).这里介绍一种错误的方案。一看到每隔6小时,很容易想到开启一个子线程计时,累计到6个小时,子线程通知下载服务开始新一轮下载。这个方案的思路是没有错的,但是却忽略了手机处于休眠状态,这个子线程其实是停止执行的,那么所谓的6个小时的效果就又可能永远达不到,而且必然不正确或者不准确。
(2).所以,需要使用到一种不休眠的办法:定时器和广播接收器。每隔6小时我们发送一个广播,广播接收器通知开始离线下载。(可参考newsrob源码和书籍《Pro Android 3》):
01
public
class
OfflineSerivice
extends
Service {
02
03
//上次成功下载的时间
04
private
long
lastDownloadTime;
05
// 省略代码... ...
06
07
public
static
void
startAlarm(Context context){
08
AlarmManager alarmManager = (AlarmManager) context.getSystemService(
"alarm"
);
09
10
//每隔6个小时发送广播到OfflineAlarmReceiver
11
//也可以设置为10分钟检测一下下载条件,而在OfflineAlarmRecrive中判断开始下载,避免6小时下载失败需再等待6小时过长时间的问题
12
Intent intent =
new
Intent(context,OfflineAlarmReceiver.
class
);
13
14
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(),
0
, intent,
0
);
15
alarmManager.cancel(pendingIntent);
16
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),
3600000
*
6
, pendingIntent);
17
}
18
}
OfflineAlarmRecriver中处理开始下载条件,并通知开始下载:
01
public
class
OfflineAlarmReceiver
extends
BroadcastReceiver {
02
@Override
03
public
void
onReceive(Context context, Intent arg1) {
04
05
// 省略代码...,初始化变量,准备工作...
06
07
if
(System.currentTimeMillis()-OfflineService.lastDownloadTime>
3600000
*
60
&&其他条件){
08
//打开离线下载服务
09
Intent alarmIntent =
new
Intent(context, OfflineService.
class
);
10
context.startService(alarmIntent);
11
}
12
}
13
14
}
前面我们提到了线程休眠的问题,需要在下载的时候能够唤醒手机,下载完成后能回到休眠状态,下面是两个工具方法:
01
public
static
PowerManager.WakeLock wakeLock;
02
/**
03
* 唤醒服务
04
*/
05
public
static
void
acquireWakeLock(Context context){
06
07
if
(wakeLock!=
null
){
08
return
;
09
}
10
PowerManager powerManager = (PowerManager)(context.getSystemService(Context.POWER_SERVICE));
11
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"com.cnblogs.download.OfflineService"
);
12
wakeLock.acquire();
13
}
14
15
/**
16
* 释放唤醒服务,返回休眠状态
17
*/
18
public
static
void
releaseWakeLock(){
19
if
(wakeLock!=
null
&&wakeLock.isHeld()){
20
wakeLock.release();
21
wakeLock =
null
;
22
}
23
}
其中PowerManager.PARTIAL_WAKE_LOCK意思是仅唤醒CPU方式,此时能自动主动检测网络状态,从而保证网络正常。
需要在Mainifest.xml中设置权限:
1
<
uses-permission
android:name
=
"android.permission.WAKE_LOCK"
/>
然后在下载服务的onStartConmmand()激活催醒状态,然后在下载完成后释放催醒状态:
1
@Override
2
public
int
onStartCommand(Intent intent,
int
flags,
int
startId) {
3
acquireWakeLock(OfflineService.
this
);
4
//省略代码... ...
5
return
super
.onStartCommand(intent, flags, startId);
6
}
5.自启动
为了代码清晰,我们再定义一个自启动的receiver:
01
/**
02
* 自启动离线下载服务
03
* @author user
04
*/
05
public
class
OfflineReceiver
extends
BroadcastReceiver {
06
@Override
07
public
void
onReceive(Context context, Intent arg1) {
08
//启动定时器
09
OfflineService.startAlarm(context);
10
}
11
}
在AndroidManifest.xml注册此接收器,如下:
1
<
receiver
android:name
=
"cn.cnblogs.download.OfflineReceiver"
>
2
<
intent-filter
>
3
<!--自启动-->
4
<
action
android:name
=
"android.intent.action.BOOT_COMPLETED"
/>
5
<
category
android:name
=
"android.intent.category.HOME"
/>
6
</
intent-filter
>
7
</
receiver
>
这样,在启动的时候,能够接受启动广播,并执行启动定时器操作。
6.小结
为了简洁明晰,开门见山,本文仅针对离线下载的最重要的关联点列举说明,而对于清理策略,手动和自动模式,界面跳转,UI设计和业务要求没有过多的涉及,但是往往这些东西才是花费你大量的时间,需要大量细节的积累和耐心的调试,我们唯一要做的事情就是不断的完善!
- App离线下载
- Android App离线下载
- App离线下载
- Android学习系列(19)--App离线下载
- Android学习系列(19)--App离线下载
- Android学习系列(19)--App离线下载
- Android学习系列(17)--App离线下载
- 离线下载
- 离线下载
- 离线下载
- 离线下载
- 离线下载
- mac xcode最新版本 迅雷下载 官网下载 离线下载 非app store
- chrome-离线app安装
- 基于深度学习架构android人脸识别[离线版]APP免费下载。
- 离线下载 Google Chrome
- firefox离线下载地址
- chrome离线下载方法
- JavaSE骑士飞行棋
- Android-2.2display系统介绍-SW架构
- Oracle数据导入导出imp/exp命令 10g以上expdp/impdp命令
- SUM服务器监控软件--通用服务器监控方案
- python脚本中的#!/usr/bin/python
- App离线下载
- c#计算时间间隔差
- 2011NBA常赛playbyplay数据库
- duplicate global variable looked up by string name
- 等待安装的新列车轮对
- .net发布静态页面代码
- Android 游戏框架(一个游戏角色在屏幕行走的demo)
- SqliteGen——自动生成 SQLite 数据库的访问类
- 内核_.config 内核配置[ZT]--make menuconfig