Project introduction

In the IT industry, especially for game projects, there is no way to test all possible situations before launching, so we often need some online bug collection tools, and Tencent’s Bugly is a very good tool. This project is to integrate Bugly into CocosCreator, the latest development tool for Cocos, a popular cross-platform game engine.

Project background

As far as I know, there are two main ways of bugly access:

  • Use the “outdated” Cocos plugin
  • Use js’s window.onerror interface

This article will introduce the third way, c++ throws an exception and reports it natively. The benefits of this way are:

  1. In contrast to the Cocos plugin, there is no need to plugin a separate c++ SDK, and android does not need to modify mk files.
  2. This method is more flexible than js script error reporting, and is easier to make a public module.

Access to the steps

Create CCC project and compile native project

First we create a CCC project

The script is mounted as follows

As you can see, we did not drag in the label node, so this code will report an error. Then we build the native project, because we need to introduce ios and Android access here, so we build android and ios platform, after building the directory

The CCC I use is 2.4.X version, which varies slightly from version to version, but does not affect access.

Android terminal access

If you are familiar with Android, please refer to the official document for access. If you are not familiar with Android, please refer to my access method

To integrate the SDK

Open build.gradle in Module(app directory), add bugly dependencies, and add the code

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

Copy the code

“Latest. release” indicates the latest version. It is recommended that you specify the latest version, so that later compilation will be faster.

Initialization and method implementation

See a lot of developers who are not familiar with Android, according to the official example, many methods written in AppActivity, in fact, this is not beautiful and not conducive to the later maintenance, recommend you to clear module division, here I create a buglyAgent. Java file under the package, to achieve the function of Bugly. This demo mainly introduces exception reporting, so two methods are implemented: initialization and exception reporting. Detailed code

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); }}Copy the code

So I set the last parameter to null. Don’t forget to initialize the SDK in the appropriate place (such as AppActivity)

public class AppActivity extends Cocos2dxActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
		// ...
        BuglyAgent.initSDK(this."Bugly的APPID");
        // ...}}Copy the code

Android’s native access ends here. You just need to remember the package name, class name, and method name of the exception reporting method that you implemented, such as in my case, cn.xyzzlky.bugly, BuglyAgent, and postException.

IOS access

The same as above, if you are familiar with ios, please refer to the official document to access, if not, you can refer to my method

The SDK access

  1. If you have Cocoa installed, connect directly to POD
  2. If not, you can either install Cocoa and use method 1, or download it and refer to the official documentation, noting the options and dependency libraries

Initialization and method implementation

The buglyAgent.h code is posted directly

@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
Copy the code

BuglyAgent. Mm code

#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
Copy the code

Again, we initialize the SDK at the appropriate location (such as appController.m)

// ...

#import "BuglyAgent.h"

// ...
@implementation AppController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // ...
    
    [BuglyAgent initSdk:@ "Bugly APPID"];
    
    // ...
}
Copy the code

IOS way of accessing to the end, also remember the name of the class and method name, my class name is BuglyAgent, for example, the method name is reportExceptionWithCategory: name: “reason: callStack: extraInfo: terminateApp:

C++ code modification

Catch exceptions

Because CCC is very close the interface exposed the exception handling, so we directly use, location in the * * / frameworks/runtime – SRC/Class/AppDelegate. CPP, as follows:

    se->setExceptionCallback([] (const char *location, const char *message, const char *stack) {
        // Send exception information to server like Tencent Bugly.
        cocos2d::log("\nUncaught Exception:\n - location : %s\n - msg : %s\n - detail : \n %s\n", location, message, stack);   
    });
Copy the code

Calling a Java method

C++ call Java methods, using CCC JniHelper class is good, call way

 JniHelper::callStaticVoidMethod("The name of the class"."Method name". Parameters);Copy the code

Note here that the class name is the full path, and the “.” of the package name needs to be replaced by “/”, for example, the method we call is

JniHelper::callStaticVoidMethod("cn/xyzzlky.bugly/BuglyAgent"."postException".5."JSError", message, stack);
Copy the code

Parameter Description:

  • 5 is a sign of JS error, Bugly defined, reference com. Tencent. Bugly. BuglyStrategy.
  • Wrong name, custom is ok, Bugly defined it this way.
  • The wrong reason
  • Wrong stack

As mentioned above, Android is already plugged in. Run it directly and Bugly will see the error.

Calling the OC method

Android is in, what about ios? Do you call oc directly as well? C++ does not support direct calls to oc. So we need to write a bridge layer of C to complete the access of OC. Start by feeding the bridge code to the CrashReport.h code

class CrashReport
{
public:

    static void reportException(const char* msg, const char* traceback);
    
    CrashReport();
};

Copy the code

CrashReport. Mm code

#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]; }}}}Copy the code

The code meaning here is consistent with Java. It can then be called by c++

// ...
#include "CrashReport.h"
// ...
	CrashReport::reportException(message, stack);
// ...
Copy the code

Also, running ios, you can see the logs in the background.

The last step

#include “crashReport. h” error, by the way, because we did not modify mk file, in Android, we can not call this method, so we need to distinguish platform, at the same time for convenient maintenance, We’re going to make the android class name constant, so the header becomes

#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
Copy the code

And the same is true of where you call it

    se->setExceptionCallback([] (const char *location, const char *message, const char *stack) {
        // Send exception information to server like Tencent Bugly.
        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
        
    });
Copy the code

Here, we’re finally done with our work.

See the effect

Here are some suggestions

  1. Backups of JAVASCRIPT files are in “JS Backups (Useful for debugging)”. Backups are recommended for every online package
  2. Js error may be inconsistent with app version because there may be a heat attack. It is recommended that you also report the JS version to Bugly

conclusion

I didn’t know there were any native call methods, but they are based on the CocosPlugin API, so there are some things to explore.

This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign