app离线下载

前段时间做了一个离线下载的模块,需求如下:

1、后台独立进程运行,可以脱离主程序运行
2、可以暂停、继续下载
3、可以判断网络状况和SD卡
4、显示下载进度
4、多个个任务在一个队列中
5、定时下载
 
一、选择离线下载的核心方法
 
后台独立运行,我们很容易想到服务(Service),但是有以下几种问题
(1)如果服务的进程和应用一致,那么在应用退出后,服务会重启一次
(2)如果服务的进程和应用不一致,进程间的通信就会麻烦一点
(3)如果服务的进程和应用一致,选择IntentService,可以避免重启问题
而且我们不需要多个任务同时下载,用IntentService完全可以,而且IntentService还有其他优势
 
二、下载管理
 
由于用IntentService,一个任务就是一个intent,服务处理一个Intent就是下载一个任务
(1)当前任务的暂停,可以设置一个下载状态的变量
 1 public static final int CANCEL_STATE = 1 ;
 2 
 3 private state int state ;
 4 
 5 ...
 6 
 7 //download
 8 
 9  
10 
11 while ( ... ) {
12 
13 ...
14 
15 if ( state == CANCEL_STATE ) {
16 
17 break ;
18 
19 }
20 
21 }

 

暂停就相当于取消了当前任务,服务去处理下一个intent。
如果要继续这个任务,就要重新发送一个Intent,为了可以从刚刚下载的地方接着下载,我们要坐下处理:
1 File file = new File ( path ) ;
2 
3 file.getParentFile().mkdirs () ;
4 
5 RandomAccessFile rdFile = new RandomAccessFile ( file, "rwd" ) ;

 

//获得刚刚下载的文件的大小

 

1 int mCurrentSize = ( int ) rdFile.length () ;
2 
3 HttpURLConnection conn = ( HttpURLConnection ) new URL ( url ).openConnection () ;
4 
5 conn.setConnectTimeout ( 5000 ) ;
6 
7 conn.setRequestMethod ( "get" ) ;

 

//从mCurrentSize后开始下载

 

conn.setRequestProperty ( "Range", "bytes=" + mCurrentSize + "-" ) ;

rdFile.seek ( mCurrentSize ) ;

 
(2)取消队列中等待下载的任务
发现IntentService不可以取消还未处理的Intent,看IntentService的源码,发现只需略作修改就可以了

//下面自定义一个可以删除intent的服务,只显示新增的代码和覆盖的部分,其他和IntentService一样

 

 1 public abstract class BaseIntentService extends Service {
 2 
 3 private final int MESSAGE_TYPE = 12 ; //消息类型
 4 
 5  
 6 
 7 @Override
 8 
 9 public void onStart ( Intent intent, int startId ) {
10 
11 Message msg = mServiceHandler.obtainMessage () ;
12 
13 msg.arg1 = startId ;
14 
15 msg.obj = intent ;
16 
17 msg.what = MESSAGE_TYPE ;
18 
19 mServiceHandler.sendMessage ( msg ) ;
20 
21 }
22 
23  
24 
25 protected boolean hasIntent ( Intent intent ) {
26 
27 return mServiceHandler.hasMessages ( MESSAGE_TYPE, intent ) ;
28 
29 }
30 
31  
32 
33 protected void removeIntent ( Intent intent ) {
34 
35 if ( mServiceHandler.hasMessages ( MESSAGE_TYPE, intent ) ) {
36 
37 mServiceHandler.removeMessages ( MESSAGE_TYPE, intent ) ;
38 
39 }
40 
41 }
42 
43  
44 
45 protected void removeAllIntent () {
46 
47 mServiceHandler.removeMessages ( MESSAGE_TYPE ) ;
48 
49 }
50 
51 }

 

这边还有另外一个需要注意的地方:在测试中发现删除不了,后来才知道是个低级错误,默认Intent的equals方法是判断两个引用是否指向一个对象,所以我们要重载Intent的 hashCode 和 equals 方法。
 
三、判断网络状态和SD卡
 
