1. 程式人生 > >Android 使用 DownloadManager 實現APP應用內更新升級

Android 使用 DownloadManager 實現APP應用內更新升級

具體實現思路:

  1. 我們通過downloaderManager來下載apk,並且本地儲存downManager.enqueue(request)返回的id值,並且通過這個id獲取apk的下載檔案路徑和下載的狀態,並且通過狀態來更新通知欄的顯示。

  2. 第一次下載成功,彈出安裝介面

    如果使用者沒有點選安裝,而是按了返回鍵,在某個時候,又再次使用了我們的APP

    如果下載成功,則判斷本地的apk的包名是否和當前程式是相同的,並且本地apk的版本號大於當前程式的版本,如果都滿足則直接啟動安裝程式。

  3. 通過BroadcastReceiver來監聽是否下載完成

檔案下載管理的實現,包括建立request和加入佇列下載,通過返回的id來獲取下載路徑和下載狀態。

public class FileDownloadManager {
    private DownloadManager downloadManager;
    private Context context;
    private static FileDownloadManager instance;

    private FileDownloadManager(Context context) {
        downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE
); this.context = context.getApplicationContext(); } public static FileDownloadManager getInstance(Context context) { if (instance == null) { instance = new FileDownloadManager(context); } return instance; } /** * @param uri * @param title
* @param description * @return download id */ public long startDownload(String uri, String title, String description,String appName) { DownloadManager.Request req = new DownloadManager.Request(Uri.parse(uri)); //設定用於下載時的網路型別,預設任何網路都可以,提供:NETWORK_BLUETOOTH、NETWORL_MOBILE、NETWORK_WIFI   req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); //設定漫遊狀態下是否可以下載  //req.setAllowedOverRoaming(false); //VISIBILTY_HIDDEN: Notification:將不會顯示,如果設定該屬性的話,必須要新增許可權 。 Android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.   //VISIBILITY_VISIBLE: Notification顯示,但是隻是在下載任務執行的過程中顯示,下載完成自動消失。(預設值) // VISIBILITY_VISIBLE_NOTIFY_COMPLETED : Notification顯示,下載進行時,和完成之後都會顯示。 //VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION :只有當任務完成時,Notification才會顯示。   req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //設定檔案的儲存的位置[三種方式] //第一種 //file:///storage/emulated/0/Android/data/your-package/files/Download/update.apk //這個檔案是應用專屬,軟體解除安裝後,下載的檔案也將全部刪除   req.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, appName+".apk"); //第二種 //file:///storage/emulated/0/Download/update.apk //req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "update.apk"); //第三種 自定義檔案路徑 //req.setDestinationUri() // 設定一些基本顯示資訊 req.setTitle(title); req.setDescription(description); //req.setMimeType("application/vnd.android.package-archive"); return downloadManager.enqueue(req);//非同步 //dm.openDownloadedFile() } /** * 獲取檔案儲存的路徑 * * @param downloadId an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @return file path * @see FileDownloadManager#getDownloadUri(long) */ public String getDownloadPath(long downloadId) { DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor c = downloadManager.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 an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @see FileDownloadManager#getDownloadPath(long) */ public Uri getDownloadUri(long downloadId) { return downloadManager.getUriForDownloadedFile(downloadId); } public DownloadManager getDownloadManager() { return downloadManager; } /** * 獲取下載狀態 * * @param downloadId an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @return int * @see DownloadManager#STATUS_PENDING * @see DownloadManager#STATUS_PAUSED * @see DownloadManager#STATUS_RUNNING * @see DownloadManager#STATUS_SUCCESSFUL * @see DownloadManager#STATUS_FAILED */ public int getDownloadStatus(long downloadId) { DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor c = downloadManager.query(query); if (c != null) { try { if (c.moveToFirst()) { return c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); } } finally { c.close(); } } return -1; } }

app的檢測安裝的實現:

public class DownLoadApk {
    public static final String TAG = DownLoadApk.class.getSimpleName();

    public static void download(Context context, String url, String title,final String appName) {
        // 獲取儲存ID
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        long downloadId =sp.getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L);
        if (downloadId != -1L) {
            FileDownloadManager fdm = FileDownloadManager.getInstance(context);
            int status = fdm.getDownloadStatus(downloadId);
            if (status == DownloadManager.STATUS_SUCCESSFUL) {
                //啟動更新介面
                Uri uri = fdm.getDownloadUri(downloadId);
                if (uri != null) {
                    if (compare(getApkInfo(context, uri.getPath()), context)) {
                        startInstall(context, uri);
                        return;
                    } else {
                        fdm.getDownloadManager().remove(downloadId);
                    }
                }
                start(context, url, title,appName);
            } else if (status == DownloadManager.STATUS_FAILED) {
                start(context, url, title,appName);
            } else {
                Log.d(TAG, "apk is already downloading");
            }
        } else {
            start(context, url, title,appName);
        }
    }

    private static void start(Context context, String url, String title,String appName) {
        long id = FileDownloadManager.getInstance(context).startDownload(url,
                title, "下載完成後點選開啟",appName);
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        sp.edit().putLong(DownloadManager.EXTRA_DOWNLOAD_ID,id).commit();
        Log.d(TAG, "apk start download " + id);
    }

    public 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程式資訊[packageName,versionName...]
     *
     * @param context Context
     * @param path    apk path
     */
    private static PackageInfo getApkInfo(Context context, String path) {
        PackageManager pm = context.getPackageManager();
        PackageInfo info = pm.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
        if (info != null) {
            return info;
        }
        return null;
    }


    /**
     * 下載的apk和當前程式版本比較
     *
     * @param apkInfo apk file's packageInfo
     * @param context Context
     * @return 如果當前應用版本小於apk的版本則返回true
     */
    private static boolean compare(PackageInfo apkInfo, Context context) {
        if (apkInfo == null) {
            return false;
        }
        String localPackage = context.getPackageName();
        if (apkInfo.packageName.equals(localPackage)) {
            try {
                PackageInfo packageInfo = context.getPackageManager().getPackageInfo(localPackage, 0);
                if (apkInfo.versionCode > packageInfo.versionCode) {
                    return true;
                }
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
}

上面的程式碼可知:我們通過獲取當前app的資訊來比較是否需要下載和是否立即安裝。第一次下載把downloadId儲存到本地,使用者下次進來的時候,取出儲存的downloadId,然後通過downloadId來獲取下載的狀態資訊。如果下載失敗,則重新下載並且把downloadId存起來。如果下載成功,則判斷本地的apk的包名是否和當前程式是相同的,並且本地apk的版本號大於當前程式的版本,如果都滿足則直接啟動安裝程式。

監聽app是否安裝完成

public class ApkInstallReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)){
              long downloadApkId =intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
              installApk(context, downloadApkId);
        }
    }

    /**
     * 安裝apk
     */
    private void installApk(Context context,long downloadApkId) {
        // 獲取儲存ID
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        long downId =sp.getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L);
        if(downloadApkId == downId){
            DownloadManager downManager= (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
            Uri downloadFileUri = downManager.getUriForDownloadedFile(downloadApkId);
            if (downloadFileUri != null) {
            Intent install= new Intent(Intent.ACTION_VIEW);
            install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive");
            install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(install);
            }else{
                Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

DownloadManager下載完成後會發出一個廣播android.intent.action.DOWNLOAD_COMPLETE新建一個廣播接收者即可:

清單配置:

先新增網路下載的許可權:

 <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

再新增靜態廣播:

 <receiver android:name=".ApkInstallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
            </intent-filter>
        </receiver>