集成腾讯bugly的热修复功能sdk步骤
来源:互联网 发布:百度还有哪些软件 编辑:程序博客网 时间:2024/05/02 10:01
首先为什么要集成bugly热修复。市面上有其他的热修复框架,为什么就用bugly?这里给出2张图大家就明白了。
引用腾讯bugly官网的一段话:
- 无需关注Tinker是如何合成补丁的
- 无需自己搭建补丁管理后台
- 无需考虑后台下发补丁策略的任何事情
- 无需考虑补丁下载合成的时机,处理后台下发的策略
- 我们提供了更加方便集成Tinker的方式
- 我们提供应用升级一站式解决方案
- 打基准包安装并上报联网(注:填写唯一的tinkerId)
- 对基准包的bug修复(可以是Java代码变更,资源的变更)
- 修改基准包路径、填写补丁包tinkerId、mapping文件路径、resId文件路径
- 执行tinkerPatchRelease打Release版本补丁包
- 选择app/build/outputs/patch目录下的补丁包并上传(注:不要选择tinkerPatch目录下的补丁包,不然上传会有问题)
- 编辑下发补丁规则,点击立即下发
- 重启基准包,请求补丁策略(SDK会自动下载补丁并合成)
- 再次重启基准包,检验补丁应用结果
1:新建基准包工程项目(人为制造有BUG的app版本)
btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) {// String str = LoadBugClass.getBugString(); String str = BugClass.bug(); Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();; } });
public class BugClass { public static String bug(){ String str = null; int str_length = str.length(); return "this is bug class"; }}这个可以看出点击一个按钮会报空指针异常。
2:接着就是配置相关属性和添加一个插件依赖了。
官方教程地址:点击打开链接
下面也给出我自己配置的过程。
首先在最外层的build.gradle文件中添加依赖,看下图:
其次新建sampleapplication和sampleapplicationLike两个java类
package com.henry.testappbugly;import android.annotation.TargetApi;import android.app.Application;import android.content.Context;import android.content.Intent;import android.content.res.AssetManager;import android.content.res.Resources;import android.os.Build;import android.support.multidex.MultiDex;import com.tencent.bugly.Bugly;import com.tencent.bugly.beta.Beta;import com.tencent.tinker.loader.app.DefaultApplicationLike;/** * Created by W61 on 2016/11/29. */public class SampleApplicationLike extends DefaultApplicationLike { public static final String TAG = "Tinker.SampleApplicationLike"; public SampleApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent, Resources[] resources, ClassLoader[] classLoader, AssetManager[] assetManager) { super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent, resources, classLoader, assetManager); } @Override public void onCreate() { super.onCreate(); // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId Bugly.init(getApplication(), "", true); } @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); // 安装tinker // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法 Beta.installTinker(this); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) { getApplication().registerActivityLifecycleCallbacks(callbacks); }}
package com.henry.testappbugly;import com.tencent.tinker.loader.app.TinkerApplication;import com.tencent.tinker.loader.shareutil.ShareConstants;/** * Created by W61 on 2016/11/29. */public class SampleApplication extends TinkerApplication { public SampleApplication() { super(ShareConstants.TINKER_ENABLE_ALL, "SampleApplicationLike所在的包名路径", "com.tencent.tinker.loader.TinkerLoader", false); }}
在在Androidmanifest.xml文件中配置权限及application类名
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.henry.testappbugly"> <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> <!--API 24以上配置--> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.tencent.bugly.hotfix.fileProvider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/> </provider> </application> <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" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/></manifest>
在到res目录下:
<?xml version="1.0" encoding="utf-8"?><paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 这里配置的两个外部存储路径是升级SDK下载的文件可能存在的路径 --> <!-- /storage/emulated/0/Download/com.bugly.upgrade.demo/.beta/apk--> <external-path name="beta_external_path" path="Download/"/> <!--/storage/emulated/0/Android/data/com.bugly.upgrade.demo/files/apk/--> <external-path name="beta_external_files_path" path="Android/data/"/></paths>
# you can copy the tinker keep rule at# build/intermediates/tinker_intermediates/tinker_multidexkeep.pro-keep class com.tencent.tinker.loader.** { *;}-keep class com.tencent.bugly.hotfix.SampleApplication { *;}-keep public class * implements com.tencent.tinker.loader.app.ApplicationLifeCycle { *;}-keep public class * extends com.tencent.tinker.loader.TinkerLoader { *;}-keep public class * extends com.tencent.tinker.loader.app.TinkerApplication { *;}# here, it is your own keep rules.# you must be careful that the class name you write won't be proguard# but the tinker class above is OK, we have already keep for you!
然后在混淆文件.pro中添加这几句代码(bugly都有说明解释)
-dontwarn com.tencent.bugly.**-keep public class com.tencent.bugly.**{*;}
最后就是app目录下的build.gradle文件配置了:
apply plugin: 'com.android.application'dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.android.support:appcompat-v7:24.1.1' // 多dex配置 compile "com.android.support:multidex:1.0.1" // 集成Bugly热更新aar(灰度时使用方式)// compile(name: 'bugly_crashreport_upgrade-1.2.0', ext: 'aar') compile "com.tencent.bugly:crashreport_upgrade:1.2.0"}android { compileSdkVersion 23 buildToolsVersion "23.0.2" // 编译选项 compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } // recommend dexOptions { jumboMode = true } // 签名配置 signingConfigs { // 签名配置 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") } } } defaultConfig { applicationId "com.henry.testappbugly" minSdkVersion 14 targetSdkVersion 23 versionCode 2 versionName "2.0" // 开启multidex multiDexEnabled true // 以Proguard的方式手动加入要放到Main.dex中的类 multiDexKeepProguard file("keep_in_main_dex.txt") } buildTypes { release { minifyEnabled true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { debuggable true minifyEnabled false signingConfig signingConfigs.debug } } sourceSets { main { jniLibs.srcDirs = ['libs'] } } repositories { flatDir { dirs 'libs' } } lintOptions { checkReleaseBuilds false abortOnError false }}def gitSha() { try { String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim() if (gitRev == null) { throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'") } return gitRev } catch (Exception e) { throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'") }}def bakPath = file("${buildDir}/bakApk/")/** * you can use assembleRelease to build you base apk * use tinkerPatchRelease -POLD_APK= -PAPPLY_MAPPING= -PAPPLY_RESOURCE= to build patch * add apk from the build/bakApk */ext { // for some reason, you may want to ignore tinkerBuild, such as instant run debug build? tinkerEnabled = true // for normal build // old apk file to build patch apk tinkerOldApkPath = "${bakPath}/app-release-1201-09-46-25.apk" // proguard mapping file to build patch apk tinkerApplyMappingPath = "${bakPath}/app-release-1201-09-46-25-mapping.txt" // resource R.txt to build patch apk, must input if there is resource changed tinkerApplyResourcePath = "${bakPath}/app-release-1201-09-46-25-R.txt" // only use for build all flavor, if not, just ignore this field tinkerBuildFlavorDirectory = "${bakPath}/app-release-1201-09-46-25"}def getOldApkPath() { return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath}def getApplyMappingPath() { return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath}def getApplyResourceMappingPath() { return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath}def getTinkerIdValue() { return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()}def buildWithTinker() { return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled}def getTinkerBuildFlavorDirectory() { return ext.tinkerBuildFlavorDirectory}/** * 更多Tinker插件详细的配置,参考:https://github.com/Tencent/tinker/wiki */if (buildWithTinker()) { // 依赖tinker插件 apply plugin: 'com.tencent.tinker.patch' apply plugin: 'com.tencent.bugly.tinker-support' tinkerSupport { } // 全局信息相关配置项 tinkerPatch { oldApk = getOldApkPath() //必选, 基准包路径 ignoreWarning = false // 可选,默认false useSign = true // 可选,默认true, 验证基准apk和patch签名是否一致 // 编译相关配置项 buildConfig { applyMapping = getApplyMappingPath() // 可选,设置mapping文件,建议保持旧apk的proguard混淆方式 applyResourceMapping = getApplyResourceMappingPath() // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配 tinkerId = "可以是签名版本号字符串等等比如:assdhfkdshfksdhfuksfhuk" // 必选,默认为null } // dex相关配置项 dex { dexMode = "jar" // 可选,默认为jar usePreGeneratedPatchDex = true // 可选,默认为false pattern = ["classes*.dex", "assets/secondary-dex-?.jar"] // 必选 loader = ["com.tencent.tinker.loader.*", "SampleApplication所在的全路径", ] } // lib相关的配置项 lib { pattern = ["lib/armeabi/*.so"] } // res相关的配置项 res { pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] ignoreChange = ["assets/sample_meta.txt"] largeModSize = 100 } // 用于生成补丁包中的'package_meta.txt'文件 packageConfig { configField("patchMessage", "tinker is sample to use") configField("platform", "all") configField("patchVersion", "1.0") } // 7zip路径配置项,执行前提是useSign为true sevenZip { zipArtifact = "com.tencent.mm:SevenZip:1.1.10" // optional // path = "/usr/local/bin/7za" // optional } } List<String> flavors = new ArrayList<>(); project.android.productFlavors.each { flavor -> flavors.add(flavor.name) } boolean hasFlavors = flavors.size() > 0 /** * bak apk and mapping */ android.applicationVariants.all { variant -> /** * task type, you want to bak */ def taskName = variant.name def date = new Date().format("MMdd-HH-mm-ss") tasks.all { if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) { it.doLast { copy { def fileNamePrefix = "${project.name}-${variant.baseName}" def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}" def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath from variant.outputs.outputFile into destPath rename { String fileName -> fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk") } from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt" into destPath rename { String fileName -> fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt") } from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt" into destPath rename { String fileName -> fileName.replace("R.txt", "${newFileNamePrefix}-R.txt") } } } } } } project.afterEvaluate { //sample use for build all flavor for one time if (hasFlavors) { task(tinkerPatchAllFlavorRelease) { group = 'tinker' def originOldPath = getTinkerBuildFlavorDirectory() for (String flavor : flavors) { def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release") dependsOn tinkerTask def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest") preAssembleTask.doFirst { String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15) project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk" project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt" project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt" } } } task(tinkerPatchAllFlavorDebug) { group = 'tinker' def originOldPath = getTinkerBuildFlavorDirectory() for (String flavor : flavors) { def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug") dependsOn tinkerTask def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest") preAssembleTask.doFirst { String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13) project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk" project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt" project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt" } } } } }}
最后run as生成有bug的基准包app
在去腾讯bugly官网将这个基准包上传上去即可。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
接下来,制作补丁包。
由于刚才点击按钮报空指针,下面将代码稍做改动如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String str = LoadBugClass.getBugString();// String str = BugClass.bug(); Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();; } }); }
public class LoadBugClass { /** * 获取bug字符串. * * @return 返回bug字符串 */ public static String getBugString() {// BugClass bugClass = new BugClass(); return "iS OK"; }}这样点击按钮就会弹出is ok了不会报错。
修改配置文件:
这里注意点,补丁包是基于基准包所生成的patch文件并不是版本升级,所以此处补丁包不需要修改versioncode,versionname
然后双击下图中所指地方:
稍等片刻就会出现下图中类容:
其中的patch_signed_7zip.apk就是补丁包了。将这个补丁包上传到腾讯bugly即可。
注意:上传完补丁包点击了立即下发,就需要重新启动基准包策略。从有bug版本的app到修复有一个时间差的。估计1到2分钟左右才能看到效果。
附上集成过程中可能遇到的坑解决办法地址:点击打开链接
最后附上自己写的demo地址:点击打开链接
- 集成腾讯bugly的热修复功能sdk步骤
- 集成腾讯bugly的热修复功能sdk步骤
- 集成腾讯bugly的热修复功能sdk步骤
- 腾讯bugly的热修复功能集成笔记
- 腾讯bugly热修复集成工程
- 腾讯Bugly热更新的集成实现
- Android热修复--腾讯bugly
- Android热修复应用篇--关于腾讯Bugly的使用
- 安卓集成腾讯bugly里的热更新
- Android中第三方SDK集成之腾讯Bugly热更新集成指南
- Bugly热修复 Android SDK接入
- 腾讯Bugly热更新集成总结
- 腾讯Bugly热更新集成以及问题
- 腾讯Bugly的简单集成
- 腾讯的热修复
- 腾讯bugly-微信tinker热修复快速接入
- 热修复的两个框架Bugly+Sophix
- 腾讯云SDK 与 腾讯Bugly集成冲突记录
- MUI之父页面刷新
- sql语法归纳
- laravel5.2 文件上传
- docker registry2 仓库搭建与使用
- 深入理解final关键字
- 集成腾讯bugly的热修复功能sdk步骤
- 第13周项目1-Prim算法的验证
- SDWebImage的内存泄漏
- 数据库中关联主子表相对于润乾填报表的设置方法
- 实例详解Spring MVC入门使用
- Junit单元测试(java unit)
- JavaScript 最佳实践:帮你提升代码质量
- 121. Best Time to Buy and Sell Stock
- 孤独的根号三