zoukankan      html  css  js  c++  java
  • 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>

     

  • 相关阅读:
    01 背包问题
    神奇的口袋[dp]
    2019考研西交大软件工程
    计算机考研有哪些值得推荐的院校?
    考研-政治经验贴***
    考研-英语经验贴2.0
    考研-作息时间安排表(总结)
    考研-英语经验贴(总结)
    考研-数学经验贴(总结)
    考研-专业课经验贴
  • 原文地址:https://www.cnblogs.com/xuanyuanzhuo-blog/p/4005123.html
Copyright © 2011-2022 走看看