亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

ShareSDK造成App崩潰的一個BUG原因分析以及Fix方法

2020-04-11 11:50:49
字體:
來源:轉載
供稿:網友

近期研究了一下Game App做社交分享,最后選擇了ShareSDK來集成,不僅是因為ShareSDK支持國內外主流社交平臺,更重要的是ShareSDK提供了專門的 cocos2d-x集成方案,有專門的文檔和代碼Demo供開發者參考。

文檔中提到了三種集成方式:純Java方式、plugin-x方式以及Cocos2d-x專用組件方式,這里選擇了ShareSDK Cocos2d-x專用組件(v2.3.7版本)的方式。按照文檔中描述的步驟進行的相對順利,在各個社交平臺的appkey生效后,我們對demo app進行了測試,居然發現app經常隨機性的崩潰,有時甚至是每次都崩潰,經過深入分析,發現這是ShareSDK Cocos2d-x專用組件的一個嚴重Bug,下面詳細說明一下Bug的產生原因以及Fix方法。

一、App崩潰的場景和代碼位置

發生崩潰的場景如下:
    App Demo中有一個"Share"按鈕,點擊該按鈕,App Demo向已經授權的社交平臺分享一些Test Content,而App Demo就在收到分享結果應答時發生了崩潰。

代碼位置大致如下:

復制代碼 代碼如下:

void AppDemo::onShareClick(CCObject* sender)
{
    … …
    C2DXShareSDK::showShareMenu(NULL, content,
                                CCPointMake(100, 100),
                                C2DXMenuArrowDirectionLeft,
                                shareResultHandler);
}

void shareResultHandler(C2DXResponseState state, C2DXPlatType platType,
                        CCDictionary *shareInfo, CCDictionary *error)
{
    switch (state) {
        case C2DXResponseStateSuccess:
            CCLog("Share Ok");
            break;
        case C2DXResponseStateFail:
            CCLog("Share Failed");
            break;
        default:
            break;
    }
}


崩潰的位置大致就在回調shareResultHandler前后的某個位 置,比較隨機。

二、現象分析

通過查看Eclipse logcat窗口的調試日志,我們發現一些規律,一些在“Share Ok后的崩潰打印出如下日志:

復制代碼 代碼如下:

04-16 01:28:33.890: D/cocos2d-x debug info(1748): Share Ok
04-16 01:28:34.090: D/cocos2d-x debug info(1748): Assert failed: reference count should greater than 0
04-16 01:28:34.090: E/cocos2d-x assert(1748): /home1/tonybai/android-dev/cocos2d-x-2.2.2/samples/Cpp/temp/AppDemo/proj.android/../../../../../cocos2dx/cocoa/CCObject.cpp function:release line:81
04-16 01:28:34.130: A/libc(1748): Fatal signal 11 (SIGSEGV) at 0×00000003 (code=1), thread 1829 (Thread-122)

猜測一下,似乎是某個CCObject在真正Release前已經被釋放了,然后后續被引用時觸發內存非法訪問。Cocos2d-x采用的是內存 計數的內存管理機制,在我的《Cocos2d-x內存管理-繞不過去的坎》一文中有描述。了解Cocos2d-x的內存管理機制是理解這個Bug 的前提條件。


三、原因分析

看來不得不挖掘一下ShareSDK組件的代碼了。AppDemo中ShareSDK組件的代碼分為兩個部分:AppDemo/Classes /C2DXShareSDK和AppDemo/proj.android/src/cn/sharesdk。前者是C++代碼,后面則是Java 代碼,兩者通過jni調用聯系在一起。我們重點來找出分享應答返回來時的關鍵聯系。

集成ShareSDK的Cocos2d-x程序會在主Activity的onCreate方法中調用ShareSDKUtils.prepare();

我們來看看prepare方法的實現:

復制代碼 代碼如下:

//AppDemo/proj.android/src/cn/sharesdk/ShareSDKUtils.java

public class ShareSDKUtils {
    private static boolean DEBUG = true;
    private static Context context;
    private static PlatformActionListener paListaner;
    private static Hashon hashon;
    … …

