1. 程式人生 > >Tinker自動熱更新

Tinker自動熱更新

 //project配置
buildscript {
    dependencies {
        classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1')
    }
}
//app/build.gradle 配置
dependencies {
    //optional, help to generate the final application 
    provided('com.tencent.tinker:tinker-android-anno:1.9.1')
    //tinker's main Android lib
compile('com.tencent.tinker:tinker-android-lib:1.9.1') } ... ... apply plugin: 'com.tencent.tinker.patch'
//如下例項
apply plugin: 'com.android.application'
apply plugin: 'com.tencent.tinker.patch'


android {
    signingConfigs {
        release {
            keyAlias 'key0'
            keyPassword '123456'
            storeFile file('/Users/eric/Downloads/Tinker/app/tinker.jks')
            storePassword '123456'
        }
    }
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.xieast.tinker"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions {
            annotationProcessorOptions {
                includeCompileClasspath true
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}


dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    //可選,用於生成application類
    provided 'com.tencent.tinker:tinker-android-anno:1.9.2'
    //tinker的核心庫
    compile 'com.tencent.tinker:tinker-android-lib:1.9.2'
    implementation 'com.android.support:multidex:1.0.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

//沒更新前的路徑 
def bakPath = file("${buildDir}/bakApk/")

ext {
    tinkerOldApkPath  = "${bakPath}/app-release-0605-15-47-00.apk"
}


def getOldApkPath() {
    return ext.tinkerOldApkPath
}

//配置
tinkerPatch {
    tinkerEnable true
    oldApk = getOldApkPath()
    ignoreWarning true
    useSign true


    buildConfig {
        tinkerId = "tinker_id_b168b32"
        keepDexApply = false
        isProtectedApp = false
    }


    dex {
        dexMode = "jar"
        pattern = ["classes*.dex", "assets/secondary-dex-?.jar"]
        loader =  ["com.xieast.tinker.MyTinkerApplication"]
    }


    lib {
        pattern = ["lib/*/*.so"]
    }


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


    packageConfig {
        configField("patchVersion", "1.0")
    }
}


List<String> flavors = new ArrayList<>();
project.android.productFlavors.each { flavor ->
    flavors.add(flavor.name)
}
boolean hasFlavors = flavors.size() > 0
def date = new Date().format("MMdd-HH-mm-ss")


/**
 * bak apk and mapping
 */
android.applicationVariants.all { variant ->
    /**
     * task type, you want to bak
     */
    def taskName = variant.name


    tasks.all {
        if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {


            it.doLast {
                copy {
                    def fileNamePrefix = "${project.name}-${variant.baseName}"
                    def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"


                    def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath
                    from variant.outputs.first().outputFile
                    into destPath
                    rename { String fileName ->
                        fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
                    }


                    from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
                    into destPath
                    rename { String fileName ->
                        fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
                    }


                    from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
                    into destPath
                    rename { String fileName ->
                        fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
                    }
                }
            }
        }
    }
}
project.afterEvaluate {
    //sample use for build all flavor for one time
    if (hasFlavors) {
        task(tinkerPatchAllFlavorRelease) {
            group = 'tinker'
            def originOldPath = getTinkerBuildFlavorDirectory()
            for (String flavor : flavors) {
                def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")
                dependsOn tinkerTask
                def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")
                preAssembleTask.doFirst {
                    String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
                    project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
//                    project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"
//                    project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"


                }


            }
        }


        task(tinkerPatchAllFlavorDebug) {
            group = 'tinker'
            def originOldPath = getTinkerBuildFlavorDirectory()
            for (String flavor : flavors) {
                def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")
                dependsOn tinkerTask
                def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")
                preAssembleTask.doFirst {
                    String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
                    project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"
//                    project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"
//                    project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"
                }


            }
        }
    }
}
//如果你的應用有一個子類為android.app.Application的類,
那麼你需要修改這個類,並將它的所有實現移動到SampleApplicationLike而
不是Application:
現在你應該改變你的Application類,使它成為TinkerApplication的一個子類。
正如你可以從它的API中看到的那樣,它是一個沒有預設建構函式的抽象類,
所以你必須定義一個無引數建構函式:
建議使用tinker-android-anno生成你Application的,你只需要為你的SampleApplicationLike類新增一個註釋
@DefaultLifeCycle(application=".MyTinkerApplication",flags = ShareConstants.TINKER_ENABLE_ALL )
public class MyAppp extends DefaultApplicationLike {
    public MyAppp(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

    @Override
public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        MultiDex.install(getApplication().getApplicationContext());
        TinkerManager.insell(this);
    }
}
/**
 *
 TinkerManager 管理類

 */


public class TinkerManager {


    private static boolean isInstalled = false;
    private static ApplicationLike mApplicationLike;


    /**
     * 安裝tinker
     *
     * @param applicationLike
     */
    public static void install(ApplicationLike applicationLike) {
        mApplicationLike = applicationLike;
        if (isInstalled) {
            return;
        }
        TinkerInstaller.install(applicationLike);
        isInstalled = true;
    }


    /**
     * 載入補丁
     *
     * @param path
     */
    public static void loadPatch(String path) {
        if (Tinker.isTinkerInstalled()) {
            TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), path);
        }
    }


    /**
     * 獲取上下文
     *
     * @return
     */
    private static Context getApplicationContext() {
        if (mApplicationLike != null) {
            return mApplicationLike.getApplication().getApplicationContext();
        }
        return null;
    }
}
/**
  MainActivity
**/


