天天看點

騰訊Bugly學習了解

LZ-Says:新的一年,新的開始~ 越是在淩亂的時候,越要堅持住自己!!!
騰訊Bugly學習了解

前言

年前的時候,就已經關注了有關熱修複,熱更新的知識,本想着項目當中多少應用一些,可現實殘酷至極~

今天,先從騰訊Bugly開刀,一起來學習了解騰訊的Bugly是如何操作~

當然感謝老妖的推薦~ 預祝在鵝廠實作人生巅峰~

本文目标

通過官方文檔,以及親自測試,希望大家從過程中能掌握Bugly使用,一起學習~

Hi,Bugly

百度搜尋:騰訊Bugly,點選進入官網:

騰訊Bugly學習了解

下面引入騰訊官方對Bugly簡述:

騰訊Bugly,為移動開發者提供專業的異常上報和營運統計,幫助開發者快速發現并解決異常,同時掌握産品營運動态,及時跟進使用者回報。

點選下方立即接入:

騰訊Bugly學習了解

當然你可以點選下面的檢視Demo,簡單操作一下:

騰訊Bugly學習了解

而LZ這裡選擇:立即接入,(首次需要建立一個産品,和其他三方操作一緻):

騰訊Bugly學習了解

依次填入相應資訊後點選儲存:

騰訊Bugly學習了解

成功之後顯示如下:

到此,我們可以看到Bugly SDK 提供我們三個使用範圍:

  • 異常上報;
  • 營運統計;
  • 版本更新 (重點)

同時,也可以關注Bugly公衆号,原因嘛:

  1. 及時檢視每周的精彩牛文;
  2. 快速檢視您負責産品的資料,比如:日報、趨勢等;
  3. 接收異常告警,第一時間掌握産品突發狀況

下面分别對提供的三種方式進行使用以及實驗。

一、異常上報

點選“異常上報”,選擇SDK 包 2.6.6的使用指南:

騰訊Bugly學習了解

我們先來看下關于異常上報平台功能介紹:

應用內建SDK後,即可在Web站點檢視應用上報的崩潰資料和聯網資料。
  • 異常概覽 檢視今日實時統計、崩潰趨勢、崩潰排行和TOP20崩潰問題等資訊;
  • 崩潰分析/卡頓分析/錯誤分析 檢視上報問題的清單;
  • 問題詳情 檢視上報問題的詳細資訊;
  • 進階搜尋 通過各種條件快速查找需要定位分析的異常

Bugly 提供倆種方式進行內建:

  • SDK 內建;
  • 遠端依賴

相比倆種內建方案,LZ這裡選擇遠端依賴,簡單友善快捷,何樂不為?

騰訊Bugly學習了解

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(例如在Application類onCreate()中初始化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背景是否接收到這個異常呢?

騰訊Bugly學習了解

嗯哼,不賴~

點選進去檢視詳情,看看這究竟還會有哪兒些意外驚喜?

騰訊Bugly學習了解

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

我們接着看,看看他還有什麼新奇的玩意???

騰訊Bugly學習了解

還有?

騰訊Bugly學習了解

一個字,好

二個字,很棒

三個字,忒TM6

。。。 。。。

相比某盟,Bugly這點讓人很是nice~

再搞個異常出來試試:

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

再來看看結果:

騰訊Bugly學習了解
騰訊Bugly學習了解

很不錯~哇咔咔~~~LZ準備以後就用它了,這麼人性化,這麼棒的東西~

騰訊Bugly學習了解

二、營運統計

經過上面的異常上報,LZ對營運統計這塊也是有了很大的興趣,雖說不懂營運,但是也要看看它提供的營運統計又是如何展示?

MMP,轉悠了半天,發現貌似隻能顯示昨天的。。。

好尴尬~

但是,可以顯示一些基本資訊,如下:

騰訊Bugly學習了解
騰訊Bugly學習了解

三、版本更新 全量更新 (重點一)

哈哈,終于等到你~~~

重點來了,比較感興趣的也終于來了~

操作流程如下:

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]

基本配置完成後,我們進入背景管理界面進行更新政策編輯:

騰訊Bugly學習了解

3.6 使用

首先,我們需要上傳更新的apk包:

騰訊Bugly學習了解

上傳完成後,進行更新政策編輯(在這裡,你會深刻體會到Bugly的人性化):

騰訊Bugly學習了解

下面簡單介紹下:

  • 更新方式: 1.推薦更新(可不更新); 2.強制更新
  • 政策下發條件:
  • 指定源版本:可以選擇指定的版本進行更新,也可以通知所有已發版本進行更新;
  • 更新管道:LZ猜測應該是APK下發管道;
  • 網絡環境:指定在某種特定網絡環境下進行更新,也可以是所有環境
  • 政策啟動條件: 1.立即啟動; 2.手動啟動; 3.定時啟動
  • 政策停止條件:(這塊可以進行灰階測試時,随機通知固定使用者數進行測試)
  • 定時停止;
  • 下發上線人數;
  • 激活上線人數
  • 自動彈窗規則: 1.總彈窗次數; 2.彈窗間隔時間
  • 最重要的便是彈框樣式,預設提供三種固定樣式,當然你也可以自定義彈框樣式!!!

編輯完成之後,如下:

騰訊Bugly學習了解

