在cocos2dx纯lua脚本逻辑开发中,由于脚本与安卓之间的沟通问题,使得在测试中,脚本错误难以被报告和记录,给测试工作带来了很大的不便。


本文通过lua调用c++方法以及jni的使用,使lua提供的错误信息能够在安卓应用的层面被报告和记录。

主要步骤:

一、信息从lua发送到c++并处理:

1、在项目的c++代码中,编写方法并暴露给lua,使得该方法可以接受来自lua的文本信息并进行处理。

2、在打印错误的方法中,调用c++暴露的接口,传递错误信息并进行处理。

3、如有需要,在发送错误信息的同事暂停游戏。

二、信息从c++发送到java并处理:

1、在项目的java代码中,编写方法,该方法以文本信息为参数并对该信息进行处理。

2、在项目的c++代码中,编写方法,该方法被步骤一中c++代码的方法所调用(或在原方法上处理),该方法使用jni调用java中的方法并将错误信息传递。

三、java对文本进行处理

1、在项目的java代码中,编写方法,被步骤二中的java方法调用(或原方法继续处理),将错误信息打印到文件同时创建对话框显示错误信息。


下面对该解决方案的详细步骤进行介绍:

一、

1、在c++代码的项目目录下,创建一个类LogToAndroid,该类具有一个方法WriteLog,参数是一个字符串,用于被lua调用传入错误信息进行处理。

void  LogToAndroid::WriteLog(string str)
{
	cout<<"receive a log from lua"<<endl;
	cout<<str<<endl;
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
<span style="white-space:pre">	</span>//此处执行步骤二调用jni方法
#else 
	
#endif

}
以上是简单表示的代码,从代码中我们可以看到
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
这个宏,表示当在安卓上进行调试时,执行中间的方法。

然后我们使用tolua工具,将这个类的方法暴露给lua,具体方法可以参考其他教程。

最后得到一个包含int register_all_logtoandroid(lua_State* tolua_S);方法的注册文件,在AppDelegate.cpp中直接或间接地调用这个方法,注册LogToAndroid这个类给lua使用。


2、然后进入到lua脚本,找到main.lua文件,里面有一个__G__TRACKBACK__的函数,这个函数我们可以在cocos引擎的luaStack.cpp中看到它的身影。

大概的作用是,当lua报错给lua引擎时,c++层通过操作lua的stack调用lua中这个方法对错误信息进行打印等处理。这个方法的具体调用路径等我弄明白了再做进一步解释,可能在有些版本中是没有的,那么这个步骤可能需要找到替代方法(具体思路是在lua或者c++中找到打印错误的地方,截获错误信息并调用下一步骤的方法)。

function __G__TRACKBACK__(errorMessage)
    local a = "----------------------------------------".."\n"
    local b = "LUA ERROR: " .. tostring(errorMessage) .. "\n"
    local c = debug.traceback("",2).."\n"

    print(a..b..c..a)
    LogToAndroid:WriteLog(a..b..c..a)
end
这个方法在quick中的作用是通过print打印错误信息,这里对错误信息进行整理后,又调用了
LogToAndroid:WriteLog(a..b..c..a)
这个方法,也就是刚才c++中暴露的方法。

至此,我们如果运行代码,可以看到c++将lua提供的错误信息又打印到了命令行界面上,这个时候错误信息以及到达c++层了。

PS:当然,截获错误信息的方法不止这一种,也可以绕过暴露c++接口这个步骤,例如直接在c++中截也是可行的,但是可能会多处改用较底层的逻辑,影响范围较大,以上方法是一种处理起来不方便但是后期改动很方便的方法(可以在lua中随便组合信息写入log中,只要调用LogToAndroid:WriteLog()即可)。


二、

1、在项目的java代码中,编写方法,该方法以文本信息为参数并对该信息进行处理。

这一步我们打开项目的java代码,可以看到AppActivity.java这个文件,先不动这个文件,在这个目录下创建一个类,我这里命名为WriteLog,创建一个静态方法

public static String WritetoLogFile(String str) throws IOException
    {
        String rstr = "i received your message";
<span style="white-space:pre">	</span>//处理错误信息
        //showTipDialog("脚本错误",str);
<span style="white-space:pre">	</span>return rstr;
    }
其中处理代码被省略,这个方法以String为输入,String为返回值,当它被c++调用时,传入传出一个String。

这里跟上一步有点不同,c++暴露接口给lua调用是很主动的,但这里,java就不需要这么主动了,因为c++够厉害,你不主动我也搞得到你。

这一步骤java这边的工作就结束了,既然java不主动,那么c++那边就稍微费劲一点了


2、在项目的c++代码中,编写方法,该方法被步骤一中c++代码的方法所调用(或在原方法上处理),该方法使用jni调用java中的方法并将错误信息传递。

我们回顾第一个步骤,c++代码中留了一段注释,“//此处执行调用jni方法”,现在我们要把这个工作完成掉。

这里的主角自然就是jni,沾了cocos的光,我们有一个类可以使用,使得jni的调用简单不少,这个类就是JniHelper,调用它很简单,只要在LogToAndroid中包含进来就可以了。

#pragma once
#include "cocos2d.h"
#include <string>
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
#include <jni.h>
#endif
using namespace std;
using namespace cocos2d;

class LogToAndroid : public Ref
{
public:
	static void WriteLog(string);
private:
};
这里要特别注意#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)这个宏,是一定要加的。

不加会怎么样呢,在PC上使用VS编译的时候,就会根据jniHelper里面的引用去找各种源文件,但是这些文件在ndk里面才有,哪怕全拷过来也不管用,因为人家是linux上用的,在使用ndk编译成so的时候,就很没这个问题了。

插播一个文章,里面介绍了jniHelper的各种使用姿势。

http://blog.csdn.net/ku726999/article/details/38553889

然后就可以使用jniHelper的方法了,看到这个代码,把上面调试用的内容删了,写进正经要用的:

void  LogToAndroid::WriteLog(string str)
{
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
	//    typedef struct JniMethodInfo_
	//    {
	//        jnienv *    env;
	//        jclass      classID;
	//        jmethodID   methodID;
	//    } JniMethodInfo;

	JniMethodInfo info;
	bool ret = JniHelper::getStaticmethodInfo(info,"org/cocos2dx/lua/WriteLog","WritetoLogFile","(Ljava/lang/String;)Ljava/lang/String;");
	if (ret)
	{
		log("call function succeed");
		//传入类ID和方法ID,小心方法名写错,第一个字母是大写
		const char *p=str.data();
		jobject para = info.env->NewStringUTF(p);
		jstring jstr = (jstring)info.env->CallStaticObjectMethod(info.classID,info.methodID,para);
		std::string text = JniHelper::jstring2string(jstr);
		log("%s",text.c_str());//尝试打印返回值

	}
#else 
	
#endif

}

我们使用的是传入传出都是String的Static方法,所以使用getStaticmethodInfo这个方法获得java方法。里面三个参数是要设置的,

"org/cocos2dx/lua/WriteLog","(Ljava/lang/String;)Ljava/lang/String;"

第一个是java方法的类所在路径,第二个是方法名,第三个是传入传出的对象

然后执行完getStaticmethodInfo这个后,info里面就有java方法的信息了,如果找到方法成功,则执行方法,用的是

		jstring jstr = (jstring)info.env->CallStaticObjectMethod(info.classID,para);

这句,其他都是在转换参数。

结束之后,我们编译执行,顺利的话就可以看到调用成功的信息和返回来的信息,如果在java那边加一句打印啥的,就可以看到c++写到java的信息了。


三、java对文本进行处理

1、在项目的java代码中,编写方法,被步骤二中的java方法调用(或原方法继续处理),将错误信息打印到文件同时创建对话框显示错误信息。

这个时候,信息已经到达java的方法了,之后想怎么处理它,姿势就很多了。

