这几天在进行Lives2D的Android移植,lib的编译都OK,然后也简单的跑起来了,然后开始添加Android端的MediaPlayer来播放音效。


MediaPlayer加入之后,问题就出现很多了,JNIEnv的存储、JMethod获取都是比较繁琐的事情,我对Android也不怎么熟悉,花了很多时间。

但是测试发现,游戏各种崩溃。



测试场景如上,一个按钮,点击就会调用JNI 来PlayAudio。


一开始崩溃原因最多的就是 use JNIEnv in other thread 。 我是迷糊的,大概估计意思是JNIEnv不能再GLES的Render Thread里面使用,于是百度谷歌了一番,最后还是DownLoad了 Cocos2d的JNIHelper 过来用。


之后就没有碰到JNIEnv 线程错误了。


继续测试,仍然崩溃,而且直接就是 Fatal signal 11 (SIGSEGV) 错误,顿时眼瞎,瑟瑟发抖。

慢慢排查。

转自http://www.liveslives.com/  http://blog.csdn.net/huutu

排查的重心放到了Lua 传递 Function 给C++ 调用的实现上,我对Lua也不怎么懂,这种实现在tolua++ 处理过后还要手动修改生成的脚本,所以我一直本着怀疑的态度,虽然在Win上没有问题。


来看看我的实现。

UIButton 有一个 SetOnClickListener 接口,设置点击回调

void UIButton::SetOnClickListener(lua_State * varlua_State){mOnClickListener = new LuaFunctionPoint();mOnClickListener->mlua_State = varlua_State;mOnClickListener->mFunctionIndexInStack = -2;mOnClickListener->mArgumentIndexInStack = -1;mOnClickListener = LuaEngine::GetSingleton()->GetLuaFunction(mOnClickListener);}


LuaFunctionPoint 是用来保存函数指针 参数指针的一个结构

class LuaFunctionPoint{public:lua_State* mlua_State;public:int mFunctionIndexInStack;int mArgumentIndexInStack;public:int mFunctionPoint;int mArgumentPoint;public:LuaFunctionPoint() :mFunctionPoint(LUA_REFNIL), mArgumentPoint(LUA_REFNIL){}};

当Lua 调用 SetOnClickListener 之后,获取到Lua中的Function 回调的索引,以及Function参数的索引保存起来。


然后通过luaL_ref 去获取指针。

LuaFunctionPoint* LuaEngine::GetLuaFunction(LuaFunctionPoint* varLuaFunctionPoint){lua_pushvalue(varLuaFunctionPoint->mlua_State, varLuaFunctionPoint->mFunctionIndexInStack);varLuaFunctionPoint->mFunctionPoint = luaL_ref(varLuaFunctionPoint->mlua_State, LUA_REGISTRYINDEX);lua_pushvalue(varLuaFunctionPoint->mlua_State, varLuaFunctionPoint->mArgumentIndexInStack);varLuaFunctionPoint->mArgumentPoint = luaL_ref(varLuaFunctionPoint->mlua_State, LUA_REGISTRYINDEX);return varLuaFunctionPoint;}

转自 http://www.liveslives.com/   http://blog.csdn.net/huutu

最后当有点击事件的时候,调用回调

void LuaEngine::ExecuteLuaFunction(LuaFunctionPoint* varLuaFunctionPoint){lua_rawgeti(m_pLua_State, LUA_REGISTRYINDEX, varLuaFunctionPoint->mFunctionPoint);lua_rawgeti(m_pLua_State, LUA_REGISTRYINDEX, varLuaFunctionPoint->mArgumentPoint);lua_call(m_pLua_State, 1, 0);}

因为这种方式 使用 tolua++ 生成代码之后,还要手动修改,给我的感觉很不好。


而且后续测试崩溃 ,lua 中也爆出各种莫名其妙的错误。

我觉得可能是我这里的实现,破坏了 lua 的堆栈。


于是,我把这里的回调设置换了一种实现。

首先 UIButton 中,不再保存Function 的 指针了,而是保存FunctionName。

void UIButton::SetOnClickListener(const char* varOnClickListener){mOnClickListener = new LuaFunctionPoint();mOnClickListener->mFunctionName = varOnClickListener;}

然后在Lua 中设置的时候,为Function 匿名函数 生成一个唯一标志,把这个标志设置传入到SetOnClickListener中。

self.mUIButton:SetOnClickListener(RegisterLuaFunction(function()print("click button")end))


RegisterLuaFunction 用来产生函数唯一标志。


然后在点击事件过来的时候,用普通的方式,调用函数

void LuaEngine::ExecuteLuaFunction(LuaFunctionPoint* varLuaFunctionPoint){Helper::LOG("------ExecuteLuaFunction Begin---------");if (mErrorPause){return;}lua_getglobal(m_pLua_State, "CallLuaFunction");if (!lua_isfunction(m_pLua_State, -1)){Helper::LOG("%s is not function", "CallLuaFunction");return;}tolua_pushstring(m_pLua_State, varLuaFunctionPoint->mFunctionName);int ret = lua_pcall(m_pLua_State, 1, 0, 0);if (ret != 0){PrintError();mErrorPause = true;}Helper::LOG("------ExecuteLuaFunction End---------");}

这种实现,绝对是安全的。


然而,测试还是一如既往的崩溃,仍然是Lua中爆出各种莫名其妙的错误。


肯定是Lua的堆栈被破坏了,但是原因呢?


灵光一闪! 对的,我总是这样。。

堆栈破坏、JNIEnv 在多个线程被使用。

这两者在我脑海中纠缠,爆炸!

Lua是不能跨线程使用的!


而在我的代码中,Render是在GLThread中,而TouchEvent 则是在Activity 线程中!!

惊!!


于是修改了Java端的代码,把TouchEvent 放到Hashtable中,然后在GLThread中去获取。

Hashtable mMotionEventHashtable=new Hashtable();

定义一个Hashtable,用来存储事件类型以及屏幕坐标。


然后在onTouchEvent 里面,把Event存入 Hashtable ,记得Lock

public boolean onTouchEvent(MotionEvent event){float x= event.getX();float y=event.getY();Renderer.reentrantLock.lock();mMotionEventHashtable.put(event.getAction(), new Vector2((int)x, (int)y));Renderer.reentrantLock.unlock();return true;}

然后在Render 的时候取出。 转自 http://www.liveslives.com/   http://blog.csdn.net/huutu

public void onDrawFrame(GL10 gl) {//处理点击事件reentrantLock.lock();Iterator> iterator=mMotionEventHashtable.entrySet().iterator();while(iterator.hasNext()){Entry entry=iterator.next();int event=entry.getKey();Vector2 pos=entry.getValue();switch (event) {case MotionEvent.ACTION_DOWN:Log.i("Lives2D", "onTouch x:"+pos.x+" y:"+pos.y);nativeWrap.onTouch(pos.x, pos.y);break;case MotionEvent.ACTION_UP:Log.i("Lives2D", "onTouchRelease x:"+pos.x+" y:"+pos.y);nativeWrap.onTouchRelease(pos.x, pos.y);break;default:break;}}mMotionEventHashtable.clear();reentrantLock.unlock();//刷帧end = System.currentTimeMillis();long time = end - begin;if(time < frame_time){try{Thread.sleep(frame_time - time);}catch (InterruptedException e){e.printStackTrace();}}begin = System.currentTimeMillis();//Log.i("Lives2D", "GLThread:"+Thread.currentThread().getId());nativeWrap.step(0.333f);}

如此,问题解决。


详细代码Github见:

https://github.com/ThisisGame/Lives2D/tree/V5_SwitchToLua_Windows


更多相关文章

  1. Android上使用ASIFT实现对视角变化更鲁棒的特征匹配
  2. android之Fragment(官网资料翻译)
  3. 2020最新Android大厂面试真题大全(附答案)
  4. android横竖屏总结
  5. 学习Android(安卓)Handler消息传递机制
  6. Android横竖屏+补充
  7. Android串口通信(Android(安卓)Studio)
  8. android的action
  9. 浅谈Java中Collections.sort对List排序的两种方法

随机推荐

  1. Android接入OpenCv实现人脸识别
  2. 疯狂Android讲义目录结构
  3. android 通过wifi 获取经纬度和获取渠道
  4. Android(安卓)SDCard UnMounted 流程分析
  5. 实现能定点移动的seekbar
  6. 关于Android软键盘弹出的问题
  7. 判断Android系统时间是否是24小时制
  8. Android 个人记账程序源码
  9. Android给View画边框
  10. Android下获取手机屏幕大小