【Android】Android程式自動更新

來源:互聯網
上載者:User

標籤:android   更新   

App自動更新的步驟可分為三步:

  1. 檢查更新(如果有更新進行第2步,否則返回)
  2. 下載新版的APK安裝包
  3. 安裝APK

下面對這三步進行解釋,其中會穿插相應代碼,App自動更新的這三步全部被封裝到了一個單獨的Updater類中,可以直接拿來使用,我會在文章最後貼出源碼github地址。

Updater 使用樣本

通過單一的類Updater可以方便的實現自動檢查更新、下載安裝包和自動安裝,可以監聽下載進度,可以自訂更新提示等。儲存路徑可以自由書寫,如果路徑中某個目錄不存在會自動建立,流式API介面便於使用。下面是使用樣本,一行代碼搞定自動更新:

String savePath = Environment.getExternalStorageDirectory()                     + "/whinc/download/whinc.apk";String updateUrl = "http://192.168.1.168:8000/update.xml";Updater.with(mContext)        .downloadListener(mListener)        .update(updateUrl)        .save(savePath)        .create()        .checkUpdate();

第一步:檢查更新

這一步需要服務端的配合,服務端存放一個XML格式的設定檔(也可以用JSON或其他格式)提供給用戶端檢查更新,update.xml 格式如下:

<?xml version="1.0" encoding="utf-8"?><info>    <version>        <code>4</code>        <name>1.0.4</name>    </version>    <url>http://192.168.1.168:8000/test.apk</url>    <description>更新 - 吧啦吧啦;修複 - 吧啦吧啦;增加 - 巴拉巴拉巴</description></info>
  • <version>標籤指定服務端的版本號碼和版本名稱,該版本號碼和版本名稱對應Android項目配置裡的versionCodeversionName(Eclipse ADT項目可在 AndroidManifest.xml中的標籤中找到,Android Studio項目在module的build.gradle中的defaultConfig中找到)。
  • <url>標籤指定APK的,
  • <description>標籤指定更新內容。

用戶端通過 HTTP 要求服務端的 update.xml檔案,然後解析 update.xml,比較服務端的版本號碼與本地版本號碼,如果服務端版本號碼大於本地版本號碼說明有更新,則根據 update.xml中指定的APK下載最新的APK,下面將會詳細說明。

