Tinker的使用

Tinker的继承和使用

Tinker-API概览:https://github.com/Tencent/tinker/wiki/Tinker-API概览
Tinker-自定义扩展:https://github.com/Tencent/tinker/wiki/Tinker-自定义扩展
Tinker案例:
https://github.com/Tencent/tinker/blob/master/tinker-sample-android/app/src/main/java/tinker/sample

添加依赖

 //可选,用于生成application类 provided不参与打包
    provided('com.tencent.tinker:tinker-android-anno:1.9.1')
    //tinker的核心库
    compile('com.tencent.tinker:tinker-android-lib:1.9.1')
     //支持多分包
    compile "com.android.support:multidex:1.0.1"

TinkerManager

public class TinkerManager {
    private static boolean isInstall = false;
    private static ApplicationLike mAppLike;

    /**
     * 完成Tinker的初始化
     */
    public static void installTinker(ApplicationLike applicationLike) {
        mAppLike = applicationLike;
        if (isInstall) {
            return;
        }
        TinkerInstaller.install(applicationLike);//完成tinker的初始化
        isInstall = true;
    }

    /**
     * 发起补丁修复请求
     */
    public static void loadPatch(String path) {
        if (Tinker.isTinkerInstalled()) {
            TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), path);
        }
    }

    //获取上下文
    private static Context getApplicationContext() {
        if (mAppLike != null) {
            return mAppLike.getApplication().getApplicationContext();
        }
        return null;
    }
}

CustomTinkerLike用于生成Application

@DefaultLifeCycle(application = ".MyTinkerApplication",
        flags = ShareConstants.TINKER_ENABLE_ALL,
        loadVerifyFlag = false)
public class CustomTinkerLike extends ApplicationLike {
    public CustomTinkerLike(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(base);
        TinkerManager.installTinker(this);
    }
}

grade参数配置

整个项目中添加依赖:

       //Tinker
        classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1')

参数配置:官网接入指南:
https://github.com/Tencent/tinker/wiki/Tinker-接入指南
有相关说明,可以直接拷贝官网提供的指南https://github.com/Tencent/tinker/blob/master/tinker-sample-android/app/build.gradle

apply plugin: 'com.android.application'
def javaVersion = JavaVersion.VERSION_1_7
def bakPath = file("${buildDir}/bakApk/")
android {
    compileSdkVersion 27
    buildToolsVersion "27.0.3"

    defaultConfig {
        applicationId "com.peakmain.tinker"
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

    compileOptions {
        sourceCompatibility javaVersion
        targetCompatibility javaVersion
    }
    signingConfigs {
        signingConfigs {
            try {
                keyAlias 'Tinker'
                keyPassword '123456'
                storeFile file('E:/Android/FestEc/tinker/src/keystore/release.jks')
                storePassword '123456'
            } catch (ex) {
                throw new InvalidUserDataException(ex.toString())
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    //可选,用于生成application类 provided不参与打包
    provided("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}"){changing=true}
    //tinker的核心库
    compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}"){changing=true}
    //支持多分包
    compile "com.android.support:multidex:1.0.1"
}

ext {
    tinkerEnabled = true
    tinkerOldApkPath = "${bakPath}/"
    tinkerID = "1.0"
    tinkerApplyMappingPath = "${bakPath}/"
    tinkerApplyResourcePath = "${bakPath}/"
}

def buildWithTinker() {
    return ext.tinkerEnabled;
}

def getOldApkPath() {
    return ext.tinkerOldApkPath
}

def getApplyMappingPath() {
    return ext.tinkerApplyMappingPath
}

def getApplyResourceMappingPath() {
    return ext.tinkerApplyResourcePath
}

def getTinkerIdValue() {
    return ext.tinkerID
}

if (buildWithTinker()) {
    //启用tinker
    apply plugin: 'com.tencent.tinker.patch'

    tinkerPatch {
        oldApk = getOldApkPath()//指定old  apk的路径
        ignoreWarning = false//是否忽略警告,false表示不忽略tinker的警告,有警告则中止文件的生成
        useSign = true//强制使用签名
        tinkerEnable = buildWithTinker()//指定是否启用tinker
        buildConfig {
            applyMapping = getApplyMappingPath()//指定old apk打包时所使用的混淆文件
            applyResourceMapping = getApplyResourceMappingPath()//指定old apk的资源文件
            tinkerId = getTinkerIdValue()//指定Tinker的id
            keepDexApply = false
        }
        dex {
            dexMode = "jar"
            //指定dex文件目录
            pattern = ["classes*.dex",
                       "assets/secondary-dex-?.jar"]
            loader = [
                    "com.peakmain.tinker.MyTinkerApplication"//指定加载patch文件时用到的类
            ]
        }
        lib {
            pattern = ["lib/*/*.so"]
        }
        res {
            pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
//指定tinker可以修改的资源路径
            ignoreChange = ["assets/sample_meta.txt"]//指定不受影响的资源路径
            largeModSize = 100//资源修改的默认大小
        }
        packageConfig {
            configField("patchMessage", "tinker is sample to use")
            configField("platform", "all")//平台
            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")
                        }
                    }
                }
            }
        }
    }
}

