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成功!!!