史上最好用的Android全量版本更新庫(kù)XUpdate使用指南

在這里插入圖片描述

項(xiàng)目簡(jiǎn)介

XUpdate是一個(gè)輕量級(jí)囊嘉、高可用性的Android全量版本更新框架。

XUpdate是為了解決在不同項(xiàng)目組性锭、不同平臺(tái)之間進(jìn)行統(tǒng)一的Android全量版本更新的庫(kù)列牺。它具有輕量贬堵、靈活、低耦合锁荔、高可用等特點(diǎn)蟀给,可以很方便地定制屬于自己的版本更新。

設(shè)計(jì)原由

在沒(méi)有XUpdate之前的版本更新阳堕,Android版本更新基本都是靠寫各種版本更新工具類來(lái)實(shí)現(xiàn)版本更新跋理,更可怕的是有時(shí)在不同項(xiàng)目組或者平臺(tái)之間,它們的版本更新完全是不一樣的嘱丢,這樣的結(jié)果就是會(huì)寫無(wú)數(shù)的版本更新工具類薪介,并且每次更換一個(gè)項(xiàng)目組或者平臺(tái)就需要從頭重寫再寫一遍,非常得麻煩越驻。當(dāng)時(shí)我就在想汁政,版本更新作為一個(gè)Android應(yīng)用基本都有,且內(nèi)容相對(duì)穩(wěn)定的功能缀旁,有沒(méi)有可能設(shè)計(jì)出一個(gè)通用的记劈、不為業(yè)務(wù)或者平臺(tái)所影響的基礎(chǔ)庫(kù)呢?

設(shè)計(jì)思路

在著手寫XUpdate之前并巍,我特地去Github上搜了一圈有關(guān)Android版本更新的內(nèi)容目木,發(fā)現(xiàn)AppUpdate這個(gè)項(xiàng)目star數(shù)量最多。但是當(dāng)我翻閱它的源碼之后發(fā)現(xiàn)懊渡,它設(shè)計(jì)得并不優(yōu)美刽射,內(nèi)部耦合非常嚴(yán)重,不過(guò)優(yōu)點(diǎn)就是Android版本更新的功能基本都涵蓋了剃执。于是我就照著它所擁有的功能誓禁,結(jié)合了我對(duì)版本更新的理解進(jìn)行了重新設(shè)計(jì),感興趣的可點(diǎn)擊查看框架UML設(shè)計(jì)圖肾档。

解決痛點(diǎn)

  • 使用簡(jiǎn)單摹恰,只需一行代碼即可完成版本更新功能辫继。
  • 功能強(qiáng)大,兼容Android6.0俗慈、7.0姑宽、8.0、9.0和10.0闺阱,支持靜默更新和自動(dòng)更新炮车,支持國(guó)際化。
  • 擴(kuò)展性強(qiáng)馏颂,可自定義請(qǐng)求API接口示血、提示彈窗、下載服務(wù)救拉、文件加密器等。
  • 搭建簡(jiǎn)單瘫拣,只需提供json內(nèi)容即可支持版本更新亿絮。
  • 配套齊全,默認(rèn)提供了后臺(tái)服務(wù)麸拄、管理界面以及各類插件派昧。

項(xiàng)目地址

為了方便大家使用, XUpdate提供了一整套的全量版本更新解決方案.


項(xiàng)目演示

客戶端效果

  • 默認(rèn)版本更新
image
  • 后臺(tái)更新
xupdate_background.png
  • 強(qiáng)制版本更新
xupdate_force.png
  • 可忽略版本更新
xupdate_ignore.png
  • 自定義提示彈窗主題
image
  • 使用系統(tǒng)彈窗提示
xupdate_system.png

后臺(tái)管理界面

  • 登錄頁(yè)面
在這里插入圖片描述
  • 后臺(tái)管理主頁(yè)
xupdate_management_2.png
  • 應(yīng)用版本添加
xupdate_management_3.png
  • 應(yīng)用版本修改
image

集成指南

添加Gradle依賴

