android離線下載的相關知識

離線下載的功能點以下:
      1.下載管理(開始、取消下載)。
      2.網絡判斷(Wi-Fi,3G)。
      3.獨立進程。
      4.定時和手機催醒。
      5.自啓動。 html

選擇離線下載的核心方法
 
後臺獨立運行,咱們很容易想到服務(Service),可是有如下幾種問題
(1)若是服務的進程和應用一致,那麼在應用退出後,服務會重啓一次
(2)若是服務的進程和應用不一致,進程間的通訊就會麻煩一點
(3)若是服務的進程和應用一致,選擇IntentService,能夠避免重啓問題
並且咱們不須要多個任務同時下載,用IntentService徹底能夠,並且IntentService還有其餘優點

 

1.下載管理
       這裏不便關注下載的細節方法,網絡下載的方法不少,大概以下:android

 1 /**
 2  * 下載文件
 3  * @param url 下載地址
 4  * @param dest 下載存放的本地文件
 5  * @param append 斷點續傳
 6  * @return
 7  * @throws Exception 
 8  */
 9 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)兩種,咱們寫一個工具類以下:網絡

 1 public class NetworkUtils {
 2  
 3     public final static int NONE = 0;//無網絡
 4     public final static int WIFI = 1;//Wi-Fi
 5     public final static int MOBILE = 2;//3G,GPRS
 6      
 7     /**
 8      * 獲取當前網絡狀態
 9      * @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下手動下載,提示用戶會消耗較大的數據流量,慎用便可。app

 1 public class Constant {
 2     //離線下載網絡設置
 3     public final static int OFFLINE_MOBILE = 0;
 4     public final static int OFFLINE_WIFI = 1;
 5     public final static int OFFLINE_OFF = 3;
 6 }
 7  
 8 public class Global {
 9     //設置默認關閉狀態,
10     //爲了應用程序下次啓動可以記住用戶選擇,在第一次啓動應用的時候,這個值最終應該存放到數據庫中,
11     public static int OfflineNetworkSetting = Constant.OFFLINE_OFF;
12 }

如今能夠根據規則比較當前網絡和離線網絡設置,斷定離線下載服務的開啓。ide

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.ui

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》):this

 1 public class OfflineSerivice extends Service {
 2     
 3     //上次成功下載的時間
 4     private long lastDownloadTime;
 5     // 省略代碼... ...
 6 
 7     public static void startAlarm(Context context){
 8         AlarmManager alarmManager = (AlarmManager) context.getSystemService("alarm");
 9         
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中處理開始下載條件,並通知開始下載url

 1 public class OfflineAlarmReceiver extends BroadcastReceiver {
 2     @Override
 3     public void onReceive(Context context, Intent arg1) {
 4         
 5         // 省略代碼...,初始化變量,準備工做...
 6         
 7         if(System.currentTimeMillis()-OfflineService.lastDownloadTime>3600000*60&&其餘條件){
 8             //打開離線下載服務
 9             Intent alarmIntent = new Intent(context, OfflineService.class);
10             context.startService(alarmIntent);
11         }
12     }
13 
14 }

前面咱們提到了線程休眠的問題,須要在下載的時候可以喚醒手機,下載完成後能回到休眠狀態,下面是兩個工具方法:

 1   public static PowerManager.WakeLock wakeLock;
 2     /**
 3      * 喚醒服務
 4      */
 5     public static void acquireWakeLock(Context context){
 6         
 7         if(wakeLock!=null){
 8             return;
 9         }
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中設置權限:

    <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:

 1 /**
 2  * 自啓動離線下載服務
 3  * @author user
 4  */
 5 public class OfflineReceiver extends BroadcastReceiver {
 6     @Override
 7     public void onReceive(Context context, Intent arg1) {
 8         //啓動定時器
 9         OfflineService.startAlarm(context);
10     }
11 }
12        在AndroidManifest.xml註冊此接收器,以下:
13 
14 <receiver android:name="cn.cnblogs.download.OfflineReceiver">
15             <intent-filter>
16                 <!--自啓動-->
17                 <action android:name="android.intent.action.BOOT_COMPLETED" /> 
18                 <category android:name="android.intent.category.HOME" /> 
19             </intent-filter>
20 </receiver>

這樣,在啓動的時候,可以接受啓動廣播,並執行啓動定時器操做。

6.小結      爲了簡潔明晰,開門見山,本文僅針對離線下載的最重要的關聯點列舉說明,而對於清理策略,手動和自動模式,界面跳轉,UI設計和業務要求沒有過多的涉及,可是每每這些東西纔是花費你大量的時間,須要大量細節的積累和耐心的調試,咱們惟一要作的事情就是不斷的完善! 

相關文章
相關標籤/搜索