經由過程url方法傳遞中文亂碼的處理辦法。本站提示廣大學習愛好者:(經由過程url方法傳遞中文亂碼的處理辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是經由過程url方法傳遞中文亂碼的處理辦法正文
Cocos2d-x從2.x版本到上周方才才宣布的Cocos2d-x 3.0 Final版,其引擎驅動焦點照舊是一個單線程的“逝世輪回”,一旦某一幀碰到了“年夜活兒”,好比Size很年夜的紋理資本加載或收集IO或年夜量盤算,畫面將 弗成防止湧現卡頓和呼應緩慢的景象。從陳舊的Win32 GUI編程那時起,Guru們就告知我們:別壅塞主線程(UI線程),讓Worker線程去做那些“年夜活兒”吧。
手機游戲,即使是休閒類的小游戲,常常也觸及年夜量紋理資本、音視頻資本、文件讀寫和收集通訊,處置的稍有不甚就會湧現畫面卡頓,交互不順暢的情形。固然引擎在某些方面供給了一些支撐,但有些時刻照樣本身祭出Worker線程這個寶貝比擬靈巧,上面就以Cocos2d-x 3.0 Final版游戲初始化為例(針對Android平台),說說若何停止多線程資本加載。
我們常常看到一些手機游戲,啟動以後起首會顯示一個帶有公司Logo的閃屏畫面(Flash Screen),然後才會進入一個游戲Welcome場景,點擊“開端”才正式進入游戲主場景。而這裡Flash Screen的展現環節常常在後台還會做別的一件事,那就是加載游戲的圖片資本,音噪音效資本和設置裝備擺設數據讀取,這算是一個“障眼法”吧,目標就是進步用 戶體驗,如許後續場景襯著和場景切換直接應用曾經cache到內存中的數據便可,無需再行加載。
1、為游戲添加FlashScene
在游戲App初始化時,我們起首創立FlashScene,讓游戲盡快顯示FlashScene畫面:
// AppDelegate.cpp
bool AppDelegate::applicationDidFinishLaunching() {
… …
FlashScene* scene = FlashScene::create();
pDirector->runWithScene(scene);
return true;
}
在FlashScene init時,我們創立一個Resource Load Thread,我們用一個ResourceLoadIndicator作為襯著線程與Worker線程之間交互的序言。
//FlashScene.h
struct ResourceLoadIndicator {
pthread_mutex_t mutex;
bool load_done;
void *context;
};
class FlashScene : public Scene
{
public:
FlashScene(void);
~FlashScene(void);
virtual bool init();
CREATE_FUNC(FlashScene);
bool getResourceLoadIndicator();
void setResourceLoadIndicator(bool flag);
private:
void updateScene(float dt);
private:
ResourceLoadIndicator rli;
};
// FlashScene.cpp
bool FlashScene::init()
{
bool bRet = false;
do {
CC_BREAK_IF(!CCScene::init());
Size winSize = Director::getInstance()->getWinSize();
//FlashScene本身的資本只能同步加載了
Sprite *bg = Sprite::create("FlashSceenBg.png");
CC_BREAK_IF(!bg);
bg->setPosition(ccp(winSize.width/2, winSize.height/2));
this->addChild(bg, 0);
this->schedule(schedule_selector(FlashScene::updateScene)
, 0.01f);
//start the resource loading thread
rli.load_done = false;
rli.context = (void*)this;
pthread_mutex_init(&rli.mutex, NULL);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t thread;
pthread_create(&thread, &attr,
resource_load_thread_entry, &rli);
bRet=true;
} while(0);
return bRet;
}
static void* resource_load_thread_entry(void* param)
{
AppDelegate *app = (AppDelegate*)Application::getInstance();
ResourceLoadIndicator *rli = (ResourceLoadIndicator*)param;
FlashScene *scene = (FlashScene*)rli->context;
//load music effect resource
… …
//init from config files
… …
//load images data in worker thread
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(
"All-Sprites.plist");
… …
//set loading done
scene->setResourceLoadIndicator(true);
return NULL;
}
bool FlashScene::getResourceLoadIndicator()
{
bool flag;
pthread_mutex_lock(&rli.mutex);
flag = rli.load_done;
pthread_mutex_unlock(&rli.mutex);
return flag;
}
void FlashScene::setResourceLoadIndicator(bool flag)
{
pthread_mutex_lock(&rli.mutex);
rli.load_done = flag;
pthread_mutex_unlock(&rli.mutex);
return;
}
我們在准時器回調函數中對indicator標記位停止檢討,當發明加載ok後,切換到接上去的游戲開端場景:
void FlashScene::updateScene(float dt)
{
if (getResourceLoadIndicator()) {
Director::getInstance()->replaceScene(
WelcomeScene::create());
}
}
到此,FlashScene的初始設計和完成完成了。Run一下嘗嘗吧。
2、處理瓦解成績
在GenyMotion的4.4.2模仿器上,游戲運轉的成果並沒有如我希冀,FlashScreen浮現後游戲就異常瓦解加入了。
經由過程monitor剖析游戲的運轉日記,我們看到了以下一些異常日記:
threadid=24: thread exiting, not yet detached (count=0)
threadid=24: thread exiting, not yet detached (count=1)
threadid=24: native thread exited without detaching
很是奇異啊,我們在創立線程時,明明設置了 PTHREAD_CREATE_DETACHED屬性了啊:
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
怎樣還會湧現這個成績,並且竟然有三條日記。翻看了一下引擎內核的代碼TextureCache::addImageAsync,在線程創立和線程主函數中也沒有發明甚麼特殊的設置。為什麼內核可以創立線程,我本身創立就會瓦解呢。Debug多個往返,成績仿佛聚焦在resource_load_thread_entry中履行的義務。在我的代碼裡,我應用SimpleAudioEngine加載了音效資本、應用UserDefault讀取了一些耐久化的數據,把這兩個義務去失落,游戲就會進入到下一個環節而不會瓦解。
SimpleAudioEngine和UserDefault能有甚麼配合點呢?Jni挪用。沒錯,這兩個接口底層要適配多個平台,而關於Android 平台,他們都用到了Jni供給的接口去挪用Java中的辦法。而Jni對多線程是有束縛的。Android開辟者官網上有這麼一段話:
All threads are Linux threads, scheduled by the kernel. They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then attached to the JavaVM. For example, a thread started with pthread_create can be attached with the JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv, and cannot make JNI calls.
由此看來pthread_create創立的新線程默許情形下是不克不及停止Jni接口挪用的,除非Attach到Vm,取得一個JniEnv對象,而且在線 程exit前要Detach Vm。好,我們來測驗考試一下,Cocos2d-x引擎供給了一些JniHelper辦法,可以便利停止Jni相干操作。
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
#include <jni.h>
#endif
static void* resource_load_thread_entry(void* param)
{
… …
JavaVM *vm;
JNIEnv *env;
vm = JniHelper::getJavaVM();
JavaVMAttachArgs thread_args;
thread_args.name = "Resource Load";
thread_args.version = JNI_VERSION_1_4;
thread_args.group = NULL;
vm->AttachCurrentThread(&env, &thread_args);
… …
//Your Jni Calls
… …
vm->DetachCurrentThread();
… …
return NULL;
}
關於甚麼是JavaVM,甚麼是JniEnv,Android Developer官方文檔中是如許描寫的:
The JavaVM provides the "invocation interface" functions, which allow you to create and destroy a JavaVM. In theory you can have multiple JavaVMs per process, but Android only allows one.
The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv as the first argument.
The JNIEnv is used for thread-local storage. For this reason, you cannot share a JNIEnv between threads.
3、處理黑屏成績
下面的代碼勝利處理了線程瓦解的成績,但成績還沒完,由於接上去我們又碰到了“黑屏”事宜。所謂的“黑屏”,其實其實不是全黑。但進入游戲 WelcomScene時,只要Scene中的LabelTTF實例能顯示出來,其他Sprite都沒法顯示。明顯確定與我們在Worker線程加載紋理 資本有關了:
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("All-Sprites.plist");
我們經由過程碎圖緊縮到一張年夜紋理的方法樹立SpriteFrame,這是Cocos2d-x推舉的優化手腕。但要想找到這個成績的本源,還得看monitor日記。我們切實其實發明了一些異常日記:
libEGL: call to OpenGL ES API with no current context (logged once per thread)
經由過程Google得知,只要Renderer Thread能力停止egl挪用,由於egl的context是在Renderer Thread創立的,Worker Thread並沒有EGL的context,在停止egl操作時,沒法找到context,是以操作都是掉敗的,紋理也就沒法顯示出來。要處理這個成績就 得檢查一下TextureCache::addImageAsync是若何做的了。
TextureCache::addImageAsync只是在worker線程停止了image數據的加載,而紋理對象Texture2D instance則是在addImageAsyncCallBack中創立的。也就是說紋理照樣在Renderer線程中創立的,是以不會湧現我們下面的 “黑屏”成績。模擬addImageAsync,我們來修正一下代碼:
static void* resource_load_thread_entry(void* param)
{
… …
allSpritesImage = new Image();
allSpritesImage->initWithImageFile("All-Sprites.png");
… …
}
void FlashScene::updateScene(float dt)
{
if (getResourceLoadIndicator()) {
// construct texture with preloaded images
Texture2D *allSpritesTexture = TextureCache::getInstance()->
addImage(allSpritesImage, "All-Sprites.png");
allSpritesImage->release();
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(
"All-Sprites.plist", allSpritesTexture);
Director::getInstance()->replaceScene(WelcomeScene::create());
}
}
完成這一修正後,游戲畫面就變得一切正常了,多線程資本加載機制正式失效。