1.先在項(xiàng)目根目錄的 build.gradle 的 repositories 添加:

allprojects {
     repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

2.然后在dependencies添加:

以下是版本說(shuō)明,選擇一個(gè)即可拢切。

  • androidx版本:2.0.0及以上
dependencies {
  ...
  // androidx版本
  implementation 'com.github.xuexiangjys:XUpdate:2.0.2'
}
  • support版本:1.1.6及以下
dependencies {
  ...
  // support版本
  implementation 'com.github.xuexiangjys:XUpdate:1.1.6'
}

初始化SDK

在Application進(jìn)行初始化配置:

【注意】這里需要注意的是蒂萎,IUpdateHttpService必須設(shè)置,否則框架將無(wú)法正常使用淮椰!IUpdateHttpService的實(shí)現(xiàn)可參照Demo中的實(shí)現(xiàn)

XUpdate.get()
    .debug(true)
    .isWifiOnly(true)    //默認(rèn)設(shè)置只在wifi下檢查版本更新
    .isGet(true)         //默認(rèn)設(shè)置使用get請(qǐng)求檢查版本
    .isAutoMode(false)   //默認(rèn)設(shè)置非自動(dòng)模式五慈,可根據(jù)具體使用配置
    .param("versionCode", UpdateUtils.getVersionCode(this)) //設(shè)置默認(rèn)公共請(qǐng)求參數(shù)
    .param("appKey", getPackageName())
    .setOnUpdateFailureListener(new OnUpdateFailureListener() { //設(shè)置版本更新出錯(cuò)的監(jiān)聽
        @Override
        public void onFailure(UpdateError error) {
            if (error.getCode() != CHECK_NO_NEW_VERSION) { //對(duì)不同錯(cuò)誤進(jìn)行處理
                ToastUtils.toast(error.toString());
            }
        }
    })
    .supportSilentInstall(true)  //設(shè)置是否支持靜默安裝,默認(rèn)是true
    .setIUpdateHttpService(new OKHttpUpdateHttpService()) //這個(gè)必須設(shè)置主穗!實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求功能泻拦。
    .init(this); 

【注意】:如果出現(xiàn)任何問(wèn)題,可開啟debug模式來(lái)追蹤問(wèn)題忽媒。如果你還需要將日志記錄在磁盤上争拐,可實(shí)現(xiàn)以下接口

XUpdate.get().setILogger(new ILogger() {
    @Override
    public void log(int priority, String tag, String message, Throwable t) {
        //實(shí)現(xiàn)日志記錄功能
    }
});

混淆配置

-keep class com.xuexiang.xupdate.entity.** { *; }

//注意,如果你使用的是自定義Api解析器解析晦雨,還需要給你自定義Api實(shí)體配上混淆架曹,如下是本demo中配置的自定義Api實(shí)體混淆規(guī)則:
-keep class com.xuexiang.xupdatedemo.entity.** { *; }

基礎(chǔ)使用

默認(rèn)版本更新

直接調(diào)用如下代碼即可完成版本更新操作:

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl)
        .update();

需要注意的是,使用默認(rèn)版本更新闹瞧,請(qǐng)求服務(wù)器返回的json格式應(yīng)包括如下內(nèi)容:

{
  "Code": 0, //0代表請(qǐng)求成功绑雄,非0代表失敗
  "Msg": "", //請(qǐng)求出錯(cuò)的信息
  "UpdateStatus": 1, //0代表不更新,1代表有版本更新夹抗,不需要強(qiáng)制升級(jí)绳慎,2代表有版本更新,需要強(qiáng)制升級(jí)
  "VersionCode": 3,
  "VersionName": "1.0.2",
  "ModifyContent": "1、優(yōu)化api接口杏愤。\r\n2靡砌、添加使用demo演示。\r\n3珊楼、新增自定義更新服務(wù)API接口通殃。\r\n4、優(yōu)化更新提示界面厕宗。",
  "DownloadUrl": "https://raw.githubusercontent.com/xuexiangjys/XUpdate/master/apk/xupdate_demo_1.0.2.apk",
  "ApkSize": 2048
  "ApkMd5": "..."  //md5值沒(méi)有的話画舌,就無(wú)法保證apk是否完整,每次都會(huì)重新下載已慢。
}

