cocos2dx安卓调试中lua脚本错误报告和记录的解决方案(适用于quick3.5)
来源:互联网 发布:app开发界面设计软件 编辑:程序博客网 时间:2024/05/22 05:29
在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>#endifusing 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", "WriteToLogFile", "(Ljava/lang/String;)Ljava/lang/String;"
第一个是java方法的类所在路径,第二个是方法名,第三个是传入传出的对象
然后执行完getStaticMethodInfo这个后,info里面就有java方法的信息了,如果找到方法成功,则执行方法,用的是
jstring jstr = (jstring)info.env->CallStaticObjectMethod(info.classID, info.methodID, 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(); }}
为了保证每次运行一个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("终止程序", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, 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)
- cocos2dx安卓打包与调试(适用quick3.5)
- cocos2dx-Lua中关于Lua和Java的互相操作的解决方案
- 在实现安卓和ios的sdk中 c++,java,lua 混编学习记录
- lua脚本调用cocos2dx项目中自定义的C++类
- 转载一个简单的lua展示【COCOS2DX-LUA 脚本开发之一】LUA语言基础在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!
- quick3.3中lua绑定c++
- 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)
- 【cocos2dx开发技巧5】脚本lua的使用--代码集锦
- 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!
- 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!
- 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!
- 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!
- cocos2dx中lua的问题解决
- cocos2dx + lua 中实现 lua的MVC
- quick-cocos2dx中 lua 和oc的通信
- cocos2dX中c++和lua的交互-1
- quick3.6 lua-binding
- 2015年上半年阅读书单
- Qt 常用类(5)—— QSize
- leetcode 题解代码整理 1-5题
- Android 基础控件汇总
- 销售数据分析模型
- cocos2dx安卓调试中lua脚本错误报告和记录的解决方案(适用于quick3.5)
- Java和maven方式使用mybatis generator
- 使用screen 关闭终端 继续运行
- web项目出现import javax.servlet.……cannot be resolved的解决方法
- Apache HBase Region Splitting and Merging
- HYSBZ 2002 弹飞绵羊
- Qt 常用类 (6) —— QFont
- Lua调用C++带参数的方法
- 20150804-IAR For STM8工程改名