Mise à jour à chaud Android Tinker + emballage multicanal + démonstration détaillée du processus de renforcement
Récemment, six projets ont été maintenus et lancés simultanément en deux mois. Beaucoup d'entre eux présentaient des problèmes mineurs, mais il fallait trop de main d'œuvre et de ressources matérielles pour disposer uniquement de versions mises à jour en ligne. J'ai donc passé une journée à intégrer Tinker, la bibliothèque de mises à jour open source de WeChat. , et mettez-le en service. La démo d'apprentissage est open source pour les codeurs qui découvrent les mises à jour chaudes.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
// TinkerPatch 插件
classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.1.8"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
Ajoutez des dépendances aux niveaux dans l'application. Remarque : Ces deux niveaux ne sont pas les mêmes. Le niveau de construction ci-dessus concerne l'ensemble du projet et le niveau de construction suivant se trouve à l'intérieur de l'application.
dependencies {
// 若使用annotation需要单独引用,对于tinker的其他库都无需再引用
provided("com.tinkerpatch.tinker:tinker-android-anno:1.8.0")
compile("com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.1.8")
}
Notez que si vous utilisez une annotation pour générer automatiquement une application, vous devez introduire séparément la bibliothèque tinker-android-anno de Tinker. De plus, nous n'avons pas besoin d'introduire d'autres bibliothèques de bricolage séparément.
Pour plus de simplicité et de commodité, nous mettons toutes les configurations liées à TinkerPatch dans tinkerpatch.gradle. Nous devons l'introduire :
postuler depuis : 'tinkerpatch.gradle'
D'accord, nous allons ensuite créer ce tinkerpatch.gradle dans le répertoire de l'application, comme indiqué dans l'image :
Ouvrez tinkerpatch.gradle et placez toutes les configurations liées à TinkerPatch dans tinkerpatch.gradle.
apply plugin: 'tinkerpatch-support'
/**
* TODO: 请按自己的需求修改为适应自己工程的参数
*/
//基包路径
def bakPath = file("${buildDir}/bakApk/")
//基包文件夹名(打补丁包的时候,需要修改)
def baseInfo = "app-1.0.1-0221-11-01-38"
//版本名称
def variantName = "release"
/**
* 对于插件各参数的详细解析请参考
*
*/
tinkerpatchSupport {
//可以在debug的时候关闭 tinkerPatch
tinkerEnable = true
//是否使用一键接入功能 默认为false 是否反射 Application 实现一键接入;
// 一般来说,接入 Tinker 我们需要改造我们的 Application, 若这里为 true, 即我们无需对应用做任何改造即可接入。
reflectApplication = true
//将每次编译产生的 apk/mapping.txt/R.txt 归档存储的位置
autoBackupApkPath = "${bakPath}"
appKey = "582e640cae57f603"// 注意!!! 需要修改成你的appkey
/** 注意: 若发布新的全量包, appVersion一定要更新 **/
appVersion = "1.0.1"
protectedApp=true//使用加固
def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/"
def name = "${project.name}-${variantName}"
/**
* 基准包的文件路径, 对应 tinker 插件中的 oldApk 参数;编译补丁包时,
* 必需指定基准版本的 apk,默认值为空,则表示不是进行补丁包的编译
*/
baseApkFile = "${pathPrefix}/${name}.apk"
/**
* 基准包的 Proguard mapping.txt 文件路径, 对应 tinker 插件 applyMapping 参数;在编译新的 apk 时候,
* 我们希望通过保持基准 apk 的 proguard 混淆方式,
* 从而减少补丁包的大小。这是强烈推荐的,编译补丁包时,我们推荐输入基准 apk 生成的 mapping.txt 文件。
*/
baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
/**
* 基准包的资源 R.txt 文件路径, 对应 tinker 插件 applyResourceMapping 参数;在编译新的apk时候,
* 我们希望通基准 apk 的 R.txt 文件来保持 Resource Id 的分配,这样不仅可以减少补丁包的大小,
* 同时也避免由于 Resource Id 改变导致 remote view 异常
*/
baseResourceRFile = "${pathPrefix}/${name}-R.txt"
/**
* 若有编译多flavors需求, 可以参照: https://github.com/TinkerPatch/tinkerpatch-flavors-sample
* 注意: 除非你不同的flavor代码是不一样的,不然建议采用zip comment或者文件方式生成渠道信息(相关工具:walle 或者 packer-ng)
**/
}
/**
* 用于用户在代码中判断tinkerPatch是否被使能
*/
android {
defaultConfig {
buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}"
}
}
/**
* 一般来说,我们无需对下面的参数做任何的修改
* 对于各参数的详细介绍请参考:
* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
*/
tinkerPatch {
ignoreWarning = false
useSign = true //是否需要签名,打正式包如果这里是true,则要配置签名,否则会编译不过去
dex {
dexMode = "jar"
pattern = ["classes*.dex"]
loader = []
}
lib {
pattern = ["lib/*/*.so"]
}
res {
pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = []
largeModSize = 100
}
packageConfig {
}
sevenZip {
zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
// path = "/usr/local/bin/7za"
}
buildConfig {
keepDexApply = false
}
}
Une autre chose qui mérite notre attention ici est la clé d'application. Après nous être connectés au site officiel de Tinker et avoir ajouté une version d'application, une clé d'application sera générée. Nous devons remplir notre propre clé d'application dans la configuration ci-dessus et joindre une image :
package com.barnettwong.tinkerdemo;
import android.app.Application;
import com.tencent.tinker.loader.app.ApplicationLike;
import com.tinkerpatch.sdk.TinkerPatch;
import com.tinkerpatch.sdk.loader.TinkerPatchApplicationLike;
/**
* Created by wang on 2019-2-20.
* reflectApplication = true 时
*/
public class tinkerApplication extends Application {
private ApplicationLike tinkerApplicationLike;
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.TINKER_ENABLE) {
// 我们可以从这里获得Tinker加载过程的信息
tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();
// 初始化TinkerPatch SDK, 更多配置可参照API章节中的,初始化SDK
TinkerPatch.init(tinkerApplicationLike)
.reflectPatchLibrary()
.setPatchRollbackOnScreenOff(true)
.setPatchRestartOnSrceenOff(true);
// 每隔3个小时去访问后台时候有更新,通过handler实现轮训的效果
new FetchPatchHandler().fetchPatchWithInterval(3);
}
}
}
Enfin, ajoutez la classe FetchPatchHandler :
package com.barnettwong.tinkerdemo;
import android.os.Handler;
import android.os.Message;
import com.tinkerpatch.sdk.TinkerPatch;
/**
* Created by wang on 2019-2-20.
*/
public class FetchPatchHandler extends Handler {
public static final long HOUR_INTERVAL = 3600 * 1000;
private long checkInterval;
/**
* 通过handler, 达到按照时间间隔轮训的效果
*/
public void fetchPatchWithInterval(int hour) {
//设置TinkerPatch的时间间隔
TinkerPatch.with().setFetchPatchIntervalByHours(hour);
checkInterval = hour * HOUR_INTERVAL;
//立刻尝试去访问,检查是否有更新
sendEmptyMessage(0);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//这里使用false即可
TinkerPatch.with().fetchPatchUpdate(false);
//每隔一段时间都去访问后台, 增加10分钟的buffer时间
sendEmptyMessageDelayed(0, checkInterval + 10 * 60 * 1000);
}
}
Enfin, ajoutez les autorisations réseau et SD correspondantes à AndroidManifest.xml, ajoutez android:name=".tinkerApplication" à l'application et joignez le code :
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:name=".tinkerApplication"
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>
Le processus spécifique d'emballage et de publication des correctifs ne sera pas détaillé ici. Si vous avez des questions, veuillez contacter [email protected].
Droit d'auteur 2019, wangfeng19930909
Sous licence Apache, version 2.0 (la « Licence ») ; vous ne pouvez pas utiliser ce fichier sauf en conformité avec la licence. Vous pouvez obtenir une copie de la licence sur http://www.apache.org/licenses/LICENSE. -2.0 Sauf disposition contraire de la loi applicable ou accord écrit, le logiciel distribué sous la licence est distribué « TEL QUEL », SANS GARANTIE OU CONDITION D'AUCUNE SORTE, expresse ou implicite. Voir la licence pour le. langage spécifique régissant les autorisations et les limitations en vertu de la Licence.