首先在Module:app/build.gredle下添加依賴集成SDK
如果只想集成單獨的異常上報可以將依賴改成這樣compile 'com.tencent.bugly:crashreport:latest.release'
添加依賴.png
然后在Module:app/build.gredle下設(shè)置支持的SO庫架構(gòu)集成NDK,同時配置打開多dex
設(shè)置SO庫.png
然后仍然在在Module:app/build.gredle下設(shè)置配置簽名(當(dāng)然測試時可以不配置)霉翔,同時也在打開了混淆配置以及優(yōu)化(當(dāng)然也可以不需要配置混淆舌胶,此處配置混淆后面會提到上傳mapping快速混淆配置請參考我以前的文章)
簽名并且混淆.png
然后仍然在在Module:app/build.gredle下設(shè)置配置lint檢測編譯器和運行時錯誤(當(dāng)然此處也可以不配置)贪惹,在這里我配置了多渠道打包,不配置也沒關(guān)系四康,建議配置不配置都試一下构舟,看看兩個打補丁時會有什么不同
linty以及多渠道.png
好了枫匾,下來這一點需要注意架诞,剛開始沒添加這個東西,打好補丁后一直報錯誤干茉,提示補丁版本不對如圖:
tinker.png
后面添加了這句打補丁后就可以了
gradle.project.png
好了谴忧,到這里啦異常統(tǒng)計肯定沒問題了,看看初始化
/**? ? ? ? * 單個異常上傳統(tǒng)計時的初始化方法? ? ? ? *@appContext上下文? ? ? ? *@crashReportAppId注冊時申請的APPID? ? ? ? *@isDebug自定義日志將會在Logcat中輸出角虫。? ? ? ? */CrashReport.initCrashReport(getApplicationContext(), Commont.APP_ID,false);/**? ? ? ? * 統(tǒng)一初始化Bugly產(chǎn)品沾谓,包含Beta? ? ? ? *@context上下文? ? ? ? *@appId注冊時申請的APPID? ? ? ? *@isDebug自定義日志將會在Logcat中輸出。? ? ? ? *? ? ? ? * 提示:已經(jīng)接入Bugly用戶改用上面的初始化方法,不影響原有的crash上報功能; init方法會自動檢測更新戳鹅,不需要再手動調(diào)用Beta.checkUpgrade(), 如需增加自動檢查時機可以使用Beta.checkUpgrade(false,false);? ? ? ? * 參數(shù)1:isManual 用戶手動點擊檢查均驶,非用戶點擊操作請傳false? ? ? ? * 參數(shù)2:isSilence 是否顯示彈窗等交互,[true:沒有彈窗和toast] [false:有彈窗或toast]? ? ? ? */Bugly.init(this, Commont.APP_ID,true);/**? ? ? ? * 統(tǒng)一初始化Bugly產(chǎn)品枫虏,包含Beta? ? ? ? *@context上下文? ? ? ? *@appId注冊時申請的APPID? ? ? ? *@isDebug自定義日志將會在Logcat中輸出妇穴。? ? ? ? *@strategyBugly高級設(shè)置? ? ? ? */Bugly.init(this, Commont.APP_ID,true, strategy);
上面有三種初始化的方法:
① 第一種為當(dāng)你只需要只是僅僅要使用該產(chǎn)品的異常上傳是依賴添加為上面說過的compile 'com.tencent.bugly:crashreport:latest.release',初始化時就使用第一種方法
②第二種為設(shè)置已經(jīng)用到了統(tǒng)一的初始化方法,版本也會自動檢查更新隶债,異常也會上報
③擁有了第二種方法的能力并且還有其他能力腾它,如果現(xiàn)在想添加一個App的打包渠道怎么搞,就用到了最后一個參數(shù)strategy作為Bugly高級設(shè)置死讹。瞒滴。。
到這里了赞警,可以說應(yīng)用升級也基本已經(jīng)完成了妓忍,我們在到Manifest里面為升級補全配置
<!-- bugly配置權(quán)限start --><!-- bugly配置權(quán)限end --><!-- Bugly升級SDK配置開始 --><!-- 想兼容Android N或者以上的設(shè)備 --><!--友盟 Appkey 自己應(yīng)用注冊申請來的--><!--渠道號,多渠道這里使用了占位符$-->
在res文件下創(chuàng)建新文件xml并創(chuàng)建文件provider_paths.xml
<?xml version="1.0"encoding="utf-8"?><!-- 這里配置的兩個外部存儲路徑是升級SDK下載的文件可能存在的路徑 --><!-- /storage/emulated/0/Download/com.bugly.upgrade.demo/.beta/apk--><!--/storage/emulated/0/Android/data/com.bugly.upgrade.demo/files/apk/-->
ok愧旦,升級也完成了世剖, 看看最后打補丁-----熱修復(fù)
我這里用到了第三方的一個插件,方便簡單一點忘瓦,我們首先去下載一個插件Bugly
AbdroidStudio插件下載.png
完了之后就再根目錄下創(chuàng)建一個tinker-support.gradle,然后再配置里面的東西
tinker-support.gradle.png
詳細配置:
apply plugin:'com.tencent.bugly.tinker-support'def bakPath = file("${buildDir}/bakApk/")/** * 此處填寫每次構(gòu)建生成的基準包目錄 */def baseApkDir ="app-0119-10-02-31"/** * 對于插件各參數(shù)的詳細解析請參考 */tinkerSupport {? ? // 開啟tinker-support插件搁廓,默認值trueenable=true// tinkerEnable功能開關(guān)? ? tinkerEnable =true// 是否編譯完成后引颈,歸檔apk到指定目錄,默認值false// 指定歸檔目錄境蜕,默認值當(dāng)前module的子目錄tinker? ? autoBackupApkDir ="${bakPath}"http:// 是否啟用覆蓋tinkerPatch配置功能蝙场,默認值false// 開啟后tinkerPatch配置不生效,即無需添加tinkerPatch? ? overrideTinkerPatchConfiguration =true// 編譯補丁包時粱年,必需指定基線版本的apk售滤,默認值為空? ? // 如果為空,則表示不是進行補丁包的編譯? ? // @{link tinkerPatch.oldApk }? ? baseApk ="${bakPath}/${baseApkDir}/app-release.apk"http:// 對應(yīng)tinker插件applyMapping? ? baseApkProguardMapping ="${bakPath}/${baseApkDir}/app-release-mapping.txt"http:// 對應(yīng)tinker插件applyResourceMapping? ? baseApkResourceMapping ="${bakPath}/${baseApkDir}/app-release-R.txt"http:// 構(gòu)建基準包和補丁包都要指定不同的tinkerId台诗,并且必須保證唯一性//? ? tinkerId ="1.0.0.4-base"http://生成對于的基包版本? 只有生成基準包的時候使用? ? tinkerId ="1.0.0.4-patch"http://對于生成的補丁包版本? 只有生成補丁包的時候使用? ? // 構(gòu)建多渠道補丁時使用? ? buildAllFlavorsDir ="${bakPath}/${baseApkDir}"http:// 是否啟用加固模式完箩,默認為false.(tinker-spport 1.0.7起支持)? ? // isProtectedApp =true// 是否開啟反射Application模式? ? enableProxyApplication =false// 是否支持新增非export的Activity(注意:設(shè)置為true才能修改AndroidManifest文件)? ? supportHotplugComponent =true}/** * 一般來說,我們無需對下面的參數(shù)做任何的修改 * 對于各參數(shù)的詳細介紹請參考: * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97 */tinkerPatch {? ? tinkerEnable =true//oldApk ="${bakPath}/${appName}/app-release.apk"ignoreWarning =false//對應(yīng)tinker插件userSign, 在運行過程中,? ? // 我們需要驗證基準apk包與補丁包的簽名是否一致拉队,我們是否需要為你簽名弊知。? ? useSign =truedex {? ? ? ? dexMode ="jar"pattern = ["classes*.dex"]? ? ? ? loader = []? ? }? ? lib {? ? ? ? pattern = ["lib/*/*.so"]? ? }? ? res {? ? ? ? pattern = ["res/*","r/*","assets/*","resources.arsc","AndroidManifest.xml"]? ? ? ? ignoreChange = []? ? ? ? largeModSize = 100? ? }? ? // 用于生成補丁包中的'package_meta.txt'文件? ? packageConfig {//? ? ? ? configField("patchMessage","tinker is sample to use")//? ? ? ? configField("platform","all")//? ? ? ? configField("patchVersion","1.0.5")? ? }? ? sevenZip {? ? ? ? zipArtifact ="com.tencent.mm:SevenZip:1.1.10"http://? ? ? ? path ="/usr/local/bin/7za"}? ? buildConfig {? ? ? ? keepDexApply =false//tinkerId ="1.0.1-base"http://applyMapping ="${bakPath}/${appName}/app-release-mapping.txt"http://? 可選,設(shè)置mapping文件粱快,建議保持舊apk的proguard混淆方式? ? ? ? //applyResourceMapping ="${bakPath}/${appName}/app-release-R.txt"http:// 可選秩彤,設(shè)置R.txt文件,通過舊apk文件保持ResId的分配? ? }}
在上述代碼中tinkerSupport{}里面有這樣一句事哭,很清楚漫雷,如果你的項目沒有用到多渠道打包,那么你可以將這一句注釋掉鳍咱,不用管它
構(gòu)建渠道補丁.png
還有這樣一句代碼:
構(gòu)建渠道補丁.png
這種情況下分為兩種降盹,enableProxyApplication默認為false
當(dāng)enableProxyApplication為false時
① 新建SampleApplicationLike繼承于DefaultApplicationLike,實現(xiàn)構(gòu)造方法谤辜,在將Application中所有要執(zhí)行的操作復(fù)制到這個類中
② 新建SampleApplication集成于TinkerApplication,構(gòu)造這樣寫
publicSampleApplication(){//記住此處com.suchengkeji.android.bugly.applications.SampleApplicationLike改為你自己新建的路徑包名super(ShareConstants.TINKER_ENABLE_ALL,"com.suchengkeji.android.bugly.applications.SampleApplicationLike","com.tencent.tinker.loader.TinkerLoader",false);? ? }@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)@OverridepublicvoidonBaseContextAttached(Context base){super.onBaseContextAttached(base);// you must install multiDex whatever tinker is installed!MultiDex.install(base);// 安裝tinker// TinkerManager.installTinker(this); 替換成下面Bugly提供的方法Beta.installTinker(this);? ? }@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)publicvoidregisterActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks){? ? ? ? getApplication().registerActivityLifecycleCallbacks(callbacks);? ? }
③ 將啟動Application改為SampleApplication
manifest.png
當(dāng)enableProxyApplication為true時
① 自定義自己的Application為MyApplication蓄坏,將所有要初始化的操作都寫在MyApplication中,當(dāng)然為了以防萬一將初始化操作做到極致丑念,將補丁安裝和多dex在attachBaseContext方法中就開始初始化
@OverrideprotectedvoidattachBaseContext(Context base){super.attachBaseContext(base);// you must install multiDex whatever tinker is installed!MultiDex.install(base);// 安裝tinker// TinkerManager.installTinker(this); 替換成下面Bugly提供的方法Beta.installTinker(this);? ? }
② 在onCreate()方法中可初始化設(shè)置Bugly一些鬼
這里額外說下Application啟動時流程: attachBaseContext(Context) -----> onCreate()
③ 'Applaction'啟動項改回來剑辫,改為我們自定義的
manifest2.png
到這里也就接近完了,現(xiàn)在看看怎么創(chuàng)建基準包和補丁包
如果我們現(xiàn)在要上線一個App為了以防萬一的突發(fā)bug要創(chuàng)建一個基準包上線渠欺,以防有bug即使熱修復(fù)
① 首先在Module:app/build.gradle中引入我們?yōu)闊嵝迯?fù)配置好的腳本插件
依賴插件腳本.png
② 現(xiàn)在就可以按照下圖生成基準包了,圖中1生成的為沒有渠道的椎眯,2很明顯是一個渠道挠将,為騰訊應(yīng)用寶的渠道,根據(jù)自己情況生成基準包
基準包生成.png
看看生成后的目錄编整,其中下面框中的三個中的兩個在上傳基準包和生成補丁包時要用到舔稀,上傳在后面,先把打補丁說了
基準包.png
如果我們的App發(fā)現(xiàn)了bug我們現(xiàn)在需要打補丁
補丁包1.png
補丁包2.png
看看打補丁后生成的目錄掌测,最后生成的7zip.apk就是要上傳的補丁包
補丁包.png
基準包内贮、mapping以及補丁包的上傳
進入官網(wǎng)登陸,沒有項目的可以新建
新建.png
復(fù)制App ID在我們的項目中配置
APPID.png
后面的直接上圖了:
我們生成的基準包
啟動
補丁的發(fā)布
image.png
如果你的應(yīng)用再次全量更新,可以撤回和停止補丁的下發(fā)
image.png
mapping的上傳
為什么要上傳mapping?
mapping是你在項目中配置了混淆時才會有的東西夜郁,主要是為了對比編譯前后方法名等一些量的混淆什燕,能更方便的調(diào)試代碼,定位錯誤位置
image.png
最后貼出以本Demo為例的源碼
MyApplication
publicclassMyApplicationextendsApplication{@OverrideprotectedvoidattachBaseContext(Context base){? ? ? ? super.attachBaseContext(base);// you must install multiDex whatever tinker is installed!MultiDex.install(base);// 安裝tinker// TinkerManager.installTinker(this); 替換成下面Bugly提供的方法Beta.installTinker(this);? ? }? ? @OverridepublicvoidonCreate(){? ? ? ? super.onCreate();? ? ? ? setBugly();? ? }privatevoidsetBugly(){/***** Beta高級設(shè)置 *****//**
? ? ? ? * true表示app啟動自動初始化升級模塊; false不會自動初始化;
? ? ? ? * 開發(fā)者如果擔(dān)心sdk初始化影響app啟動速度竞端,可以設(shè)置為false屎即,
? ? ? ? * 在后面某個時刻手動調(diào)用Beta.init(getApplicationContext(),false);
? ? ? ? */Beta.autoInit =true;/**
? ? ? ? * true表示初始化時自動檢查升級; false表示不會自動檢查升級,需要手動調(diào)用Beta.checkUpgrade()方法;
? ? ? ? */Beta.autoCheckUpgrade =true;/**
? ? ? ? * 設(shè)置升級檢查周期為60s(默認檢查周期為0s),60s內(nèi)SDK不重復(fù)向后臺請求策略);
? ? ? ? */Beta.upgradeCheckPeriod =60*1000;/**
? ? ? ? * 設(shè)置啟動延時為1s(默認延時3s)事富,APP啟動1s后初始化SDK技俐,避免影響APP啟動速度;
? ? ? ? */Beta.initDelay =1*1000;/**
? ? ? ? * 設(shè)置通知欄大圖標(biāo),largeIconId為項目中的圖片資源;
? ? ? ? */Beta.largeIconId = R.mipmap.ic_launcher;/**
? ? ? ? * 設(shè)置狀態(tài)欄小圖標(biāo)统台,smallIconId為項目中的圖片資源Id;
? ? ? ? */Beta.smallIconId = R.mipmap.ic_launcher;/**
? ? ? ? * 設(shè)置更新彈窗默認展示的banner雕擂,defaultBannerId為項目中的圖片資源Id;
? ? ? ? * 當(dāng)后臺配置的banner拉取失敗時顯示此banner,默認不設(shè)置則展示“l(fā)oading“;
? ? ? ? */Beta.defaultBannerId = R.mipmap.ic_launcher;/**
? ? ? ? * 設(shè)置sd卡的Download為更新資源保存目錄;
? ? ? ? * 后續(xù)更新資源會保存在此目錄贱勃,需要在manifest中添加WRITE_EXTERNAL_STORAGE權(quán)限;
? ? ? ? */Beta.storageDir = Environment? ? ? ? ? ? ? ? .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);/**
? ? ? ? * 已經(jīng)確認過的彈窗在APP下次啟動自動檢查更新時會再次顯示;
? ? ? ? */Beta.showInterruptedStrategy =true;/**
? ? ? ? * 只允許在MainActivity上顯示更新彈窗井赌,其他activity上不顯示彈窗; 不設(shè)置會默認所有activity都可以顯示彈窗;
? ? ? ? */Beta.canShowUpgradeActs.add(MainActivity.class);/***** Bugly高級設(shè)置 *****/BuglyStrategy strategy =newBuglyStrategy();/**
? ? ? ? * 設(shè)置app渠道號
? ? ? ? */strategy.setAppChannel(ChannelUtils.getAppMetaData(this,"UMENG_CHANNEL"));//? ? ? ? /**//? ? ? ? * 單個異常上傳統(tǒng)計時的初始化方法//? ? ? ? * @appContext 上下文//? ? ? ? * @crashReportAppId 注冊時申請的APPID//? ? ? ? * @isDebug 自定義日志將會在Logcat中輸出。//? ? ? ? *///? ? ? ? CrashReport.initCrashReport(getApplicationContext(), Commont.APP_ID, false);////? ? ? ? /**//? ? ? ? * 統(tǒng)一初始化Bugly產(chǎn)品募寨,包含Beta//? ? ? ? * @context 上下文//? ? ? ? * @appId 注冊時申請的APPID//? ? ? ? * @isDebug 自定義日志將會在Logcat中輸出族展。//? ? ? ? *//? ? ? ? * 提示:已經(jīng)接入Bugly用戶改用上面的初始化方法,不影響原有的crash上報功能; init方法會自動檢測更新,不需要再手動調(diào)用Beta.checkUpgrade(), 如需增加自動檢查時機可以使用Beta.checkUpgrade(false,false);//? ? ? ? * 參數(shù)1:isManual 用戶手動點擊檢查拔鹰,非用戶點擊操作請傳false//? ? ? ? * 參數(shù)2:isSilence 是否顯示彈窗等交互仪缸,[true:沒有彈窗和toast] [false:有彈窗或toast]//? ? ? ? *///? ? ? ? Bugly.init(this, Commont.APP_ID, true);/**
? ? ? ? * 統(tǒng)一初始化Bugly產(chǎn)品,包含Beta
? ? ? ? * @context 上下文
? ? ? ? * @appId 注冊時申請的APPID
? ? ? ? * @isDebug 自定義日志將會在Logcat中輸出列肢。
? ? ? ? * @strategy Bugly高級設(shè)置
? ? ? ? */Bugly.init(this, Commont.APP_ID,true, strategy);? ? }}
SampleApplication
/**
* 注意:這個類集成TinkerApplication類恰画,這里面不做任何操作,所有Application的代碼都會放到ApplicationLike繼承類當(dāng)中
* 參數(shù)解析
* 參數(shù)1:tinkerFlags 表示Tinker支持的類型 dex only瓷马、library only or all suuport拴还,default: TINKER_ENABLE_ALL
* 參數(shù)2:delegateClassName Application代理類 這里填寫你自定義的ApplicationLike
* 參數(shù)3:loaderClassName Tinker的加載器,使用默認即可
* 參數(shù)4:tinkerLoadVerifyFlag 加載dex或者lib是否驗證md5欧聘,默認為false
*/publicclassSampleApplicationextendsTinkerApplication{publicSampleApplication(){//將‘com.suchengkeji.android.bugly.applications.SampleApplicationLike’替換為自己的包名及路徑super(ShareConstants.TINKER_ENABLE_ALL,"com.suchengkeji.android.bugly.applications.SampleApplicationLike","com.tencent.tinker.loader.TinkerLoader",false);? ? }}
SampleApplicationLike
publicclassSampleApplicationLikeextendsDefaultApplicationLike{publicSampleApplicationLike(Application application,inttinkerFlags,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? boolean tinkerLoadVerifyFlag,longapplicationStartElapsedTime,longapplicationStartMillisTime, Intent tinkerResultIntent){? ? ? ? super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);? ? }? ? @OverridepublicvoidonCreate(){? ? ? ? super.onCreate();// 這里實現(xiàn)SDK初始化片林,appId替換成你的在Bugly平臺申請的appId// 調(diào)試時,將第三個參數(shù)改為true//? ? ? ? Bugly.init(getApplication(), APP_ID, false);setBUG();? ? }privatevoidsetBUG(){/***** Beta高級設(shè)置 *****//**
? ? ? ? * true表示app啟動自動初始化升級模塊; false不會自動初始化;
? ? ? ? * 開發(fā)者如果擔(dān)心sdk初始化影響app啟動速度怀骤,可以設(shè)置為false费封,
? ? ? ? * 在后面某個時刻手動調(diào)用Beta.init(getApplicationContext(),false);
? ? ? ? */Beta.autoInit =true;/**
? ? ? ? * true表示初始化時自動檢查升級; false表示不會自動檢查升級,需要手動調(diào)用Beta.checkUpgrade()方法;
? ? ? ? */Beta.autoCheckUpgrade =true;/**
? ? ? ? * 設(shè)置升級檢查周期為60s(默認檢查周期為0s),60s內(nèi)SDK不重復(fù)向后臺請求策略);
? ? ? ? */Beta.upgradeCheckPeriod =60*1000;/**
? ? ? ? * 設(shè)置啟動延時為1s(默認延時3s)蒋伦,APP啟動1s后初始化SDK弓摘,避免影響APP啟動速度;
? ? ? ? */Beta.initDelay =1*1000;/**
? ? ? ? * 設(shè)置通知欄大圖標(biāo),largeIconId為項目中的圖片資源;
? ? ? ? */Beta.largeIconId = R.mipmap.ic_launcher;/**
? ? ? ? * 設(shè)置狀態(tài)欄小圖標(biāo)痕届,smallIconId為項目中的圖片資源Id;
? ? ? ? */Beta.smallIconId = R.mipmap.ic_launcher;/**
? ? ? ? * 設(shè)置更新彈窗默認展示的banner韧献,defaultBannerId為項目中的圖片資源Id;
? ? ? ? * 當(dāng)后臺配置的banner拉取失敗時顯示此banner末患,默認不設(shè)置則展示“l(fā)oading“;
? ? ? ? */Beta.defaultBannerId = R.mipmap.ic_launcher;/**
? ? ? ? * 設(shè)置sd卡的Download為更新資源保存目錄;
? ? ? ? * 后續(xù)更新資源會保存在此目錄,需要在manifest中添加WRITE_EXTERNAL_STORAGE權(quán)限;
? ? ? ? */Beta.storageDir = Environment? ? ? ? ? ? ? ? .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);/**
? ? ? ? * 已經(jīng)確認過的彈窗在APP下次啟動自動檢查更新時會再次顯示;
? ? ? ? */Beta.showInterruptedStrategy =true;/**
? ? ? ? * 只允許在MainActivity上顯示更新彈窗锤窑,其他activity上不顯示彈窗; 不設(shè)置會默認所有activity都可以顯示彈窗;
? ? ? ? */Beta.canShowUpgradeActs.add(MainActivity.class);/***** Bugly高級設(shè)置 *****/BuglyStrategy strategy =newBuglyStrategy();/**
? ? ? ? * 設(shè)置app渠道號
? ? ? ? * ChannelUtils.getAppMetaData(this, "UMENG_CHANNEL")獲取渠道號
? ? ? ? */strategy.setAppChannel(ChannelUtils.getAppMetaData(getApplication(),"UMENG_CHANNEL"));/***** 統(tǒng)一初始化Bugly產(chǎn)品璧针,包含Beta *****/Bugly.init(getApplication(), Commont.APP_ID,true, strategy);? ? }? ? @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)? ? @OverridepublicvoidonBaseContextAttached(Context base){? ? ? ? super.onBaseContextAttached(base);// you must install multiDex whatever tinker is installed!MultiDex.install(base);// 安裝tinker// TinkerManager.installTinker(this); 替換成下面Bugly提供的方法Beta.installTinker(this);? ? }? ? @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)publicvoidregisterActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks){? ? ? ? getApplication().registerActivityLifecycleCallbacks(callbacks);? ? }}
MainActivity
publicclassMainActivityextendsAppCompatActivityimplementsAdapterView.OnItemClickListener{@BindView(R.id.list_view)? ? ListView listView;? ? String[] strings = {"異常上報","應(yīng)用更新","熱修復(fù)測試"};@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.activity_main);? ? ? ? ButterKnife.bind(this);? ? ? ? setList();? ? }privatevoidsetList(){? ? ? ? listView.setAdapter(newBaseAdapter() {@OverridepublicintgetCount(){returnstrings.length;? ? ? ? ? ? }@OverridepublicObjectgetItem(intposition){returnstrings[position];? ? ? ? ? ? }@OverridepubliclonggetItemId(intposition){returnposition;? ? ? ? ? ? }@OverridepublicViewgetView(intposition, View convertView, ViewGroup parent){@SuppressLint("ViewHolder") View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.list_item, parent,false);? ? ? ? ? ? ? ? TextView textView = (TextView) view.findViewById(R.id.list_item_text);? ? ? ? ? ? ? ? textView.setText(strings[position]);returnview;? ? ? ? ? ? }? ? ? ? });? ? ? ? listView.setOnItemClickListener(this);? ? }@OverridepublicvoidonItemClick(AdapterView parent, View view,intposition,longid){? ? ? ? Log.d(LogUtils.TAG(), position +"");switch(position) {case0:/***** 異常崩潰上傳 *****/CrashReport.testJavaCrash();break;case1:/***** 檢查更新 *****/loadUpgradeInfo();? ? ? ? ? ? ? ? loadAppInfo();? ? ? ? ? ? ? ? Beta.checkUpgrade();break;case2:/***** 崩潰上傳(此處要打修復(fù)補丁) *****/FixBugClass.getTimer(MainActivity.this);//? ? ? ? ? ? ? ? BugClass.getTimer(MainActivity.this);break;? ? ? ? }? ? }@OverrideprotectedvoidonStart(){super.onStart();//添加權(quán)限PermissionGen.with(this)? ? ? ? ? ? ? ? .addRequestCode(Commont.PHOTO_PERMISS)? ? ? ? ? ? ? ? .permissions(? ? ? ? ? ? ? ? ? ? ? ? Manifest.permission.INTERNET,? ? ? ? ? ? ? ? ? ? ? ? Manifest.permission.READ_PHONE_STATE,? ? ? ? ? ? ? ? ? ? ? ? Manifest.permission.ACCESS_NETWORK_STATE,? ? ? ? ? ? ? ? ? ? ? ? Manifest.permission.ACCESS_WIFI_STATE,? ? ? ? ? ? ? ? ? ? ? ? Manifest.permission.READ_LOGS,? ? ? ? ? ? ? ? ? ? ? ? Manifest.permission.WRITE_EXTERNAL_STORAGE)? ? ? ? ? ? ? ? .request();? ? }@PermissionSuccess(requestCode = Commont.PHOTO_PERMISS)publicvoidrequestPhotoSuccess(){//成功之后的處理//.......Log.d(LogUtils.TAG(),"動態(tài)權(quán)限加載成功");? ? }@PermissionFail(requestCode = Commont.PHOTO_PERMISS)publicvoidrequestPhotoFail(){//失敗之后的處理果复,我一般是跳到設(shè)置界面Log.d(LogUtils.TAG(),"動態(tài)權(quán)限加載失敗");? ? }/**
? ? * 獲取升級信息
? ? */privatevoidloadUpgradeInfo(){/***** 獲取升級信息 *****/UpgradeInfo upgradeInfo = Beta.getUpgradeInfo();if(upgradeInfo ==null) {? ? ? ? ? ? Log.d(LogUtils.TAG(),"無升級信息");return;? ? ? ? }? ? ? ? StringBuilder info =newStringBuilder();? ? ? ? info.append("id: ").append(upgradeInfo.id).append("\n");? ? ? ? info.append("標(biāo)題: ").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("發(fā)布時間: ").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("彈窗次數(shù): ").append(upgradeInfo.popTimes).append("\n");? ? ? ? info.append("發(fā)布類型(0:測試 1:正式): ").append(upgradeInfo.publishType).append("\n");? ? ? ? info.append("彈窗類型(1:建議 2:強制 3:手工): ").append(upgradeInfo.upgradeType);? ? ? ? Log.d(LogUtils.TAG(), info +"");? ? }privatevoidloadAppInfo(){try{? ? ? ? ? ? StringBuilder info =newStringBuilder();? ? ? ? ? ? PackageInfo packageInfo =this.getPackageManager().getPackageInfo(this.getPackageName(),? ? ? ? ? ? ? ? ? ? ? ? ? ? PackageManager.GET_CONFIGURATIONS);intversionCode = packageInfo.versionCode;? ? ? ? ? ? String versionName = packageInfo.versionName;? ? ? ? ? ? info.append("appid: ").append(Commont.APP_ID).append(" ")? ? ? ? ? ? ? ? ? ? .append("channel: ")? ? ? ? ? ? ? ? ? ? .append(ChannelUtils.getAppMetaData(MainActivity.this,"UMENG_CHANNEL"))? ? ? ? ? ? ? ? ? ? .append(" ")? ? ? ? ? ? ? ? ? ? .append("version: ").append(versionName).append(".").append(versionCode);? ? ? ? ? ? Log.d(LogUtils.TAG(), info +"");? ? ? ? }catch(Exception e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }}
Commont
publicclassCommont{/***? 騰訊Bugly產(chǎn)品ID? ***/publicstaticfinalString APP_ID ="5e****70c08";/***? 權(quán)限處理標(biāo)識? ***/publicstaticfinalint PHOTO_PERMISS =100;}
ChannelUtils
/**
? ? * 獲取app當(dāng)前的渠道號或application中指定的meta-data
? ? *
? ? * @return 如果沒有獲取成功(沒有對應(yīng)值陈莽,或者異常),則返回值為空
? ? */publicstaticStringgetAppMetaData(Context context,Stringkey) {if(context ==null|| TextUtils.isEmpty(key)) {returnnull;? ? ? ? }StringchannelNumber =null;try{? ? ? ? ? ? PackageManager packageManager = context.getPackageManager();if(packageManager !=null) {? ? ? ? ? ? ? ? ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);if(applicationInfo !=null) {if(applicationInfo.metaData !=null) {? ? ? ? ? ? ? ? ? ? ? ? channelNumber = applicationInfo.metaData.getString(key);? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }catch(PackageManager.NameNotFoundException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }returnchannelNumber;? ? }//? ? ? 在需要的地方調(diào)用//? ? String channelNumber = getAppMetaData(getBaseContext(), "UMENG_CHANNEL");//獲取app當(dāng)前的渠道號}
BugClass
/**
* 有錯誤的bug類
*/publicclassBugClass{privatestaticString timeType ="yyyy-MM-dd HH:mm:ss";publicstaticvoidgetTimer(Context c){? ? ? ? String date =null;? ? ? ? Log.d(LogUtils.TAG(),"上線后的緊急BUG");? ? ? ? Log.d(LogUtils.TAG(), date.length() +"");//此處留一個空指針bug@SuppressLint("SimpleDateFormat")? ? ? ? SimpleDateFormat sDateFormat =newSimpleDateFormat(timeType);? ? ? ? date = sDateFormat.format(newjava.util.Date());? ? ? ? Toast.makeText(c, date, Toast.LENGTH_SHORT).show();? ? }}
FixBugClass
/**
* 修復(fù)過bug的補丁類
*/publicclassFixBugClass{privatestaticString timeType ="yyyy-MM-dd HH:mm:ss";publicstaticvoidgetTimer(Context c){? ? ? ? String date =null;? ? ? ? Log.d(LogUtils.TAG(),"上線后的緊急BUG修復(fù)補丁");//Log.d(LogUtils.TAG(), date.length() + "");//去掉此處的bug@SuppressLint("SimpleDateFormat")? ? ? ? SimpleDateFormat sDateFormat =newSimpleDateFormat(timeType);? ? ? ? date = sDateFormat.format(newjava.util.Date());? ? ? ? Toast.makeText(c, date, Toast.LENGTH_SHORT).show();? ? }}
注意點虽抄,打完補丁之后在運行應(yīng)用是走搁,第一次仍然會崩潰,不過在崩潰時會自己打補丁修復(fù)迈窟,第二次以后就好了
好了私植,到這里就完了,此文章僅供參考车酣,具體以官方文檔為準,有更好的思路或方法曲稼,請不吝告知,如有錯誤湖员,請及時指出贫悄。。
作者:一s獨秀
鏈接:http://www.reibang.com/p/fbab774dab8e
來源:簡書
簡書著作權(quán)歸作者所有娘摔,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處窄坦。