Android熱更新 — Bugly + Tinker

如需轉載請評論或簡信,并注明出處,未經允許不得轉載

目錄

官網

Tinker官方github

Bugly產品管理中心

版本更新狀況

Latest commit4247313 8 days ago

目前時間2020年5月27日,可以看出到目前為止Tinker一直在更新

使用場景

Tinker是微信官方的Android熱補丁解決方案,它支持動態下發代碼、So庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。當然,你也可以使用Tinker來更新你的插件

下面的表格來源于Tinker官方wiki

Tinker QZone AndFix Robust
類替換 yes yes no no
So替換 yes no no no
資源替換 yes yes no no
全平臺支持 yes yes yes yes
即時生效 no no yes yes
性能損耗 較小 較大 較小 較小
補丁包大小 較小 較大 一般 一般
開發透明 yes yes no no
復雜度 較低 較低 復雜 復雜
gradle支持 yes no no no
Rom體積 較大 較小 較小 較小
成功率 較高 較高 一般 最高

Tinker的已知問題

由于原理與系統限制,Tinker有以下已知問題:

  1. Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大組件(1.9.0支持新增非export的Activity);
  2. 由于Google Play的開發者條款限制,不建議在GP渠道動態更新代碼;
  3. 在Android N上,補丁對應用啟動時間有輕微的影響;
  4. 不支持部分三星android-21機型,加載補丁時會主動拋出"TinkerRuntimeException:checkDexInstall failed"
  5. 對于資源替換,不支持修改remoteView。例如transition動畫,notification icon以及桌面圖標。

Tinker的TODO

Tinker經過幾次全量上線,也發現了一些熱補丁的問題。有以下的一些優化工作尚未完成:

  1. Crash 啟動保護;
  2. Android Q 或更新的系統上降低補丁帶來的開銷。

集成步驟

根據Tinker官網的步驟,我們能夠完成一個Tinker的本地繼承。但是熱更新往往需要一個后臺系統來管理和下發補丁包,這樣一來就增加了使用熱更新的成本。好在騰訊的Bugly提供了Tinker補丁管理系統,而且目前是免費的,所以本文就基于騰訊的Bugly來介紹如何集成Tinker熱更新

在Bugly上創建產品,獲取APP ID

  1. 進入bugly產品管理,點擊新建產品

  2. 輸入產品信息


  3. 完成后回到我的產品,點擊進入


  4. 點擊產品設置,獲取APP ID,記錄下來


添加插件依賴

工程根目錄下“build.gradle”文件中添加:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        // tinkersupport插件, 其中lastest.release指拉取最新版本,也可以指定明確版本號,例如1.0.4
        classpath "com.tencent.bugly:tinker-support:1.1.5"
    }
}

注意:自 tinkersupport 1.0.3 版本起無需再配tinker插件的classpath。

版本對應關系:

tinker-support 1.1.3 對應 tinker 1.9.8

tinker-support 1.1.2 對應 tinker 1.9.6

tinker-support 1.1.1 對應 tinker 1.9.1

tinker-support 1.0.9 對應 tinker 1.9.0

tinker-support 1.0.8 對應 tinker 1.7.11

tinker-support 1.0.7 對應 tinker 1.7.9

tinker-support 1.0.4 對應 tinker 1.7.7

tinker-support 1.0.3 對應 tinker 1.7.6

tinker-support 1.0.2 對應 tinker 1.7.5(需配置tinker插件的classpath)

集成SDK

gradle配置

  android {
       defaultConfig {
          ndk {
            //設置支持的SO庫架構
            abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
          }
       }
  }
  dependencies {
      // 多dex配置
        api "com.android.support:multidex:1.0.1"
        // bugly遠程倉庫
        api 'com.tencent.bugly:crashreport_upgrade:1.3.6'
        // 指定tinker依賴版本(注:應用升級1.3.5版本起,不再內置tinker)
        api 'com.tencent.tinker:tinker-android-lib:1.9.9'
  }

注意:

1.升級SDK已經集成crash上報功能,已經集成Bugly的用戶需要注釋掉原來Bugly的jcenter庫;

2.已經配置過符號表的Bugly用戶保留原有符號表配置;

3.Bugly SDK(2.1.5及以上版本)已經將Java Crash和Native Crash捕獲功能分開,如果想使用NDK庫,需要配置: compile 'com.tencent.bugly:nativecrashreport:latest.release'

在app module的“build.gradle”文件中添加:

// 依賴插件腳本
apply from: 'tinker-support.gradle'

tinker-support.gradle內容如下所示(示例配置):

注:您需要在同級目錄下創建tinker-support.gradle這個文件

apply plugin: 'com.tencent.bugly.tinker-support'

def bakPath = file("${buildDir}/bakApk/")

/**
 * 此處填寫每次構建生成的基準包目錄
 */
def baseApkDir = "app-0208-15-10-00"

/**
 * 對于插件各參數的詳細解析請參考
 */
tinkerSupport {

    // 開啟tinker-support插件,默認值true
    enable = true

    // 指定歸檔目錄,默認值當前module的子目錄tinker
    autoBackupApkDir = "${bakPath}"

    // 是否啟用覆蓋tinkerPatch配置功能,默認值false
    // 開啟后tinkerPatch配置不生效,即無需添加tinkerPatch
    overrideTinkerPatchConfiguration = true

    // 編譯補丁包時,必需指定基線版本的apk,默認值為空
    // 如果為空,則表示不是進行補丁包的編譯
    // @{link tinkerPatch.oldApk }
    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"

    // 對應tinker插件applyMapping
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"

    // 對應tinker插件applyResourceMapping
    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    // 構建基準包和補丁包都要指定不同的tinkerId,并且必須保證唯一性
    tinkerId = "base-1.0.1"

    // 構建多渠道補丁時使用
    // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

    // 是否啟用加固模式,默認為false.(tinker-spport 1.0.7起支持)
    // isProtectedApp = true

    // 是否開啟反射Application模式
    enableProxyApplication = false

    // 是否支持新增非export的Activity(注意:設置為true才能修改AndroidManifest文件)
    supportHotplugComponent = true

}