自動(dòng)版本更新

自動(dòng)版本更新:自動(dòng)檢查版本 + 自動(dòng)下載apk + 自動(dòng)安裝apk(靜默安裝)曲聂。
只需要設(shè)置isAutoMode(true),不過(guò)如果設(shè)備沒(méi)有root權(quán)限的話,是無(wú)法做到完全的自動(dòng)更新(因?yàn)殪o默安裝需要root權(quán)限)佑惠。除此之外,對(duì)于某些特殊設(shè)備可能需要自定義安裝監(jiān)聽才能實(shí)現(xiàn)靜默安裝朋腋。

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl)
        .isAutoMode(true) //如果需要完全無(wú)人干預(yù),自動(dòng)更新膜楷,需要root權(quán)限【靜默安裝需要】
        .update();

支持后臺(tái)更新

開啟支持后臺(tái)更新后, 用戶點(diǎn)擊“后臺(tái)更新”按鈕后,就可以進(jìn)入到后臺(tái)更新,不用一直在更新界面等待.

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl)
        .supportBackgroundUpdate(true)
        .update();

自定義版本更新主題樣式

通過(guò)設(shè)置更新頂部圖片旭咽、主題色、按鈕文字顏色赌厅、寬高比率等來(lái)實(shí)現(xiàn)自定義主題樣式.

  • promptThemeColor: 設(shè)置主題顏色
  • promptButtonTextColor: 設(shè)置按鈕的文字顏色
  • promptTopResId: 設(shè)置頂部背景圖片
  • promptWidthRatio: 設(shè)置版本更新提示器寬度占屏幕的比例穷绵,默認(rèn)是-1,不做約束
  • promptHeightRatio: 設(shè)置版本更新提示器高度占屏幕的比例特愿,默認(rèn)是-1仲墨,不做約束
XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl)
        .promptThemeColor(ResUtils.getColor(R.color.update_theme_color))
        .promptButtonTextColor(Color.WHITE)
        .promptTopResId(R.mipmap.bg_update_top)
        .promptWidthRatio(0.7F)
        .update();

強(qiáng)制版本更新

就是用戶不更新的話,程序?qū)o(wú)法正常使用洽议。

  • 如果你使用的是默認(rèn)版本更新返回api的話, 只需要服務(wù)端返回UpdateStatus字段為2即可宗收。

  • 如果你自定義請(qǐng)求返回api的話,只需要設(shè)置UpdateEntitymIsForce字段為true即可亚兄。


進(jìn)階使用

版本更新信息實(shí)體

UpdateEntity作為框架各個(gè)環(huán)節(jié)接口的通信媒介混稽,了解它們的作用對(duì)后面接口的自定義非常關(guān)鍵。

  • UpdateEntity字段屬性
字段名 類型 默認(rèn)值 備注
mHasUpdate boolean false 是否有新版本
mIsForce boolean false 是否強(qiáng)制安裝:不安裝無(wú)法使用app
mIsIgnorable boolean false 是否可忽略該版本
mVersionCode int 0 最新版本code
mVersionName String unknown_version 最新版本名稱
mUpdateContent String "" 更新內(nèi)容
mDownloadEntity DownloadEntity 下載信息實(shí)體
mIsSilent boolean false 是否靜默下載:有新版本時(shí)不提示直接下載
mIsAutoInstall boolean true 是否下載完成后自動(dòng)安裝
  • DownloadEntity字段屬性
字段名 類型 默認(rèn)值 備注
mDownloadUrl String "" 下載地址
mCacheDir String "" 文件下載的目錄
mMd5 String "" 下載文件的md5值审胚,用于校驗(yàn)匈勋,防止下載的apk文件被替換(最新演示demo中有計(jì)算md5值的工具)
mSize long 0 下載文件的大小【單位:KB】
mIsShowNotification boolean false 是否在通知欄上顯示下載進(jìn)度
  • PromptEntity字段屬性
