1、app升級下載現在不推薦使用downloadmanager下載:
原因有下面的幾個方面:
(1)三星note系列部分手機需要手動打開這個權限才能用這個功能,而有些國產手機更加nb了直接個閹割了(downloadmanager),所以考慮到手機的適配性,最后自己編寫app下載的后台代碼
2、但是這里還是對downloadmanager下載進行一些詳細的分析,很多下載的思路還是值得借鑒的
1、首先后台的apk的搭建
1、打開本地Tomcat服務器,放入一個Apk文件
apk存放在webapps/root目錄下
// 首先確保瀏覽器能夠訪問http://localhost:8080/123456.apk
下面我將內容大致分為以下幾個部分:
(1)App版本檢測
(2)Apk下載
(3)Apk更新安裝
(4)對以上功能進行封裝
基於以上4部分,我們逐一展開。
1.App版本檢測:
要實現App的更新下載,我們上面介紹了,前提是服務器要保存一個App的版本號(通常的方式是保存versionCode,當然你要對比versionName也沒關系)。當用戶去手動檢測版本,或者進入首頁自動檢測時,第一步是需要請求服務器的版本號,拿到版本號之后與當前App版本號(當前版本號可通過PackageInfo獲取)進行對比。服務器返回的版本號大於當前App版本號,證明App已經有更新,那么進入第2步。
2.Apk下載
Apk文件是保存在服務器的。我們可以通過Http流將其下載到本地手機,然后更新安裝。Android中下載的方式很多種:HttpUrlConnection,Retrofit,okHttp,以及Android原生的下載工具類DownLoadManager 等等。我們采用的方式是Google推薦的下載工具類DownLoadManager。關於DownLoadManager的使用其實很簡單,簡單概括如下:
(1)通過getSystemService獲取DownLoadManager。
(2)初始化DownLoadManager的Request,構建下載請求。
(3)調用DownLoadManager的enqueue異步發起請求,該方法返回值為標識當前下載任務的id,即downloadId。
(4)當下載完成后,系統會發出條件為android.intent.action.DOWNLOAD_COMPLETE的廣播,我們可以自定義廣播接受器,然后在onReceive中處理下載完成的邏輯即可。
詳細使用方式大家可以參考網上的教程,此處就不再贅述。
上面通過下載啰嗦了一堆。此時我們要想一個問題:當我們下載完成后,並沒有安裝。當用戶再次進入App時該如何操作?
有朋友會說,那就再去下載一次,然后繼續執行更新安裝呀!哈哈,這種方式是沒有錯誤的,但是如果用戶惡意行為,每次下載完成都不安裝,那我們豈不是每次都要去下載100次,1000次。。(然后手機boom!!!)這種方式肯定是不能采用的。那么我們該如何解決呢?
很簡單,當我們在下載之前,先去指定的文件夾下查看有木有已經下載好的Apk,並且該Apk的版本是高於本App的版本,此時我們就去執行安裝操作。如果上面條件不成立,此時再去執行下載操作。
3.Apk更新安裝
相信大家對於如何安裝一個Apk都比較熟悉吧,原理也是比較簡單的。
(1)通過downloadId獲取下載的Uri。
(2)將Uri設置到Itent的setDataAndType作為啟動條件。
(3)調用startActivity啟動對應Intent即可。
以上3步,即可完成App的更新功能。
整體的流程很清晰:
版本檢測 → Apk下載 (檢查是否存在未安裝的Apk) → Apk安裝 → 完成更新
下面,通過代碼來具體分析整個流程:
關於App版本檢測其實就是一個Http請求,不再多說。我們從Apk下載開始:
上面我們提到,在下載之前需要去檢測是否存在已經下載的Apk。通過什么獲取呢?沒錯,肯定是downloadId了。
1> 如果存在downloadId,那么我們通過downloadId獲取當前下載的狀態status。status分為成功,失敗兩種狀態。
(1)當status為成功狀態時,即已經下載完成,我們就通過downloadId獲取下載文件的Uri。然后可以通過Uri獲取PackageInfo,與當前App進行包名和版本號的對比,當包名相同,並且當前版本號是小於下載的Apk版本號兩個條件同時成立時,直接執行安裝操作。否則,執行remove,通過downloadId刪除下載任務以及文件,繼續執行下載。
(2)當status為失敗狀態時,即下載未完成,我們就直接執行重新下載即可。
2> 如果不存在downloadId,即沒有下載過Apk,執行下載即可。
下載完成后,系統會發出廣播,在廣播中,我們對比downloadId是否相同,相同情況下,直接通過downloadId獲取Uri,然后跳轉到安裝界面,提示用戶安裝即可:
所以,別忘了在下載之前要先將該大喇叭(廣播接受器)注冊。
最后,當我們安裝完成后,再次進入App,就將其已下載的Apk文件進行刪除(將該方法放在onCreate生命周期中即可):
上面通過downloadApk獲取下載文件的地址。downloadApk地址是在下載完成后廣播接收器中保存的。
通過上面的步驟,我們就完成了App更新下載安裝的全部工作。相信大家也有了更深的認識和理解。
下面博客:
http://chuansong.me/n/1090429551927
http://chuansong.me/n/1090429551927
也是對downloadManger做了詳細的解釋
接下來我們看下代碼
程序的框架如下所示:
package co.huiqu.webapp; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import co.huiqu.webapp.download.DownLoadUtils; import co.huiqu.webapp.download.DownloadApk; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); //1.注冊下載廣播接收器 DownloadApk.registerBroadcast(this); //2.apk按照成功之后再次進場到app刪除已存在的Apk DownloadApk.removeFile/**/(this); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { /* String packageName = "com.android.providers.downloads"; Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + packageName)); startActivity(intent);*/ //3.如果手機已經啟動下載程序,執行downloadApk。否則跳轉到設置界面 if (DownLoadUtils.getInstance(getApplicationContext()).canDownload()) { //DownloadApk.downloadApk(getApplicationContext(), "http://www.huiqu.co/public/download/apk/huiqu.apk", "Hobbees更新", "Hobbees"); DownloadApk.downloadApk(getApplicationContext(), "http://10.12.8.13:8080/123456.apk", "酷狗更新", "Hobbees下載"); } else { DownLoadUtils.getInstance(getApplicationContext()).skipToDownloadManager(); } } }); } @Override protected void onDestroy() { //4.反注冊廣播接收器 DownloadApk.unregisterBroadcast(this); super.onDestroy(); } }
sharePrefer的工具類代碼:
package co.huiqu.webapp.config; import java.util.Set; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; /** * * 保存系統信息 * @author song * @date 2015年11月4日 */ public class SystemParams { private static SystemParams instance; private static SharedPreferences sharedPrederences = null; private SystemParams() { } //在Application初始化 public static void init(Context context) { sharedPrederences = context.getSharedPreferences("hobbees", Context.MODE_PRIVATE); } public static SystemParams getInstance() { if(instance == null) { synchronized (SystemParams.class) { if(instance == null) { instance = new SystemParams(); } } } return instance; } /**get**/ public int getInt(String key){ return sharedPrederences.getInt(key, 0); } public int getInt(String key,int defValue){ return sharedPrederences.getInt(key, defValue); } public float getFloat(String key){ return sharedPrederences.getFloat(key, 0); } public float getFloat(String key,float defValue) { return sharedPrederences.getFloat(key, defValue); } public long getLong(String key){ return sharedPrederences.getLong(key, 0); } public long getLong(String key,long defValue) { return sharedPrederences.getLong(key, defValue); } public String getString(String key){ return sharedPrederences.getString(key, null); } public String getString(String key,String defValue) { return sharedPrederences.getString(key, defValue); } public boolean getBoolean(String key){ return sharedPrederences.getBoolean(key, false); } public boolean getBoolean(String key,boolean defValue) { return sharedPrederences.getBoolean(key, defValue); } /**set**/ public void setInt(String key,int value) { Editor editor = sharedPrederences.edit(); editor.putInt(key, value); editor.commit(); } public void setFloat(String key,float value) { Editor editor = sharedPrederences.edit(); editor.putFloat(key, value); editor.commit(); } public void setLong(String key,long value) { Editor editor = sharedPrederences.edit(); editor.putLong(key, value); editor.commit(); } public void setString(String key,String value) { Editor editor = sharedPrederences.edit(); editor.putString(key, value); editor.commit(); } public void setBoolean(String key,boolean value) { Editor editor = sharedPrederences.edit(); editor.putBoolean(key, value); editor.commit(); } public void setSetString(String key,Set<String> values) { Editor editor = sharedPrederences.edit(); editor.putStringSet(key, values); editor.commit(); } public void remove(String key) { Editor editor = sharedPrederences.edit(); editor.remove(key); editor.commit(); } public void clear() { Editor editor = sharedPrederences.edit(); editor.clear().commit(); } }
package co.huiqu.webapp.download; import android.app.DownloadManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.widget.Toast; import co.huiqu.webapp.config.SystemParams; /** * Created by Song on 2016/11/2. */ public class ApkInstallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { long downloadApkId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { Toast.makeText(context,"收到apk下載完成的廣播",Toast.LENGTH_LONG).show(); int status = DownLoadUtils.getInstance(context).checkStatus(downloadApkId); switch (status) { //下載暫停 case DownloadManager.STATUS_PAUSED: //獲得下載暫停的原因等信息 Toast.makeText(context,"暫停下載"+DownLoadUtils.getInstance(context).getPausedReason(downloadApkId),Toast.LENGTH_LONG).show(); break; //下載延遲 case DownloadManager.STATUS_PENDING: //獲得下載延遲的原因 break; //正在下載 case DownloadManager.STATUS_RUNNING: break; //下載完成 case DownloadManager.STATUS_SUCCESSFUL: //下載完成安裝APK installApk(context, downloadApkId); break; //下載失敗 case DownloadManager.STATUS_FAILED: // 獲得下載失敗的原因 Toast.makeText(context,"暫停下載"+DownLoadUtils.getInstance(context).getFailedReason(downloadApkId),Toast.LENGTH_LONG).show(); break; } }else if(DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(intent.getAction())){ //點擊通知欄取消下載 Toast.makeText(context,"通知欄被點擊",Toast.LENGTH_LONG).show(); } } /** * 安裝apk */ private void installApk(Context context,long downloadId) { long downId = SystemParams.getInstance().getLong(DownloadManager.EXTRA_DOWNLOAD_ID, -1L); if(downloadId == downId) { DownloadManager downManager= (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); Uri downloadUri = downManager.getUriForDownloadedFile(downloadId); SystemParams.getInstance().setString("downloadApk",downloadUri.getPath()); if (downloadUri != null) { Intent install= new Intent(Intent.ACTION_VIEW); install.setDataAndType(downloadUri, "application/vnd.android.package-archive"); install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); } else { Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show(); } } } }
package co.huiqu.webapp.download; import android.app.DownloadManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Environment; import android.util.Log; import android.widget.Toast; import java.io.File; import co.huiqu.webapp.config.SystemParams; /** * Apk下載 * Created by Song on 2016/11/2. */ public class DownloadApk { private static ApkInstallReceiver apkInstallReceiver; /** * 下載APK文件 * @param context * @param url * @param title * @param appName */ public static void downloadApk(Context context, String url, String title,final String appName) { //獲取存儲的下載ID long downloadId = SystemParams.getInstance().getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L); if(downloadId != -1) { //存在downloadId DownLoadUtils downLoadUtils = DownLoadUtils.getInstance(context); //獲取當前狀態 int status = downLoadUtils.getDownloadStatus(downloadId); if(DownloadManager.STATUS_SUCCESSFUL == status) { //狀態為下載成功 //獲取下載路徑URI Uri downloadUri = downLoadUtils.getDownloadUri(downloadId); if(null != downloadUri) { //存在下載的APK,如果兩個APK相同,啟動更新界面。否之則刪除,重新下載。 if(compare(getApkInfo(context,downloadUri.getPath()),context)) { startInstall(context, downloadUri); return; } else { //刪除下載任務以及文件 downLoadUtils.getDownloadManager().remove(downloadId); } } start(context, url, title,appName); } else if(DownloadManager.STATUS_FAILED == status) { //下載失敗,重新下載 start(context, url, title,appName); }else { Log.d(context.getPackageName(), "apk is already downloading"); } } else { //不存在downloadId,沒有下載過APK start(context, url, title,appName); } } /** * 開始下載 * @param context * @param url * @param title * @param appName */ private static void start(Context context, String url, String title,String appName) { if(hasSDKCard()) { long id = DownLoadUtils.getInstance(context).download(url, title, "下載完成后點擊打開", appName); SystemParams.getInstance().setLong(DownloadManager.EXTRA_DOWNLOAD_ID,id); } else { Toast.makeText(context,"手機未安裝SD卡,下載失敗",Toast.LENGTH_LONG).show(); } } public static void registerBroadcast(Context context) { apkInstallReceiver = new ApkInstallReceiver(); context.registerReceiver(apkInstallReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); context.registerReceiver(apkInstallReceiver, new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED)); } public static void unregisterBroadcast(Context context) { if(null != apkInstallReceiver) { context.unregisterReceiver(apkInstallReceiver); } } /** * 跳轉到安裝界面 * @param context * @param uri */ private static void startInstall(Context context, Uri uri) { Intent install = new Intent(Intent.ACTION_VIEW); install.setDataAndType(uri, "application/vnd.android.package-archive"); install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); } /** * 獲取APK程序信息 * @param context * @param path * @return */ private static PackageInfo getApkInfo(Context context, String path) { PackageManager pm = context.getPackageManager(); PackageInfo pi = pm.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES); if(null != pi) { return pi; } return null; } /** * 比較兩個APK的信息 * @param apkInfo * @param context * @return */ private static boolean compare(PackageInfo apkInfo,Context context) { if(null == apkInfo) { return false; } String localPackageName = context.getPackageName(); if(localPackageName.equals(apkInfo.packageName)) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(localPackageName, 0); //比較當前APK和下載的APK版本號 if (apkInfo.versionCode > packageInfo.versionCode) { //如果下載的APK版本號大於當前安裝的APK版本號,返回true return true; } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } return false; } /** * 是否存在SD卡 */ private static boolean hasSDKCard() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } /** * 刪除已下載的文件 */ public static void removeFile(Context context) { String filePath = SystemParams.getInstance().getString("downloadApk",null); if(null != filePath) { File downloadFile = new File(filePath); if(null != downloadFile && downloadFile.exists()) { //刪除之前先判斷用戶是否已經安裝了,安裝了才刪除。 if(!compare(getApkInfo(context,filePath),context)) { downloadFile.delete(); Log.e("----", "已刪除"); } } } } }
package co.huiqu.webapp.download; import android.app.DownloadManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.util.Log; import android.widget.Toast; import java.io.File; import co.huiqu.webapp.config.SystemParams; /** * 封裝 DownLoadManager 下載 * Created by Song on 2016/11/2. */ public class DownLoadUtils { private Context mContext; private DownloadManager mDownloadManager; private static volatile DownLoadUtils instance; private DownLoadUtils(Context context) { this.mContext = context.getApplicationContext(); mDownloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); } /** * 獲取單例對象 * * @param context * @return */ public static DownLoadUtils getInstance(Context context) { if (instance == null) { synchronized (DownLoadUtils.class) { if (instance == null) { instance = new DownLoadUtils(context); return instance; } } } return instance; } /** * 下載 * * @param uri * @param title * @param description * @param appName * @return downloadId */ public long download(String uri, String title, String description, String appName) { //1.構建下載請求 DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(uri)); downloadRequest.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE); downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); /**設置漫游狀態下是否可以下載*/ downloadRequest.setAllowedOverRoaming(false); /**如果我們希望下載的文件可以被系統的Downloads應用掃描到並管理, 我們需要調用Request對象的setVisibleInDownloadsUi方法,傳遞參數true.*/ downloadRequest.setVisibleInDownloadsUi(true); //文件保存位置 //file:///storage/emulated/0/Android/data/your-package/files/Download/appName.apk downloadRequest.setDestinationInExternalFilesDir(mContext, Environment.DIRECTORY_DOWNLOADS, appName + ".apk"); // 設置一些基本顯示信息 downloadRequest.setTitle(title); downloadRequest.setDescription(description); //req.setMimeType("application/vnd.android.package-archive"); return mDownloadManager.enqueue(downloadRequest);//異步請求 } /** * 獲取文件下載路徑 * * @param downloadId * @return */ public String getDownloadPath(long downloadId) { DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor c = mDownloadManager.query(query); if (c != null) { try { if (c.moveToFirst()) { return c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI)); } } finally { c.close(); } } return null; } /** * 獲取文件保存的地址 * * @param downloadId * @return */ public Uri getDownloadUri(long downloadId) { return mDownloadManager.getUriForDownloadedFile(downloadId); } public DownloadManager getDownloadManager() { return mDownloadManager; } /** * 獲取下載狀態 * * @param downloadId * @return */ public int getDownloadStatus(long downloadId) { DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor c = mDownloadManager.query(query); if (c != null) { try { if (c.moveToFirst()) { return c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); } } finally { c.close(); } } return -1; } /** * 判斷下載管理程序是否可用 * * @return */ public boolean canDownload() { try { int state = mContext.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads"); if (state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED || state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER || state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { return false; } } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 進入 啟用/禁用 下載管理程序界面 */ public void skipToDownloadManager() { String packageName = "com.android.providers.downloads"; Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + packageName)); mContext.startActivity(intent); } /** * 進入 啟用/禁用 下載管理程序界面 */ public int checkStatus(long downloadId) { DownloadManager.Query query = new DownloadManager.Query(); //通過下載的id查找 query.setFilterById(downloadId); Cursor c = mDownloadManager.query(query); int status = -1; if (c.moveToFirst()) { status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)); } return status; } /** * 查詢當前下載暫停失敗的原因 */ public String getPausedReason(long downloadId) { DownloadManager.Query query = new DownloadManager.Query(); //通過下載的id查找 query.setFilterById(downloadId); query.setFilterByStatus(DownloadManager.STATUS_PAUSED); // Query the Download Manager for paused downloads. Cursor pausedDownloads = mDownloadManager.query(query); // Find the column indexes for the data we require. int reasonIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_REASON); int titleIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_TITLE); int fileSizeIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES); int bytesDLIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR); // Iterate over the result Cursor. while (pausedDownloads.moveToNext()) { // Extract the data we require from the Cursor. String title = pausedDownloads.getString(titleIdx); int fileSize = pausedDownloads.getInt(fileSizeIdx); int bytesDL = pausedDownloads.getInt(bytesDLIdx); // Translate the pause reason to friendly text. int reason = pausedDownloads.getInt(reasonIdx); String reasonString = "Unknown"; switch (reason) { case DownloadManager.PAUSED_QUEUED_FOR_WIFI: reasonString = "Waiting for WiFi"; break; case DownloadManager.PAUSED_WAITING_FOR_NETWORK: reasonString = "Waiting for connectivity"; break; case DownloadManager.PAUSED_WAITING_TO_RETRY: reasonString = "Waiting to retry"; break; default: break; } // Construct a status summary StringBuilder sb = new StringBuilder(); sb.append(title).append("\n"); sb.append(reasonString).append("\n"); sb.append("Downloaded ").append(bytesDL).append(" / ").append(fileSize); return sb.toString(); } return ""; } /** * 查詢當前下載暫停失敗的原因 */ public String getFailedReason(long downloadId) { DownloadManager.Query query = new DownloadManager.Query(); //通過下載的id查找 query.setFilterById(downloadId); query.setFilterByStatus(DownloadManager.STATUS_FAILED); // Query the Download Manager for paused downloads. Cursor pausedDownloads = mDownloadManager.query(query); // Find the column indexes for the data we require. int reasonIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_REASON); int titleIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_TITLE); int fileSizeIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES); int bytesDLIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR); // Iterate over the result Cursor. while (pausedDownloads.moveToNext()) { // Extract the data we require from the Cursor. String title = pausedDownloads.getString(titleIdx); int fileSize = pausedDownloads.getInt(fileSizeIdx); int bytesDL = pausedDownloads.getInt(bytesDLIdx); // Translate the pause reason to friendly text. int reason = pausedDownloads.getInt(reasonIdx); String reasonString = "Unknown"; switch (reason) { case DownloadManager.ERROR_HTTP_DATA_ERROR: reasonString = "服務器異常"; break; case DownloadManager.ERROR_INSUFFICIENT_SPACE: reasonString = "存儲空間不足"; break; case DownloadManager.ERROR_FILE_ERROR: reasonString = "找不到該文件"; break; default: break; } // Construct a status summary StringBuilder sb = new StringBuilder(); sb.append(title).append("\n"); sb.append(reasonString).append("\n"); sb.append("Downloaded ").append(bytesDL).append(" / ").append(fileSize); return sb.toString(); } return ""; } }
清單文件:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="co.huiqu.webapp"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <application android:name=".A" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".download.ApkInstallReceiver"> <intent-filter> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> <action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" /> </intent-filter> </receiver> </application> <!--在SDCard中創建與刪除文件權限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 往SDCard寫入數據權限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 從SDCard讀取數據權限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- 訪問網絡權限 --> <uses-permission android:name="android.permission.INTERNET"/> </manifest>