/**
 * 一般來說,我們無需對下面的參數做任何的修改
 * 對于各參數的詳細介紹請參考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    //oldApk ="${bakPath}/${appName}/app-release.apk"
    ignoreWarning = false
    useSign = true
    dex {
        dexMode = "jar"
        pattern = ["classes*.dex"]
        loader = []
    }
    lib {
        pattern = ["lib/*/*.so"]
    }

    res {
        pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        ignoreChange = []
        largeModSize = 100
    }

    packageConfig {
    }
    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
//        path = "/usr/local/bin/7za"
    }
    buildConfig {
        keepDexApply = false
        //tinkerId = "1.0.1-base"
        //applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" //  可選,設置mapping文件,建議保持舊apk的proguard混淆方式
        //applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可選,設置R.txt文件,通過舊apk文件保持ResId的分配
    }
}

更詳細的配置項參考tinker-support配置說明

到這一步完成之后,構建項目,可能會遇到一個問題

can't find tinkerProcessReleaseManifest, you must init tinker plugin first!

官方的問題解答里面說可以關閉Instant Run,但是我用的AS版本是3.6.3,已經沒有Instant Run功能了。最終通過修改gradle tool版本為3.4.0得以解決,具體原因目前還不是特別清楚

classpath 'com.android.tools.build:gradle:3.4.0'

初始化SDK

enableProxyApplication = false是Tinker推薦的接入方式,一定程度上會增加接入成本,但具有更好的兼容性

集成Bugly升級SDK之后,我們需要按照以下方式自定義ApplicationLike來實現Application的代碼

自定義Application

需要將項目中的Applicaton配置為繼承TinkerApplication的類(這個Application需要配置到manifest文件中):

public class SampleApplication extends TinkerApplication {
    public SampleApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL, "xxx.xxx.SampleApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }
}

注意:這個類集成TinkerApplication類,這里面不做任何操作,所有Application的代碼都會放到ApplicationLike繼承類當中
參數解析
參數1:tinkerFlags 表示Tinker支持的類型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL
參數2:delegateClassName Application代理類 這里填寫你自定義的ApplicationLike
參數3:loaderClassName Tinker的加載器,使用默認即可
參數4:tinkerLoadVerifyFlag 加載dex或者lib是否驗證md5,默認為false

自定義ApplicationLike

這里就需要用到之前在Bugly平臺申請的appId

public class SampleApplicationLike extends DefaultApplicationLike {

    public static final String TAG = "Tinker.SampleApplicationLike";

    public SampleApplicationLike(Application application, int tinkerFlags,
            boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,
            long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }


    @Override
    public void onCreate() {
        super.onCreate();
        // 這里實現SDK初始化,appId替換成你的在Bugly平臺申請的appId
        // 調試時,將第三個參數改為true
        Bugly.init(getApplication(), "265bca5587", false);
    }


    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        // 安裝tinker
        // TinkerManager.installTinker(this); 替換成下面Bugly提供的方法
        Beta.installTinker(this);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
        getApplication().registerActivityLifecycleCallbacks(callbacks);
    }

}

注意:tinker需要你開啟MultiDex,你需要在dependencies中進行配置compile "com.android.support:multidex:1.0.1"才可以使用MultiDex.install方法; SampleApplicationLike這個類是Application的代理類,以前所有在Application的實現必須要全部拷貝到這里,在onCreate方法調用SDK的初始化方法,在onBaseContextAttached中調用Beta.installTinker(this);

AndroidManifest.xml配置

在AndroidMainfest.xml中進行以下配置:

1. 權限配置

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2. Activity配置

<activity
    android:name="com.tencent.bugly.beta.ui.BetaActivity"
    android:configChanges="keyboardHidden|orientation|screenSize|locale"
    android:theme="@android:style/Theme.Translucent" />

3. 配置FileProvider

注意:如果您想兼容Android N或者以上的設備,必須要在AndroidManifest.xml文件中配置FileProvider來訪問共享路徑的文件

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>

如果你使用的第三方庫也配置了同樣的FileProvider, 可以通過繼承FileProvider類來解決合并沖突的問題,示例如下:

<provider
    android:name=".utils.BuglyFileProvider"
    android:authorities="${applicationId}.fileProvider"
    android:exported="false"
    android:grantUriPermissions="true"
    tools:replace="name,authorities,exported,grantUriPermissions">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"
        tools:replace="name,resource"/>
</provider>

這里要注意一下,FileProvider類是在androidx.core.content包中的,檢查你的工程是否引入該類庫

混淆配置

為了避免混淆SDK,在Proguard混淆文件中增加以下配置:

-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆規則
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }

APK 簽名

如果是自己寫demo測試Tinker的使用,不要忘記APK是需要簽名的,如果對APK簽名不是很了解的同學可以看我的這邊文章手把手教你APK簽名

Bugly后臺

做完上面的步驟后,Bugly+Tinker的集成已經完成,APK完成后就需要打包上線,Bugly提供了全套的打包上線流程以及強大的后臺能力支持,具體可以參考

https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix-demo/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。