啟動應用,稍等一下:

騰訊Bugly學習了解

這裡充分說明的選的圖檔一定要小,一定要小,一定要小!!!不然圖檔位置一直顯示loading。。。

點選立即更新後,立即更新變成目前下載下傳進度。下載下傳完成後自動安裝:

騰訊Bugly學習了解

簡單的使用,相信大家已經胸有成竹,下面進入目前階段的知識問答階段~

騰訊Bugly學習了解

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檢視效果:

騰訊Bugly學習了解

Issue 2: 我想設定手動檢測更新,怎麼破?

一般來說,LZ建議,可以設定自動彈框次數,然後搭配手動檢測版本更新。這樣比較合理點。

那麼下面的例子,隻是簡單禁用了啟動自動更新,然後配合使用者手動點選檢測更新。嗯,就是這樣~

首先,需要設定自動更新為false,也就是不自動更新:

Beta.autoCheckUpgrade = false;      

接着,通過Button的點選事件,進行手動調用檢測更新方法:

public void getUpdateVersion(View view) {
        // 手動檢測更新
        Beta.checkUpgrade();
    }      

檢視效果:

騰訊Bugly學習了解

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);
    }      

檢視效果:

騰訊Bugly學習了解

Issue 4: UI天馬星空,非要個性的更新框,怎麼破?

比如說,要實作下面這個效果,腫麼破呢?(PS:不能嫌棄哦~~~)

騰訊Bugly學習了解

騰訊的貼心小夥伴當然為我們想到了喽:

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學習了解

文末底部附上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;

    @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);
    }
}      

到此為2018年1月22日23:28:16更新。明日想着完善。雖說依舊官方文檔,但是還是要熟悉下整個流程~

四、版本更新 微信Tinker 打更新檔 (重點二)

小前言

首先,我們來說下很常見的一個案例:

小A在測試通過之後,釋出了1.0版本,但是在使用者實際使用過程中,突然發現由于小A在某個方面沒有考慮完善,導緻使用者在某些特殊場景會發生崩潰閃退的現象。那麼,這個時候怎麼辦呢?

針對以上内容,大家想想怎麼辦呢?

有的小夥伴說了,更新啊,傻啊?但是我們不能總是更新,這樣導緻使用者方案,換位思考,如果你正在用的軟體,總是在提示更新,而每次更新毫無新意,似乎壓根沒有變化,一次又一次你煩不煩。那麼最後的方式又是什麼呢?

最好的解決方案當然是,在使用者不知情的情況下,修複。      

那麼,在目前情況下,提供了很多種解決方案,而今天,基于騰訊Bugly我們來學習了解微信Tinker使用。

為什麼使用微信Tinker?

  • 無需關注Tinker是如何合成更新檔的;
  • 無需自己搭建更新檔管理背景;
  • 無需考慮背景下發更新檔政策的任何事情;
  • 無需考慮更新檔下載下傳合成的時機,處理背景下發的政策;
  • 我們提供了更加友善內建Tinker的方式;
  • 我們通過HTTPS及簽名校驗等機制保障更新檔下發的安全性;
  • 豐富的下發次元控制,有效控制更新檔影響範圍;
  • 我們提供了應用更新一站式解決方案;

目前缺陷

騰訊Bugly學習了解

啦啦啦,開車

第一步:工程目錄下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插件,預設值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
    }
}      

第三步:配置依賴插件腳本

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 : [email protected]
 * 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,輕按兩下。

騰訊Bugly學習了解

檢視日志發現,它内部為我們反射好了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" />      

有的小夥伴問了,從哪兒看呢?

表急,給你來張圖:

騰訊Bugly學習了解

方式二:改造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);
    }

    @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);
    }

}      

第五步:配置正式、測試簽名檔案以及打包後自動簽名

在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目錄,将正式證書以及測試證書放置其中,如下圖:

騰訊Bugly學習了解

這裡為友善,正式測試使用一個證書。

第六步:打基準包

首先搞一個異常出來玩玩,點選按鈕,崩潰,這個很easy~

之後直接選擇build下的assembleRelease,如下圖:

騰訊Bugly學習了解

成功後輸入如下:

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,新增資源後,打更新檔包

騰訊Bugly學習了解

如上圖所示,這裡需要将baseApkDir内容修改為剛剛打好的基準包,也就是指定為這個包進行打對應更新檔包。

接着,修改唯一ID值:

// 建構基準包和更新檔包都要指定不同的tinkerId,并且必須保證唯一性
    tinkerId = "patch-1.0.4"      

這時候,按照下圖操作:

騰訊Bugly學習了解

打包完成後,我們檢視輸入内容:

騰訊Bugly學習了解

我們可以很清晰的看到,預設将我們已修改的内容打到更新檔包之中了。

而此時的關鍵日志如下:

------ 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,我們選擇上傳更新檔包,配合下發測試:

這裡需要注意的是,我們需要先打開之前的有問題的基礎包,它内部會自動上報,如果不打開,上傳更新檔包變回找不到目标版本!!!
騰訊Bugly學習了解
騰訊Bugly學習了解
騰訊Bugly學習了解

随後,殺掉程序,重新進入,發現輸入如下日志:

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]      

打開頁面如下:

騰訊Bugly學習了解

結束語

OK,關于Bugly的介紹到此完畢~