LZ-Says:新的一年,新的开始~ 越是在凌乱的时候,越要坚持住自己!!!

前言

年前的时候,就已经关注了有关热修复,热更新的知识,本想着项目当中多少应用一些,可现实残酷至极~

今天,先从腾讯Bugly开刀,一起来学习了解腾讯的Bugly是如何操作~

当然感谢老妖的推荐~ 预祝在鹅厂实现人生巅峰~

本文目标

通过官方文档,以及亲自测试,希望大家从过程中能掌握Bugly使用,一起学习~

Hi,Bugly

百度搜索:腾讯Bugly,点击进入官网:

下面引入腾讯官方对Bugly简述:

腾讯Bugly,为移动开发者提供专业的异常上报和运营统计,帮助开发者快速发现并解决异常,同时掌握产品运营动态,及时跟进用户反馈。

点击下方立即接入:

当然你可以点击下面的查看Demo,简单操作一下:

而LZ这里选择:立即接入,(首次需要创建一个产品,和其他三方操作一致)

依次填入相应信息后点击保存:

成功之后显示如下:

到此,我们可以看到Bugly SDK 提供我们三个使用范围:

  • 异常上报;

  • 运营统计;

  • 版本升级 (重点)

同时,也可以关注Bugly公众号,原因嘛:

  1. 及时查看每周的精彩牛文;
  2. 快速查看您负责产品的数据,比如:日报、趋势等;
  3. 接收异常告警,第一时间掌握产品突发状况

下面分别对提供的三种方式进行使用以及实验。

一、异常上报

点击“异常上报”,选择SDK 包 2.6.6的使用指南:

我们先来看下关于异常上报平台功能介绍:

应用集成SDK后,即可在Web站点查看应用上报的崩溃数据和联网数据。

  • 异常概览 查看今日实时统计、崩溃趋势、崩溃排行和TOP20崩溃问题等信息;

  • 崩溃分析/卡顿分析/错误分析 查看上报问题的列表;

  • 问题详情 查看上报问题的详细信息;

  • 高级搜索 通过各种条件快速查找需要定位分析的异常

Bugly 提供俩种方式进行集成:

  • SDK 集成;

  • 远程依赖

相比俩种集成方案,LZ这里选择远程依赖,简单方便快捷,何乐不为?

LZ操作步骤如下:

1.1 添加远程依赖:

    // 其中latest.release指代最新Bugly SDK版本号,也可以指定明确的版本号,例如2.1.9compile 'com.tencent.bugly:crashreport:latest.release'// 其中latest.release指代最新Bugly NDK版本号,也可以指定明确的版本号,例如3.0compile 'com.tencent.bugly:nativecrashreport:latest.release'

1.2 设置NDK支持架构:

    ndk {// 设置支持的SO库架构abiFilters 'armeabi', 'arm64-v8a' //, 'x86', 'armeabi-v7a', 'x86_64'}

1.3 点击Sync,同步配置。

1.4 添加权限

    <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" />

如果老帖你搞了混淆操作,为了避免混淆Bugly,在Proguard混淆文件中增加以下配置:

-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}

1.5 关于初始化,Bugly提供了俩种方式:

1.5.1 简单初始化

获取APP ID并将以下代码复制到项目Application类onCreate()中,Bugly会为自动检测环境并完成配置:

CrashReport.initCrashReport(getApplicationContext(), "注册时申请的APPID", true);
1.5.2 AndroidManifest+代码配置

Bugly2.0及以上版本还支持通过“AndroidManifest.xml”来配置APP信息。

如果同时又通过代码中配置了APP信息,则最终以代码配置的信息为准。

下面直接从官方地址贴出关键代码:

<application
    <!-- 配置APP ID --><meta-data
            android:name="BUGLY_APPID"android:value="<APP_ID>" /><!-- 配置APP版本号 --><meta-data
            android:name="BUGLY_APP_VERSION"android:value="<APP_Version>" /><!-- 配置APP渠道号 --><meta-data
            android:name="BUGLY_APP_CHANNEL"android:value="<APP_Channel>" /><!-- 配置Bugly调试模式(true或者false)--><meta-data
            android:name="BUGLY_ENABLE_DEBUG"android:value="<isDebug>" />
</application>

设置了AndroidManifest配置参数后,则初始化需要调用如下:

CrashReport.initCrashReport(getApplicationContext());

1.6 需要注意

为了保证运营数据的准确性,建议不要在异步线程初始化Bugly。

第三个参数为SDK调试模式开关,调试模式的行为特性如下:

  • 输出详细的Bugly SDK的Log;

  • 每一条Crash都会被立即上报;

  • 自定义日志将会在Logcat中输出。

建议在测试阶段建议设置成true,发布时设置为false。

1.7 增加上报进程控制

这里再次引用官方描述:

如果App使用了多进程且各个进程都会初始化Bugly(例如在Application类onCreate()中初始化Bugly),那么每个进程下的Bugly都会进行数据上报,造成不必要的资源浪费。

因此,为了节省流量、内存等资源,建议初始化的时候对上报进程进行控制,只在主进程下上报数据:判断是否是主进程(通过进程名是否为包名来判断),并在初始化Bugly时增加一个上报进程的策略配置。

So,修改后的BaseApplication初始化方式如下:

    private boolean mIsDebug = true;@Overridepublic void onCreate() {super.onCreate();// 初始化Bugly异常上报initBugly();}private void initBugly() {Context context = getApplicationContext();// 获取当前包名String packageName = context.getPackageName();// 获取当前进程名String processName = getProcessName(android.os.Process.myPid());// 设置是否为上报进程CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(context);strategy.setUploadProcess(processName == null || processName.equals(packageName));// 初始化BuglyCrashReport.initCrashReport(context, "70ecd90765", mIsDebug, strategy);}/*** 获取进程号对应的进程名** @param pid 进程号* @return 进程名*/private static String getProcessName(int pid) {BufferedReader reader = null;try {reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline"));String processName = reader.readLine();if (!TextUtils.isEmpty(processName)) {processName = processName.trim();}return processName;} catch (Throwable throwable) {throwable.printStackTrace();} finally {try {if (reader != null) {reader.close();}} catch (IOException exception) {exception.printStackTrace();}}return null;}

下面,我们搞一个崩溃的玩玩,看看Bugly有木有这么神奇~

int i = 10 / 0;

运行apk,发现奔溃,接着我们查看Bugly后台是否接收到这个异常呢?

嗯哼,不赖~

点击进去查看详情,看看这究竟还会有哪儿些意外惊喜?

比某盟强忒多了~!!!人性化哈,有木有???

我们接着看,看看他还有什么新奇的玩意???

还有?

一个字,好
二个字,很棒
三个字,忒TM6
。。。 。。。

相比某盟,Bugly这点让人很是nice~

再搞个异常出来试试:

    int[] nums = {1, 2, 3};int num = nums[5];

再来看看结果:


很不错~哇咔咔~~~LZ准备以后就用它了,这么人性化,这么棒的东西~

二、运营统计

经过上面的异常上报,LZ对运营统计这块也是有了很大的兴趣,虽说不懂运营,但是也要看看它提供的运营统计又是如何展示?

MMP,转悠了半天,发现貌似只能显示昨天的。。。

好尴尬~

但是,可以显示一些基本信息,如下:

三、版本升级 全量升级 (重点一)

哈哈,终于等到你~~~

重点来了,比较感兴趣的也终于来了~

操作流程如下:

3.1 引入依赖

    // 版本升级compile 'com.tencent.bugly:crashreport_upgrade:latest.release'
自动集成时会自动包含Bugly SO库

3.2 设置支持的SO库架构

    ndk {// 设置支持的SO库架构abiFilters 'armeabi', 'arm64-v8a' //, 'x86', 'armeabi-v7a', 'x86_64'}

3.3 配置权限

    <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" />

3.4 兼容高版本

这块可忽略,引用官方描述:

1.3.1及以上版本,可以不用进行以上配置,aar已经在AndroidManifest配置了,并且包含了对应的资源文件。

    <provider
        android:name="android.support.v4.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>

在res下定义xml目录,随后创建:provider_paths.xml文件。文件里内容如下:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android"><!-- /storage/emulated/0/Download/${applicationId}/.beta/apk--><external-path name="beta_external_path" path="Download/"/><!--/storage/emulated/0/Android/data/${applicationId}/files/apk/--><external-path name="beta_external_files_path" path="Android/data/"/>
</paths>

3.5 统一初始化方式

    /*** 初始化 Bugly*/private void initBugly() {
//        参数1:上下文对象
//        参数2:注册时申请的APPID
//        参数3:是否开启debug模式,true表示打开debug模式,false表示关闭调试模式Bugly.init(getApplicationContext(), "70ecd90765", true);}

提示:已经接入Bugly用户改用上面的初始化方法,不影响原有的crash上报功能; init方法会自动检测更新,不需要再手动调用Beta.checkUpgrade(), 如需增加自动检查时机可以使用Beta.checkUpgrade(false,false);

  • 参数1:isManual 用户手动点击检查,非用户点击操作请传false

  • 参数2:isSilence 是否显示弹窗等交互,[true:没有弹窗和toast] [false:有弹窗或toast]

基本配置完成后,我们进入后台管理界面进行升级策略编辑:

3.6 使用

首先,我们需要上传更新的apk包:

上传完成后,进行升级策略编辑(在这里,你会深刻体会到Bugly的人性化):

下面简单介绍下:

  • 升级方式: 1.推荐升级(可不升级); 2.强制升级

  • 策略下发条件:

    • 指定源版本:可以选择指定的版本进行升级,也可以通知所有已发版本进行升级;

    • 升级渠道:LZ猜测应该是APK下发渠道;

    • 网络环境:指定在某种特定网络环境下进行升级,也可以是所有环境

  • 策略启动条件: 1.立即启动; 2.手动启动; 3.定时启动

  • 策略停止条件:(这块可以进行灰度测试时,随机通知固定用户数进行测试)

    • 定时停止;

    • 下发上线人数;

    • 激活上线人数

  • 自动弹窗规则: 1.总弹窗次数; 2.弹窗间隔时间

  • 最重要的便是弹框样式,默认提供三种固定样式,当然你也可以自定义弹框样式!!!

编辑完成之后,如下:

启动应用,稍等一下:

这里充分说明的选的图片一定要小,一定要小,一定要小!!!不然图片位置一直显示loading。。。

点击立即更新后,立即更新变成当前下载进度。下载完成后自动安装:

简单的使用,相信大家已经胸有成竹,下面进入目前阶段的知识问答阶段~

Issue 1: 我想飞,能带我飞么?

  • 你想怎么飞?

  • 我想怎么设置就怎么设置,如下:

    • 设置自动初始化

    • 设置开关自动检查

    • 设置升级检查周期

    • 设置初始化延迟

    • 设置通知栏图标

    • 设置更新弹窗bannner图

    • 设置更新资源存储目录

    • 设置开启显示打断策略

    • 设置自定义UI

    • 设置升级对话框生命周期回调

  • 小Case~瞧好吧~~~

将BaseApplication初始化Bugly替换如下方法:

    private void initHeightBugly() {/*** true表示app启动自动初始化升级模块;* false不会自动初始化;* 开发者如果担心sdk初始化影响app启动速度,可以设置为false,* 在后面某个时刻手动调用Beta.init(getApplicationContext(),false);*/Beta.autoInit = true;/*** true表示初始化时自动检查升级;* false表示不会自动检查升级,需要手动调用Beta.checkUpgrade()方法;*/Beta.autoCheckUpgrade = true;/*** 设置升级检查周期为60s(默认检查周期为0s),60s内SDK不重复向后台请求策略);*/Beta.upgradeCheckPeriod = 60 * 1000;/*** 设置启动延时为1s(默认延时3s),APP启动1s后初始化SDK,避免影响APP启动速度;*/Beta.initDelay = 1 * 1000;/*** 设置通知栏大图标,largeIconId为项目中的图片资源;*/Beta.largeIconId = R.drawable.hlq_img;/*** 设置状态栏小图标,smallIconId为项目中的图片资源Id;*/Beta.smallIconId = R.drawable.img;/*** 设置更新弹窗默认展示的banner,defaultBannerId为项目中的图片资源Id;* 当后台配置的banner拉取失败时显示此banner,默认不设置则展示“loading“;*/Beta.defaultBannerId = R.drawable.timg;/*** 设置sd卡的Download为更新资源保存目录;* 后续更新资源会保存在此目录,需要在manifest中添加WRITE_EXTERNAL_STORAGE权限;*/Beta.storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);/*** 点击过确认的弹窗在APP下次启动自动检查更新时会再次显示;*/Beta.showInterruptedStrategy = true;/*** 只允许在MainActivity上显示更新弹窗,其他activity上不显示弹窗;* 不设置会默认所有activity都可以显示弹窗;*/Beta.canShowUpgradeActs.add(MainActivity.class);/***** 统一初始化Bugly产品,包含Beta *****/Bugly.init(this, APP_ID, true);}

再次启动app查看效果:

Issue 2: 我想设置手动检测更新,怎么破?

一般来说,LZ建议,可以设置自动弹框次数,然后搭配手动检测版本更新。这样比较合理点。

那么下面的例子,只是简单禁用了启动自动更新,然后配合用户手动点击检测更新。嗯,就是这样~

首先,需要设置自动更新为false,也就是不自动更新:

Beta.autoCheckUpgrade = false;

接着,通过Button的点击事件,进行手动调用检测更新方法:

    public void getUpdateVersion(View view) {// 手动检测更新Beta.checkUpgrade();}

查看效果:

Issue 3: 阳阳说,捎带着来个详情呗。

    private void getUpdateVersionInfo() {if (mBtnID == null)return;/***** 获取升级信息 *****/UpgradeInfo upgradeInfo = Beta.getUpgradeInfo();if (upgradeInfo == null) {mBtnID.setText("无升级信息");return;}StringBuilder info = new StringBuilder();info.append("id: ").append(upgradeInfo.id).append("\n");info.append("标题: ").append(upgradeInfo.title).append("\n");info.append("升级说明: ").append(upgradeInfo.newFeature).append("\n");info.append("versionCode: ").append(upgradeInfo.versionCode).append("\n");info.append("versionName: ").append(upgradeInfo.versionName).append("\n");info.append("发布时间: ").append(upgradeInfo.publishTime).append("\n");info.append("安装包Md5: ").append(upgradeInfo.apkMd5).append("\n");info.append("安装包下载地址: ").append(upgradeInfo.apkUrl).append("\n");info.append("安装包大小: ").append(upgradeInfo.fileSize).append("\n");info.append("弹窗间隔(ms): ").append(upgradeInfo.popInterval).append("\n");info.append("弹窗次数: ").append(upgradeInfo.popTimes).append("\n");info.append("发布类型(0:测试 1:正式): ").append(upgradeInfo.publishType).append("\n");info.append("弹窗类型(1:建议 2:强制 3:手工): ").append(upgradeInfo.upgradeType);mBtnID.setText(info);}

查看效果:

Issue 4: UI天马星空,非要个性的升级框,怎么破?

比如说,要实现下面这个效果,肿么破呢?(PS:不能嫌弃哦~~~)

腾讯的贴心小伙伴当然为我们想到了喽:

1. 在BaseApplication初始化的时候,设置如下:

Beta.upgradeDialogLayoutId = R.layout.upgrade_dialog;
Beta.strUpgradeDialogInstallBtn="立即更新";
Beta.strUpgradeDialogCancelBtn="";

2. 创建upgrade_dialog文件,依据官方文档进行设置:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#8000"android:gravity="center"android:orientation="vertical"><ImageView
        android:id="@+id/imageview"android:layout_width="280dp"android:layout_height="140dp"android:scaleType="fitXY"android:src="@drawable/update_title" /><android.support.v7.widget.LinearLayoutCompat
        android:layout_width="280dp"android:layout_height="wrap_content"android:background="#fff"android:orientation="vertical"><TextView
            android:layout_width="match_parent"android:layout_height="wrap_content"android:ellipsize="end"android:maxLines="1"android:padding="10dp"android:tag="beta_title"android:textColor="#273238"android:textSize="18sp"tools:text="title" /><View
            android:layout_width="match_parent"android:layout_height="1px"android:background="#99273238" /><ScrollView
            android:layout_width="wrap_content"android:layout_height="wrap_content"android:overScrollMode="never"android:padding="6dp"android:scrollbars="none"><android.support.v7.widget.LinearLayoutCompat
                android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><!-- 【必设】升级信息控件tag:beta_upgrade_info--><TextView
                    android:layout_width="match_parent"android:layout_height="wrap_content"android:tag="beta_upgrade_info"android:text="info"android:textColor="#757575"android:textSize="14sp" /><TextView
                    android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingTop="8dp"android:text="@string/strUpgradeDialogFeatureLabel"android:textColor="#273238"android:textSize="14sp" /><!-- 【必设】更新属性控件tag:beta_upgrade_feature--><TextView
                    android:layout_width="match_parent"android:layout_height="wrap_content"android:tag="beta_upgrade_feature"android:text="feature"android:textColor="#273238"android:textSize="14sp" /></android.support.v7.widget.LinearLayoutCompat></ScrollView><!-- 【必设】确认按钮tag:beta_confirm_button--><TextView
            android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#ff00"android:ellipsize="end"android:gravity="center"android:padding="10dp"android:tag="beta_confirm_button"android:textColor="#273238"android:textSize="16sp"android:textStyle="bold" /></android.support.v7.widget.LinearLayoutCompat><!-- 【必设】取消按钮tag:beta_cancel_button--><TextView
        android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:background="@android:drawable/ic_menu_close_clear_cancel"android:tag="beta_cancel_button"android:visibility="gone" /></LinearLayout>

然后,运行一波瞅瞅~

Issue 5: 小伙子说了,我不想用腾讯的,也不想仅仅自定义提示框,我想自己玩,怎么破?

文末底部附上Bugly SDK下载地址以及关于自定义Activity官方链接:

https://bugly.qq.com/docs/user-guide/advance-features-android-beta/?v=20180119105842#2activity

嘿嘿嘿,小伙伴自己玩去吧~很是easy~

这里简单附上官方demo关键代码:

package com.bugly.upgrade.demo;import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;import com.tencent.bugly.beta.Beta;
import com.tencent.bugly.beta.download.DownloadListener;
import com.tencent.bugly.beta.download.DownloadTask;/*** 自定义Activity.*/
public class UpgradeActivity extends Activity {private TextView tv;private TextView title;private TextView version;private TextView size;private TextView time;private TextView content;private Button cancel;private Button start;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_upgrade);tv = getView(R.id.tv);title = getView(R.id.title);version = getView(R.id.version);size = getView(R.id.size);time = getView(R.id.time);content = getView(R.id.content);cancel = getView(R.id.cancel);start = getView(R.id.start);updateBtn(Beta.getStrategyTask());tv.setText(tv.getText().toString() + Beta.getStrategyTask().getSavedLength() + "");title.setText(title.getText().toString() + Beta.getUpgradeInfo().title);version.setText(version.getText().toString() + Beta.getUpgradeInfo().versionName);size.setText(size.getText().toString() + Beta.getUpgradeInfo().fileSize + "");time.setText(time.getText().toString() + Beta.getUpgradeInfo().publishTime + "");content.setText(Beta.getUpgradeInfo().newFeature);start.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {DownloadTask task = Beta.startDownload();updateBtn(task);if (task.getStatus() == DownloadTask.DOWNLOADING) {finish();}}});cancel.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Beta.cancelDownload();finish();}});Beta.registerDownloadListener(new DownloadListener() {@Overridepublic void onReceive(DownloadTask task) {updateBtn(task);tv.setText(task.getSavedLength() + "");}@Overridepublic void onCompleted(DownloadTask task) {updateBtn(task);tv.setText(task.getSavedLength() + "");}@Overridepublic void onFailed(DownloadTask task, int code, String extMsg) {updateBtn(task);tv.setText("failed");}});}@Overrideprotected void onResume() {super.onResume();}@Overrideprotected void onStop() {super.onStop();}@Overrideprotected void onDestroy() {super.onDestroy();Beta.unregisterDownloadListener();}public void updateBtn(DownloadTask task) {switch (task.getStatus()) {case DownloadTask.INIT:case DownloadTask.DELETED:case DownloadTask.FAILED: {start.setText("开始下载");}break;case DownloadTask.COMPLETE: {start.setText("安装");}break;case DownloadTask.DOWNLOADING: {start.setText("暂停");}break;case DownloadTask.PAUSED: {start.setText("继续下载");}break;default:break;}}public <T extends View> T getView(int id) {return (T) findViewById(id);}
}

到此为2018年1月22日23:28:16更新。明日想着完善。虽说依旧官方文档,但是还是要熟悉下整个流程~

四、版本升级 微信Tinker 打补丁 (重点二)

小前言

首先,我们来说下很常见的一个案例:

小A在测试通过之后,发布了1.0版本,但是在用户实际使用过程中,突然发现由于小A在某个方面没有考虑完善,导致用户在某些特殊场景会发生崩溃闪退的现象。那么,这个时候怎么办呢?

针对以上内容,大家想想怎么办呢?

有的小伙伴说了,升级啊,傻啊?但是我们不能总是升级,这样导致用户方案,换位思考,如果你正在用的软件,总是在提示升级,而每次升级毫无新意,似乎压根没有变化,一次又一次你烦不烦。那么最后的方式又是什么呢?

最好的解决方案当然是,在用户不知情的情况下,修复。

那么,在目前情况下,提供了很多种解决方案,而今天,基于腾讯Bugly我们来学习了解微信Tinker使用。

为什么使用微信Tinker?

  • 无需关注Tinker是如何合成补丁的;

  • 无需自己搭建补丁管理后台;

  • 无需考虑后台下发补丁策略的任何事情;

  • 无需考虑补丁下载合成的时机,处理后台下发的策略;

  • 我们提供了更加方便集成Tinker的方式;

  • 我们通过HTTPS及签名校验等机制保障补丁下发的安全性;

  • 丰富的下发维度控制,有效控制补丁影响范围;

  • 我们提供了应用升级一站式解决方案;

目前缺陷

啦啦啦,开车

第一步:工程目录下build配置插件

classpath "com.tencent.bugly:tinker-support:latest.release"

第二步:app目录下创建tinker-support.gradle文件

apply plugin: 'com.tencent.bugly.tinker-support'
// 创建一个目录
def bakPath = file("${buildDir}/bakApk/")/*** 此处填写每次构建生成的基准包目录* 只需要在每次打补丁包才会更改*/
def baseApkDir = "app-01-23-00-00"/*** 对于插件各参数的详细解析请参考*/
tinkerSupport {// 开启tinker-support插件,默认值trueenable = true// 指定归档目录,默认值当前module的子目录tinkerautoBackupApkDir = "${bakPath}"// 是否启用覆盖tinkerPatch配置功能,默认值false// 开启后tinkerPatch配置不生效,即无需添加tinkerPatchoverrideTinkerPatchConfiguration = true// 编译补丁包时,必需指定基线版本的apk,默认值为空// 如果为空,则表示不是进行补丁包的编译// @{link tinkerPatch.oldApk }baseApk = "${bakPath}/${baseApkDir}/app-release.apk"// 对应tinker插件applyMapping 开启混淆会生成baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"// 对应tinker插件applyResourceMappingbaseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"// 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性tinkerId = "base-1.0.1"// 构建多渠道补丁时使用// buildAllFlavorsDir = "${bakPath}/${baseApkDir}"// 是否启用加固模式,默认为false.(tinker-spport 1.0.7起支持)isProtectedApp = true// 是否开启反射Application模式enableProxyApplication = true
}/*** 一般来说,我们无需对下面的参数做任何的修改* 对于各参数的详细介绍请参考:* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97*/
tinkerPatch {ignoreWarning = falseuseSign = truedex {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}
}