字段名 類型 默認(rèn)值 備注
mThemeColor int R.color.xupdate_default_theme_color 主題色(進(jìn)度條和按鈕的背景色)
mTopResId int R.drawable.xupdate_bg_app_top 頂部背景圖片資源id
mButtonTextColor int 0 按鈕文字顏色
mSupportBackgroundUpdate boolean false 是否支持后臺(tái)更新
mWidthRatio float -1(無(wú)約束) 版本更新提示器寬度占屏幕的比例
mHeightRatio float -1(無(wú)約束) 版本更新提示器高度占屏幕的比例

組成結(jié)構(gòu)

在了解了版本更新的結(jié)構(gòu)和各部分的功能后,我們就可以根據(jù)我們實(shí)際的需求進(jìn)行自定義了.以下是版本更新的組成結(jié)構(gòu):

  • 版本更新檢查器IUpdateChecker:檢查是否有最新版本。

  • 版本更新解析器IUpdateParser:解析服務(wù)端返回的數(shù)據(jù)結(jié)果膳叨。

  • 版本更新提示器IUpdatePrompter:展示最新的版本信息洽洁。

  • 版本更新下載器IUpdateDownloader:下載最新的版本APK安裝包。

  • 網(wǎng)絡(luò)請(qǐng)求服務(wù)接口IUpdateHttpService:定義了進(jìn)行網(wǎng)絡(luò)請(qǐng)求的相關(guān)接口菲嘴。

除此之外饿自,還有兩個(gè)監(jiān)聽器:

  • 版本更新失敗的監(jiān)聽器OnUpdateFailureListener汰翠。

  • 版本更新apk安裝的監(jiān)聽器OnInstallListener

更新調(diào)度核心:

  • 版本更新業(yè)務(wù)代理IUpdateProxy:負(fù)責(zé)版本更新的流程控制昭雌,調(diào)用update開始進(jìn)行版本更新流程复唤。

理論上,以上所有組成部分都開放了自定義的api,我們只需要根據(jù)我們的需求實(shí)現(xiàn)對(duì)應(yīng)的接口即可完成自定義.

自定義版本更新解析器

如果你不想使用默認(rèn)版本更新返回的接口數(shù)據(jù), 那么你可以實(shí)現(xiàn)IUpdateParser接口即可實(shí)現(xiàn)解析器的自定義, 示例如下:

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl3)
        .updateParser(new CustomUpdateParser()) //設(shè)置自定義的版本更新解析器
        .update();

public class CustomUpdateParser implements IUpdateParser {
    @Override
    public UpdateEntity parseJson(String json) throws Exception {
        CustomResult result = JsonUtil.fromJson(json, CustomResult.class);
        if (result != null) {
            return new UpdateEntity()
                    .setHasUpdate(result.hasUpdate)
                    .setIsIgnorable(result.isIgnorable)
                    .setVersionCode(result.versionCode)
                    .setVersionName(result.versionName)
                    .setUpdateContent(result.updateLog)
                    .setDownloadUrl(result.apkUrl)
                    .setSize(result.apkSize);
        }
        return null;
    }
}

自定義版本更新檢查器+版本更新解析器+版本更新提示器

  • 實(shí)現(xiàn)IUpdateChecker接口即可實(shí)現(xiàn)檢查器的自定義。

  • 實(shí)現(xiàn)IUpdateParser接口即可實(shí)現(xiàn)解析器的自定義烛卧。

  • 實(shí)現(xiàn)IUpdatePrompter接口即可實(shí)現(xiàn)提示器的自定義佛纫。

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl3)
        .updateChecker(new DefaultUpdateChecker() {
            @Override
            public void onBeforeCheck() {
                super.onBeforeCheck();
                CProgressDialogUtils.showProgressDialog(getActivity(), "查詢中...");
            }
            @Override
            public void onAfterCheck() {
                super.onAfterCheck();
                CProgressDialogUtils.cancelProgressDialog(getActivity());
            }
        })
        .updateParser(new CustomUpdateParser())
        .updatePrompter(new CustomUpdatePrompter(getActivity()))
        .update();


