Android運(yùn)維--app更新升級(jí)

整個(gè)流程分為三步:
1.版本檢測(cè)
2.新apk文件下載
3.覆蓋安裝
基本流程就是這樣的了, 區(qū)別在于怎么安排, 用什么策略更新. 網(wǎng)上的思路基本上是, 在主頁(yè)里檢測(cè)apk版本, 需要更新就彈一個(gè)對(duì)話框給用戶(hù), 提醒其更新. 用戶(hù)確定更新, 就在主頁(yè)中或者開(kāi)一個(gè)服務(wù), 利用downloadmanager進(jìn)行文件下載廣播注冊(cè), 下載完了在廣播中進(jìn)行自動(dòng)安裝(downloadmanager下載完了會(huì)發(fā)出一條廣播).
在實(shí)際項(xiàng)目中, 考慮到版本維護(hù)的成本, 更新策略選擇強(qiáng)制用戶(hù)更新. 具體做法有點(diǎn)類(lèi)似與應(yīng)用商店的靜默安裝, 有更新就先后臺(tái)下載, 等到下次打開(kāi)彈出一個(gè)對(duì)話框進(jìn)行安裝.

一. 版本檢測(cè)

思路
一般最新的版本信息會(huì)以json文件的形式放在服務(wù)器上, 我們要做的就是利用OKhttp3下載這個(gè)json文件, 解析取出對(duì)應(yīng)字段, 和本地versionName比較, 不同就進(jìn)入下一步.
OKhttp3
Android OkHttp完全解析 是時(shí)候來(lái)了解OkHttp了
Android 一個(gè)改善的okHttp封裝庫(kù)
Android Https相關(guān)完全解析 當(dāng)OkHttp遇到Https
本地版本信息獲取

PackageManager packageManager = getPackageManager();
#0 means all the flags are turned off
PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
String version = packageInfo.versionName;
二. 新apk文件下載

思路
這里使用Google推薦的DownloadManager來(lái)進(jìn)行異步下載
DownloadManager
如果你不想糾結(jié)什么斷點(diǎn)續(xù)傳, 網(wǎng)絡(luò)環(huán)境啥的, 那么你就使用DownloadManager吧

//首先, 構(gòu)建一個(gè)下載請(qǐng)求, uri 是你的下載地址,可以使用Uri.parse("http://")包裝成Uri對(duì)象
DownloadManager.Request req = new DownloadManager.Request(uri);

//通過(guò)setAllowedNetworkTypes方法可以設(shè)置允許在何種網(wǎng)絡(luò)下下載
req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);

// 此方法表示在下載過(guò)程中通知欄會(huì)一直顯示該下載,在下載完成后仍然會(huì)顯示,
// 直到用戶(hù)點(diǎn)擊該通知或者消除該通知愿题。還有其他參數(shù)可供選擇
//req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
//我希望靜悄悄地下載
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);

// 設(shè)置下載文件存放的路徑,同樣你可以選擇以下方法存放在你想要的位置途茫。
// setDestinationUri
// setDestinationInExternalPublicDir
req.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, title);

// 設(shè)置一些基本顯示信息
//req.setTitle("Android.apk");
//req.setDescription("下載完后請(qǐng)點(diǎn)擊打開(kāi)");
req.setMimeType("application/vnd.android.package-archive");

// 構(gòu)建完下載任務(wù), 傳入downloadmanager進(jìn)行下載, 是不是很像我們使用迅雷下載電影
DownloadManager dm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
long downloadId = dm.enqueue(req);

