1. 程式人生 > >Tinker熱修復接入詳解(入坑並出坑篇)

Tinker熱修復接入詳解(入坑並出坑篇)

注:當然一開始要參考Tinker的詳細說明,連結如下:

https://github.com/Tencent/tinker/wiki

下面就是我自己一步一步操作,並完成接入Tinker,而且入坑並出坑的過程。

一:android studio自己建立個工程

二:工程的build.gradle中新增以下程式碼:

 dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
        //tinker依賴
        classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.11')

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }


三:在app/build.gradle中新增如下:(整個檔案程式碼如下:根據自己工程情況適當修改名稱)

apply plugin: 'com.android.application'
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        applicationId "com.oneandroid.activity"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        multiDexEnabled true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    signingConfigs {
        release {
            keyAlias 'oneAndroid'
            keyPassword '123456'
            storeFile file('jks/sign.jks')
            storePassword '123456'
        }
    }

    buildTypes {
        debug {
            signingConfig signingConfigs.release
        }
        release {
            signingConfig signingConfigs.release
            minifyEnabled false
            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.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha7'
    compile 'com.android.support:design:25.3.0'

    testCompile 'junit:junit:4.12'
    //可選,用於生成application類
    provided('com.tencent.tinker:tinker-android-anno:1.7.11')
    //tinker的核心庫
    compile('com.tencent.tinker:tinker-android-lib:1.7.11')
    compile "com.android.support:multidex:1.0.1"

}

apply plugin: 'com.tencent.tinker.patch'
tinkerPatch {
    //有問題的apk的地址
    oldApk = "/Users/zhaoguang/Documents/kanzhun/as_workspace/oldApk/app-debug.apk"
    ignoreWarning = false
    useSign = true
    buildConfig{
        tinkerId = "1.0"
    }
    packageConfig{
        //寫這個為了修復一個bug,詳見github issue #22
        configField("TINKER_ID", "tinker_id_1.0")
    }
    dex{
        dexMode = "jar"
        pattern = ["classes*.dex", "assets/secondary-dex-?.jar"]
        loader = ["com.tencent.tinker.loader.*", "com.oneandroid.activity.Application"]
    }
    lib{
        pattern = ["lib/armeabi/*.so","lib/arm64-v8a/*.so","lib/armeabi-v7a/*.so","lib/mips/*.so","lib/mips64/*.so","lib/x86/*.so","lib/x86_64/*.so"]
    }
    res{
        pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        largeModSize = 100
    }
    sevenZip{
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
    }
}



說明:關於上面app/build.gradle:

1)首先建立sign.jks(不懂如何建立,參看:http://blog.csdn.net/yy1300326388/article/details/48344411)

2)在1)基礎上,在app/build.gradle中增加如下程式碼:(程式碼中的別名,密碼,儲存位置必須與建立sign.jks時所填寫保持一致

signingConfigs {
        release {
            keyAlias 'oneAndroid'
            keyPassword '123456'
            storeFile file('jks/sign.jks')
            storePassword '123456'
        }
    }

3)在app/build.gradle中增加如下程式碼:(這樣設定保證打debug和release包都是使用同一個簽名)
buildTypes {
        debug {
            signingConfig signingConfigs.release
        }
        release {
            signingConfig signingConfigs.release
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

4)在app/build.gradle中,新增Tinker依賴和多Dex方式,如下:

//可選,用於生成application類
    provided('com.tencent.tinker:tinker-android-anno:1.7.11')
    //tinker的核心庫
    compile('com.tencent.tinker:tinker-android-lib:1.7.11')
    compile "com.android.support:multidex:1.0.1"

5)在app/build.gradle中,增加tinkerPatch 任務程式碼,並且增加引用該外掛:程式碼如下:

apply plugin: 'com.tencent.tinker.patch'
tinkerPatch {
    //有問題的apk的地址
    oldApk = "/Users/zhaoguang/Documents/kanzhun/as_workspace/oldApk/app-debug.apk"
    ignoreWarning = false
    useSign = true
    buildConfig{
        tinkerId = "1.0"
    }
    packageConfig{
        //寫這個為了修復一個bug,詳見github issue #22
        configField("TINKER_ID", "tinker_id_1.0")
    }
    dex{
        dexMode = "jar"
        pattern = ["classes*.dex", "assets/secondary-dex-?.jar"]
        loader = ["com.tencent.tinker.loader.*", "com.oneandroid.activity.Application"]
    }
    lib{
        pattern = ["lib/armeabi/*.so","lib/arm64-v8a/*.so","lib/armeabi-v7a/*.so","lib/mips/*.so","lib/mips64/*.so","lib/x86/*.so","lib/x86_64/*.so"]
    }
    res{
        pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        largeModSize = 100
    }
    sevenZip{
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
    }
}