public class CustomUpdatePrompter implements IUpdatePrompter {

    private Context mContext;

    public CustomUpdatePrompter(Context context) {
        mContext = context;
    }

    @Override
    public void showPrompt(@NonNull UpdateEntity updateEntity, @NonNull IUpdateProxy updateProxy, @NonNull PromptEntity promptEntity) {
        showUpdatePrompt(updateEntity, updateProxy);
    }

    /**
     * 顯示自定義提示
     *
     * @param updateEntity
     * @param updateProxy
     */
    private void showUpdatePrompt(final @NonNull UpdateEntity updateEntity, final @NonNull IUpdateProxy updateProxy) {
        String updateInfo = UpdateUtils.getDisplayUpdateInfo(mContext, updateEntity);

        new AlertDialog.Builder(mContext)
                .setTitle(String.format("是否升級(jí)到%s版本?", updateEntity.getVersionName()))
                .setMessage(updateInfo)
                .setPositiveButton("升級(jí)", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        updateProxy.startDownload(updateEntity, new OnFileDownloadListener() {
                            @Override
                            public void onStart() {
                                HProgressDialogUtils.showHorizontalProgressDialog(mContext, "下載進(jìn)度", false);
                            }

                            @Override
                            public void onProgress(float progress, long total) {
                                HProgressDialogUtils.setProgress(Math.round(progress * 100));
                            }

                            @Override
                            public boolean onCompleted(File file) {
                                HProgressDialogUtils.cancel();
                                return true;
                            }

                            @Override
                            public void onError(Throwable throwable) {
                                HProgressDialogUtils.cancel();
                            }
                        });
                    }
                })
                .setNegativeButton("暫不升級(jí)", null)
                .setCancelable(false)
                .create()
                .show();
    }

自定義文件加密校驗(yàn)器

本框架默認(rèn)使用的文件加密校驗(yàn)方法是MD5加密方式总放,當(dāng)然如果你不想使用MD5加密呈宇,你也可以自定義文件加密器IFileEncryptor,以下是MD5文件加密器的實(shí)現(xiàn)供參考:

/**
 * 默認(rèn)的文件加密計(jì)算使用的是MD5加密
 *
 * @author xuexiang
 * @since 2019-09-06 14:21
 */
public class DefaultFileEncryptor implements IFileEncryptor {
    /**
     * 加密文件
     *
     * @param file
     * @return
     */
    @Override
    public String encryptFile(File file) {
        return Md5Utils.getFileMD5(file);
    }

    /**
     * 檢驗(yàn)文件是否有效(加密是否一致)
     *
     * @param encrypt 加密值, 如果encrypt為空,直接認(rèn)為是有效的
     * @param file    需要校驗(yàn)的文件
     * @return 文件是否有效
     */
    @Override
    public boolean isFileValid(String encrypt, File file) {
        return TextUtils.isEmpty(encrypt) || encrypt.equalsIgnoreCase(encryptFile(file));
    }
}

最后再調(diào)用XUpdate.get().setIFileEncryptor方法設(shè)置即可生效局雄。

只使用XUpdate的下載器功能進(jìn)行apk的下載

XUpdate.newBuild(getActivity())
        .apkCacheDir(PathUtils.getExtDownloadsPath()) //設(shè)置下載緩存的根目錄
        .build()
        .download(mDownloadUrl, new OnFileDownloadListener() {   //設(shè)置下載的地址和下載的監(jiān)聽
            @Override
            public void onStart() {
                HProgressDialogUtils.showHorizontalProgressDialog(getContext(), "下載進(jìn)度", false);
            }

            @Override
            public void onProgress(float progress, long total) {
                HProgressDialogUtils.setProgress(Math.round(progress * 100));
            }

            @Override
            public boolean onCompleted(File file) {
                HProgressDialogUtils.cancel();
                ToastUtils.toast("apk下載完畢甥啄,文件路徑:" + file.getPath());
                return false;
            }

            @Override
            public void onError(Throwable throwable) {
                HProgressDialogUtils.cancel();
            }
        });