下面是檢查更新的代碼:

    /**     * 檢查 App 版本號碼     *     * @return 如果有新版本返回true,否則返回false     */    private boolean checkVersion() {        URL url;        HttpURLConnection httpConn = null;        try {            url = new URL(mCheckUpdateUrl);            httpConn = (HttpURLConnection) url.openConnection();            httpConn.setConnectTimeout(200000);            httpConn.setReadTimeout(200000);            httpConn.setUseCaches(false);       // disable cache for current http connection            httpConn.connect();            if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) {                InputStream inputStream = httpConn.getInputStream();                // 解析 XML 資料                if (!parseXml(inputStream)) {                    return false;                }                // 比較本地版本號碼與伺服器版本號碼                PackageInfo packageInfo = mContext.getPackageManager()                        .getPackageInfo(mContext.getPackageName(), 0);                if (packageInfo.versionCode < mRemoteVersionCode) {                    return true;                }            } else {                return false;            }        } catch (MalformedURLException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (PackageManager.NameNotFoundException e) {            e.printStackTrace();        } finally {            httpConn.disconnect();        }        return false;    }

首先建立HTTPURLConnection訪問服務端update.xml檔案,然後解析服務端返回的update.xml檔案,並儲存版本資訊、APK和更新日誌,解析完後通過擷取當前用戶端的版本號碼與服務端版本號碼比較,如果服務端版本號碼更大,說明服務端有更新的版本,checkVersion() 方法返回true,否則返回false。

下面時檢查更新的代碼,需要注意的是,Android中不允許在主線程(UI線程)中發起網路請求,所以checkVersion()的調用需要放在非主線程中。實現非同步請求的方式有多種,這裡我使用 AsyncTask。

    public void checkUpdate() {        new AsyncTask<Void, Void, Boolean>() {            @Override            protected Boolean doInBackground(Void... params) {                boolean hasNewVersion = checkVersion();                return hasNewVersion;            }            @Override            protected void onPostExecute(Boolean hasNewVersion) {                super.onPostExecute(hasNewVersion);                if (mCheckUpdateListener == null                        || !mCheckUpdateListener.onCompleted(hasNewVersion, mRemoteVersionCode,                        mRemoteVersionName, mUpdateLog, mApkDownloadUrl)) {                    if (hasNewVersion) {                        showUpdateDialog();                    }                }            }        }.execute();    }
下載新版的APK安裝包

showUpdateDialog()調用後顯示更新提示對話方塊,在對話方塊確認按鈕點擊事件中,首先建立DownloadManager.Request對象,然後設定該對象的各種屬性如下載儲存路徑、通知欄標題等,最後將該下載請求放到系統服務DownloadManager的下載隊列中,交給系統去處理下載邏輯。 為了監聽下載完成事件,代碼裡註冊了廣播DownloadManager.ACTION_DOWNLOAD_COMPLETE。下載進度通過註冊ContentObserver來監聽。

    /**     * 顯示更新對話方塊     */    private void showUpdateDialog() {        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);        builder.setTitle(mTitle);        builder.setMessage(mUpdateLog);        builder.setPositiveButton(mDialogOkBtnTxt, new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                dialog.dismiss();                // 後台下載                mDownloadMgr = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);                DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mApkDownloadUrl));                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {                    // 如果儲存路徑包含子目錄,需要先遞迴建立目錄                    if (!createDirIfAbsent(mSavePath)) {                        Log.e("TAG", "apk save path can not be created:" + mSavePath);                        return;                    }                    request.setDestinationUri(Uri.fromFile(new File(mSavePath)));                    request.setTitle(mNotificationTitle);                    request.setTitle(mNotificationMessage);                    // 註冊廣播,監聽下載完成事件                    mContext.registerReceiver(mCompleteReceiver,                            new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));                    // 註冊監聽下載進度                    mContext.getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"),                            true, mContentObserver);                    mDownloadId = mDownloadMgr.enqueue(request);                } else {                    Log.e("TAG", "can not access external storage!");                    return;                }                Toast.makeText(mContext, "正在後台下載...", Toast.LENGTH_SHORT).show();            }        });        builder.setNegativeButton(mDialogCancelBtnTxt, new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                dialog.cancel();            }        });        builder.create().show();    }    /**     * 如果參數 path 指定的路徑中的目錄不存在就建立指定目錄,確保path路徑的父目錄存在     *     * @param path 絕對路徑(包含檔案名稱,例如 ‘/sdcard/storage/download/test.apk‘)     * @return 如果成功建立目錄返回true,否則返回false     */    private boolean createDirIfAbsent(String path) {        String[] array = path.trim().split(File.separator);        List<String> dirNames = Arrays.asList(array).subList(1, array.length - 1);        StringBuilder pathBuilder = new StringBuilder(File.separator);        for (String d : dirNames) {            pathBuilder.append(d);            File f = new File(pathBuilder.toString());            if (!f.exists() && !f.mkdir()) {                return false;            }            pathBuilder.append(File.separator);        }        return true;    }
安裝APK

一旦Apk下載完成就會收到廣播訊息,此時可以執行安裝APK的動作,不過要先通過下載Id判斷該廣播事件是否是由於我們的APK下載完成發出的,因為系統可能同時有多個下載任務,通過下載id區分。

        mCompleteReceiver = new BroadcastReceiver() {            @Override            public void onReceive(Context context, Intent intent) {                long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);                if (downloadId == mDownloadId) {                    installApk();                    release();                }            }        };

下面是 installApk() 方法,首先通過下載Id從DownloadManager中檢索到下載的APK儲存路徑,然後通過Intent安裝下載的APK,代碼非常簡單。注意,Intent設定標識為Intent.FLAG_ACTIVITY_NEW_TASK,否則不能正常啟動安裝程式。

    /**     * 替換安裝當前App,注意:簽名一致     */    private void installApk() {        // 擷取下載的 APK 地址        Uri apkUri = mDownloadMgr.getUriForDownloadedFile(mDownloadId);        Intent intent = new Intent(Intent.ACTION_VIEW);        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");        mContext.startActivity(intent);    }
github 源碼

whinc/Android-UpdateManager

比較好的參考資料:

DownloadManager | Android Developers
Android系統下載管理DownloadManager功能介紹及使用樣本

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

【Android】Android程式自動更新

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.