public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";


    private TextView txtShow;
    private Button btnLoad;
    private Button btnNew;
    private Button btnNew2;


    private String patchDir = "patch";


    private String patchName = "tk.apk";


    private File patchFile;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        txtShow = findViewById(R.id.txt_show);
        btnLoad = findViewById(R.id.btn_load_patch);
        btnNew = findViewById(R.id.btn_new_function);
        btnNew2 = findViewById(R.id.btn_new_gradle);
        //獲得補丁路徑
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            File rootDirectory = Environment.getExternalStorageDirectory();
            File patchPath = new File(rootDirectory, patchDir);
            if (!patchPath.exists()) {
                patchPath.mkdirs();
            }


            patchFile = new File(patchPath, patchName);
        }


        btnLoad.setOnClickListener(this);
        btnNew.setOnClickListener(this);
        btnNew2.setOnClickListener(this);


    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_load_patch:
    //載入補丁
                String path = patchFile.getAbsolutePath();
                Log.i(TAG, "path: " + path);
                TinkerManager.loadPatch(path);
                break;
            case R.id.btn_new_function:
                Toast.makeText(this, "這是新功能", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn_new_gradle:
                Toast.makeText(this, "這是Gradle打包的", Toast.LENGTH_SHORT).show();
                break;
        }
    }


}

相關推薦

Tinker自動更新

 //project配置 buildscript { dependencies { classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1') } }//app/build