只使用XUpdate的APK安裝的功能

_XUpdate.startInstallApk(getContext(), FileUtils.getFileByPath(PathUtils.getFilePathByUri(getContext(), data.getData()))); //填寫文件所在的路徑

如果你的apk安裝與眾不同,你可以實(shí)現(xiàn)自己的apk安裝器哎榴。你只需要實(shí)現(xiàn)OnInstallListener接口型豁,并通過(guò)XUpdate.setOnInstallListener進(jìn)行設(shè)置即可生效。


常見問(wèn)題

接入的問(wèn)題

1.問(wèn):為什么我剛接入的時(shí)候尚蝌,一直報(bào)錯(cuò)updateHttpService == null?

答:你需要仔細(xì)閱讀接入文檔,必須在Application中按要求初始化XUpdate充尉,而其中IUpdateHttpService必須設(shè)置飘言,除非你自定義版本檢查器和版本更新下載器,否則框架將無(wú)法正常使用驼侠!

2.問(wèn):為什么我在開發(fā)調(diào)試的時(shí)候姿鸿,能夠出現(xiàn)最新版本的提示,但是打出來(lái)的包卻什么反應(yīng)也沒(méi)有?

答:出現(xiàn)這個(gè)問(wèn)題倒源,一般是少了混淆配置苛预。如果你使用了自定義的版本更新解析器,請(qǐng)對(duì)你的接口實(shí)體進(jìn)行混淆配置笋熬。

3.問(wèn):為什么我點(diǎn)擊下載后文件是能下載下來(lái)的热某,但是進(jìn)度條不更新,或者打印出進(jìn)度條的值是-1?

答:出現(xiàn)這種情況可以從兩個(gè)方面來(lái)排查胳螟。

  • 如果你打印出進(jìn)度條的值是-1昔馋,那很有可能是服務(wù)端提供的下載服務(wù)本身就不支持進(jìn)度。因?yàn)槿绻阍谡?qǐng)求服務(wù)端下載文件的時(shí)候糖耸,服務(wù)端在請(qǐng)求頭中沒(méi)有返回?cái)?shù)據(jù)長(zhǎng)度秘遏,即contentLength(Content-Length)沒(méi)有設(shè)置,是未知的嘉竟,那么是不可能有進(jìn)度的邦危。這個(gè)你可以通過(guò)抓包來(lái)查看響應(yīng)頭中是否設(shè)置了“Content-Length”洋侨。

  • 如果你使用的服務(wù)端本身已經(jīng)確認(rèn)是支持進(jìn)度的。那么就可能需要考慮是不是你的IUpdateHttpServicedownload接口實(shí)現(xiàn)有問(wèn)題倦蚪,你務(wù)必要保證接口DownloadCallbackonProgress方法能被正常執(zhí)行希坚。

4.問(wèn):為什么我執(zhí)行了版本更新的方法,它卻一直提示無(wú)最新版本或者是一直在進(jìn)行版本更新?

答:出現(xiàn)這個(gè)問(wèn)題审丘,你首先得明確一點(diǎn)的是吏够,你判斷是否有最新版本的依據(jù)是什么。到底是依據(jù)VersionCode還是VersionName滩报,這個(gè)取決于你實(shí)際使用的場(chǎng)景锅知。明確完這一點(diǎn),你才可以根據(jù)日志去判斷到底是前端出了問(wèn)題還是后端出了問(wèn)題脓钾。

5.問(wèn):這個(gè)最新版本我已經(jīng)下載過(guò)了售睹,只不過(guò)沒(méi)安裝,在下一次進(jìn)行版本更新的檢查時(shí)可训,為什么我還要重新下載一次昌妹?