下載的基本配置就是這樣, 還是挺簡(jiǎn)單的, 并且性能較高.
在實(shí)際使用中我們更應(yīng)該關(guān)心的是下載前的準(zhǔn)備工作和下載后的善后工作.
下載前的準(zhǔn)備工作
首先, 我的需求是發(fā)現(xiàn)有新版本, 先下載, 下載完后等到用戶(hù)再次打開(kāi)軟件進(jìn)行更新操作. 所以這里就有一個(gè)判斷最新軟件包是否下載完的邏輯判斷, 沒(méi)有就下載, 如果已經(jīng)就直接進(jìn)入到安裝環(huán)節(jié).

 /**
     * 下載APK文件
     *
     * @param context
     * @param url
     * @param info
     * @param appName
     */
    public void downloadApk(MainActivity mainActivity, final Context context, String url, String info, final String appName) {
        //獲取存儲(chǔ)的下載ID
        long downloadId = (long) SPUtil.get(context, DownloadManager.EXTRA_DOWNLOAD_ID, -1L);
        if (downloadId != -1) {
            //獲取當(dāng)前狀態(tài)
            int status = getDownloadStatus(downloadId);
            //狀態(tài)為下載成功
            if (DownloadManager.STATUS_SUCCESSFUL == status) {
                //獲取下載路徑URI
                final Uri downloadUri = getDownloadUri(downloadId);
                if (downloadUri != null) {
                    //存在下載的APK桌吃,如果兩個(gè)APK相同娜睛,啟動(dòng)更新界面哆键。否之則刪除掘托,重新下載。
                    //安裝邏輯......
                        return;
                    } else {
                        //刪除下載任務(wù)以及文件
                        mDownloadManager.remove(downloadId);
                    }
                }
                start(context, url, info, appName);
            } else if (DownloadManager.STATUS_FAILED == status) {
                //下載失敗,重新下載
                start(context, url, info, appName);
            } else {
                Log.d(context.getPackageName(), "apk is already downloading");
            }
        } else {
            //不存在downloadId籍嘹,沒(méi)有下載過(guò)APK
            start(context, url, info, appName);
        }
    }

上面代碼中涉及獲取下載文件以及判斷下載狀態(tài)的操作, 均與downloadmanager有關(guān):

//獲取下載文件
DownloadManager dm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor c = dm.query(query);
if (c != null) {
    if (c.moveToFirst()) {
        String fileUri = c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI));
        // TODO 你可以在這里處理你的文件
    }
    c.close();
}

//獲取下載狀態(tài)
DownloadManager dm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor c = dm.query(query);
if (c != null && c.moveToFirst()) {
    int status = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
    switch (status) {
        case DownloadManager.STATUS_PENDING:
            break;
        case DownloadManager.STATUS_PAUSED:
            break;
        case DownloadManager.STATUS_RUNNING:
            break;
        case DownloadManager.STATUS_SUCCESSFUL:
            break;
        case DownloadManager.STATUS_FAILED:
            break;
}
if (c != null) {
    c.close();
}

當(dāng)然, 下載前需要先判斷一下能不能下載, WiFi有沒(méi)有打開(kāi), 沒(méi)有打開(kāi)跳轉(zhuǎn)到手機(jī)設(shè)置頁(yè)

    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);
    }

下載后的善后工作
下載完后, downloadmanager會(huì)發(fā)送一條DownloadManager.ACTION_DOWNLOAD_COMPLETE廣播闪盔,并傳遞downloadId作為參數(shù), 我們可以自定義一個(gè)廣播接收器接收這條廣播完成自動(dòng)安裝. 但這里我們時(shí)重啟APP后安裝, 所以用不到這個(gè)廣播功能.
但是這里我們要做的工作是安裝完后刪除apk文件, 雖然很多手機(jī)能夠自動(dòng)安裝后刪除, 但為了以防萬(wàn)一嘛.
這個(gè)刪除判斷放在下載文件前, 邏輯是: 如果存在apk文件并且已經(jīng)安裝過(guò)了就刪除.

    public void removeFile(Context context) {
        //獲取存儲(chǔ)的下載ID
        long downloadId = (long) SPUtil.get(context, DownloadManager.EXTRA_DOWNLOAD_ID, -1L);
        if (downloadId != -1) {
            //或者使用mDownloadManager.remove(downloadId)直接刪除文件;
            Uri filePath = getDownloadUri(downloadId);
            if (filePath != null) {
                //刪除之前先判斷用戶(hù)是否已經(jīng)安裝了,安裝了才刪除, 判斷邏輯還是檢測(cè)versionCode
                if (!compare(getApkInfo(context, filePath.getPath()), context)) {
                    File downloadFile = new File(filePath.getPath());
                    if (null != downloadFile && downloadFile.exists()) {
                        downloadFile.delete();
                    }
                }
            }
        }
    }
