android-新聞客戶端-離線下載的簡單實現(圖片部分)
轉載請註明:來自Xuye_(http://blog.csdn.net/x1876631/article/details/44202471)的專欄
1、寫在前面:
做android應用開發也有段時間了,也做了幾個專案。之前一直在看各個前輩大牛的部落格,心裡總是有些感動。正是因為有許許多多這樣樂於分享知識的人,大家才能共同進步。我也受益於此從一個小菜鳥在自學中茁壯成長起來。現在我也有了些經驗,希望自己的一些拙見能幫助到需要它的人。
2、成文原因:
最近一直在做android的新聞類客戶端,其中需要完成個【離線下載】的功能。這種功能算是新聞客戶端的標配,在網上找了很久也沒有特別切合的實現demo。
現在自己實現了,就把【圖片離線下載,顯示下載進度,無網路時也可檢視】這個功能寫成demo分享給大家。
3、實際效果展示(直接上圖):
效果簡單總結:
下載圖片儲存到本地,實時顯示下載內容和進度,結束後無網路時能檢視圖片。下載過程中可以點選按鈕取消下載,退出應用時會自動取消下載。
3.1、先斷開網路,開啟應用listview圖片列表,可以看到載入都失敗了:
3.2、在離線下載頁點選【離線下載按鈕】,出現下載提示notification,同時按鈕UI文字變為"取消圖片離線下載":
3.3、下拉通知欄,顯示下載進度通知notification:
3.4、下載完畢結束service,通知消失。此時斷網去檢視圖片列表頁,發現圖片在無網的情況下也都顯示出來了(極少部分超時或者過大圖片下載失敗了):
3.5、以上基本展示了圖片離線下載的過程和效果。容錯處理:如果沒有網路時會直接結束下載,並提示錯誤:
4、程式碼相關:
4.1、圖片離線下載主要用到的幾個類:
UILApplication.java:應用全域性類,在這裡設定及初始化了Universal-image-loader載入框架、含有啟動和停止下載的函式
ImageListActivity.java:圖片列表顯示類,這裡呼叫了UIL框架載入圖片
OfflineDownloadActivity.java:圖片離線下載頁,點選按鈕啟動離線下載service,再點選則取消下載
OfflineDownloadService.java:圖片離線下載後臺服務service,這個service裡含有下載和停止下載的邏輯
具體如圖:
ImageListActivity.java和OfflineDownloadActivity.java沒什麼好說的,一個listview,一個只有個按鈕。
UILApplication.java裡的universal-image-loader載入框架的配置和使用請參考一下csdn的博文,寫的很清楚詳細了:
在主頁面(HomeActivity.java)點選離線下載按鈕
——>跳轉到離線下載頁(OfflineDownloadActivity.java)
——>在離線下載頁點選下載按鈕,呼叫application類的startOfflineDownloadService()方法
——>啟動離線下載service(OfflineDownloadService.java)
——>執行service的onStartCommand()方法,其又執行下載執行緒imageDownload()方法
——>下載函式中迴圈執行圖片下載方法imageLoader.loadImage()
——>執行loadImage()中圖片下載成功或者失敗的回撥函式onLoadingFailed()或onLoadingComplete()
——>在這2個方法裡執行imagedownloadResult()方法,判斷進度(有以下1、2、3可能)
1——>如果可以更新進度,則執行sendMsg(更新)去更新進度(執行handler的update項)
2——>如果下載完成,則執行sendMsg(完成)去結束下載service(執行handler的finish項)
3——>如果下載錯誤,則執行sendMsg(錯誤)去結束下載service(執行handler的error項)
補充:
1、service傳遞資訊給activity(用廣播),參考:
2、關於通知notification的學習和注意事項,參考:
Notification分析——你可能遇到的各種問題
3、關於檔案後臺service下載的demo和講解,參考:
<!-- 離線圖片下載頁 -->
<activity android:name="main.OfflineDownloadActivity" />
<!-- 離線圖片下載service-->
<service android:name="main.OfflineDownloadService"> <intent-filter>
<!-- 為該Service元件的intent-filter配置action -->
<action android:name="com.nostra13.example.universalimageloader.action.OFFLINE_DOWNLOAD_SERVICE" />
</intent-filter>
</service>
<!-- 離線下載完成廣播 -->
<receiver android:name="main.OfflineDownload$OfflineDownloadReceiver"> <intent-filter>
<action android:name="com.nostra13.example.universalimageloader.action.OFFLINE_DOWNLOAD_BROADCAST" />
</intent-filter>
</receiver>
這裡切記service和broadcastReceiver的啟動匹配符action要唯一,加上包名是比較好的辦法。
否則如果多個應用(A/B/C)使用的是統一action啟動符,A/B/C又同時在手機上,此時A啟動service時可能會啟動B/C的service。
不要問我為什麼知道,犯懶copy就是這麼悲劇。
5.2、連線網路、網路狀態的判斷、sd卡讀寫的許可權:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
5.3、取消下載,停止service中的執行緒的方法:/**
* 停止執行緒執行標識
* 取消下載時,設定執行緒暫停執行標識變為true
* 除了stopServiceFlag,其他的設定是由於離線下載按鈕頁取消呼叫此方法
*/
public static void setStop() {
stopServiceFlag = true;
count=0;
oldImageCount = 0;
manager.cancel(flag);
imageLoader.stop();//取消下載時停止圖片的下載
}
呼叫service裡的setStop(),將其中的stopServiceFlag欄位設為true,代表執行緒已被人為停止。在每次下載前都會對stopServiceFlag進行檢查,
如果為true,則直接跳出迴圈,取消下載,結束service。
5.4、限制更新進度通知notification的次數:/**
* 圖片下載結果處理,計算進度,更新notification
*
* @param notification 下載進度通知notification
* @param imageNumber 正在下載的圖片下標號
*/
private void imagedownloadResult(Notification notification,int imageNumber){
//下載成功或者失敗都增加進度
count += 1.0 / downloadimages.length;
isImageDownloadFinish[imageNumber] = true;
//判斷進度差值,更新notification
if(count-oldImageCount>IMAGE_DIFFERENCE_COUNT_VALUE){
oldImageCount = count;
sendMsg(UPDATE_NOTIFICATION, count, notification,flag, "url:"+downloadimages[imageNumber]);
}
Log.e(TAG, "count--->" + count+" , imageNumber--->"+imageNumber+" is download finish!");
if(count>=1||isDownloadFinish()){
//如果進度到達100%或者所有圖片都下載完了,結束下載
sendMsg(DOWNLOAD_FINISH, 1, notification, flag,"下載完成");
}
}
imagedownloadResult()是下載圖片回撥函式中的結果處理邏輯。
無論下載成功還是失敗都讓進度增加(失敗可能是連線超時或者圖片過大導致的),
然後去更新進度。但是這裡更新進度的次數要做限制,如果更新過快會使介面操作時非常卡!(我的都宕機了...)
這裡使用新老進度差值是否大於臨界值(count-oldImageCount>IMAGE_DIFFERENCE_COUNT_VALUE)來限制更新。
5.5、下載完成的判斷:
還是上面那個函式,每次下載圖片的回撥都會使進度count增加,本來只對進度進行判斷就可以了,
但是在實際操作中發現進度在結束時會稍大於或稍小於100%,所以增加了圖片下載完成標識組isImageDownloadFinish[],
每次下載完一個圖片(無論成功或失敗)只要有結果就設定下載完成標識為true。但所有標識都完成了以後就結束下載。
之前想用下載到最後一個,對i判斷作為完成標識,總是不對。後來列印了一下下載情況,就知道怎麼回事了,如圖:
可以看到UIL自己使用了多執行緒非同步下載,後面的圖片可能會先下完,所以還是要判斷count。 5.6、離線下載按鈕UI文字的顯示和改變: 正常來說,要實現的效果是:點選下載,再點選取消;下載完成或者錯誤,文字從取消變為下載。 我們一般不會在下載時一直停在下載頁,所以每次都要對下載狀態做判斷,顯示文字。 辦法是使用shareRreference儲存一個是否正在下載的本地標誌。這裡使用的標誌名為:isOfflineDownload。 每次開始下載時設定為true。取消、結束、出錯時設定為false。如程式碼所示:
/**
* 開始離線圖片下載
*/
public static final void startOfflineDownloadService(){
Intent intent = new Intent(Constants.OfflineDownloadServiceAction);
getApp().startService(intent);
SP.edit().putBoolean("isOfflineDownload", true).commit();
Log.e(TAG, "startOfflineDownloadService--->ok");
}
/**
* 取消離線離線下載
*/
public static final void stopOfflineDownloadService(){
Intent intent = new Intent(Constants.OfflineDownloadServiceAction);
OfflineDownloadService.setStop();
getApp().stopService(intent);
//設定下載完成標識
SP.edit().putBoolean("isOfflineDownload", false).commit();
Log.e(TAG, "stopOfflineDownloadService--->ok");
}
而每次進入下載也是根據此標識來設定按鈕顯示文字的,如程式碼所示:
/**
* 初始化控制元件
*/
private void initView(){
downloadText = (TextView) findViewById(R.id.downLoadText);
downlload = (LinearLayout) findViewById(R.id.downLoad);
downlload.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//如果開始了離線下載,點選後取消離線下載
if(SP.getBoolean("isOfflineDownload", false)){
Log.e(tag, "offline_download--->cancel");
//取消離線下載
UILApplication.stopOfflineDownloadService();
downloadText.setText(getResources().getString(R.string.download));
SP.edit().putBoolean("isOfflineDownload", false).commit();
}else {
Log.e(tag, "offline_download--->begin");
//如果沒有開始,啟動下載service
UILApplication.startOfflineDownloadService();
//記錄已經開始離線下載
SP.edit().putBoolean("isOfflineDownload", true).commit();
//顯示可以取消離線下載
downloadText.setText(getResources().getString(R.string.download_cancel));
}
}
});
}
下載結束時service會發送廣播通知下載頁更新UI,這裡在service裡要發廣播,在下載頁要註冊廣播接受者類。如下:
/**
* 離線下載完成後傳送廣播,家頁離線下載欄文字由"取消離線下載"變為"離線下載"
* @author Xuye
*
*/
public static class OfflineDownloadReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
downloadText.setText("離線圖片下載");
}
}
/** 離線下載完成,傳送廣播,更改離線下載按鈕的UI文字 */
private void sendFinishBroadcastReceiver() {
Intent intent = new Intent();
intent.setAction(Constants.OfflineDownloadBroadcastReceiverAction);
sendBroadcast(intent);
}
切記傳送廣播時使用的action字串要和manifest.xml裡設定的一樣才有效,且內部類形式的廣播在manifest.xml的設定要用:
外部類名$內部類名,廣播才會生效。如下:
<!-- 離線下載完成廣播 -->
<receiver android:name="main.OfflineDownload$OfflineDownloadReceiver">
<intent-filter>
<action android:name="com.nostra13.example.universalimageloader.action.OFFLINE_DOWNLOAD_BROADCAST" />
</intent-filter>
</receiver>
6、寫在最後:以上就是離線下載圖片功能簡單實現的講解,其實想寫一個完整的離線下載,但是文字部分會暴露公司介面,所以就沒有demo了。 另外demo還有很多不完善。比如單執行緒下載速度慢,連線超時無處理等問題。 這次的文章希望能起到一個拋磚引玉的作用,以後會繼續完善,歡迎拍磚。 當然,必須得有demo,下載地址: