mirror of https://github.com/axmolengine/axmol.git
Fix cocos2d/cocos2d-js#1658: Fix js_load_remote_image issue with multithread and improve Downloader
This commit is contained in:
parent
a2fc7a85d6
commit
fdec552ed7
|
@ -908,7 +908,7 @@ bool js_cocos2dx_ext_release(JSContext *cx, uint32_t argc, jsval *vp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
__JSDownloaderDelegator::__JSDownloaderDelegator(JSContext *cx, JS::HandleObject obj, const std::string &url, JS::HandleValue callback)
|
__JSDownloaderDelegator::__JSDownloaderDelegator(JSContext *cx, JS::HandleObject obj, const std::string &url, JS::HandleObject callback)
|
||||||
: _cx(cx)
|
: _cx(cx)
|
||||||
, _url(url)
|
, _url(url)
|
||||||
, _buffer(nullptr)
|
, _buffer(nullptr)
|
||||||
|
@ -917,7 +917,27 @@ __JSDownloaderDelegator::__JSDownloaderDelegator(JSContext *cx, JS::HandleObject
|
||||||
_obj.ref().set(obj);
|
_obj.ref().set(obj);
|
||||||
_jsCallback.construct(_cx);
|
_jsCallback.construct(_cx);
|
||||||
_jsCallback.ref().set(callback);
|
_jsCallback.ref().set(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
__JSDownloaderDelegator::~__JSDownloaderDelegator()
|
||||||
|
{
|
||||||
|
_obj.destroyIfConstructed();
|
||||||
|
_jsCallback.destroyIfConstructed();
|
||||||
|
if (_buffer != nullptr)
|
||||||
|
free(_buffer);
|
||||||
|
_downloader->setErrorCallback(nullptr);
|
||||||
|
_downloader->setSuccessCallback(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
__JSDownloaderDelegator *__JSDownloaderDelegator::create(JSContext *cx, JS::HandleObject obj, const std::string &url, JS::HandleObject callback)
|
||||||
|
{
|
||||||
|
__JSDownloaderDelegator *delegate = new (std::nothrow) __JSDownloaderDelegator(cx, obj, url, callback);
|
||||||
|
delegate->autorelease();
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __JSDownloaderDelegator::startDownload()
|
||||||
|
{
|
||||||
if (Director::getInstance()->getTextureCache()->getTextureForKey(_url))
|
if (Director::getInstance()->getTextureCache()->getTextureForKey(_url))
|
||||||
{
|
{
|
||||||
onSuccess(nullptr, nullptr, nullptr);
|
onSuccess(nullptr, nullptr, nullptr);
|
||||||
|
@ -929,8 +949,9 @@ __JSDownloaderDelegator::__JSDownloaderDelegator(JSContext *cx, JS::HandleObject
|
||||||
_downloader->setErrorCallback( std::bind(&__JSDownloaderDelegator::onError, this, std::placeholders::_1) );
|
_downloader->setErrorCallback( std::bind(&__JSDownloaderDelegator::onError, this, std::placeholders::_1) );
|
||||||
_downloader->setSuccessCallback( std::bind(&__JSDownloaderDelegator::onSuccess, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) );
|
_downloader->setSuccessCallback( std::bind(&__JSDownloaderDelegator::onSuccess, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) );
|
||||||
|
|
||||||
long contentSize = _downloader->getContentSize(_url);
|
cocos2d::extension::Downloader::HeaderInfo info = _downloader->getHeader(_url);
|
||||||
if (contentSize == -1) {
|
long contentSize = info.contentSize;
|
||||||
|
if (contentSize == -1 || info.responseCode >= 400) {
|
||||||
cocos2d::extension::Downloader::Error err;
|
cocos2d::extension::Downloader::Error err;
|
||||||
onError(err);
|
onError(err);
|
||||||
}
|
}
|
||||||
|
@ -942,56 +963,69 @@ __JSDownloaderDelegator::__JSDownloaderDelegator(JSContext *cx, JS::HandleObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__JSDownloaderDelegator::~__JSDownloaderDelegator()
|
void __JSDownloaderDelegator::download()
|
||||||
{
|
{
|
||||||
if (_buffer != nullptr)
|
retain();
|
||||||
free(_buffer);
|
startDownload();
|
||||||
_downloader->setErrorCallback(nullptr);
|
}
|
||||||
_downloader->setSuccessCallback(nullptr);
|
|
||||||
|
void __JSDownloaderDelegator::downloadAsync()
|
||||||
|
{
|
||||||
|
retain();
|
||||||
|
auto t = std::thread(&__JSDownloaderDelegator::startDownload, this);
|
||||||
|
t.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void __JSDownloaderDelegator::onError(const cocos2d::extension::Downloader::Error &error)
|
void __JSDownloaderDelegator::onError(const cocos2d::extension::Downloader::Error &error)
|
||||||
{
|
{
|
||||||
if (!_jsCallback.ref().isNull()) {
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([this]
|
||||||
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
|
{
|
||||||
JS::RootedObject global(cx, ScriptingCore::getInstance()->getGlobalObject());
|
JS::RootedValue callback(_cx, OBJECT_TO_JSVAL(_jsCallback.ref()));
|
||||||
|
if (!callback.isNull()) {
|
||||||
JSAutoCompartment ac(_cx, _obj.ref());
|
JS::RootedObject global(_cx, ScriptingCore::getInstance()->getGlobalObject());
|
||||||
|
JSAutoCompartment ac(_cx, global);
|
||||||
|
|
||||||
jsval succeed = BOOLEAN_TO_JSVAL(false);
|
jsval succeed = BOOLEAN_TO_JSVAL(false);
|
||||||
JS::RootedValue retval(cx);
|
JS::RootedValue retval(_cx);
|
||||||
JS_CallFunctionValue(cx, global, _jsCallback.ref(), JS::HandleValueArray::fromMarkedLocation(1, &succeed), &retval);
|
JS_CallFunctionValue(_cx, global, callback, JS::HandleValueArray::fromMarkedLocation(1, &succeed), &retval);
|
||||||
}
|
}
|
||||||
this->release();
|
release();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void __JSDownloaderDelegator::onSuccess(const std::string &srcUrl, const std::string &storagePath, const std::string &customId)
|
void __JSDownloaderDelegator::onSuccess(const std::string &srcUrl, const std::string &storagePath, const std::string &customId)
|
||||||
{
|
{
|
||||||
Image *image = new Image();
|
Image *image = new Image();
|
||||||
jsval valArr[2];
|
|
||||||
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
|
|
||||||
JS::RootedObject global(cx, ScriptingCore::getInstance()->getGlobalObject());
|
|
||||||
cocos2d::TextureCache *cache = Director::getInstance()->getTextureCache();
|
cocos2d::TextureCache *cache = Director::getInstance()->getTextureCache();
|
||||||
|
|
||||||
JSAutoCompartment ac(_cx, _obj.ref() ? _obj.ref() : global);
|
|
||||||
|
|
||||||
Texture2D *tex = cache->getTextureForKey(_url);
|
Texture2D *tex = cache->getTextureForKey(_url);
|
||||||
|
if (!tex)
|
||||||
|
{
|
||||||
|
if (image->initWithImageData(_buffer, _size))
|
||||||
|
{
|
||||||
|
tex = Director::getInstance()->getTextureCache()->addImage(image, _url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
image->release();
|
||||||
|
|
||||||
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([this, tex]
|
||||||
|
{
|
||||||
|
JS::RootedObject global(_cx, ScriptingCore::getInstance()->getGlobalObject());
|
||||||
|
JSAutoCompartment ac(_cx, global);
|
||||||
|
|
||||||
|
jsval valArr[2];
|
||||||
if (tex)
|
if (tex)
|
||||||
{
|
{
|
||||||
valArr[0] = BOOLEAN_TO_JSVAL(true);
|
valArr[0] = BOOLEAN_TO_JSVAL(true);
|
||||||
js_proxy_t* p = jsb_get_native_proxy(tex);
|
js_proxy_t* p = jsb_get_native_proxy(tex);
|
||||||
valArr[1] = OBJECT_TO_JSVAL(p->obj);
|
if (!p)
|
||||||
}
|
|
||||||
else if (image->initWithImageData(_buffer, _size))
|
|
||||||
{
|
{
|
||||||
tex = Director::getInstance()->getTextureCache()->addImage(image, _url);
|
JS::RootedObject texProto(_cx, jsb_cocos2d_Texture2D_prototype);
|
||||||
valArr[0] = BOOLEAN_TO_JSVAL(true);
|
JSObject *obj = JS_NewObject(_cx, jsb_cocos2d_Texture2D_class, texProto, global);
|
||||||
|
|
||||||
JS::RootedObject texProto(cx, jsb_cocos2d_Texture2D_prototype);
|
|
||||||
JSObject *obj = JS_NewObject(cx, jsb_cocos2d_Texture2D_class, texProto, global);
|
|
||||||
// link the native object with the javascript object
|
// link the native object with the javascript object
|
||||||
js_proxy_t* p = jsb_new_proxy(tex, obj);
|
p = jsb_new_proxy(tex, obj);
|
||||||
JS::AddNamedObjectRoot(cx, &p->obj, "cocos2d::Texture2D");
|
JS::AddNamedObjectRoot(_cx, &p->obj, "cocos2d::Texture2D");
|
||||||
|
}
|
||||||
valArr[1] = OBJECT_TO_JSVAL(p->obj);
|
valArr[1] = OBJECT_TO_JSVAL(p->obj);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1000,21 +1034,14 @@ void __JSDownloaderDelegator::onSuccess(const std::string &srcUrl, const std::st
|
||||||
valArr[1] = JSVAL_NULL;
|
valArr[1] = JSVAL_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
image->release();
|
JS::RootedValue callback(_cx, OBJECT_TO_JSVAL(_jsCallback.ref()));
|
||||||
|
if (!callback.isNull())
|
||||||
if (!_jsCallback.ref().isNull()) {
|
|
||||||
JS::RootedValue retval(cx);
|
|
||||||
JS_CallFunctionValue(cx, global, _jsCallback.ref(), JS::HandleValueArray::fromMarkedLocation(2, valArr), &retval);
|
|
||||||
}
|
|
||||||
this->release();
|
|
||||||
}
|
|
||||||
|
|
||||||
void __JSDownloaderDelegator::download(JSContext *cx, JS::HandleObject obj, const std::string &url, JS::HandleValue callback)
|
|
||||||
{
|
{
|
||||||
auto t = std::thread([cx, obj, url, callback]() {
|
JS::RootedValue retval(_cx);
|
||||||
new __JSDownloaderDelegator(cx, obj, url, callback);
|
JS_CallFunctionValue(_cx, global, callback, JS::HandleValueArray::fromMarkedLocation(2, valArr), &retval);
|
||||||
|
}
|
||||||
|
release();
|
||||||
});
|
});
|
||||||
t.detach();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// jsb.loadRemoteImg(url, function(succeed, result) {})
|
// jsb.loadRemoteImg(url, function(succeed, result) {})
|
||||||
|
@ -1022,14 +1049,15 @@ bool js_load_remote_image(JSContext *cx, uint32_t argc, jsval *vp)
|
||||||
{
|
{
|
||||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||||
JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
|
JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
|
||||||
if (argc == 2) {
|
if (argc == 2)
|
||||||
|
{
|
||||||
std::string url;
|
std::string url;
|
||||||
bool ok = jsval_to_std_string(cx, args.get(0), &url);
|
bool ok = jsval_to_std_string(cx, args.get(0), &url);
|
||||||
JS::RootedValue callback(cx, args.get(1));
|
JSB_PRECONDITION2(ok, cx, false, "js_load_remote_image : Error processing arguments");
|
||||||
|
JS::RootedObject callback(cx, args.get(1).toObjectOrNull());
|
||||||
|
|
||||||
__JSDownloaderDelegator::download(cx, obj, url, callback);
|
__JSDownloaderDelegator *delegate = __JSDownloaderDelegator::create(cx, obj, url, callback);
|
||||||
|
delegate->downloadAsync();
|
||||||
JSB_PRECONDITION2(ok, cx, false, "js_console_log : Error processing arguments");
|
|
||||||
|
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -32,12 +32,17 @@
|
||||||
class __JSDownloaderDelegator : cocos2d::Ref
|
class __JSDownloaderDelegator : cocos2d::Ref
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void download(JSContext *cx, JS::HandleObject obj, const std::string &url, JS::HandleValue callback);
|
void downloadAsync();
|
||||||
|
void download();
|
||||||
|
|
||||||
|
static __JSDownloaderDelegator *create(JSContext *cx, JS::HandleObject obj, const std::string &url, JS::HandleObject callback);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
__JSDownloaderDelegator(JSContext *cx, JS::HandleObject obj, const std::string &url, JS::HandleValue callback);
|
__JSDownloaderDelegator(JSContext *cx, JS::HandleObject obj, const std::string &url, JS::HandleObject callback);
|
||||||
~__JSDownloaderDelegator();
|
~__JSDownloaderDelegator();
|
||||||
|
|
||||||
|
void startDownload();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onSuccess(const std::string &srcUrl, const std::string &storagePath, const std::string &customId);
|
void onSuccess(const std::string &srcUrl, const std::string &storagePath, const std::string &customId);
|
||||||
void onError(const cocos2d::extension::Downloader::Error &error);
|
void onError(const cocos2d::extension::Downloader::Error &error);
|
||||||
|
@ -46,8 +51,8 @@ private:
|
||||||
std::shared_ptr<cocos2d::extension::Downloader> _downloader;
|
std::shared_ptr<cocos2d::extension::Downloader> _downloader;
|
||||||
std::string _url;
|
std::string _url;
|
||||||
JSContext *_cx;
|
JSContext *_cx;
|
||||||
mozilla::Maybe<JS::RootedValue> _jsCallback;
|
mozilla::Maybe<JS::PersistentRootedObject> _jsCallback;
|
||||||
mozilla::Maybe<JS::RootedObject> _obj;
|
mozilla::Maybe<JS::PersistentRootedObject> _obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
void register_all_cocos2dx_extension_manual(JSContext* cx, JS::HandleObject global);
|
void register_all_cocos2dx_extension_manual(JSContext* cx, JS::HandleObject global);
|
||||||
|
|
|
@ -332,6 +332,11 @@ long Downloader::getContentSize(const std::string &srcUrl)
|
||||||
return info.contentSize;
|
return info.contentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Downloader::HeaderInfo Downloader::getHeader(const std::string &srcUrl)
|
||||||
|
{
|
||||||
|
return prepareHeader(srcUrl);
|
||||||
|
}
|
||||||
|
|
||||||
void Downloader::getHeaderAsync(const std::string &srcUrl, const HeaderCallback &callback)
|
void Downloader::getHeaderAsync(const std::string &srcUrl, const HeaderCallback &callback)
|
||||||
{
|
{
|
||||||
setHeaderCallback(callback);
|
setHeaderCallback(callback);
|
||||||
|
@ -412,13 +417,11 @@ void Downloader::downloadToBuffer(const std::string &srcUrl, const std::string &
|
||||||
CURLcode res = curl_easy_perform(curl);
|
CURLcode res = curl_easy_perform(curl);
|
||||||
if (res != CURLE_OK)
|
if (res != CURLE_OK)
|
||||||
{
|
{
|
||||||
_fileUtils->removeFile(data.path + data.name + TEMP_EXT);
|
std::string msg = StringUtils::format("Unable to download file to buffer: [curl error]%s", curl_easy_strerror(res));
|
||||||
std::string msg = StringUtils::format("Unable to download file: [curl error]%s", curl_easy_strerror(res));
|
|
||||||
this->notifyError(msg, customId, res);
|
this->notifyError(msg, customId, res);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
curl_easy_cleanup(curl);
|
{
|
||||||
|
|
||||||
Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{
|
||||||
if (!ptr.expired())
|
if (!ptr.expired())
|
||||||
{
|
{
|
||||||
|
@ -433,6 +436,9 @@ void Downloader::downloadToBuffer(const std::string &srcUrl, const std::string &
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
}
|
||||||
|
|
||||||
void Downloader::downloadAsync(const std::string &srcUrl, const std::string &storagePath, const std::string &customId/* = ""*/)
|
void Downloader::downloadAsync(const std::string &srcUrl, const std::string &storagePath, const std::string &customId/* = ""*/)
|
||||||
{
|
{
|
||||||
FileDescriptor fDesc;
|
FileDescriptor fDesc;
|
||||||
|
|
|
@ -107,7 +107,7 @@ public:
|
||||||
std::string url;
|
std::string url;
|
||||||
std::string contentType;
|
std::string contentType;
|
||||||
double contentSize;
|
double contentSize;
|
||||||
double responseCode;
|
long responseCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unordered_map<std::string, DownloadUnit> DownloadUnits;
|
typedef std::unordered_map<std::string, DownloadUnit> DownloadUnits;
|
||||||
|
@ -139,6 +139,8 @@ public:
|
||||||
|
|
||||||
long getContentSize(const std::string &srcUrl);
|
long getContentSize(const std::string &srcUrl);
|
||||||
|
|
||||||
|
HeaderInfo getHeader(const std::string &srcUrl);
|
||||||
|
|
||||||
void getHeaderAsync(const std::string &srcUrl, const HeaderCallback &callback);
|
void getHeaderAsync(const std::string &srcUrl, const HeaderCallback &callback);
|
||||||
|
|
||||||
void downloadToBufferAsync(const std::string &srcUrl, unsigned char *buffer, const long &size, const std::string &customId = "");
|
void downloadToBufferAsync(const std::string &srcUrl, unsigned char *buffer, const long &size, const std::string &customId = "");
|
||||||
|
|
|
@ -192,7 +192,6 @@ var RemoteTextureTest = TextureCacheTestBase.extend({
|
||||||
_title:"Remote Texture Test",
|
_title:"Remote Texture Test",
|
||||||
_subtitle:"",
|
_subtitle:"",
|
||||||
_remoteTex: "http://cn.cocos2d-x.org/image/logo.png",
|
_remoteTex: "http://cn.cocos2d-x.org/image/logo.png",
|
||||||
_sprite : null,
|
|
||||||
onEnter:function () {
|
onEnter:function () {
|
||||||
this._super();
|
this._super();
|
||||||
if('opengl' in cc.sys.capabilities && !cc.sys.isNative){
|
if('opengl' in cc.sys.capabilities && !cc.sys.isNative){
|
||||||
|
@ -205,19 +204,21 @@ var RemoteTextureTest = TextureCacheTestBase.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
startDownload: function() {
|
startDownload: function() {
|
||||||
cc.textureCache.addImageAsync(this._remoteTex, this.texLoaded, this);
|
var imageUrlArray = ["http://www.cocos2d-x.org/s/upload/v35.jpg", "http://www.cocos2d-x.org/s/upload/testin.jpg", "http://www.cocos2d-x.org/s/upload/geometry_dash.jpg", "http://cn.cocos2d-x.org/image/logo.png"];
|
||||||
|
|
||||||
|
for (var i = 0; i < imageUrlArray.length; i++) {
|
||||||
|
cc.textureCache.addImageAsync(imageUrlArray[i], this.texLoaded, this);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
texLoaded: function(texture) {
|
texLoaded: function(texture) {
|
||||||
if (texture instanceof cc.Texture2D) {
|
if (texture instanceof cc.Texture2D) {
|
||||||
cc.log("Remote texture loaded: " + this._remoteTex);
|
cc.log("Remote texture loaded");
|
||||||
if (this._sprite) {
|
|
||||||
this.removeChild(this._sprite);
|
var sprite = new cc.Sprite(texture);
|
||||||
}
|
sprite.x = cc.winSize.width/2;
|
||||||
this._sprite = new cc.Sprite(texture);
|
sprite.y = cc.winSize.height/2;
|
||||||
this._sprite.x = cc.winSize.width/2;
|
this.addChild(sprite);
|
||||||
this._sprite.y = cc.winSize.height/2;
|
|
||||||
this.addChild(this._sprite);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cc.log("Fail to load remote texture");
|
cc.log("Fail to load remote texture");
|
||||||
|
|
Loading…
Reference in New Issue