離線下載的功能點以下:
1.下載管理(開始、取消下載)。
2.網絡判斷(Wi-Fi,3G)。
3.獨立進程。
4.定時和手機催醒。
5.自啓動。 html
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設計和業務要求沒有過多的涉及,可是每每這些東西纔是花費你大量的時間,須要大量細節的積累和耐心的調試,咱們惟一要作的事情就是不斷的完善!