    public static void prepare() {
        UIHandler.prepare();
        context = Cocos2dxActivity.getContext().getApplicationContext();
        hashon = new Hashon();
        final Callback cb = new Callback() {
            public boolean handleMessage(Message msg) {
                onJavaCallback((String) msg.obj);
                return false;
            }
        };

        paListaner = new PlatformActionListener() {
            public void onComplete(Platform platform, int action, HashMap<String, Object> res) {
                if (DEBUG) {
                    System.out.println("onComplete");
                    System.out.println(res == null ? "" : res.toString());
                }
                HashMap<String, Object> map = new HashMap<String, Object>();
                map.put("platform", ShareSDK.platformNameToId(platform.getName()));
                map.put("action", action);
                map.put("status", 1); // Success = 1, Fail = 2, Cancel = 3
                map.put("res", res);
                Message msg = new Message();
                msg.obj = hashon.fromHashMap(map);
                UIHandler.sendMessage(msg, cb);
            }

    … …
}

可以看出監聽Complete事件的listener將message的處理都交給了cb,而cb調用了onJavaCallback方法。

onJavaCallback方法是jni導出的方法,它的實現在 AppDemo/Classes/C2DXShareSDK/Android/ShareSDKUtils.cpp里面。

復制代碼 代碼如下:

JNIEXPORT void JNICALL Java_cn_sharesdk_ShareSDKUtils_onJavaCallback
  (JNIEnv * env, jclass thiz, jstring resp) {
    CCJSONConverter* json = CCJSONConverter::sharedConverter();
    const char* ccResp = env->GetStringUTFChars(resp, JNI_FALSE);
    CCLog("ccResp = %s", ccResp);
    CCDictionary* dic = json->dictionaryFrom(ccResp);
    env->ReleaseStringUTFChars(resp, ccResp);
    CCNumber* status = (CCNumber*) dic->objectForKey("status"); // Success = 1, Fail = 2, Cancel = 3
    CCNumber* action = (CCNumber*) dic->objectForKey("action"); //  1 = ACTION_AUTHORIZING,  8 = ACTION_USER_INFOR,9 = ACTION_SHARE
    CCNumber* platform = (CCNumber*) dic->objectForKey("platform");
    CCDictionary* res = (CCDictionary*) dic->objectForKey("res");
    // TODO add codes here
    if(1 == status->getIntValue()){
        callBackComplete(action->getIntValue(), platform->getIntValue(), res);
    }else if(2 == status->getIntValue()){
        callBackError(action->getIntValue(), platform->getIntValue(), res);
    }else{
        callBackCancel(action->getIntValue(), platform->getIntValue(), res);
    }

    dic->autorelease();
}

這就是兩塊代碼的關鍵聯系。而問題似乎就出在onJavaCallback方 法里,因為我們看到了該方法中使用了Cocos2d-x的數據結構類。

我們來看一下onJavaCallback方法是在哪個線程里執行的。Cocos2d-x App至少有兩個線程,一個UI Thread(Activity),一個Render Thread。顯然onJavaCallback是在UI Thread中被執行的。但是我們知道Cocos2d-x的AutoreleasePool是在Render Thread中管理的,并在幀切換時進行釋放操作的。

我們似乎聞到了問題的味道。Cocos2d-x基本上算是一個"單線程"游戲架構,所有的渲染操作、渲染樹節點邏輯管理、絕大多數游戲邏輯都在 Render Thread中進行,UI Thread更多的是接收系統事件,并傳遞給Render Thread處理。Cocos2d-x的內存管理在這樣的“單線程”背景下是沒有大問題的,都是串行操作,不存在thread racing的情況。但一旦另外一個線程也調用內存管理接口進行對象內存操作時,問題就出現了,Cocos2d-x的內存池管理不是線程安全的。

我們回到上面代碼,重點看一下json轉dic的方法,該方法將分享應答字符串轉換為內部的dictionary結構:

復制代碼 代碼如下:

//AppDemo/Classes/C2DXShareSDK/Android/JSON/CCJSONConverter.cpp

CCDictionary * CCJSONConverter::dictionaryFrom(const char *str)
{
    cJSON * json = cJSON_Parse(str);
    if (!json || json->type!=cJSON_Object) {
        if (json) {
            cJSON_Delete(json);
        }
        return NULL;
    }
    CCAssert(json && json->type==cJSON_Object, "CCJSONConverter:wrong json format");
    CCDictionary * dictionary = CCDictionary::create();
    convertJsonToDictionary(json, dictionary);
    cJSON_Delete(json);
    return dictionary;
}

void CCJSONConverter::convertJsonToDictionary(cJSON *json, CCDictionary *dictionary)
{
    dictionary->removeAllObjects();
    cJSON * j = json->child;
    while (j) {
        CCObject * obj = getJsonObj(j);
        dictionary->setObject(obj, j->string);
        j = j->next;
    }
}

CCObject * CCJSONConverter::getJsonObj(cJSON * json)
{
    switch (json->type) {
        case cJSON_Object:
        {
            CCDictionary * dictionary = CCDictionary::create();          
            convertJsonToDictionary(json, dictionary);
            return dictionary;
        }
        case cJSON_Array:
        {
            CCArray * array = CCArray::create();
            convertJsonToArray(json, array);
            return array;
        }
        case cJSON_String:
        {
            CCString * string = CCString::create(json->valuestring);
            return string;
        }
        case cJSON_Number:
        {
            CCNumber * number = CCNumber::create(json->valuedouble);
            return number;
        }
        case cJSON_True:
        {
            CCNumber * boolean = CCNumber::create(1);
            return boolean;
        }
        case cJSON_False:
       {
            CCNumber * boolean = CCNumber::create(0);
            return boolean;
        }
        case cJSON_NULL:
        {
            CCNull * null = CCNull::create();
            return null;
        }
        default:
        {
            CCLog("CCJSONConverter encountered an unrecognized type");
            return NULL;
        }
    }
}


可以看出整個解析過程,都直接用的是傳統的Cocos2d-x對象構造方法:create。在每個對象的create中,代碼都會調用該對象的 autorelease方法。而這個方法本身就是線程不安全的,且即便autorelease調用ok,在下一幀切換時,這些對象將都會被release 掉,如果在UI Thread中再引用這些對象的地址,那勢必造成內存的非法訪問,而引發程序崩潰。

四、Fix方法

可能有朋友會問,create后,我retain一下可否?答案是否。因此create的創建不是線程安全的,create和retain兩個調 用之間存在時間差,而在這段時間內,該對象就有可能被render thread釋放掉。

Fix方法很簡單,就是在UI Thread中不使用Cocos2d-x的內存管理機制,我們用傳統的new來替代create,并將 Java_cn_sharesdk_ShareSDKUtils_onJavaCallback最后的autorelease改為release,這樣就 不用勞煩Render Thread來幫我們釋放內存了。CCDictionary的destructor調用時還會將Dictionarny內部所有Element自動釋放掉。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲xxxxx性| 精品调教chinesegay| 久久久国产影院| 国产精品亚洲综合天堂夜夜| 韩国欧美亚洲国产| 午夜精品福利视频| 国产一区二区三区视频免费| 亚洲高清免费观看高清完整版| 国产精品第七影院| 日韩国产欧美精品一区二区三区| 亚洲aⅴ日韩av电影在线观看| 精品国内产的精品视频在线观看| 亚洲人成在线观看| 欧美在线激情视频| 欧美激情视频在线| 亚洲理论片在线观看| 亚洲国产精品久久精品怡红院| 日韩欧美国产中文字幕| 91久久久久久久久久久久久| 国产精品入口免费视| 欧美日韩国产中文精品字幕自在自线| 精品国产福利在线| 国产成人精品一区二区在线| 午夜精品视频在线| 国产精品精品一区二区三区午夜版| 色偷偷偷亚洲综合网另类| 亚洲在线视频观看| 亚洲福利在线视频| 欧美色视频日本高清在线观看| 自拍偷拍亚洲精品| 九九热最新视频//这里只有精品| 亚洲欧洲在线观看| 精品一区二区亚洲| 欧美精品免费播放| 亚洲精品影视在线观看| 国产区精品视频| 国产日本欧美一区二区三区| 久久精彩免费视频| 亚洲人成电影网站色| 国产精品成人在线| 午夜精品一区二区三区在线视频| 日韩精品极品视频免费观看| 亚洲欧美日韩国产中文| 色天天综合狠狠色| 26uuu另类亚洲欧美日本老年| 国语对白做受69| 国产一区二区黑人欧美xxxx| 91视频国产高清| 国产成人精品久久二区二区91| 国产综合在线看| 欧美福利视频网站| 久久频这里精品99香蕉| 国产精品福利片| 2018中文字幕一区二区三区| 国产成人在线播放| 久久精品国产久精国产思思| 欧美日韩中文字幕| 欧美日韩国产色视频| 亚洲精品久久久久| 久久久精品视频在线观看| 日韩中文字幕国产精品| 国产色婷婷国产综合在线理论片a| 亚洲乱码一区av黑人高潮| www.久久久久久.com| 亚洲精品综合久久中文字幕| 亚洲第一网站免费视频| 成年人精品视频| 亚洲美女在线观看| 日韩高清欧美高清| 久久久精品国产亚洲| 日韩精品免费电影| 久久综合网hezyo| 成人黄色av网| 欧美精品激情在线观看| 欧美日韩一区二区三区在线免费观看| 黑人巨大精品欧美一区免费视频| 国产精品99免视看9| 91丝袜美腿美女视频网站| 98精品在线视频| 国产精品欧美一区二区三区奶水| 91产国在线观看动作片喷水| 日韩欧美高清在线视频| 7777精品视频| 久久香蕉国产线看观看av| 亚洲人成伊人成综合网久久久| 欧美视频中文在线看| www.亚洲人.com| 国产精品狼人色视频一区| 最近2019中文字幕大全第二页| 在线精品国产欧美| 久久影院资源站| 日韩小视频在线观看| 欧美高清视频在线| 国产日韩在线观看av| 在线观看国产欧美| 68精品国产免费久久久久久婷婷| 亚洲欧美国产日韩中文字幕| 精品久久香蕉国产线看观看gif| 亚洲欧美在线第一页| 日韩久久精品电影| 91免费国产网站| 日韩中文在线中文网三级| 538国产精品一区二区在线| 日韩亚洲在线观看| 91老司机在线| 久久精品国产一区二区三区| 久久久人成影片一区二区三区| 国产剧情久久久久久| 91精品国产777在线观看| 性欧美长视频免费观看不卡| 色综合导航网站| 国产欧美日韩亚洲精品| 欧美色视频日本高清在线观看| 国模精品视频一区二区三区| xxxx欧美18另类的高清| 日韩视频免费中文字幕| 欧美网站在线观看| 国产日韩欧美中文在线播放| 色偷偷888欧美精品久久久| 欧美日韩一区二区三区在线免费观看| 国产精品美女无圣光视频| 91久久中文字幕| 5566成人精品视频免费| 欧美激情视频一区二区三区不卡| 精品香蕉一区二区三区| 亚洲视频自拍偷拍| 欧美激情高清视频| 国产99视频在线观看| 亚洲经典中文字幕| 中日韩美女免费视频网站在线观看| 欧美极品少妇xxxxⅹ喷水| 91tv亚洲精品香蕉国产一区7ujn| 2019av中文字幕| 欧美一区二区影院| 国产精品久久久久av免费| 啪一啪鲁一鲁2019在线视频| 91视频国产一区| 91国产中文字幕| 国产精品日韩电影| 国产精品久久久久久久一区探花| 91免费欧美精品| 亚洲电影免费观看高清完整版在线| 欧美疯狂性受xxxxx另类| 欧美日韩裸体免费视频| 亚洲夜晚福利在线观看| 久久人人爽人人爽人人片av高清| 久久精品视频在线| 久久久久久久久久av| 日韩av影院在线观看| 久久精视频免费在线久久完整在线看| 欧美整片在线观看| 精品小视频在线| 青草热久免费精品视频| 日韩av日韩在线观看| 成人美女av在线直播| 日韩视频免费中文字幕| 欧美国产日韩一区二区在线观看| 国产精品久久精品| 欧美日韩国产精品一区二区不卡中文| 欧美日韩免费区域视频在线观看| 日韩中文字幕在线| 欧美性猛交xxxx富婆弯腰| 精品magnet| 国产成人精品国内自产拍免费看|