gradle.properties添加

TINKER_VERSION=1.9.1

修改整个项目的build

        //Tinker
        classpath ("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}"){changing=true}

生成签名apk之后修改builder中ext


image.png

build之后选择tinkerPatchRelease


image.png

此时会生成tinkerPatch


image.png

有时候会报错:使用Tinker can't the get signConfig for this build,这个代表你没有添加签名配置,可是代码明明添加了,还是报错,解决办法

 buildTypes {
        release {
            minifyEnabled true
          //添加这一行
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

此时就已经完成生成patch文件

TinkerService进行后台下载

public class TinkerService extends Service {
    private static final String FILE_END = ".apk";//文件后缀名
    private static final int DOWNLOAD_PATCH = 0x01;
    private static final int UPDATE_PATCH = 0x02;

    private String mPatchFileDir;//patch要保存的文件夹
    private String mFilePatch;//patch文件保存的路径
    private BasePatch mBasePatchInfo;//服务器patch信息

    @Override
    public void onCreate() {
        super.onCreate();
        init();
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_PATCH:
                    checkPatchInfo();
                    break;
                case DOWNLOAD_PATCH:
                    downloadPatch();
                    break;
            }
        }
    };

    private void downloadPatch() {
        mFilePatch=mPatchFileDir.concat(String.valueOf(System.currentTimeMillis()))
                .concat(FILE_END);
        RequestCenter.downloadFile(mBasePatchInfo.data.downloadUrl, mFilePatch, new DisposeDownloadListener() {
            @Override
            public void onProgress(int progrss) {

            }

            @Override
            public void onSuccess(Object responseObj) {
                TinkerManager.loadPatch(mFilePatch);
            }

            @Override
            public void onFailure(Object reasonObj) {
             stopSelf();
            }
        });
    }

    private void checkPatchInfo() {
        RequestCenter.requestPatchUpdateInfo(new DisposeDataListener() {
            @Override
            public void onSuccess(Object responseObj) {
                mBasePatchInfo = (BasePatch) responseObj;
                mHandler.sendEmptyMessage(DOWNLOAD_PATCH);
            }

            @Override
            public void onFailure(Object reasonObj) {
                stopSelf();
            }
        });

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //检查是否有patch更新
        mHandler.sendEmptyMessage(UPDATE_PATCH);
        return START_NOT_STICKY;//被系统回收将不再重启
    }

    private void init() {
        mPatchFileDir = getExternalCacheDir().getAbsolutePath() + "/tpatch";
        File patchFileDir = new File(mPatchFileDir);
        try {
            if (patchFileDir == null || !patchFileDir.exists()) {
                patchFileDir.mkdir();
            }
        } catch (Exception e) {
            e.printStackTrace();
            //无法正常创建,停止服务
            stopSelf();
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容