騰訊Bugly腺毫,為移動(dòng)開發(fā)者提供專業(yè)的異常上報(bào)和運(yùn)營(yíng)統(tǒng)計(jì),幫助開發(fā)者快速發(fā)現(xiàn)并解決異常个唧,同時(shí)掌握產(chǎn)品運(yùn)營(yíng)動(dòng)態(tài)渠退,及時(shí)跟進(jìn)用戶反饋忙迁。
最近 iOS 項(xiàng)目中集成了 Bugly 脐彩,集成該框架的過(guò)程中,90%的問題通過(guò)仔細(xì)閱讀 Bugly 官方文檔 都可以解決姊扔。
?? 本文并不會(huì)教你如何注冊(cè)申請(qǐng) Bugly 帳號(hào)惠奸,也不會(huì)教你如何集成 Bugly SDK,因?yàn)楣俜轿臋n是最權(quán)威的恰梢,這里只是記錄幾個(gè)注意點(diǎn)(傳說(shuō)中的坑3看ā)。
初始化 Bugly 的注意點(diǎn)
方式一删豺,最簡(jiǎn)單
在工程AppDelegate.m
的application:didFinishLaunchingWithOptions:
方法中初始化:
#import "AppDelegate.h"
#import <Bugly/Bugly.h>
// Bugly 帳號(hào)中創(chuàng)建產(chǎn)品后,產(chǎn)品信息中的 AppId
static NSString *const KBuglyAppId = @"**********";
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Bugly startWithAppId:KBuglyAppId];
return YES;
}
方式二愧怜,官方文檔中的"高級(jí)功能"
Bugly支持讀取 Info.plist
文件讀取SDK初始化參數(shù)呀页,可配置的參數(shù)如下:
- Appid
- Key: BuglyAppIDString
- Value: 字符串類型
- 渠道標(biāo)識(shí)
- Key: BuglyAppChannelString
- Value: 字符串類型
- 版本信息
- Key: BuglyAppVersionString
- Value: 字符串類型
- 開啟Debug信息顯示
- Key: BuglyDebugEnable
- Value: BOOL類型
我們?cè)O(shè)置 Info.plist
文件如下:
如下初始化方式,則會(huì)讀取Info.plist
內(nèi)添加的key-value配置進(jìn)行SDK初始化:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 讀取Info.plist中的參數(shù)初始化SDK
[Bugly startWithAppId:nil];
return YES;
}
以上方式初始化 Bugly 確實(shí)沒問題拥坛,可是默認(rèn)的 BuglyConfig
還有幾個(gè)監(jiān)控開關(guān)沒開蓬蝶,我也想順便開啟一下:
// 可能存在問題的代碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BuglyConfig *config = [[BuglyConfig alloc] init];
config.blockMonitorEnable = YES; // 卡頓監(jiān)控開關(guān),默認(rèn)關(guān)閉
config.blockMonitorTimeout = 5;
config.unexpectedTerminatingDetectionEnable = YES; // 非正常退出事件記錄開關(guān)猜惋,默認(rèn)關(guān)閉
// 讀取Info.plist中的參數(shù)初始化SDK
[Bugly startWithAppId:nil config:config];
return YES;
}
?? 注意丸氛,這樣初始化就會(huì)有問題,如果配置了Info.plist
字段著摔,又在初始化時(shí)傳入了自定義的 BuglyConfig
實(shí)例缓窜,那么在 Info.plist
中配置的 BuglyAppChannelString
、BuglyAppVersionString
谍咆、BuglyDebugEnable
會(huì)因?yàn)楸桓采w而失效禾锤。
方式三,自定義配置
如果需要自定義配置 BuglyConfig
實(shí)例摹察,這里就不建議不同時(shí)配置Info.plist
字段(或者可以只配置BuglyAppIDString
字段):
完整代碼:
#import "AppDelegate.h"
#import <Bugly/Bugly.h>
static NSString *const KBuglyAppId = @"**********";
@implementation AppDelegate
#pragma mark - UIApplicationDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self p_configureForBugly];
return YES;
}
#pragma mark - Private
- (void)p_configureForBugly {
BuglyConfig *config = [[BuglyConfig alloc] init];
config.channel = @"App Store";
config.blockMonitorEnable = YES; // 卡頓監(jiān)控開關(guān)恩掷,默認(rèn)關(guān)閉
config.blockMonitorTimeout = 5;
config.unexpectedTerminatingDetectionEnable = YES; // 非正常退出事件記錄開關(guān),默認(rèn)關(guān)閉
config.delegate = self;
#ifdef DEBUG
config.debugMode = YES; // debug 模式下供嚎,開啟調(diào)試模式
config.reportLogLevel = BuglyLogLevelVerbose; // 設(shè)置打印日志級(jí)別
#else
config.debugMode = NO; // release 模式下黄娘,關(guān)閉調(diào)試模式
config.reportLogLevel = BuglyLogLevelWarn; // 設(shè)置自定義日志上報(bào)的級(jí)別,默認(rèn)不上報(bào)自定義日志
#endif
[Bugly startWithAppId:KBuglyAppId config:config];
}
?? 如果你配置完 Bugly 之后克滴,
BLYLogError(fmt, ...)
BLYLogWarn(fmt, ...)
BLYLogInfo(fmt, ...)
BLYLogDebug(fmt, ...)
BLYLogVerbose(fmt, ...)
如上的日志不會(huì)在控制臺(tái)輸出逼争,或者部分類型日志不會(huì)輸出,那你應(yīng)該注意到需要設(shè)置這段代碼:
#ifdef DEBUG
config.debugMode = YES; // debug 模式下劝赔,開啟調(diào)試模式
config.reportLogLevel = BuglyLogLevelVerbose; // 設(shè)置打印日志級(jí)別
#else
config.debugMode = NO; // release 模式下氮凝,關(guān)閉調(diào)試模式
config.reportLogLevel = BuglyLogLevelWarn; // 設(shè)置自定義日志上報(bào)的級(jí)別,默認(rèn)不上報(bào)自定義日志
#endif
關(guān)于 SDK 回調(diào)問題
BuglyConfig.h
文件中還有一個(gè)可以遵守的協(xié)議 BuglyDelegate
用于在系統(tǒng)崩潰時(shí)可以同時(shí)上傳自定義數(shù)據(jù)望忆。
@protocol BuglyDelegate <NSObject>
@optional
/**
* 發(fā)生異常時(shí)回調(diào)
*
* @param exception 異常信息
*
* @return 返回需上報(bào)記錄罩阵,隨異常上報(bào)一起上報(bào)
*/
- (NSString * BLY_NULLABLE)attachmentForException:(NSException * BLY_NULLABLE)exception;
@end
我遇到的問題是竿秆,出于代碼清晰的目的,將應(yīng)用初始化配置稿壁、第三方框架初始化配置幽钢、推送、分享相關(guān)的代碼都放在分類中了:
并且在分類中遵守了 <BuglyDelegate>
協(xié)議傅是,也實(shí)現(xiàn)了簡(jiǎn)單的回調(diào)方法:
- (NSString * BLY_NULLABLE)attachmentForException:(NSException * BLY_NULLABLE)exception {
return @"需要上傳的數(shù)據(jù)...";
}
正常情況下匪燕,應(yīng)用發(fā)生崩潰后,Bugly 會(huì)立即上報(bào)異常喧笔,同時(shí)在「崩潰分析」-「跟蹤數(shù)據(jù)」-「附件信息」中顯示回調(diào)方法中上傳的數(shù)據(jù)文件 crash_attach.log:
實(shí)際上把回調(diào)代碼寫在分類中時(shí)帽驯,應(yīng)用發(fā)生崩潰時(shí),并不會(huì)觸發(fā)協(xié)議方法书闸。所以務(wù)必要把所有與 Bugly 初始化及回調(diào)代碼寫在 AppDelegate.m
文件中尼变。
最后,附上 Bugly 集成的完整正確代碼示例:
#import "AppDelegate.h"
#import <Bugly/Bugly.h>
static NSString *const KBuglyAppId = @"**********";
@interface AppDelegate () <BuglyDelegate>
@end
@implementation AppDelegate
#pragma mark - UIApplicationDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self p_configureForBugly];
return YES;
}
#pragma mark - Private
- (void)p_configureForBugly {
BuglyConfig *config = [[BuglyConfig alloc] init];
config.channel = @"App Store";
config.blockMonitorEnable = YES; // 卡頓監(jiān)控開關(guān)浆劲,默認(rèn)關(guān)閉
config.blockMonitorTimeout = 5;
config.unexpectedTerminatingDetectionEnable = YES; // 非正常退出事件記錄開關(guān)嫌术,默認(rèn)關(guān)閉
config.delegate = self;
#ifdef DEBUG
config.debugMode = YES; // debug 模式下,開啟調(diào)試模式
config.reportLogLevel = BuglyLogLevelVerbose; // 設(shè)置自定義日志上報(bào)的級(jí)別牌借,默認(rèn)不上報(bào)自定義日志
#else
config.debugMode = NO; // release 模式下度气,關(guān)閉調(diào)試模式
config.reportLogLevel = BuglyLogLevelWarn;
#endif
[Bugly startWithAppId:KBuglyAppId config:config];
}
#pragma mark - BuglyDelegate
- (NSString * BLY_NULLABLE)attachmentForException:(NSException * BLY_NULLABLE)exception {
NSDictionary *dictionary = @{@"Name":exception.name,
@"Reason":exception.reason};
return [NSString stringWithFormat:@"Exception:%@",dictionary];
}
@end
過(guò)早的優(yōu)化是萬(wàn)惡之源∨虮ǎ——高德納
對(duì)依賴進(jìn)行抽象化和封裝
參考對(duì) Flurry 的封裝方法
#import <Foundation/Foundation.h>
/**
封裝 Flurry 埋點(diǎn)
*/
@interface HPInstrumentation : NSObject
+(void)startWithAPIKey:(NSString *)apiKey;
+(void)logEvent:(NSString *)name;
+(void)logEvent:(NSString *)name withParams:(NSDictionary *)params;
+(void)logPageViewForTabBarController:(UITabBarController *)vc;
@end
#import "HPInstrumentation.h"
#import "Flurry.h"
@implementation HPInstrumentation
+(void)startWithAPIKey:(NSString *)apiKey
{
[Flurry startSession:apiKey];
}
+(void)logEvent:(NSString *)name
{
NSLog(@"<HPInst> %@", name);
[Flurry logEvent:name];
}
+(void)logEvent:(NSString *)name withParams:(NSDictionary *)params
{
NSLog(@"<HPInst> %@ -> %@", name, params);
[Flurry logEvent:name withParameters:params];
}
+(void)logPageViewForTabBarController:(UITabBarController *)vc {
NSLog(@"<HPInst> PV: %@", [vc class]);
[Flurry logAllPageViewsForTarget:vc];
}
@end
嘗試對(duì) Bugly 進(jìn)行封裝
#import <Foundation/Foundation.h>
/**
對(duì)依賴進(jìn)行抽象化和封裝
如果直接在項(xiàng)目中使用崩潰報(bào)告系統(tǒng)磷籍,那么埋點(diǎn)會(huì)分散在項(xiàng)目中的各個(gè)角落,不利于后期替換或更改现柠。
低耦合:增加一個(gè)中間層之后择示,可以隨時(shí)對(duì)依賴的第三方框架進(jìn)行切換,無(wú)須再去修改分散在各個(gè)類中的代碼晒旅。
*/
@interface HQLInstrumentation : NSObject
+ (void)logEvent:(NSString *)name;
+ (void)logEvent:(NSString *)name withParamenters:(NSString *)parameters;
@end
#import "HQLInstrumentation.h"
#import <Bugly/Bugly.h>
@implementation HQLInstrumentation
+ (void)logEvent:(NSString *)name {
[BuglyLog log:@"%@", name];
}
+ (void)logEvent:(NSString *)name withParamenters:(NSString *)parameters {
[BuglyLog log:@"%@:%@",name,parameters];
}
@end
相關(guān)框架
- JJException - 保護(hù) Objective-C 應(yīng)用不閃退的框架栅盲。
- KSCrash - iOS崩潰報(bào)告框架
- PLCrashReporter - Reliable, open-source crash reporting for iOS and Mac OS X.