Android 騰訊Bugly(封裝tinker更新遇到的坑

這篇文章主要是來講遇到的問題的,如果需要整合教程最好還是到官方的文件中心https://bugly.qq.com/docs/廢話不多說,開始坑1: 所有針對bugly 屬性的設定不生效。原因:屬性設定一定要放在Bugly.init(getApplicationContext(

四四、關於Webstorm webpack不能自動更新問題

專案是vue+webpack構建的,以前的時候熱更新還挺正常的,一改動程式碼瀏覽器也就立馬重新整理了,現在基本都不重新整理了,每次都手動npm run dev 重啟服務才能看到更新的,很煩,查詢了很多

基於微信Tinker更新詳細說明

  先來吐槽一下,這個更新方法簡直6的沒話說,經我詳細的測試,可以更新類及新增類,刪除類什麼的,以及對XML資原始檔的更新,好像還能更新library,但是我還沒測試過,總之可以可以很強勢,但是在整合的過程中也很多坑,集了我一天多,報錯太多了,網上資料還不怎麼詳

關於webpack下更新?&自動刷新?的小記(非vue-cli)

入口 實踐 創建 line color 並不會 inline -m 16px 寫本隨筆時:webpack4.6.0 為何標題用?號,因為老衲也不知是否用詞正確,大概是這樣的說法: webpack4.0引入生產模式和開發模式,在開發時使用 webpack 打包後不壓縮,所以只

Android更新技術——Tinker、nuwa、AndFix、Dexposed

一、熱修復技術作用   線上app BUG緊急修復,不重新發版,不重新安裝,線上遠端修復問題 二、侷限性與適用場景 補丁只能針對單一客戶端版本,隨著版本差異變大補丁體積也會增大; 補丁不能支援所有的修改,例如AndroidManifest; 補丁無論對程式碼還是資源的更新成功率都無法達到1

vue 中npm run dev自動開啟瀏覽器與更新

一、自動開啟瀏覽器 config/index.js 修改autoOpenBrowser:ture 重啟專案 二、熱更新 1. config/index.js 修改dev中 poll選項值為true(或者時間) 2.build/webpack.dev.conf.js 修改d

ionic4.0 詳細更新 自動模式以及手動模式--秒殺網上一切更新因為網上沒有手動更新的詳細程式碼 測試無誤上線釋出了的東西.

ionic4.0 詳細熱更新 自動模式以及手動模式(全)–純手打,獨一無二,絕對好用. 網上一大堆ionic 熱更新,但是都是說的最基本的自動更新操作,安裝幾個外掛,改幾個配置檔案,就草草了事,讓我真的很煩躁,通過自己不斷的查詢,終於守得雲開見月明,找到了手動

Android Tinker 更新

磚友們首先要了解為什麼要熱更新? 此博文直接整合熱更新 當一個App釋出之後,突然發現了一個嚴重bug需要進行緊急修復,這時候公司各方就會 忙得焦頭爛額:重新打包App、測試、向各個應用市場和渠道換包、提示使用者升級、使用者下 載、覆蓋安裝。有時候僅僅是為了修改了一行程式

Android更新Tinker + 多渠道打包 + 加固的流程詳解

一、Tinker熱修復 關於熱修復的作用,不用多說了,一句話概括就是通過讓使用者無感的方式來修復線上應用的bug。這裡介紹的是微信Tinker。 下面的接入方式都是參考自Tinker官方文件來。我這裡主要是把我接入的步驟(通過AndroidStudio +

Android開發——整合Tinker更新框架出現java.lang.NoClassDefFoundError

前言 在Android開發當中,Tinker熱更新是很常見的使用框架,但是我今天在整合的時候卻出現異響不到的事情,出現bug了。應用啟動出現Tinker.UncaughtHandler: TinkerUncaughtHandler catch exception

更新-Bugly-Tinker使用詳解

Bugly熱更新是基於Tinker使用的。 為什麼使用Bugly呢,因為他目前還沒有收費。 費話不多說了,言歸正傳 使用教程,跟著步驟一步一步教你整合 gradle整合方式 1、新增依賴 compile 'com.tencent.bugly:crashreport_upg

Bugly更新修復失敗的一種解決方法(Tinker Exception:can't recognize dex mode:)

就是補丁顯示已下發,但是啟用總是0,也就是可能是下載補丁成功了,但是卻沒有安裝成功。 通過開啟log檢視原因,開啟方式如下圖: 結果發現,當補丁下載成功後,我的後續操作就報錯了,主要是下面一句: Throwable:Tinker Exception

Android更新:微信Tinker框架的接入與測試

轉載於:http://www.jianshu.com/p/aadcf2ea69a6 Android熱修復框架的對比(最終選擇微信Tinker) Android熱修復框架的對比 AndFix作為native解決方案,首先面臨的是穩定性與相容性問題,更重要的是它無法實現類替換,它是需要大量額外的開發成本的;

騰訊Tinker更新的詳細整合與使用

因為專案需要集成了騰訊的Tinker熱更新,因騰訊文件有些晦澀所以我做了點整理記錄在此,若有錯誤可在官方文件查閱關於目前常用的熱更新第三方如下:第一步:新增外掛依賴工程根目錄下“build.gradle”檔案中新增:buildscript { repositories{

Android更新開源專案Tinker原始碼解析系列之一:Dex更新

Tinker是微信的第一個開源專案,主要用於安卓應用bug的熱修復和功能的迭代。 Tinker github地址:https://github.com/Tencent/tinker 首先向微信致敬,感謝毫無保留的開源出了這麼一款優秀的熱更新專案。

更新Tinker的整合使用

請珍惜勞動者的汗水,一分耕耘一分收穫,轉載請註明出處,謝謝! 初衷:如今熱更新越來越火,各大廠也陸續開源自己的熱更新框架。目前主流的熱更新大概有以下一些,未統計到的望給予補充。 正好年底了有點時間,也總結一下。 1、Tinker 微信  2、QZone  QQ空間  3、

學習Tinker+美團的walle(多渠道打包+更新)總結

我是先看視訊,按照視訊配置,發現應用怎麼都聯網不成功 然後我把應用刪了,重新按照sdk網頁文件配置成功了. 所以建議初學者視訊可以看,但是配置按照網頁的配置來,這樣減少不必要的麻煩 首先新建一個空的工程 第一步:新增外掛依賴 工程根目錄下“build.gradle”

cocos2dx 3.1.1 線上更新 自動更新(使用AssetsManager更新遊戲資源包)

//Upgrade.cpp #include "Upgrade.h" #include "CCLuaEngine.h" #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) #include <dirent.h> #include <s

一次整合使用Tinker更新的體驗

熱載入 簡單的說 如果Android要載入一個類 就會呼叫ClassLoader的findClass方法 在dex中查詢這個類 找到後加載到記憶體 而我們要做的就是在findClass的時候讓類載入找到的是我們修復過後的類,而不是未修復的類。