編輯:Android開發實例
宜未雨而綢缪,毋臨渴而掘井。----朱用純《治家格言》
離線下載,在有網絡的情況下下載服務器數據,以便無網絡時也能閱讀,就是離線閱讀。
離線下載的功能點如下:
1.下載管理(開始、取消下載)。
2.網絡判斷(Wi-Fi,3G)。
3.獨立進程。
4.定時和手機催醒。
5.自啟動。
1.下載管理
這裡不便關注下載的細節方法,網絡下載的方法很多,大概如下:
/**
* 下載文件
* @param url 下載地址
* @param dest 下載存放的本地文件
* @param append 斷點續傳
* @return
* @throws Exception
*/
public long download(String url, File dest, boolean append) throws Exception{
//初始化變量
//准備工作
// ... ...
try {
// ... ...
while((readSize = is.read(buffer)) > 0){
//網絡判斷
os.write(buffer, 0, readSize);
os.flush();
//如果需要停止下載,如取消,跳出當前下載
}
}
} finally {
// ... ...
}
// ... ...
}
這裡要注意幾點:
(1).在下載的時候,我們希望能及時檢測到網絡狀況,比如由Wi-Fi切換到3G網絡下,我們應該能及時停止下載。
(2).當用戶選擇取消下載的時候,我們也能停止當前下載。
2.網絡判斷
獲取當前網絡狀態,主要分為Wi-Fi和Mobile(包括3G,GPRS)兩種,我們寫一個工具類如下:
public class NetworkUtils {
public final static int NONE = 0;//無網絡
public final static int WIFI = 1;//Wi-Fi
public final static int MOBILE = 2;//3G,GPRS
/**
* 獲取當前網絡狀態
* @param context
* @return
*/
public static int getNetworkState(Context context){
ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//手機網絡判斷
State state = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
if(state == State.CONNECTED||state == State.CONNECTING){
return MOBILE;
}
//Wifi網絡判斷
state = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
if(state == State.CONNECTED||state == State.CONNECTING){
return WIFI;
}
return NONE;
}
}
根據網絡狀態,我們能夠控制下載方式:
(1).下載量很大的情況下,我們不大可能在3G情況下進行下載,容易引起用戶的反感和擔憂。
(2).當客戶十分確認可以在3G情況下進行下載,那麼也是允許的。
所以,這裡提出一個需求,我們要為下載方式設置一個靈活的等級,結合離線下載的特點,我們給出3中方案由用戶選擇:
(1).移動數據情況下自動下載
(2).只允許Wi-Fi情況下自動下載
(3).關閉下載
這裡只列出了自動下載,是因為如果不是自動下載,手動下載用戶可以隨意控制,無需設置,當然設計到丟流量情況下,如3G下手動下載,提示用戶會消耗較大的數據流量,慎用即可。
public class Constant {
//離線下載網絡設置
public final static int OFFLINE_MOBILE = 0;
public final static int OFFLINE_WIFI = 1;
public final static int OFFLINE_OFF = 3;
}
public class Global {
//設置默認關閉狀態,
//為了應用程序下次啟動能夠記住用戶選擇,在第一次啟動應用的時候,這個值最終應該存放到數據庫中,
public static int OfflineNetworkSetting = Constant.OFFLINE_OFF;
}
現在可以根據規則比較當前網絡和離線網絡設置,判定離線下載服務的開啟。
3.獨立進程
離線下載,無論何時何地,只要適宜進行,則當進行,目前主流的做法是建立後台服務。
public class OfflineSerivice extends Service {
// ... ...
}
(1).OfflineService的進程如果默認和應用程序一致,則在應用進程kill的時候,會重啟一次(網易新聞在離線下載的時候,退出應用,下載會停頓一小會兒就是這個原因),如果影響不大,這個方案也是可選的。
(2).OfflineService的進程和應用程序分開,如應用程序進程為"cn.cnblogs.tianxia.download",則離線下載服務的進程設置為"cn.cnblogs.tianxia.download.offline",撇清和應用程序的進程的關系。當然,這個會帶來一個新的問題,進程間通信,當然因為離線下載和應用程序間的模塊比較獨立,這個問題還算比較好規避。
(3).OfflineService的進程如果默認和應用程序一致,但是OfflineService繼承IntentService,可避免重啟的問題,這個是《Pro Android 3》書中提到的方法,非常的好用,但是非常遺憾,本人最近才看到,暫時沒有親手測驗,不敢在工作中試用。
按理說,方案3是最佳方案, 但是個人原因,選擇了方案2.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cnblogs.download">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<service android:name="cn.cnblogs.download.OfflineService" android:process="cn.cnblogs.download.offline"/>
</application>
</manifest>
4.定時下載和手機催醒
根據用戶設置,在wifi的情況下自動下載,但是自動下載的方案有很多種,頻繁的更新下載,定點下載(早上8點,下午4點),間隔下載(每隔6小時)。
這裡,我們選擇每隔6個小時下載。
(1).這裡介紹一種錯誤的方案。一看到每隔6小時,很容易想到開啟一個子線程計時,累計到6個小時,子線程通知下載服務開始新一輪下載。這個方案的思路是沒有錯的,但是卻忽略了手機處於休眠狀態,這個子線程其實是停止執行的,那麼所謂的6個小時的效果就又可能永遠達不到,而且必然不正確或者不准確。
(2).所以,需要使用到一種不休眠的辦法:定時器和廣播接收器。每隔6小時我們發送一個廣播,廣播接收器通知開始離線下載。(可參考newsrob源碼和書籍《Pro Android 3》):
public class OfflineSerivice extends Service {
//上次成功下載的時間
private long lastDownloadTime;
// 省略代碼... ...
public static void startAlarm(Context context){
AlarmManager alarmManager = (AlarmManager) context.getSystemService("alarm");
//每隔6個小時發送廣播到OfflineAlarmReceiver
//也可以設置為10分鐘檢測一下下載條件,而在OfflineAlarmRecrive中判斷開始下載,避免6小時下載失敗需再等待6小時過長時間的問題
Intent intent = new Intent(context,OfflineAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, intent, 0);
alarmManager.cancel(pendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(), 3600000*6, pendingIntent);
}
}
OfflineAlarmRecriver中處理開始下載條件,並通知開始下載:
public class OfflineAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent arg1) {
// 省略代碼...,初始化變量,准備工作...
if(System.currentTimeMillis()-OfflineService.lastDownloadTime>3600000*60&&其他條件){
//打開離線下載服務
Intent alarmIntent = new Intent(context, OfflineService.class);
context.startService(alarmIntent);
}
}
}
前面我們提到了線程休眠的問題,需要在下載的時候能夠喚醒手機,下載完成後能回到休眠狀態,下面是兩個工具方法:
public static PowerManager.WakeLock wakeLock;
/**
* 喚醒服務
*/
public static void acquireWakeLock(Context context){
if(wakeLock!=null){
return;
}
PowerManager powerManager = (PowerManager)(context.getSystemService(Context.POWER_SERVICE));
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "com.cnblogs.download.OfflineService");
wakeLock.acquire();
}
/**
* 釋放喚醒服務,返回休眠狀態
*/
public static void releaseWakeLock(){
if(wakeLock!=null&&wakeLock.isHeld()){
wakeLock.release();
wakeLock = null;
}
}
其中PowerManager.PARTIAL_WAKE_LOCK意思是僅喚醒CPU方式,此時能自動主動檢測網絡狀態,從而保證網絡正常。
需要在Mainifest.xml中設置權限:
<uses-permission android:name="android.permission.WAKE_LOCK" />
然後在下載服務的onStartConmmand()激活催醒狀態,然後在下載完成後釋放催醒狀態:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
acquireWakeLock(OfflineService.this);
//省略代碼... ...
return super.onStartCommand(intent, flags, startId);
}
5.自啟動
為了代碼清晰,我們再定義一個自啟動的receiver:
/**
* 自啟動離線下載服務
* @author user
*/
public class OfflineReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent arg1) {
//啟動定時器
OfflineService.startAlarm(context);
}
}
在AndroidManifest.xml注冊此接收器,如下:
<receiver android:name="cn.cnblogs.download.OfflineReceiver">
<intent-filter>
<!--自啟動-->
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
這樣,在啟動的時候,能夠接受啟動廣播,並執行啟動定時器操作。
6.小結
為了簡潔明晰,開門見山,本文僅針對離線下載的最重要的關聯點列舉說明,而對於清理策略,手動和自動模式,界面跳轉,UI設計和業務要求沒有過多的涉及,但是往往這些東西才是花費你大量的時間,需要大量細節的積累和耐心的調試,我們唯一要做的事情就是不斷的完善!
Android快速分析apk工具aapt的使用教程
前面walfred已經介紹了使用apktool對apk進行逆向編譯,通過apktool我們的確可以反編譯已經序列化後的AndroidManifest.xml和資源
android monkey自動化測試改為java調用monkeyrunner Api
眾所周知,一般情況下我們使用android中的monkeyrunner進行自動化測試時,使用的是python語言來寫測試腳本。不過,最近發現可以用java調用mo
Android 完全退出應用程序的解決方法
有一種需要,我們在菜單項中點擊退出應用程序,應用程序就退出,不需要回到MainActivity設計:有兩個應用界面MainActivity和BActivity,以
Android JSON解析器
JSON代表JavaScript對象符號。它是一個獨立的數據交換格式,是XML的最佳替代品。本章介紹了如何解析JSON文件,並從中提取所需的信息。Android提供了四個