1.Walle 1.1 接入 在 Project 级的 build.gradle
内加入:
build.gradle 1 2 3 4 5 dependencies { ... classpath 'com.meituan.android.walle:plugin:1.1.7' ... }
再在 app moudle 内的 build.gradle
中加入:
build.gradle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 apply from: 'walle.gradle' android { ... signingConfigs { release { keyAlias 'keyAlias' keyPassword 'keyPassword' storeFile file('storeFile path' ) storePassword 'storePassword' } } buildTypes { release { ... signingConfig signingConfigs.release ... } } } dependencies { ... implementation 'com.meituan.android.walle:library:1.1.7' ... }
app moudle 内新建一个 walle.gradle
:
walle.gradle 1 2 3 4 5 6 7 8 9 10 11 12 apply plugin: 'walle' android { walle { apkOutputFolder = new File("${project.buildDir}/outputs/channels" ) apkFileNameFormat = '${projectName}-v${versionName}-${channel}-${buildTime}.apk' channelFile = new File("${project.getProjectDir()}/channel" ) } }
同文件夹内 新建一个叫 channel
的文件,填写渠道名,每行一个,例如:
channel 1 2 3 4 5 6 7 8 9 Tencent Oppo Vivo HuaWei XiaoMi QiHu AppChina QinYu Dev
1.2 使用 使用如下命令打包:
1 2 3 4 gradlew clean assembleReleaseChannels gradlew aseembleReleaseChannels -PchannelList=渠道名
打出的渠道包在:build/outputs/channels/
中,路径可配。
2. Tinker + Bugly 2.1 接入 在 Project 级的 build.gradle
内加入:
build.gradle 1 2 3 4 5 dependencies { ... classpath "com.tencent.bugly:tinker-support:1.2.1" ... }
再在 app moudle 内的 build.gradle
中加入:
build.gradle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 apply from: 'tinker-support.gradle' android { ... defaultConfig { ... multiDexEnabled true ... } dexOptions { jumboMode = true } } dependencies { ... implementation 'com.tencent.bugly:crashreport_upgrade:1.4.5' implementation 'com.tencent.tinker:tinker-android-lib:1.9.14.9' ... }
app moudle 内新建一个 tinker-support.gradle
:
tinker-support.gradle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 apply plugin: 'com.tencent.bugly.tinker-support' def bakPath = file("${buildDir}/bakApk/" ) def baseApkDir = "app-1019-18-18-37" tinkerSupport { enable = true autoBackupApkDir = "${bakPath}" overrideTinkerPatchConfiguration = true baseApk = "${bakPath}/${baseApkDir}/app-release.apk" baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt" baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt" tinkerId = "1.0-patch-1" enableProxyApplication = false supportHotplugComponent = true } tinkerPatch { ignoreWarning = false useSign = 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" } buildConfig { keepDexApply = false } }
2.2 初始化 2.2.1 enableProxyApplication = false
这是Tinker推荐的接入方式,一定程度上会增加接入成本,但具有更好的兼容性。
在以上 tinker-support.gradle
中 enableProxyApplication = false
的情况下:
自定义 Application,:
SampleApplication.java 1 2 3 4 5 6 7 public class SampleApplication extends TinkerApplication { public SampleApplication () { super (ShareConstants.TINKER_ENABLE_ALL, "xxx.xxx.xxx.SampleApplicationLike" , "com.tencent.tinker.loader.TinkerLoader" , false , true ); } }
注意:这个类集成TinkerApplication类,这里面不做任何操作,所有Application的代码都会放到ApplicationLike继承类当中 参数解析 参数1:tinkerFlags 表示Tinker支持的类型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL 参数2:delegateClassName Application代理类 这里填写你自定义的ApplicationLike 参数3:loaderClassName Tinker的加载器,使用默认即可 参数4:tinkerLoadVerifyFlag 加载dex或者lib是否验证md5,默认为false 参数5:useDelegateLastClassLoaderOnAPI29AndAbove 在API29及以上使用 DelegateLastClassLoader
将 AndroidManifest
中 application
节点的 android:name
属性设置为该自定义 Application.
AndroidManifest.xml 1 2 3 4 <application android:name =".SampleApplication" > ...</application >
自定义 ApplicationLike :
SampleApplicationLike.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 public class SampleApplicationLike extends DefaultApplicationLike { private static final String TAG = "ApplicationLike" ; public AppLike (Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) { super (application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public void onBaseContextAttached (Context base) { super .onBaseContextAttached(base); MultiDex.install(base); Beta.installTinker(this ); } @Override public void onCreate () { super .onCreate(); Beta.betaPatchListener = new BetaPatchListener () { @Override public void onPatchReceived (String patchFile) { Log.d(TAG, "补丁下载地址: " + patchFile); } @Override public void onDownloadReceived (long savedLength, long totalLength) { Log.d(TAG, String.format(Locale.getDefault(), "%s %d%%" , Beta.strNotificationDownloading, (int ) (totalLength == 0 ? 0 : savedLength * 100 / totalLength))); } @Override public void onDownloadSuccess (String msg) { Log.d(TAG, "补丁下载成功: " + msg); } @Override public void onDownloadFailure (String msg) { Log.d(TAG, "补丁下载失败: " + msg); } @Override public void onApplySuccess (String msg) { Log.d(TAG, "补丁应用成功: " + msg); } @Override public void onApplyFailure (String msg) { Log.e(TAG, "补丁应用失败: " + msg); } @Override public void onPatchRollback () { Log.d(TAG, "补丁回滚" ); } }; Bugly.setIsDevelopmentDevice(getApplication(), true ); Bugly.init(getApplication(), "BUGLY_APP_ID" , true ); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public void registerActivityLifecycleCallback (Application.ActivityLifecycleCallbacks callbacks) { getApplication().registerActivityLifecycleCallbacks(callbacks); } @Override public void onTerminate () { super .onTerminate(); Beta.unInit(); } }
注意:Tinker 需要开启 MultiDex; SampleApplicationLike 这个类是 Application 的代理类,以前所有在 Application 的实现必须要全部拷贝到这里,在 onCreate
方法调用SDK的初始化方法,在 onBaseContextAttached
中调用 Beta.installTinker(this);
。
2.2.2 enableProxyApplication = true App.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class App extends Application { @Override public void onCreate () { super .onCreate(); Bugly.init(this , "BUGLY_APP_ID" , true ); } @Override protected void attachBaseContext (Context base) { super .attachBaseContext(base); MultiDex.install(base); Beta.installTinker(); } }
注:无须你改造Application,主要是为了降低接入成本,我们插件会动态替换 AndroidMinifest 文件中的 Application 为我们定义好用于反射真实Application的类(需要您接入SDK 1.2.2版本 和 插件版本 1.0.3 以上)。
2.3 AndroidManifest 配置 2.3.1 权限配置 AndroidManifest.xml 1 2 3 4 5 6 <uses-permission android:name ="android.permission.READ_PHONE_STATE" /> <uses-permission android:name ="android.permission.INTERNET" /> <uses-permission android:name ="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name ="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name ="android.permission.READ_LOGS" /> <uses-permission android:name ="android.permission.WRITE_EXTERNAL_STORAGE" />
2.3.2 Actvity 配置 AndroidManifest.xml 1 2 3 4 <activity android:name ="com.tencent.bugly.beta.ui.BetaActivity" android:configChanges ="keyboardHidden|orientation|screenSize|locale" android:theme ="@android:style/Theme.Translucent" />
2.3.3 配置 FileProvider
注意:如果您想兼容 Android N
或者以上的设备,必须要在 AndroidManifest.xml
文件中配置 FileProvider
来访问共享路径的文件。
AndroidManifest.xml 1 2 3 4 5 6 7 8 9 <provider android:name ="androidx.core.content.FileProvider" android:authorities ="${applicationId}.fileProvider" android:exported ="false" android:grantUriPermissions ="true" > <meta-data android:name ="android.support.FILE_PROVIDER_PATHS" android:resource ="@xml/provider_paths" /> </provider >
如果你使用的第三方库也配置了同样的 FileProvider
, 可以通过继承 FileProvider
类来解决合并冲突的问题,示例如下:
AndroidManifest.xml 1 2 3 4 5 6 7 8 9 10 11 <provider android:name =".utils.BuglyFileProvider" android:authorities ="${applicationId}.fileProvider" android:exported ="false" android:grantUriPermissions ="true" tools:replace ="name,authorities,exported,grantUriPermissions" > <meta-data android:name ="android.support.FILE_PROVIDER_PATHS" android:resource ="@xml/provider_paths" tools:replace ="name,resource" /> </provider >
在 res
目录新建 xml
文件夹,创建 provider_paths.xml
文件如下:
provider_paths.xml 1 2 3 4 5 6 7 <?xml version="1.0" encoding="utf-8" ?> <paths xmlns:android ="http://schemas.android.com/apk/res/android" > <external-path name ="beta_external_path" path ="Download/" /> <external-path name ="beta_external_files_path" path ="Android/data/" /> </paths >
注:Tinker 1.3.1 及以上版本,可以不用进行以上配置,aar 已经在 AndroidManifest 配置了,并且包含了对应的资源文件。
2.4 使用 首先设置 tinker-support.gradle
中的 tinkerId
,要确保 tinkerId
的唯一性,且不要与 App 版本号相同,使用 Walle 打包的同时,也会在 bakApk 里生成 基准包,如果发布之后发现Bug,则修改 tinkerId
,将 tinker-support.gradle
中的 baseApkDir
修改为 bakApk 文件夹中 基准包 的文件夹名,然后使用
1 gralew buildTinkerPatchRelease
打出补丁,补丁文件在 app\build\outputs\patch\release
文件夹内,再使用 Bugly 后台上传,官方文档
3.踩过的坑 Bugly 的 Tinker Support 目前还不兼容 build tool 3.2.0 以上的版本,gradle 版本 4.6 以上也不兼容,否则会报各种错误
1 2 3 classpath 'com.android.tools.build:gradle:3.2.0' distributionUrl=https\:
3.2 Java 8 语法 同时如果项目的编译语法设置为 Java 8 的话,会抛出如下错误:
1 Java 8 language support, as requested by 'android.enableD8.desugaring= true' in your gradle.properties file, is not supported when 'android.useDexArchive= false'.
需要在 Project Properties
级的 gradle.properties
中加入:
1 2 android.enableD8.desugaring=false android.useDexArchive= true
4. Demo 地址 Github
基准包在 base 分支,修复包在 patch 分支
更新 2020.11.07 :
根据 tinker 官方 github issue 中 其他人的配置:
1 2 3 4 5 6 classpath 'com.android.tools.build:gradle:3.4.1' distributionUrl=https\: implementation 'com.tencent.bugly:crashreport_upgrade:1.4.5' implementation 'com.tencent.tinker:tinker-android-lib:1.9.14.5'
这样配置解决了 build gradle tool 版本过低导致 Java 8 语法的问题
app minSdkVersion 低于19 ,multiDexEnabled = true
的时候,如果报出 xxxxloader.class 之类的不在 mianDex 时,需要手动去设置 mainDexFile,将之前报的 class 手动分配到 mainDex 中
app minSdkVersion >= 21 时,打补丁包同样会报错,因为 google 在 api >= 21 之后 mainDexFile 已经不生效了,这时需要在 tinker-support.gradle
中加入
tinker-support.gradle 1 2 3 4 5 6 7 tinkerSupport { ... ignoreWarning = true ... }