微信開源了自己的熱修復Tinker庫,一直沒用過,所以想搞一個小Demo來體驗一下
什么是Tinker?
Tinker是微信官方的Android熱補丁解決方案,它支持動態下發代碼、So庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。
Tinker 傳送門:github主頁 wiki介紹 Tinker的接入指南
開始接入
Tinker 的sample 的地址為 sample
1、gradle配置
參照 Tinker sample的做法 在 gradle.properties文件末尾添加
TINKER_VERSION=1.7.11
在項目的 build.gradle 中指定classpath
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath "com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}"
}
}
參考 sample 的build.gradle 進行配置
1、在android {}標簽中添加 簽名配置
signingConfigs {
release {
try {
storeFile file("./keystore/release.keystore")
storePassword "testres"
keyAlias "testres"
keyPassword "testres"
} catch (ex) {
throw new InvalidUserDataException(ex.toString())
}
}
debug {
storeFile file("./keystore/debug.keystore")
}
}
2、在dependencies{}標簽添加核心庫
compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
provided("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
3、其余粘貼sample 中的 配置但是要進行修改
注意修改 ignoreWarning = true **
修改tinkerId = "tinkerId" //getTinkerIdValue()**
如果涉及到 getTinkerIdValue 的都修改成 tinkerId
然后編譯就能通過了
2、Application配置
與以往的很多庫不同,這里并不是在自定義的Application 中進行初始化之類的‘
@SuppressWarnings("unused")
@DefaultLifeCycle(application = ".SampleApplication",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class SampleApplicationLike extends DefaultApplicationLike {
private 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);
}
/**
* install multiDex before install tinker
* so we don't need to put the tinker lib classes in the main dex
*
* @param base
*/
@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);
TinkerInstaller.install(this);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
getApplication().registerActivityLifecycleCallbacks(callback);
}
}
AndroidManifest.xml 文件進行配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxh.thinkertest">
<!--Tinker 需要讀取Sd卡中的差異包apk 需要權限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!--這里的是我們SampleApplicationLike的注解部分,Tinker會在運行時生成該類-->
<application
android:name=".SampleApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
3、配置結束開始測試使用
module 執行DeBug 運行,將項目打包生成 apk 文件,運行在手機中。以App為例,會 在 build-> badApk->生成Apk文件 文件是以 日期時間來命名的 ,比較好區分
bad_apk_gen
修改配置 build.gradle 文件
//for normal build
//old apk file to build patch apk
tinkerOldApkPath = "${bakPath}/app-debug-0629-17-10-31.apk" //這里配置 剛剛生成的 oldApk 文件
//proguard mapping file to build patch apk
tinkerApplyMappingPath = "${bakPath}/app-debug-1018-17-32-47-mapping.txt" //proguard的map映射文件
//resource R.txt to build patch apk, must input if there is resource changed
tinkerApplyResourcePath = "${bakPath}/app-debug-0629-17-10-31-R.txt" //如果修改了 resource內容,將該文件也更新到最新
修改項目中的代碼,并構建 差異包
修改MainActivity 中的代碼
public class MainActivity extends AppCompatActivity {
private TextView test_main;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test_main = (TextView) findViewById(R.id.test_main);
//在差異包中修改了 界面中的顯示文本 "I am In Path Apk"
test_main.setText("I am In Patch Apk");
}
public void loadPath(View view) {
String path = Environment.getExternalStoragePublicDirectory(DOWNLOAD_SERVICE).getAbsolutePath() + "/patch_signed_7zip.apk";
File file = new File(path);
if (file.exists()) {
Toast.makeText(this, "補丁存在", Toast.LENGTH_SHORT).show();
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), path);
} else {
Toast.makeText(this, "補丁不存在", Toast.LENGTH_SHORT).show();
}
}
}
修改完成執行 tinker 的 gradle 命令 tinkerPatchDebug
可以在命令行或者這里點擊運行,然后等待 構建生成 patch 包
在此過程中可能會失敗,重試一次,生成的差異包會在該目錄下:
將生成的 patch_signed_7zip.apk 文件 導入到sd卡中,但是導入到哪個目錄呢?看下面的代碼
public void loadPath(View view) {
//這里制定了 加載差異包的文件名和目錄
String path = Environment.getExternalStoragePublicDirectory(DOWNLOAD_SERVICE).getAbsolutePath() + "/patch_signed_7zip.apk";
File file = new File(path);
if (file.exists()) {
Toast.makeText(this, "補丁存在", Toast.LENGTH_SHORT).show();
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), path);
} else {
Toast.makeText(this, "補丁不存在", Toast.LENGTH_SHORT).show();
}
}
導入的目錄要與 代碼中的目錄保持一致
再次運行剛剛打包到手機中的應用 點擊 loadPath按鈕看效果如下
這里只是體驗了一下Tinker的簡單實用,具體在項目中的使用還需要進一步的去研究,相信還有很多坑在等著我!