Android下載進度監(jiān)聽和通知的處理詳解
本文實例為大家分享了Android下載進度監(jiān)聽和通知的具體代碼,供大家參考,具體內(nèi)容如下
下載管理器
關(guān)于下載進度的監(jiān)聽,這個比較簡單,以apk文件下載為例,需要處理3個回調(diào)函數(shù),分別是:
1.下載中
2.下載成功
3.下載失敗
因此對應的回調(diào)接口就有了:
public interface DownloadCallback {
/**
* 下載成功
* @param file 目標文件
*/
void onComplete(File file);
/**
* 下載失敗
* @param e
*/
void onError(Exception e);
/**
* 下載中
* @param count 總大小
* @param current 當前下載的進度
*/
void onLoading(long count, long current);
}
接下來就是線程池的管理了,當然你也可以直接使用Executors工具類中提供的幾個靜態(tài)方法來創(chuàng)建線程池,這里我是手動創(chuàng)建線程池的,代碼如下:
public class ThreadManager {
private static ThreadPool mThreadPool;
/**
* 獲取線程池
*
* @return
*/
public static ThreadPool getThreadPool() {
if (null == mThreadPool) {
synchronized (ThreadManager.class) {
if (null == mThreadPool) {
// cpu個數(shù)
int cpuNum = Runtime.getRuntime().availableProcessors();
//線程個數(shù)
int count = cpuNum * 2 + 1;
mThreadPool = new ThreadPool(count, count, 0);
}
}
}
return mThreadPool;
}
public static class ThreadPool {
int corePoolSize;// 核心線程數(shù)
int maximumPoolSize;// 最大線程數(shù)
long keepAliveTime;// 保持活躍時間(休息時間)
private ThreadPoolExecutor executor;
/**
* 構(gòu)造方法初始化
*
* @param corePoolSize
* @param maximumPoolSize
* @param keepAliveTime
*/
private ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.keepAliveTime = keepAliveTime;
}
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "ThreadManager #" + mCount.getAndIncrement());
}
};
/**
* 執(zhí)行線程任務
*
* @param r
*/
public void execute(Runnable r) {
//參1:核心線程數(shù);參2:最大線程數(shù);參3:保持活躍時間(休息時間);參4:活躍時間單位;參5:線程隊列;參6:線程工廠;參7:異常處理策略
if (null == executor) {
executor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
sThreadFactory/*Executors.defaultThreadFactory()*/,
new ThreadPoolExecutor.AbortPolicy());
}
// 將當前Runnable對象放在線程池中執(zhí)行
executor.execute(r);
}
/**
* 從線程池的任務隊列中移除一個任務
* 如果當前任務已經(jīng)是運行狀態(tài)了,那么就表示不在任務隊列中了,也就移除失敗.
*/
public void cancle(Runnable r) {
if (null != executor && null != r) {
executor.getQueue().remove(r);
}
}
/**
* 是否關(guān)閉了線程池
* @return
*/
public boolean isShutdown(){
return executor.isShutdown();
}
/**
* 關(guān)閉線程池
*/
public void shutdown() {
executor.shutdown();
}
}
}
接下來就是一個下載管理器的封裝了.
public class DownloadManager {
private DownloadCallback callback;
private Context context;
private String url;
private String fileName;
/**
* 初始化
* @param context 上下文
* @param url 目標文件url
* @param fileName 下載保存的文件名
* @param callback 下載回調(diào)函數(shù)
*/
public DownloadManager(final Context context, final String url, final String fileName, DownloadCallback callback) {
this.context = context;
this.url = url;
this.fileName = fileName;
this.callback = callback;
}
/**
* 開始下載
*/
public void startDownload() {
if (null == callback) return;
ThreadManager.getThreadPool().execute(new Runnable() {
@Override
public void run() {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(10000);
long total = conn.getContentLength();
long curr = 0;
File file = new File(PathUtils.getDirectory(context, "apk"), fileName);
if (!file.exists()) {
file.createNewFile();
} else {
file.delete();
}
FileOutputStream fos = new FileOutputStream(file);
if (200 == conn.getResponseCode()) {
InputStream in = conn.getInputStream();
byte[] buff = new byte[1024];
int len = 0;
while ((len = in.read(buff)) != -1) {
fos.write(buff, 0, len);
curr += len;
callback.onLoading(total, curr);
}
in.close();
fos.close();
if (curr == total) {
callback.onComplete(file);
} else {
throw new Exception("curr != total");
}
} else {
throw new Exception("" + conn.getResponseCode());
}
} catch (Exception e) {
e.printStackTrace();
callback.onError(e);
} finally {
if (null != conn) {
conn.disconnect();
}
}
}
});
}
}
涉及的工具類如下:
PathUtils
public class PathUtils {
/**
* 獲取cache目錄下的rootDir目錄
*
* @param context
* @param rootDir
* @return
*/
public static File getDirectory(Context context, String rootDir) {
String cachePath = context.getApplicationContext().getCacheDir().getAbsolutePath();
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
if (Build.VERSION.SDK_INT <= 8) {
cachePath = Environment.getExternalStorageDirectory().getAbsolutePath();
} else if (context.getApplicationContext().getExternalCacheDir() != null) {
cachePath = context.getApplicationContext().getExternalCacheDir().getAbsolutePath();
}
}
File rootF = new File(cachePath + File.separator + rootDir);
if (!rootF.exists()) {
rootF.mkdirs();
}
//修改目錄權(quán)限可讀可寫可執(zhí)行
String cmd = "chmod 777 -R " + rootF.getPath();
CmdUtils.execCmd(cmd);
return rootF;
}
}
CmdUtils
public class CmdUtils {
public static void execCmd(String cmd) {
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
}
下載通知服務
同樣以apk下載為例,要實現(xiàn)下載通知服務的話,就用到了Notification和Service,Notification用來通知下載進度并顯示給用戶看,Service用于后臺默默的下載文件,這里我用到了IntentService,它的好處在于任務執(zhí)行完畢后會自動關(guān)閉服務.同時程序用如果其他地方還想監(jiān)聽到下載的進度,那么可以在IntentService下載服務中通過發(fā)送廣播告知進度.
ok,下面的代碼可以直接用戶實現(xiàn)apk的升級更新的操作.
public class UpdateService extends IntentService {
private File apkFile;
private String url;
private String fileName;
private NotificationCompat.Builder builderNotification;
private NotificationManager updateNotificationManager;
private int appNameID;
private int iconID;
private PendingIntent updatePendingIntent;
private boolean isUpdating;
public static final String ACTION_UPDATE_PROGRESS = "blog.csdn.net.mchenys.mobilesafe.ACTION_UPDATE_PROGRESS";
private Handler updateHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
UpdateService.this.onFailNotification();
break;
case 1:
UpdateService.this.downComplete();
break;
}
super.handleMessage(msg);
}
};
public UpdateService() {
super("UpdateService");
}
/**
* 開始更新
*
* @param context
* @param url 更新的url
* @param fileName 下載保存apk的文件名稱
*/
public static void startUpdate(Context context, String url, String fileName) {
Intent intent = new Intent(context, UpdateService.class);
intent.putExtra("url", url);
intent.putExtra("fileName", fileName);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
//WorkerThread
if (!this.isUpdating && intent != null) {
initData(intent);
initNotification();
downloadFile(true);
}
}
/**
* 初始數(shù)據(jù)
*
* @param intent
*/
private void initData(Intent intent) {
this.isUpdating = true;
this.url = intent.getStringExtra("url");
this.fileName = intent.getStringExtra("fileName");
this.apkFile = new File(PathUtils.getDirectory(getApplicationContext(), "mobilesafe"), fileName);
if (!this.apkFile.exists()) {
try {
this.apkFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
} else {
this.apkFile.delete();
}
this.appNameID = R.string.app_name;
this.iconID = R.mipmap.ic_logo;
}
/**
* 初始化通知
*/
private void initNotification() {
Intent updateCompletingIntent = new Intent();
updateCompletingIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
updateCompletingIntent.setClass(this.getApplication().getApplicationContext(), UpdateService.class);
this.updatePendingIntent = PendingIntent.getActivity(this, this.appNameID, updateCompletingIntent, PendingIntent.FLAG_CANCEL_CURRENT);
this.updateNotificationManager = (NotificationManager) this.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
this.builderNotification = new NotificationCompat.Builder(this.getApplicationContext());
this.builderNotification.setSmallIcon(this.iconID).
setContentTitle(this.getResources().getString(this.appNameID)).
setContentIntent(updatePendingIntent).
setAutoCancel(true).
setTicker("開始更新").
setDefaults(1).
setProgress(100, 0, false).
setContentText("下載進度").
build();
this.updateNotificationManager.notify(this.iconID, this.builderNotification.build());
}
/**
* 開始下載apk
*
* @param append 是否支持斷點續(xù)傳
*/
private void downloadFile(final boolean append) {
final Message message = updateHandler.obtainMessage();
int currentSize = 0; //上次下載的大小
long readSize = 0L;//已下載的總大小
long contentLength = 0;//服務器返回的數(shù)據(jù)長度
if (append) {
FileInputStream fis = null;
try {
fis = new FileInputStream(UpdateService.this.apkFile);
currentSize = fis.available();//獲取上次下載的大小,斷點續(xù)傳可用
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
HttpURLConnection conn = null;
InputStream is = null;
FileOutputStream fos = null;
try {
conn = (HttpURLConnection) new URL(UpdateService.this.url).openConnection();
conn.setRequestProperty("User-Agent", "Android");
if (currentSize > 0) {
conn.setRequestProperty("RANGE", "bytes=" + currentSize + "-");
}
conn.setConnectTimeout(10000);
conn.setReadTimeout(20000);
contentLength = conn.getContentLength();
if (conn.getResponseCode() == 404) {
throw new Exception("Cannot find remote file:" + UpdateService.this.url);
}
is = conn.getInputStream();
fos = new FileOutputStream(UpdateService.this.apkFile, append);
//實時更新通知
UpdateService.this.builderNotification.setSmallIcon(iconID).
setContentTitle(UpdateService.this.getResources().getString(UpdateService.this.appNameID)).
setContentIntent(updatePendingIntent).
setAutoCancel(true).
setTicker("正在更新").
setDefaults(0).
setContentText("下載進度").
build();
byte[] buffer = new byte[8*1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
readSize += len;//累加已下載的大小
int progress = (int) (readSize * 100 / contentLength);
Log.d("UpdateService", (readSize * 100 / contentLength)+"");
if (progress % 8 == 0) {
UpdateService.this.builderNotification.setProgress(100, progress, false);
UpdateService.this.updateNotificationManager.notify(iconID, UpdateService.this.builderNotification.build());
//發(fā)送廣播,通知外界下載進度
sendUpdateProgress(progress);
}
}
if (readSize == 0L) {
message.what = 0;
} else if (readSize == contentLength) {
message.what = 1;
sendUpdateProgress(100);
}
} catch (Exception e) {
e.printStackTrace();
message.what = 0;
} finally {
if (conn != null) {
conn.disconnect();
}
IOUtils.close(is);
IOUtils.close(fos);
updateHandler.sendMessage(message);
}
}
/**
* 發(fā)送更新進度
*
* @param progress
*/
private void sendUpdateProgress(int progress) {
Intent intent = new Intent(ACTION_UPDATE_PROGRESS);
intent.putExtra("progress", progress);
sendBroadcast(intent);
}
/**
* 下載失敗通知用戶重新下載
*/
private void onFailNotification() {
this.builderNotification.setSmallIcon(iconID).
setContentTitle("更新失敗,請重新下載").
setContentIntent(updatePendingIntent).
setAutoCancel(true).
setTicker("更新失敗").
setDefaults(1).
setProgress(100, 0, false).
setContentText("下載進度").
build();
this.updateNotificationManager.notify(iconID, this.builderNotification.build());
}
/**
* 下載完畢,后通知用戶點擊安裝
*/
private void downComplete() {
UpdateService.this.isUpdating = false;
String cmd = "chmod 777 " + this.apkFile.getPath();
CmdUtils.execCmd(cmd);
Uri uri = Uri.fromFile(this.apkFile);
Intent updateCompleteIntent = new Intent("android.intent.action.VIEW");
updateCompleteIntent.addCategory("android.intent.category.DEFAULT");
updateCompleteIntent.setDataAndType(uri, "application/vnd.android.package-archive");
this.updatePendingIntent = PendingIntent.getActivity(this, this.appNameID, updateCompleteIntent, PendingIntent.FLAG_UPDATE_CURRENT);
this.builderNotification.setSmallIcon(this.iconID).
setContentTitle(this.getResources().getString(this.appNameID)).
setContentIntent(this.updatePendingIntent).
setAutoCancel(true).
setTicker("更新完成").
setDefaults(1).
setProgress(0, 0, false).
setContentText("更新完成,點擊安裝").
build();
this.updateNotificationManager.notify(this.iconID, this.builderNotification.build());
}
}
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android中Invalidate和postInvalidate的更新view區(qū)別
Android中實現(xiàn)view的更新有兩組方法,一組是invalidate,另一組是postInvalidate,其中前者是在UI線程自身中使用,而后者在非UI線程中使用,感興趣的朋友可以了解下哦2013-01-01
Android編程實現(xiàn)手繪及保存為圖片的方法(附demo源碼下載)
這篇文章主要介紹了Android編程實現(xiàn)手繪及保存為圖片的方法,涉及Android畫布的使用及圖片的操作技巧,并附帶了demo源碼供讀者下載,需要的朋友可以參考下2015-12-12
Android編程判斷是否連接網(wǎng)絡(luò)的方法【W(wǎng)iFi及3G判斷】
這篇文章主要介紹了Android編程判斷是否連接網(wǎng)絡(luò)的方法,結(jié)合實例形式分析了Android針對WiFi及3G網(wǎng)絡(luò)連接的判斷方法,需要的朋友可以參考下2017-02-02
Android實現(xiàn)給TableLayou繪制邊框的方法
這篇文章主要介紹了Android實現(xiàn)給TableLayou繪制邊框的方法,涉及Android TableLayou布局控制相關(guān)技巧,需要的朋友可以參考下2016-03-03
android studio與手機連接調(diào)試步驟詳解
這篇文章主要為大家詳細介紹了android studio與手機連接調(diào)試步驟,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07
Android使用PullToRefresh實現(xiàn)上拉加載和下拉刷新效果的代碼
這篇文章主要介紹了Android使用PullToRefresh實現(xiàn)上拉加載和下拉刷新效果 的相關(guān)資料,需要的朋友可以參考下2016-07-07
Android?LinearLayout快速設(shè)置每個item間隔
這篇文章主要介紹了Android?LinearLayout快速設(shè)置每個item間隔的相關(guān)資料,需要的朋友可以參考下2023-07-07