答:出現(xiàn)這個(gè)問(wèn)題,只能證明你的后端在返回版本信息的時(shí)候并沒(méi)有返回最新版本文件的MD5值握截,或者返回了你沒(méi)有設(shè)置飞崖。如果你設(shè)置了MD5值,那么就是你設(shè)置的MD5值和文件計(jì)算出來(lái)的MD5值不匹配谨胞,這種情況下固歪,你的APK文件極有可能被篡改了(當(dāng)然在這種情況下,你也不能正常安裝)胯努,或者是你們前后端的MD5值計(jì)算算法不一致(一般不存在這種情況)牢裳。

6.問(wèn):為什么我最新的應(yīng)用下載了,但是點(diǎn)擊安裝按鈕后一直提示更新失敗呢叶沛?

答:出現(xiàn)這種問(wèn)題的情況有很多種蒲讯。

  • 首先你需要確保能否找到下載下來(lái)的最新APK,如果你設(shè)置了MD5值的話灰署,還需要判斷下載下來(lái)的最新APK計(jì)算出來(lái)的MD5值和后臺(tái)接口返回的MD5值是否一致(計(jì)算文件的MD5值Demo中有對(duì)應(yīng)的方法)判帮;
  • 其次你需要手動(dòng)安裝一下APK,確保APK文件沒(méi)問(wèn)題(簽名一致、文件完整)氓侧,能正常安裝脊另;
  • 最后你可以在多臺(tái)設(shè)備上嘗試一下,確保不是設(shè)備自身的問(wèn)題约巷。
  • 如果以上方法都不能解決問(wèn)題偎痛,很遺憾,那么你只能自定義安裝監(jiān)聽器OnInstallListener接口独郎,實(shí)現(xiàn)能夠正確安裝APK的方法了踩麦。

7.問(wèn):在版本更新的過(guò)程中出現(xiàn)了錯(cuò)誤枚赡,我該如何進(jìn)行排查?

答:最好的解決方法當(dāng)然是打斷點(diǎn)逐個(gè)進(jìn)行排查啦谓谦!當(dāng)然在打斷點(diǎn)前贫橙,我們需要調(diào)用XUpdate.get().debug(true)開啟debug模式,打印相關(guān)日志反粥,明確出錯(cuò)的位置卢肃,這樣才能更快地解決問(wèn)題啦!

8.問(wèn):為什么版本更新彈窗彈不出來(lái)才顿,報(bào)System.err: at com.xuexiang.xupdate.widget.BaseDialog.init(BaseDialog.java:72)錯(cuò)誤莫湘?

答:最好的解決方法就是傳入的context使用的是AppCompatActivity, 而不是Activity或者FragmentActivity!如果你一定要使用Activity或者FragmentActivity郑气,那么請(qǐng)?jiān)O(shè)置其主題為Theme.AppCompat類型的主題幅垮。

自定義的問(wèn)題

經(jīng)常有使用者反饋不知道該如何自定義接口(面對(duì)一堆接口,不知道該如何下手)尾组,進(jìn)行個(gè)性化的定制忙芒,以滿足版本更新實(shí)現(xiàn)的需求,下面我將一一列舉問(wèn)題和解決的方法讳侨。

1.問(wèn):我使用的是retrofit自定義的接口呵萨,不想使用IUpdateHttpService那套通用請(qǐng)求方式來(lái)查詢最新版本,我該怎么辦跨跨?

答:可以自定義版本更新檢查器IUpdateChecker甘桑,它主要負(fù)責(zé)的是查詢是否存在最新版本〈醵#可參考框架默認(rèn)提供的版本更新檢查器來(lái)自定義。

2.問(wèn):我不想使用框架默認(rèn)的請(qǐng)求服務(wù)器返回的json格式铆帽,因?yàn)楣镜暮蠖擞凶约旱囊惶讛?shù)據(jù)返回格式咆耿,我該怎么辦?

