美文网首页
[转]腾讯Bugly学习了解

[转]腾讯Bugly学习了解

作者: Thor_果冻 | 来源:发表于2019-03-25 13:23 被阅读0次

    开始想自己写的,但是看了下面这篇博客写了比我了解的多太多了就直接变成转载了。该博客只能参考作用,请根据最新的官方文档进行对应的更改。

    建议主要要参考官方文档,实在有点问题在参考该篇博客,现在文档很全面。

    我的项目现在只用的异常上报和版本升级
    转载学习博客地址:腾讯Bugly学习了解 ,对作者博客进行了重新MD格式排版。
    官网文档

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

    进入官网立即接入:

    接入图

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

    bugly控制台界面

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

    我的产品界面

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

    新建我的产品

    成功之后显示如下:

    新建我的产品成功界面

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

    • 异常上报;
    • 运营统计;
    • 版本升级 (重点)

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

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

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

    一、异常上报

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

    sdk下载界面

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

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

    • 异常概览 查看今日实时统计、崩溃趋势、崩溃排行和TOP20崩溃问题等信息;
    • 崩溃分析/卡顿分析/错误分析 查看上报问题的列表;
    • 问题详情 查看上报问题的详细信息;
    • 高级搜索 通过各种条件快速查找需要定位分析的异常

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

    • SDK 集成;

    • 远程依赖

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

    何乐而不为

    LZ操作步骤如下:

    1.1 添加远程依赖:

    // 其中latest.release指代最新Bugly SDK版本号,也可以指定明确的版本号,例如2.1.9
    compile 'com.tencent.bugly:crashreport:latest.release'
    // 其中latest.release指代最新Bugly NDK版本号,也可以指定明确的版本号,例如3.0
    compile '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(例如在ApplicationonCreate()中初始化Bugly),那么每个进程下的Bugly都会进行数据上报,造成不必要的资源浪费。

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

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

    private boolean mIsDebug = true;
    
    @Override
    public 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));
        // 初始化Bugly
        CrashReport.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后台是否接收到这个异常呢?

    10/0的Crash日志

    嗯哼,不赖~

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

    Crash日志详情

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

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

    跟踪数据界面

    还有?

    设备信息界面

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

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

    再搞个异常出来试试:

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

    再来看看结果:

    Crash基本信息 Crash详情界面

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

    二、运营统计

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

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

    好尴尬~

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

    运营概览界面

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

    官方文档:Bugly Android 应用升级 SDK 使用指南

    哈哈,终于等到你~~~

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

    操作流程如下:

    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 统一初始化方式

    /**
     * 初始化 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使用

    发布新升级界面

    上传完成后,进行升级策略编辑(在这里,你会深刻体会到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文件,依据官方文档进行设置(Bugly Android 应用升级 SDK 高级配置)

    <?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: 小伙子说了,我不想用腾讯的,也不想仅仅自定义提示框,我想自己玩,怎么破?

    自定义Activity官方链接

    嘿嘿嘿,小伙伴自己玩去吧很是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;
    
        @Override
        protected 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() {
                @Override
                public void onClick(View v) {
                    DownloadTask task = Beta.startDownload();
                    updateBtn(task);
                    if (task.getStatus() == DownloadTask.DOWNLOADING) {
                        finish();
                    }
                }
            });
    
            cancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Beta.cancelDownload();
                    finish();
                }
            });
            Beta.registerDownloadListener(new DownloadListener() {
                @Override
                public void onReceive(DownloadTask task) {
                    updateBtn(task);
                    tv.setText(task.getSavedLength() + "");
                }
    
                @Override
                public void onCompleted(DownloadTask task) {
                    updateBtn(task);
                    tv.setText(task.getSavedLength() + "");
                }
    
                @Override
                public void onFailed(DownloadTask task, int code, String extMsg) {
                    updateBtn(task);
                    tv.setText("failed");
    
                }
            });
        }
    
        @Override
        protected void onResume() {
            super.onResume();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
        }
    
        @Override
        protected 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);
        }
    }
    

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

    官方:Bugly Android热更新使用指南,官方地址中还有视频教程,可以观看一下。最好根据最新的文档改

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

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

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

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

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

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

    为什么使用微信Tinker?

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

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

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

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

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

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

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

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

    目前缺陷

    目前缺陷

    1. 工程目录下build配置插件

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

    2. 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插件,默认值true
        enable = true
        // 指定归档目录,默认值当前module的子目录tinker
        autoBackupApkDir = "${bakPath}"
        // 是否启用覆盖tinkerPatch配置功能,默认值false
        // 开启后tinkerPatch配置不生效,即无需添加tinkerPatch
        overrideTinkerPatchConfiguration = true
        // 编译补丁包时,必需指定基线版本的apk,默认值为空
        // 如果为空,则表示不是进行补丁包的编译
        // @{link tinkerPatch.oldApk }
        baseApk = "${bakPath}/${baseApkDir}/app-release.apk"
        // 对应tinker插件applyMapping 开启混淆会生成
        baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"
        // 对应tinker插件applyResourceMapping
        baseApkResourceMapping = "${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 = 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
        }
    }
    

    3. 配置依赖插件脚本

    apply from: 'tinker-support.gradle'
    

    4. 集成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 {
    
        @Override
        public void onCreate() {
            super.onCreate();
            // 初始化Bugly
            Bugly.init(this, "84b331c8e5", true);
        }
    
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            // Dex分包
            MultiDex.install(base);
            // 安装Tinker 加载补丁
            Beta.installTinker();
        }
    }
    

    生成基准包测试查看:

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

    Gradle projects

    查看日志发现,它内部为我们反射好了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-data
        android:name="TINKER_ID"
        android:value="base-1.0.1" />
    
    <meta-data
        android: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. 创建BaseApplication
    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);
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            // 初始化Bugly
            Bugly.init(getApplication(), "84b331c8e5", 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
            Beta.installTinker(this);
        }
    
        @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
        public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
            getApplication().registerActivityLifecycleCallbacks(callbacks);
        }
    
    }
    

    5. 配置正式、测试签名文件以及打包后自动签名

    在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 false
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            debuggable true
            minifyEnabled false
            signingConfig signingConfigs.debug
        }
    }
    

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

    放置证书

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

    6. 打基准包

    首先搞一个异常出来玩玩,点击按钮,崩溃,这个很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
    

    7. 修改bug,新增资源后,打补丁包

    打补丁

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

    接着,修改唯一ID值:

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

    这时候,按照下图操作:

    1

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

    2

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

    而此时的关键日志如下:

    ------ 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'.
    

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

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

    上传补丁1 上传补丁2 上传补丁3

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

    
    onUpgradeReceived: title: 
      newFeature: 贺利权啦啦啦
      publishTime: 0
      publishType: 0
      appBasicInfo: {
        appId: 886c59966f
        platformId: 1
        versionCode: 0
        versionName: null
        buildNo: 0
        iconUrl: null
        apkId: 0
        channelId: null
        md5: c7b1c09d28c6756743b36d8a405f2e7974fa85a0
        sdkVer: 
        bundleId: null
      }
      apkBaseInfo: {
        apkMd5: c7b1c09d28c6756743b36d8a405f2e7974fa85a0
        apkUrl: https://s.beta.gtimg.com/rdmimg/hot_patch/886c59966f/7bd13f04-9d49-44ce-a9c9-89ffeb272b1c.zip
        manifestMd5: 
        fileSize: 43470
        signatureMd5: 
      }
      updateStrategy: 0
      popTimes: 0
      popInterval: 0
      diffApkInfo: {
        null}
      netType: null
      reserved: 2, {
        (
            H1
            false
        )
        (
            H2
            1
        )
      }
      strategyId: f4e1b349-35dd-4288-889f-e96af8a7e22a
      status: 1
      updateTime: 1517157553000
      updateType: 3
       [type: 3]
    

    打开页面如下:

    打开页面效果

    参考资料

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

    相关文章

      网友评论

          本文标题:[转]腾讯Bugly学习了解

          本文链接:https://www.haomeiwen.com/subject/hxzmvqtx.html