三. 覆蓋安裝

思路
最后就是覆蓋安裝了, 看你選擇什么策略了, 我這里是直接彈出一個(gè)用戶(hù)不能拒絕的對(duì)話框, 讓他更新, 以節(jié)省后期維護(hù)成本

    if (compare(getApkInfo(context, downloadUri.getPath()), context)) {
        AlertDialog.Builder builder = new AlertDialog.Builder(mainActivity);
        builder.setTitle("檢測(cè)到有新版本!");
        builder.setMessage(info);
        builder.setCancelable(false);
        builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                startInstall(context, downloadUri);
            }
        });
        mAlertDialog = builder.create();
        handler.sendEmptyMessageDelayed(0, 1000);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辱士,一起剝皮案震驚了整個(gè)濱河市泪掀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌识补,老刑警劉巖族淮,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凭涂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)贴妻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)切油,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人名惩,你說(shuō)我怎么就攤上這事澎胡。” “怎么了娩鹉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵攻谁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我弯予,道長(zhǎng)戚宦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任锈嫩,我火速辦了婚禮受楼,結(jié)果婚禮上垦搬,老公的妹妹穿的比我還像新娘。我一直安慰自己艳汽,他們只是感情好猴贰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著河狐,像睡著了一般米绕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上馋艺,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天栅干,我揣著相機(jī)與錄音,去河邊找鬼丈钙。 笑死非驮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的雏赦。 我是一名探鬼主播劫笙,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼星岗!你這毒婦竟也來(lái)了填大?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤俏橘,失蹤者是張志新(化名)和其女友劉穎允华,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體寥掐,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡靴寂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了召耘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片百炬。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖污它,靈堂內(nèi)的尸體忽然破棺而出剖踊,到底是詐尸還是另有隱情,我是刑警寧澤衫贬,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布德澈,位于F島的核電站,受9級(jí)特大地震影響固惯,放射性物質(zhì)發(fā)生泄漏梆造。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一缝呕、第九天 我趴在偏房一處隱蔽的房頂上張望澳窑。 院中可真熱鬧斧散,春花似錦、人聲如沸摊聋。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)麻裁。三九已至箍镜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間煎源,已是汗流浹背色迂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留手销,地道東北人歇僧。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锋拖,于是被迫代替她去往敵國(guó)和親诈悍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,081評(píng)論 25 707
  • 一兽埃、前言 提到 APK 更新侥钳,大家可能會(huì)想到友盟(umeng)更新,市場(chǎng)上已有數(shù)萬(wàn)款應(yīng)用在使用友盟自動(dòng)更新的服務(wù)柄错。...
    文淑閱讀 7,106評(píng)論 2 40
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理舷夺,服務(wù)發(fā)現(xiàn),斷路器售貌,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • 公司開(kāi)發(fā)時(shí)候给猾,應(yīng)該最常用的就是APP升級(jí)功能,倒不是說(shuō)的是熱修復(fù)等技術(shù)颂跨,而是普通的檢測(cè)到服務(wù)器版本比本地手機(jī)版本高...
    青蛙要fly閱讀 4,554評(píng)論 12 86
  • 峻嶺青松越峰巔耙册, 白樺叢林世外園; 潺潺溪水聞聲樂(lè), 戀景欣怡不歸還毫捣。
    月夜秋荷閱讀 208評(píng)論 0 1