这里,我们根据调试需要,选择了输出到文件以及弹框两种处理,弹框后可以对游戏进行截图同时停止游戏,而写入文件则可以查找错误信息并和图片对应查看。

处理方法对写应用的人来说应该很简单了,下面直接贴代码

public class WriteLog {
	static String fileName;
	static String dirName;
    <span style="white-space:pre">	</span>static FileWriter fw = null;  
    <span style="white-space:pre">	</span>static BufferedWriter bw = null;  
    <span style="white-space:pre">	</span>static AppActivity mainDlg = null;
    <span style="white-space:pre">	</span>static AlertDialog.Builder builder= null;
    <span style="white-space:pre">	</span>static boolean ignoreDlg = false;
    <span style="white-space:pre">	</span>static Handler mHandler = null;
    public static void init(Handler handler)
    {
        WriteLog.mHandler = handler;
    }
	public static String CreateFile(AppActivity dlg) throws IOException
	{
		mainDlg = dlg;
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH_mm_ss");//设置日期格式
		fileName = "LOT_"+df.format(new Date())+".txt";// new Date()为获取当前系统时间
		SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");//设置日期格式
		String dateName = date.format(new Date());		
		dirName = "mnt/sdcard/Log_of_towerDefence/"+dateName+"/";
		File file = new File(dirName); 
		
		if (!file.exists()) {  
		    try {  
		        //按照指定的路径创建文件夹  
		        file.mkdirs();  
		    } catch (Exception e) {  
		        // Todo: handle exception  
		    }  
		}  
		File dir = new File(dirName+fileName);  
        if (!dir.exists()) {  
              try {  
                  //在指定的文件夹中创建文件  
                  dir.createNewFile();  
            } catch (Exception e) {  
            }  
        }  

        fw = new FileWriter(dirName+fileName,true);//  
        // 创建FileWriter对象,用来写入字符流  
        bw = new BufferedWriter(fw); // 将缓冲对文件的输出  
        bw.write(fileName+"\n"); // 写入文件  
        bw.newLine();  
        bw.flush(); // 刷新该流的缓冲  
        bw.close();  
        fw.close();  
		return dirName+fileName;

	}
	
    public static String WritetoLogFile(String str) throws IOException
    {
    	
        String rstr = "i received your message";
        SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
		String datetime = dt.format(new Date());// new Date()为获取当前系统时间
		String myreadline = str;
		myreadline = datetime + "\n" + str;        
        fw = new FileWriter(dirName+fileName,true);//  
        // 创建FileWriter对象,用来写入字符流  
        bw = new BufferedWriter(fw); // 将缓冲对文件的输出  
        bw.write(myreadline + "\n"); // 写入文件  
        bw.newLine();  
        bw.flush(); // 刷新该流的缓冲  
        bw.close();  
        fw.close();  
        showTipDialog("脚本错误",str);
        return rstr;
    }
    
    
    private static void showTipDialog(final String title,final String text)
    {
        Message msg = mHandler.obtainMessage();
        msg.what = 1;
        DialogMessage dm = new DialogMessage(title,text);
        dm.titile = title;
        dm.message = text;
        msg.obj = dm;
        msg.sendToTarget();
    }
}



 这里被c++调的方法是WritetoLogFile,里面调了弹窗的方法showTipDialog。 
 

为了保证每次运行一个log文件,又写了CreateFile方法,留着被项目初始化文件调。

还有一个init,也是留着被初始化文件调,是为了获得handler传递消息。

最后,弹窗的函数其实是一个发送消息的函数,真正的弹窗在初始化文件中。

最后看一下初始化文件做的事情,

在类定义

public class AppActivity extends Cocos2dxActivity{
后面加入一个处理弹窗的handler
protected Handler mHandler = new Handler(){
		@Override
       public void handleMessage(Message msg) {
           switch(msg.what)
           {
           case 1:
               DialogMessage dm = (DialogMessage)msg.obj;
               new AlertDialog.Builder(AppActivity.this)
               .setTitle(dm.titile)
               .setMessage(dm.message).setNegativeButton("返回画面",new DialogInterface.OnClickListener() {

                   @Override
                   public void onClick(DialogInterface dialog,int which) {
                       dialog.dismiss();
                   }
               })
               .setPositiveButton("终止程序",int which) {
                       dialog.dismiss();
                       System.exit(0);
                   }
               })
               .create().show();
               break;
           }
       }
然后在初始化方法
protected void onCreate(Bundle savedInstanceState) {

中添加两行

WriteLog.init(mHandler);

        try {
        	WriteLog.CreateFile(this);
        }catch (Exception e)
        {
     	   e.printstacktrace();
        }
        

于是大功告成,可以在安卓的SD卡中找到运行的错误log,同时错误的时候会出现弹窗。

cocos2dx安卓调试中lua脚本错误报告和记录的解决方案适用于quick3.5的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. ios – 如何使用Objective C类中的多个参数调用Swift函数?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  6. ios – Swift 4添加手势:覆盖vs @objc

    我想在我的视图中添加一个手势,如下所示:但是,在Swift4中,我的编译器给出了以下错误:建议添加@objc以将此实例方法公开给Objective-C.实现此目的的另一个选项将覆盖touchesBegan()函数并使用它来处理点击.我试图以“Swift”的方式做到这一点,而不必带入Obj-C.有没有纯粹的Swift方式来添加这个轻击手势而不使用@objc?

  7. ios – 将视频分享到Facebook

    我正在编写一个简单的测试应用程序,用于将视频从iOS上传到Facebook.由于FacebookSDK的所有文档都在Objective-C中,因此我发现很难在线找到有关如何使用Swift执行此操作的示例/教程.到目前为止我有这个在我的UI上放置一个共享按钮,但它看起来已禁用,从我读到的这是因为没有内容设置,但我看不出这是怎么可能的.我的getVideoURL()函数返回一个NSURL,它肯定包含视

  8. ios – 以编程方式在Swift中添加联系人

    我想在Swift中以编程方式添加联系人.我发现了一些Objective-C示例,但我没有让它们工作,甚至在Objective-C中也没有.我不希望这涉及到AddressBookUI,因为我想从我自己的UI中获取值.解决方法这是在Swift中添加联系人的快速方法.我在我的iPhone5iOS7.1上验证了它,因为我发现模拟器并不总是与我的手机对AB的东西相同.您可以添加一个按钮并指向此方法:顺便说一下–它假设你已经分配了一个地址簿var,你可以通过覆盖viewDidAppear来打开视图.它也会执行安全提示

  9. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  10. ios – 为目标c中的方法传递未知类型的参数,可能吗?

    是否可以将未知类型的参数传递给objective-C方法?在C#中你可以写实现这一点,但我知道Objective-C没有泛型,所以有没有其他方法可以在Objective-C中实现这一点?我需要这个,因为我想创建一个方法来改变不同对象的文本颜色,如UITextField和UIButton的占位符文本.所以我的计划是创建一个名为textWhite的方法,然后在此方法中检查对象的类型,然后运行匹配的代码以使文本颜色变为白色.解决方法是的,可以传递未知类型的参数.见下面的例子.请参考使用id对象的链接作为参数Us

随机推荐

  1. 【cocos2d-x 3.x 学习笔记】对象内存管理

    Cocos2d-x的内存管理cocos2d-x中使用的是上面的引用计数来管理内存,但是又增加了一些自己的特色。cocos2d-x中通过Ref类来实现引用计数,所有需要实现内存自动回收的类都应该继承自Ref类。下面是Ref类的定义:在cocos2d-x中创建对象通常有两种方式:这两中方式的差异可以参见我另一篇博文“对象创建方式讨论”。在cocos2d-x中提倡使用第二种方式,为了避免误用第一种方式,一般将构造函数设为protected或private。参考资料:[1]cocos2d-x高级开发教程2.3节[

  2. 利用cocos2dx 3.2开发消灭星星六如何在cocos2dx中显示中文

    由于编码的不同,在cocos2dx中的Label控件中如果放入中文字,往往会出现乱码。为了方便使用,我把这个从文档中获取中文字的方法放在一个头文件里面Chinese.h这里的tex_vec是cocos2dx提供的一个保存文档内容的一个容器。这里给出ChineseWords,xml的格式再看看ChineseWord的实现Chinese.cpp就这样,以后在需要用到中文字的地方,就先include这个头文件然后调用ChineseWord函数,获取一串中文字符串。

  3. 利用cocos2dx 3.2开发消灭星星七关于星星的算法

    在前面,我们已经在GameLayer中利用随机数初始化了一个StarMatrix,如果还不知道怎么创建星星矩阵请回去看看而且我们也讲了整个游戏的触摸事件的派发了。

  4. cocos2dx3.x 新手打包APK注意事项!

    这个在编译的时候就可以发现了比较好弄这只是我遇到的,其他的以后遇到再补充吧。。。以前被这两个问题坑了好久

  5. 利用cocos2dx 3.2开发消灭星星八游戏的结束判断与数据控制

    如果你看完之前的,那么你基本已经拥有一个消灭星星游戏的雏形。开始把剩下的两两互不相连的星星消去。那么如何判断是GameOver还是进入下一关呢。。其实游戏数据贯穿整个游戏,包括星星消除的时候要加到获得分数上,消去剩下两两不相连的星星的时候的加分政策等,因此如果前面没有做这一块的,最好回去搞一搞。

  6. 利用cocos2dx 3.2开发消灭星星九为游戏添加一些特效

    needClear是一个flag,当游戏判断不能再继续后,这个flag变为true,开始消除剩下的星星clearSumTime是一个累加器ONE_CLEAR_TIME就是每颗星星消除的时间2.连击加分信息一般消除一次星星都会有连击信息和加多少分的信息。其实这些combo标签就是一张图片,也是通过控制其属性或者runAction来实现。源码ComboEffect.hComboEffect.cpp4.消除星星粒子效果消除星星时,为了实现星星爆裂散落的效果,使用了cocos2d提供的粒子特效引擎对于粒子特效不了

  7. 02 Cocos2D-x引擎win7环境搭建及创建项目

    官网有搭建的文章,直接转载记录。环境搭建:本文介绍如何搭建Cocos2d-x3.2版本的开发环境。项目创建:一、通过命令创建项目前面搭建好环境后,怎样创建自己的Cocos2d-x项目呢?先来看看Cocos2d-x3.2的目录吧这就是Cocos2d-x3.2的目录。输入cocosnew项目名–p包名–lcpp–d路径回车就创建成功了例如:成功后,找到这个项目打开proj.win32目录下的Hello.slnF5成功了。

  8. 利用cocos2dx 3.2开发消灭星星十为游戏添加音效项目源码分享

    一个游戏,声音也是非常的重要,其实cocos2dx里面的简单音效引擎的使用是非常简单的。我这里只不过是用一个类对所有的音效进行管理罢了。Audio.hAudio.cpp好了,本系列教程到此结束,第一次写教程如有不对请见谅或指教,谢谢大家。最后附上整个项目的源代码点击打开链接

  9. 03 Helloworld

    程序都有一个入口点,在C++就是main函数了,打开main.cpp,代码如下:123456789101112131415161718#include"main.h"#include"AppDelegate.h"#include"cocos2d.h"USING_NS_CC;intAPIENTRY_tWinMain{UNREFERENCED_ParaMETER;UNREFERENCED_ParaMETER;//createtheapplicationinstanceAppDelegateapp;return

  10. MenuItemImage*图标菜单创建注意事项

    学习cocos2dx,看的是cocos2d-x3.x手游开发实例详解,这本书错误一大把,本着探索求知勇于发现错误改正错误的精神,我跟着书上的例子一起调试,当学习到场景切换这个小节的时候,出了个错误,卡了我好几个小时。

返回
顶部