第三步:配置依赖插件脚本

apply from: 'tinker-support.gradle'

Sync之后,发现报如下异常:

Error:Could not get unknown property ‘apkVariantData’ for object of type com.android.build.gradle.in

A: Gradle升级到3.0以后去除了apkVariantData这个API,所以我们将3.0的Gradle改为如下:

classpath 'com.android.tools.build:gradle:2.3.3'

之后再次Sync,又报出如下异常:

Error:Cause: buildToolsVersion is not specified.

A: 给build.gradle中设置buildToolsVersion。

buildToolsVersion "26.0.2"

第四步:集成SDK

设置相应权限:

    <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" />

配置远程依赖:

    compile "com.android.support:multidex:1.0.1" // 多dex配置compile 'com.tencent.bugly:crashreport_upgrade:latest.release'

设置so支持CPU架构:

    ndk {// 设置支持的SO库架构abiFilters 'armeabi', 'arm64-v8a' //, 'x86', 'armeabi-v7a', 'x86_64'}

方式一:快速集成 一键接入

继承Application,简单进行部分配置即可。

这里因为是使用反射Application的方式,所以需要设置为true,如下:

    // 是否开启反射Application模式enableProxyApplication = true

Application关键代码如下:

package com.hlq.buglytest;import android.app.Application;
import android.content.Context;
import android.support.multidex.MultiDex;import com.tencent.bugly.Bugly;
import com.tencent.bugly.beta.Beta;/*** author : HLQ* e-mail : 925954424@qq.com* time   : 2018/01/18* desc   : 一键接入方式 简单快捷 但是兼容性不是很好* version: 1.0*/
public class BaseApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// 初始化BuglyBugly.init(this, "84b331c8e5", true);}@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);// Dex分包MultiDex.install(base);// 安装Tinker 加载补丁Beta.installTinker();}
}

生成基准包测试查看:

找到右侧Gradle projects,选择build下assembleDebug,双击。

查看日志发现,它内部为我们反射好了Application,如下:

tinkerpatch change application name from com.hlq.buglytest.BaseApplication to com.tencent.bugly.beta.tinker.TinkerPatchReflectApplication

查看打好的包中的主配置文件,发现它已反射为已指定的Application:

android:name="com.tencent.bugly.beta.tinker.TinkerPatchReflectApplication"

并且你会发现,默认会为我们添加俩个参数:

  • tinker-id;

  • 原有的Application。

详情如下:

    <meta-dataandroid:name="TINKER_ID"android:value="base-1.0.1" /><meta-dataandroid:name="TINKER_PATCH_APPLICATION"android:value="com.hlq.buglytest.BaseApplication" />

有的小伙伴问了,从哪儿看呢?

表急,给你来张图:

方式二:改造Application 兼容性较好

1.关闭反射Application模式;

enableProxyApplication = false

2.继承TinkerApplication;

public class BaseApplication extends TinkerApplication {public BaseApplication() {super(ShareConstants.TINKER_ENABLE_ALL,"com.hlq.buglytest.BaseApplicationLike","com.tencent.tinker.loader.TinkerLoader",false);}
}

3.创建BaseApplicationLike;

public class BaseApplicationLike extends DefaultApplicationLike {public BaseApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);}@Overridepublic void onCreate() {super.onCreate();// 初始化BuglyBugly.init(getApplication(), "84b331c8e5", true);}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)@Overridepublic void onBaseContextAttached(Context base) {super.onBaseContextAttached(base);// you must install multiDex whatever tinker is installed!MultiDex.install(base);// 安装tinkerBeta.installTinker(this);}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {getApplication().registerActivityLifecycleCallbacks(callbacks);}}

第五步:配置正式、测试签名文件以及打包后自动签名

在build目录下添加如下:

    // 签名配置signingConfigs {release {try {storeFile file("./keystore/debug.keystore")storePassword "android"keyAlias "androiddebugkey"keyPassword "android"} catch (ex) {throw new InvalidUserDataException(ex.toString())}}debug {storeFile file("./keystore/debug.keystore")}} // 构建类型buildTypes {release {minifyEnabled falsesigningConfig signingConfigs.releaseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}debug {debuggable trueminifyEnabled falsesigningConfig signingConfigs.debug}}

记得要在app下创建一个keystore目录,将正式证书以及测试证书放置其中,如下图:

这里为方便,正式测试使用一个证书。

第六步:打基准包

首先搞一个异常出来玩玩,点击按钮,崩溃,这个很easy~

之后直接选择build下的assembleRelease,如下图:

成功后输入如下:

Copy the output files into backup dir
Target dir: F:\HLQ_Study\BuglyDemo\app\build\bakApk\app-0128-23-51-20
Copy app-release.apk to F:\HLQ_Study\BuglyDemo\app\build\bakApk\app-0128-23-51-20/app-release.apk
Copy mapping.txt to F:\HLQ_Study\BuglyDemo\app\build\bakApk\app-0128-23-51-20/app-release-mapping.txt
Copy R.txt to F:\HLQ_Study\BuglyDemo\app\build\bakApk\app-0128-23-51-20/app-release-R.txt

第七步:修改bug,新增资源后,打补丁包

如上图所示,这里需要将baseApkDir内容修改为刚刚打好的基准包,也就是指定为这个包进行打对应补丁包。

接着,修改唯一ID值:

