前言
目前从我了解的渠道来看,bugly接入的方式主要有两种:
- 使用“年久失修”的Cocos plugin
- 使用js的window.onError接口
本文要介绍的是第三种方式,c++抛出异常,原生进行上报,这种方式的好处是:
- 相对Cocos plugin而言,无需另外接入一套c++的sdk,并且安卓无需修改mk文件。
- 相对js脚本报错,这种方式更加灵活,且更容易做成公共模块
接入
创建CCC工程并编译原生项目
首先我们创建一个CCC工程
脚本挂载如下
可以看到我们并没有拖入label节点,所以这段代码是会报错的,然后我们构建原生工程,因为这里要介绍ios和android的接入,所以我们构建了android和ios两个平台,构建后的目录
我使用的CCC是2.4.X版本,不同版本会略有差异,但不影响接入。
Bugly原生SDK接入
安卓端接入
如果你对安卓比较熟悉,请参考官方文档接入,如果你不是很熟悉,可以参考我的接入方式
集成sdk
打开Module(就是app目录)的build.gradle,添加bugly的依赖,添加完毕后的代码
1 2 3 4 5 6 7 8 9
| dependencies { implementation fileTree(dir: '../libs', include: ['*.jar','*.aar']) implementation fileTree(dir: 'libs', include: ['*.jar','*.aar']) implementation fileTree(dir: "/Applications/CocosCreator/Creator/2.4.3-rc.1/CocosCreator.app/Contents/Resources/cocos2d-x/cocos/platform/android/java/libs", include: ['*.jar']) implementation project(':libcocos2dx') implementation 'com.tencent.bugly:crashreport:latest.release' implementation 'com.tencent.bugly:nativecrashreport:latest.release' }
|
这里的latest.release指最新版本,推荐各位指定版本,这样以后编译速度会快点。
初始化及方法实现
看到很多对安卓不太了解的开发者,根据官方事例,将许多方法写到了AppActivity中,其实这样既不美观也不利于后期维护,推荐各位将模块划分清楚,这里我在包下面建立一个BuglyAgent.java文件,来实现bugly的功能。
本demo主要介绍异常上报,所以实现了两个方法:初始化及异常上报。详细代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package cn.xyzzlky.bugly;
import android.content.Context;
import com.tencent.bugly.crashreport.CrashReport;
import java.util.Map;
public class BuglyAgent { public static void initSDK(Context context, String appId) { CrashReport.initCrashReport(context.getApplicationContext(), appId, false); }
public static void postException(int category, String name, String reason, String stack){ postException(category, name, reason, stack, null); }
public static void postException(int category, String name, String reason, String stack, Map<String, String> extraInfo){ CrashReport.postException(category, name, reason, stack, extraInfo); } }
|
方便c++那边的代码调用,所以我直接将最后一个参数设置为null了。别忘了在合适的位置(比如AppActivity)初始化SDK
1 2 3 4 5 6 7 8 9
| public class AppActivity extends Cocos2dxActivity {
@Override protected void onCreate(Bundle savedInstanceState) { BuglyAgent.initSDK(this, "Bugly的APPID"); } }
|
Android的原生接入到这里就结束了,你只要记住你实现异常上报方法的包名、类名和方法名就可以了,比如我这里的包名是,cn.xyzzlky.bugly,类名是BuglyAgent,方法名是postException。
IOS接入
如上一致,如果你对ios比较熟悉,请直接参考官方文档接入,如果不熟悉,可以参考我的方法
SDK接入
- 如果你已经安装了cocoa,请直接pod接入
- 如果没有,你可以安装cocoa,然后用方法1,也可以下载下来,然后参考官方文档,注意选项和依赖库
初始化及方法实现
相关代码不多介绍,直接贴出来
BuglyAgent.h代码
1 2 3 4 5 6
| @interface BuglyAgent : NSObject
+ (void)initSdk:(NSString *)appId; + (void)reportExceptionWithCategory:(NSUInteger)category name:(NSString *)name reason:(NSString *)reason callStack:(NSArray *)stackArray extraInfo:(NSDictionary *)info terminateApp:(BOOL)terminate;
@end
|
BuglyAgent.mm 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #import "BuglyAgent.h" #import <Bugly/Bugly.h>
@implementation BuglyAgent
+ (void)initSdk:(NSString *)appId { [Bugly startWithAppId:appId]; }
+ (void)reportExceptionWithCategory:(NSUInteger)category name:(NSString *)name reason:(NSString *)reason callStack:(NSArray *)stackArray extraInfo:(NSDictionary *)info terminateApp:(BOOL)terminate { [Bugly reportExceptionWithCategory:category name:name reason:reason callStack:stackArray extraInfo:info terminateApp:terminate]; }
@end
|
同样的我们在适当的位置(比如AppController.m)初始化sdk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
#import "BuglyAgent.h"
@implementation AppController
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [BuglyAgent initSdk:@"Bugly的APPID"]; }
|
IOS的接入方式也完毕了,同样记住类名和方法名,比如我的类名是BuglyAgent,方法名是reportExceptionWithCategory:name:reason:callStack:extraInfo:terminateApp:
C++代码修改
捕获异常
因为CCC很贴心的暴露了异常捕获的接口,所以我们直接拿来用就好了,位置在**/frameworks/runtime-src/Class/AppDelegate.cpp中,如下:
1 2 3 4
| se->setExceptionCallback([](const char *location, const char *message, const char *stack) { cocos2d::log("\nUncaught Exception:\n - location : %s\n - msg : %s\n - detail : \n %s\n", location, message, stack); });
|
调用java方法
c++调用java的方法,使用CCC的JniHelper类就好了,调用方式
1
| JniHelper::callStaticVoidMethod("类名", "方法名", ...参数);
|
这里注意类名是全路径,并且包名的”.”需要用”/“代替,比如我们调用的方法为
1
| JniHelper::callStaticVoidMethod("cn/xyzzlky.bugly/BuglyAgent", "postException", 5, "JSError", message, stack);
|
参数解释:
- 5是JS错误的标志,Bugly定义的,参考com.tencent.bugly.BuglyStrategy。
- 错误的名字,自定义是可以的,Bugly是这么定义的。
- 错误的原因
- 错误的堆栈
如上,安卓已经接入完毕(其实还有个步骤,下面介绍),直接运行,bugly就可以看到错误了。
调用OC方法
安卓接入了,那ios呢?是不是也直接调用oc呢?不好意思,c++不支持直接调用oc,怎么办?所以我们要写一个c的桥接层,来完成oc的接入。
先把桥接代码奉上
CrashReport.h 代码
1 2 3 4 5 6 7 8 9
| class CrashReport { public:
static void reportException(const char* msg, const char* traceback); CrashReport(); };
|
CrashReport.mm 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include "CrashReport.h"
#define BUGLY_CLASS @"BuglyAgent" #define BUGLY_METHOD_EXCEPTION @"reportExceptionWithCategory:name:reason:callStack:extraInfo:terminateApp:"
CrashReport::CrashReport(){ }
void CrashReport::reportException(const char* msg, const char* traceback) { Class clazz = NSClassFromString(BUGLY_CLASS); if (clazz) { SEL selector = NSSelectorFromString(BUGLY_METHOD_EXCEPTION); NSMethodSignature* signature = [clazz methodSignatureForSelector:selector]; NSInteger category = 5; NSString *name = @"JSError"; NSString *reason = NULL == msg ? @"" : @(msg); NSString *track = NULL == traceback ? @"" : @(traceback); NSArray *trackArray = [track componentsSeparatedByString:@"\n"]; NSDictionary *info = nil; BOOL terminate = NO; if (signature) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature]; if (invocation) { [invocation setTarget:clazz]; [invocation setSelector:selector]; [invocation setArgument:&category atIndex:2]; [invocation setArgument:&name atIndex:3]; [invocation setArgument:&reason atIndex:4]; [invocation setArgument:&trackArray atIndex:5]; [invocation setArgument:&info atIndex:6]; [invocation setArgument:&terminate atIndex:7]; [invocation invoke]; } } } }
|
此处的代码含义,与java一致。
然后就可以供c++调用
1 2 3 4 5
| #include "CrashReport.h"
CrashReport::reportException(message, stack);
|
同样,运行ios,后台就可以看到日志了。
最后一步
是不是发现,接完ios,安卓貌似报错了,提示#include "CrashReport.h"
有错误,对了,因为我们没有进行mk文件修改,在安卓中,我们是没法调用这个方法的,所以我们还要对平台区分,同时为了维护方便,我们把安卓的类名进行常量定义,所以头文件变为
1 2 3 4 5
| #if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #define BUGLY_CLZ_NAME "cn/xyzzlky/bugly/BuglyAgent" #elif(CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "CrashReport.h" #endif
|
而调用的地方同理
1 2 3 4 5 6 7 8 9 10 11
| se->setExceptionCallback([](const char *location, const char *message, const char *stack) { cocos2d::log("\nUncaught Exception:\n - location : %s\n - msg : %s\n - detail : \n %s\n", location, message, stack); #if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) JniHelper::callStaticVoidMethod(BUGLY_CLZ_NAME, "postException", 5, "JSError", message, stack); #elif(CC_TARGET_PLATFORM == CC_PLATFORM_IOS) CrashReport::reportException(message, stack); #endif });
|
这里,我们的工作总算接入完毕了。
效果查看
这里有些建议
- 我这js没有加密,如果加密了,原js文件在”js backups (useful for debugging)”,建议每个线上包,将这个备份下
- 因为可能有热更,所以js报错与app版本可能不一致,推荐各位将js的版本也上报给bugly
总结
一路下来,其实并没有什么难点,而其中原生调用方法,可能各位没看到,其实一开始我也不知道还有这些方法,是根据CocosPlugin的api反推的,所以一些东西还是要多去探索,相关demo会在公众号上传。最后,原创不易,欢迎转载。如果此文对你有所帮助,可以给点打赏。
原文链接: https://blog.xyzzlky.cn/posts/13005/
版权声明: 转载请注明出处.