其中:

5.1) oldApk為你電腦存放有問題的apk路徑。

注意開始除錯接入Tinker時,最好打debug包,這樣Tinker有問題會有日誌輸出:在LogCat視窗該日誌的Tag就是你的"包名:patch"

5.2) 很多網上的帖子都寫成:

configField("TINKER_ID", "1.0")
而我按此測試發現Logcat中Tinker報錯:說patch的tinkerId與oldApk包中tinkerId不一致,所以我改成一致的才通過測試如下程式碼中的:
 //寫這個為了修復一個bug,詳見github issue #22
        configField("TINKER_ID", "tinker_id_1.0")

5.3)注意下面程式碼中下面這行:要改為你自己的包名

loader = ["com.tencent.tinker.loader.*", "com.oneandroid.activity.Application"]


四:編寫MyApplication,程式碼如下:

package com.oneandroid.activity;

import android.app.Application;
import android.content.Context;
import android.content.Intent;

import com.tencent.tinker.anno.DefaultLifeCycle;
import com.tencent.tinker.lib.tinker.TinkerInstaller;
import com.tencent.tinker.loader.app.DefaultApplicationLike;
import com.tencent.tinker.loader.shareutil.ShareConstants;

/**
 * Created by zhaoguang on 17/6/5.
 */

@DefaultLifeCycle(
        application = "com.oneandroid.activity.Application",
        flags = ShareConstants.TINKER_ENABLE_ALL
)
public class MyApplication extends DefaultApplicationLike {

    public MyApplication(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();
    }

    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        TinkerInstaller.install(this);
    }
}

注意:MyApplication繼承自DefaultApplicationLike,而不是Application,還要在MyApplication開始處寫註解:@DefaultLifeCycle

並且注意要在AndroidManifest.xml中配置如下:注意application標籤的android:name="com.oneandroid.activity.Application",而不是MyApplication,這是與DefaultLifeCycle中設定的application值相同,另外在AndroidManifest.xml中要至少增加讀寫SD卡的許可權

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.oneandroid.activity" >

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
        android:name="com.oneandroid.activity.Application"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.oneandroid.activity.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


五:編寫有問題的MainActivity.java,具體如下:

package com.oneandroid.activity;

import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.tencent.tinker.lib.tinker.TinkerInstaller;

public class MainActivity extends AppCompatActivity {

    private TextView mText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mText=(TextView)findViewById(R.id.txt_tip);
        //mText.setText("這是沒有bug版本");

        /* 補丁的路徑,從伺服器下載到手機 */
        String patchPath = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/666/patch_signed_7zip.apk";
        TinkerInstaller.onReceiveUpgradePatch(this.getApplication(), patchPath);
        Log.d("FFFFFF","====patchPath="+patchPath);

    }

}

其中註釋掉那行程式碼時,為錯誤程式碼,開啟註釋後為正確程式碼(通過這種方式模擬你的程式碼錯誤和正確的兩種情況)

變數:patchPath的值為從你公司伺服器下載到Android手機存放補丁的路徑,其中patch_signed_7zip.apk為TInker生產的補丁的預設名稱,這裡我存放補丁在我Android手機內部儲存目錄下的666檔案內

TinkerInstaller.onReceiveUpgradePatch(this.getApplication(), patchPath);
        
這行就是Tinker安裝Android設定的patchPath目錄中的補丁的程式碼。

自此以上接入Tinker結束,下面是測試熱修復。

六:MainActivity.java保持那一行程式碼註釋掉,然後點選執行Android studio中Gradle中的assembleDebug,如下圖:

在app/build/outputs/apk/目錄中生產app-debug.apk(有問題的apk),把它放置到上文5.1)中設定的你的電腦路徑下,並且通過adb install -r安裝到你的Android手機內,並執行,這時為錯誤的程式


七:將MainActivity.java中那行註釋開啟(模擬修復後的程式碼),開啟終端:設定好gradle的環境變數(不會請自行百度),執行:gradle tinkerPatchDebug , 看到Build Success之後,會在你工程中app/build/outputs/tinkerPatch目錄下看到生產的補丁檔案:patch_signed_7zip.apk,如下圖:然後把atch_signed_7zip.apk放置到你在MainActivity.java中設定的變數patchPath對應的目錄下,本文為Android手機內建儲存目錄下新建的666檔案內,kill 掉剛才錯誤的apk程序,重新開啟該apk。會發現註釋那行程式碼開啟生效了,自此接入Tinker成功!!!