編輯:關於Android編程
關於下載進度的監聽,這個比較簡單,以apk文件下載為例,需要處理3個回調函數,分別是:
1.下載中
2.下載成功
3.下載失敗
因此對應的回調接口就有了:
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工具類中提供的幾個靜態方法來創建線程池,這裡我是手動創建線程池的,代碼如下:
public class ThreadManager { private static ThreadPool mThreadPool; /** * 獲取線程池 * * @return */ public static ThreadPool getThreadPool() { if (null == mThreadPool) { synchronized (ThreadManager.class) { if (null == mThreadPool) { // cpu個數 int cpuNum = Runtime.getRuntime().availableProcessors(); //線程個數 int count = cpuNum * 2 + 1; mThreadPool = new ThreadPool(count, count, 0); } } } return mThreadPool; } public static class ThreadPool { int corePoolSize;// 核心線程數 int maximumPoolSize;// 最大線程數 long keepAliveTime;// 保持活躍時間(休息時間) private ThreadPoolExecutor executor; /** * 構造方法初始化 * * @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()); } }; /** * 執行線程任務 * * @param r */ public void execute(Runnable r) { //參1:核心線程數;參2:最大線程數;參3:保持活躍時間(休息時間);參4:活躍時間單位;參5:線程隊列;參6:線程工廠;參7:異常處理策略 if (null == executor) { executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue(), sThreadFactory/*Executors.defaultThreadFactory()*/, new ThreadPoolExecutor.AbortPolicy()); } // 將當前Runnable對象放在線程池中執行 executor.execute(r); } /** * 從線程池的任務隊列中移除一個任務 * 如果當前任務已經是運行狀態了,那麼就表示不在任務隊列中了,也就移除失敗. */ public void cancle(Runnable r) { if (null != executor && null != r) { executor.getQueue().remove(r); } } /** * 是否關閉了線程池 * @return */ public boolean isShutdown(){ return executor.isShutdown(); } /** * 關閉線程池 */ 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 下載回調函數 */ 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(); } //修改目錄權限可讀可寫可執行 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下載為例,要實現下載通知服務的話,就用到了Notification和Service,Notification用來通知下載進度並顯示給用戶看,Service用於後台默默的下載文件,這裡我用到了IntentService,它的好處在於任務執行完畢後會自動關閉服務.同時程序用如果其他地方還想監聽到下載的進度,那麼可以在IntentService下載服務中通過發送廣播告知進度.
ok,下面的代碼可以直接用戶實現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); } } /** * 初始數據 * * @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 是否支持斷點續傳 */ private void downloadFile(final boolean append) { final Message message = updateHandler.obtainMessage(); int currentSize = 0; //上次下載的大小 long readSize = 0L;//已下載的總大小 long contentLength = 0;//服務器返回的數據長度 if (append) { FileInputStream fis = null; try { fis = new FileInputStream(UpdateService.this.apkFile); currentSize = fis.available();//獲取上次下載的大小,斷點續傳可用 } 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()); //發送廣播,通知外界下載進度 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); } } /** * 發送更新進度 * * @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()); } }
锲而捨之,朽木不折;锲而不捨,金石可镂。——荀況今天學習了一下Service的用法就和大家一起來討論Android中Service的相關知識點,如
先給大家展示效果圖:package com.example.walkerlogin1; import android.app.Activity; import andro
寫了個應用,實現了一組WebView的順序,倒序,和隨機加載。之中使用了延時,為什麼要使用呢?請看下圖: package com.zms.csdngo; im
效果如下:C#實現代碼using Android.App;using Android.OS;using Android.Widget;namespace SpinnerD