获得当前的网络状态,如果不是wifi就停止下载,如果SD卡没有挂载,也停止下载
 1 public class NetworkUtils {
 2 
 3 public static boolean existWifi ( Context context ) {
 4 
 5 ConnectivityManager connManager = ( ConnectivityManager ) context.getSystemService ( Context.CONNECTIVITY_SERVICE ) ;
 6 
 7 NetworkInfo info = connManager.getActiveNetworkInfo () ;
 8 
 9 return ( null != info && ConnectivityManager.TYPE_WIFI == info.getType () ) ;
10 
11 }
12 
13 }
 1 public class SDUtils {
 2 
 3 public static boolean existSDCard () {
 4 
 5 return ( android.os.Environment.getExternalStorageStage().
 6 
 7 equals( android.os.Environment.MEDIA_MOUNTED ) ) ;
 8 
 9 }
10 
11 }

 

四、显示下载进度
 
(1)应用程序中显示下载进度,利用ResultReceiver
 1 public class MainActivity extends Activity {
 2 
 3 ...
 4 
 5  
 6 
 7 public void startDownload () {
 8 
 9 Intent intent = new ...
10 
11 intent.putExtra ( "receiver", new DownloadReceiver() ) ;
12 
13 ...
14 
15 startService ( intent ) ;
16 
17 }
18 
19  
20 
21 public class DownloadReceiver extends ResultReceiver {
22 
23 ...
24 
25 }
26 
27  
28 
29 }

//在IntentService中的onHandleIntent中,获得ResultReceiver

ResultReceiver receiver = intent.getParcelableExtra ( "receiver" ) ;

 

//在循环下载处理中,发送下载进度

1 Bundle resultData = new Bundle () ;
2 
3 resultData.putString ( "progress", percent ) ;
4 
5 receiver.send ( UPDATE_PROGRESS, resultData ) ;

 

(2)在通知栏中,更新下载进度,利用Notifications
 
五、定时下载
 
这个和闹钟的原理相类似,定义一个定时器和广播接收器,利用AlarmManager
 1 public static void startAlarm ( Context context, long time ) {
 2 
 3 AlarmManager am = (AlarmManager) context.getSystemService ( Context.ALARM_SERVICE ) ;
 4 
 5 Intent intent = new Intent ( context, AlarmReceiver.class ) ;
 6 
 7 PendingIntent pIntent = PendingIntent.getBroadcast ( context, 0, intent, 0 ) ;
 8 
 9 am.cancel ( pIntent ) ;
10 
11 am.setRepeating ( AlarmManager.RTC_WAKEUP, time, AlarmManager.INTERVAL_DAY, pIntent ) ;
12 
13 }
14 
15  
16 
17 public class AlarmReceiver extends BroadcastReceiver {
18 
19 @Override
20 
21 public void onReceive ( Context context, Intent intent ) {
22 
23 //启动下载服务
24 
25 。。。
26 
27 }
28 
29 }

 

我们在下载的时候可以唤醒手机,下载后可以回到休眠状态,利用PowerManager
 1 public class WakeLockUtils {
 2 
 3 public static WakeLock wl ;
 4 
 5 public static String tag ; // 服务的包名
 6 
 7  
 8 
 9 public static void acquirePratialWakeLock ( Context context ) {
10 
11 if ( null != wl ) return ;
12 
13 PowerManager pm = ( PoweerManager ) ( context.getSystemService ( Context.POWER_SERVICE ) ) ;
14 
15 wl = pm.newWakeLock ( PowerManager.PARTIAL_WAKE_LOCK, tag ) ;
16 
17 wl.acquire () ;
18 
19 }
20 
21  
22 
23 public static void releaseWakeLock () {
24 
25 if ( null != wl && wl.isHeld () ) {
26 
27 wl.release () ;
28 
29 wl = null ;
30 
31 }
32 
33 }
34 
35 }
36 
37  

 

六、自启动

 

为了让我们的闹钟可以在开机后自动startAlarm
 1 public AutoRunReceiver extends BroadcastReceiver {
 2 
 3 public void onReceive ( Context context, Intent intent ) {
 4 
 5 //启动定时器
 6 
 7  
 8 
 9 。。。startAlram () ;
10 
11 }
12 
13 }

 

 

AndroidManifest.xml注册此接收器:

 1 <receiver android:name="包名">
 2 
 3 <intent-filter>
 4 
 5 <action android:name="android.intent.action.BOOT_COMPLETED"/>
 6 
 7 <category android:name="android.intent.category.HOME"/>
 8 
 9 </intent-filter>
10 
11 </receiver>

 

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。