答:可以自定義版本更新解析器IUpdateParser爹橱,它主要負(fù)責(zé)的是解析服務(wù)端返回的數(shù)據(jù)結(jié)果萨螺,并構(gòu)建更新信息實(shí)體UpdateEntity。具體可參考自定義版本更新解析器, 也可參考框架默認(rèn)提供的版本更新解析器來(lái)自定義愧驱。

3.問(wèn):我覺得框架提供的一套默認(rèn)的版本更新提示界面不符合我們公司的UI風(fēng)格慰技,我能自定義一套自己的版本更新提示界面嗎?

答:可以自定義版本更新提示器IUpdatePrompter组砚,它主要負(fù)責(zé)的是展示最新的版本信息吻商。具體可參考自定義版本更新提示器, 也可參考框架默認(rèn)提供的版本更新提示器來(lái)自定義。

4.問(wèn):我總覺得框架中提供的最新版本APK下載服務(wù)速度不行糟红,我想實(shí)現(xiàn)自己的下載服務(wù)艾帐,并做相關(guān)下載進(jìn)度的提示乌叶,可以嗎?

答:可以自定義版本更新下載器IUpdateDownloader柒爸,它主要負(fù)責(zé)的是下載最新的版本APK安裝包准浴。可參考框架默認(rèn)提供的版本更新下載器來(lái)自定義捎稚。

5.問(wèn):我的應(yīng)用和普通應(yīng)用有些特別乐横,并不能使用系統(tǒng)的安裝api安裝程序,我該怎么辦今野?

答:如果你的apk安裝與眾不同葡公,你可以實(shí)現(xiàn)自己的apk安裝器。你只需要實(shí)現(xiàn)OnInstallListener接口腥泥,并通過(guò)XUpdate.setOnInstallListener進(jìn)行設(shè)置即可生效匾南。

【注意】以上實(shí)現(xiàn)的自定義接口,都可以通過(guò)XUpdate進(jìn)行全局和局部的設(shè)置蛔外。

錯(cuò)誤碼

錯(cuò)誤碼 備注
2000 查詢更新失敗
2001 沒(méi)有wifi
2002 沒(méi)有網(wǎng)絡(luò)
2003 正在進(jìn)行版本更新
2004 無(wú)最新版本
2005 版本檢查返回空
2006 版本檢查返回json解析失敗
2007 已經(jīng)被忽略的版本
2008 應(yīng)用下載的緩存目錄為空
3000 版本提示器異常錯(cuò)誤
3001 版本提示器所在Activity頁(yè)面被銷毀
4000 新應(yīng)用安裝包下載失敗
4001 讀寫權(quán)限申請(qǐng)失敗
5000 apk安裝失敗
5100 未知錯(cuò)誤

資源鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛆楞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子夹厌,更是在濱河造成了極大的恐慌豹爹,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矛纹,死亡現(xiàn)場(chǎng)離奇詭異臂聋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)或南,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門孩等,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人采够,你說(shuō)我怎么就攤上這事肄方。” “怎么了蹬癌?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵权她,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我逝薪,道長(zhǎng)隅要,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任董济,我火速辦了婚禮步清,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘感局。我一直安慰自己尼啡,他們只是感情好暂衡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著崖瞭,像睡著了一般狂巢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上书聚,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天唧领,我揣著相機(jī)與錄音,去河邊找鬼雌续。 笑死斩个,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驯杜。 我是一名探鬼主播受啥,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鸽心!你這毒婦竟也來(lái)了滚局?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤顽频,失蹤者是張志新(化名)和其女友劉穎藤肢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糯景,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘁圈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蟀淮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片最住。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖怠惶,靈堂內(nèi)的尸體忽然破棺而出温学,到底是詐尸還是另有隱情,我是刑警寧澤甚疟,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逃延,受9級(jí)特大地震影響览妖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜揽祥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一讽膏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拄丰,春花似錦府树、人聲如沸俐末。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卓箫。三九已至,卻和暖如春垄潮,著一層夾襖步出監(jiān)牢的瞬間烹卒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工弯洗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旅急,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓牡整,卻偏偏與公主長(zhǎng)得像藐吮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逃贝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355