    // 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性tinkerId = "patch-1.0.4"

这时候,按照下图操作:

打包完成后,我们查看输入内容:

我们可以很清晰的看到,默认将我们已修改的内容打到补丁包之中了。

而此时的关键日志如下:

------ Tinker Support ------
Tinker patch output dir: F:\HLQ_Study\BuglyDemo\app\build\outputs/tinkerPatch/release
Get TINKER_ID = base-1.0.1, NEW_TINKER_ID = patch-1.0.1
Generate patch description file: YAPATCH.MF
Tinker patch file: F:\HLQ_Study\BuglyDemo\app\build\outputs\tinkerPatch\release\patch_signed.apk
Copy patch_signed.apk to F:\HLQ_Study\BuglyDemo\app\build\outputs\patch\release\patch_signed.apk
Add YAPATCH.MF into the file: patch_signed.apk
Tinker patch file: F:\HLQ_Study\BuglyDemo\app\build\outputs\tinkerPatch\release\patch_signed_7zip.apk
Copy patch_signed_7zip.apk to F:\HLQ_Study\BuglyDemo\app\build\outputs\patch\release\patch_signed_7zip.apk
Add YAPATCH.MF into the file: patch_signed_7zip.apk
Tinker patch file: F:\HLQ_Study\BuglyDemo\app\build\outputs\tinkerPatch\release\patch_unsigned.apk
Copy patch_unsigned.apk to F:\HLQ_Study\BuglyDemo\app\build\outputs\patch\release\patch_unsigned.apk
Add YAPATCH.MF into the file: patch_unsigned.apk
Delete the patch description file: YAPATCH.MF
------ Tinker Support end ------BUILD SUCCESSFUL in 15s
42 actionable tasks: 29 executed, 13 up-to-date
0:03:25: External task execution finished 'buildTinkerPatchRelease'.

The end,我们选择上传补丁包,配合下发测试:

这里需要注意的是,我们需要先打开之前的有问题的基础包,它内部会自动上报,如果不打开,上传补丁包变回找不到目标版本!!!

随后,杀掉进程,重新进入,发现输入如下日志:


onUpgradeReceived: title: newFeature: 贺利权啦啦啦publishTime: 0publishType: 0appBasicInfo: {appId: 886c59966fplatformId: 1versionCode: 0versionName: nullbuildNo: 0iconUrl: nullapkId: 0channelId: nullmd5: c7b1c09d28c6756743b36d8a405f2e7974fa85a0sdkVer: bundleId: null}apkBaseInfo: {apkMd5: c7b1c09d28c6756743b36d8a405f2e7974fa85a0apkUrl: https://s.beta.gtimg.com/rdmimg/hot_patch/886c59966f/7bd13f04-9d49-44ce-a9c9-89ffeb272b1c.zipmanifestMd5: fileSize: 43470signatureMd5: }updateStrategy: 0popTimes: 0popInterval: 0diffApkInfo: {null}netType: nullreserved: 2, {(H1false)(H21)}strategyId: f4e1b349-35dd-4288-889f-e96af8a7e22astatus: 1updateTime: 1517157553000updateType: 3[type: 3]

打开页面如下:

结束语

OK,关于Bugly的介绍到此完毕~

当然这里面还有很多的细节需要挖掘,LZ这里就不一一介绍了~~~

最近忙啊~~~

愿各位老铁年会抽大奖,公司多发奖金~~~

参考资料

  1. 腾讯Bugly SDK下载 ;
  2. 微信Tinker LZ地址;
  3. TinkerSupport插件使用指南;
  4. 腾讯Bugly视频教程;
  5. TinkerSupport插件使用指南;
  6. Bugly-Android-Demo GitHub地址;

腾讯Bugly学习了解相关推荐

  1. 【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制

    为什么80%的码农都做不了架构师?>>>    本文作者:鲁可--腾讯SNG专项测试组 测试工程师 背景 承上经典随机Crash之一:线程安全 问题的模型 好几次灰度top1.top ...

  2. 【腾讯Bugly干货分享】Android ListView与RecyclerView对比浅析--缓存机制

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5811d... 作者:黄宁源 一,背景 RecyclerView是谷歌官方出的一 ...

  3. 【腾讯Bugly干货分享】Android Linker 与 SO 加壳技术

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57e3a3bc42eb88da6d4be143 作者:王赛 1. 前言 Andr ...

  4. 【腾讯Bugly干货分享】彻底弄懂 Http 缓存机制 - 基于缓存策略三要素分解法

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/qOMO0LIdA47j3RjhbCWUEQ 作者:李 ...

  5. 【腾讯Bugly干货分享】腾讯验证码的十二年

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/581301b146dfb1456904df8d Dev Club 是一个交流移动 ...

  6. 【腾讯Bugly干货分享】JSPatch 成长之路

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/579ef... Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社 ...

  7. 【腾讯Bugly干货分享】WebVR如此近-three.js的WebVR示例解析

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57c7ff1689a6c9121b1adb16 作者:苏晏烨 关于WebVR 最 ...

  8. 【腾讯bugly干货分享】微信Android热补丁实践演进之路

    为什么80%的码农都做不了架构师?>>>    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.ph ...

  9. 腾讯Bugly异常崩溃SDK接入

    首先登入Bugly,创建应用,记录下AppId ①下载SDK,通过Cocoapods集成 pod 'Bugly' #腾讯异常崩溃日志服务 ②导入头文件,并初始化 /** 腾讯Bugly */#impo ...

  10. 【腾讯Bugly干货分享】Android Patch 方案与持续交付

    为什么80%的码农都做不了架构师?>>>    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57a31921 ...

最新文章

  1. 记一次 调节有音量界面 上移的bug
  2. mysql innodb 数据复制_五千万的数据 复制到另张表 是先复制数据再加索引还是先建好索引再复制数据 表引擎Innodb...
  3. list 去重_测试面试题集Python列表去重
  4. 边缘计算 — 边缘网络
  5. Nginx 内置变量,细化规则,真实IP获取及限制连接请求
  6. mybatis批量插入10万条数据的优化过程
  7. HTML 表单和输入
  8. BZOJ 1263 整数划分(数学+高精度)
  9. linux for循环原理,linux for循环
  10. EnableQ在线问卷调查引擎概览
  11. linux 更新目录时间戳,修改linux 文件的时间戳
  12. 德标螺纹规格对照表_德标、国标对照表
  13. react小书没读完的记录
  14. 怎么解决电脑任务管理器被系统管理员停用?
  15. 23位子网掩码是多少_子网掩码23位的是多少
  16. 【Matlab图像处理】自动报靶系统(重弹孔)【含GUI源码 973期】
  17. window.print 打印文章时设置字体及解决每页跨页截断问题
  18. mysql procedure 存储过程
  19. 使用组策略将用户文件夹重定向到D盘
  20. systemctl enable与systemctl start的区别

热门文章

  1. Unity 之 ShaderGraph 模拟“吃鸡”海面效果入门级教程
  2. Cyclical Learning Rates
  3. PC端-移动端自适应屏幕
  4. 2016-2017 ACM-ICPC, NEERC, Southern Subregional Contest A Toda2 贪心
  5. Multi-Instance Multi-Label Learning Networks for Aspect-Category Sentiment Analysis
  6. chemdraw如何改中文_教您如何掌握ChemDraw页面设置技巧
  7. Lync添加自定义菜单
  8. IE无法打开二级页面
  9. A Piece of ODE
  10. Java 如何给现有PDF文档添加页码