mirror of https://github.com/axmolengine/axmol.git
CCDownloader-android implements File Task Download.
This commit is contained in:
parent
9b4fe5b5a1
commit
7e2fe05050
|
@ -21,30 +21,218 @@
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include <unordered_map> // temp
|
||||
|
||||
#include "network/CCDownloader-android.h"
|
||||
|
||||
#include "network/CCDownloader.h"
|
||||
#include "platform/android/jni/JniHelper.h"
|
||||
|
||||
namespace cocos2d {
|
||||
using namespace std;
|
||||
#define JCLS_DOWNLOADER "org/cocos2dx/lib/Cocos2dxDownloader"
|
||||
#define JCLS_TASK "com/loopj/android/http/RequestHandle"
|
||||
#define JARG_STR "Ljava/lang/String;"
|
||||
#define JARG_DOWNLOADER "L" JCLS_DOWNLOADER ";"
|
||||
#define JARG_TASK "L" JCLS_TASK ";"
|
||||
|
||||
namespace network {
|
||||
using namespace std;
|
||||
|
||||
static bool _registerNativeMethods(JNIEnv* env);
|
||||
|
||||
unordered_map<int, cocos2d::network::DownloaderAndroid*> sDownloaderMap;
|
||||
|
||||
namespace cocos2d { namespace network {
|
||||
|
||||
static int sTaskCounter;
|
||||
static int sDownloaderCounter;
|
||||
|
||||
struct DownloadTaskAndroid : public IDownloadTask
|
||||
{
|
||||
DownloadTaskAndroid()
|
||||
:id(++sTaskCounter)
|
||||
{
|
||||
DLLOG("Construct DownloadTaskAndroid: %p", this);
|
||||
}
|
||||
virtual ~DownloadTaskAndroid()
|
||||
{
|
||||
|
||||
DLLOG("Destruct DownloadTaskAndroid: %p", this);
|
||||
}
|
||||
|
||||
int id;
|
||||
shared_ptr<const DownloadTask> task; // reference to DownloadTask, when task finish, release
|
||||
};
|
||||
|
||||
DownloaderAndroid::DownloaderAndroid(const DownloaderHints& hints)
|
||||
: _id(++sDownloaderCounter)
|
||||
, _impl(nullptr)
|
||||
{
|
||||
|
||||
// use local static variable make sure native methods registered once
|
||||
static bool _registered = _registerNativeMethods(JniHelper::getEnv());
|
||||
DLLOG("Construct DownloaderAndroid: %p", this);
|
||||
JniMethodInfo methodInfo;
|
||||
if (JniHelper::getStaticMethodInfo(methodInfo,
|
||||
JCLS_DOWNLOADER,
|
||||
"createDownloader",
|
||||
"(II" JARG_STR ")" JARG_DOWNLOADER))
|
||||
{
|
||||
jobject jStr = methodInfo.env->NewStringUTF(hints.tempFileNameSuffix.c_str());
|
||||
jobject jObj = methodInfo.env->CallStaticObjectMethod(
|
||||
methodInfo.classID,
|
||||
methodInfo.methodID,
|
||||
_id,
|
||||
hints.timeoutInSeconds,
|
||||
jStr
|
||||
);
|
||||
_impl = methodInfo.env->NewGlobalRef(jObj);
|
||||
DLLOG("android downloader: jObj: %p, _impl: %p", jObj, _impl);
|
||||
sDownloaderMap.insert(make_pair(_id, this));
|
||||
methodInfo.env->DeleteLocalRef(jStr);
|
||||
methodInfo.env->DeleteLocalRef(jObj);
|
||||
methodInfo.env->DeleteLocalRef(methodInfo.classID);
|
||||
}
|
||||
}
|
||||
|
||||
DownloaderAndroid::~DownloaderAndroid()
|
||||
{
|
||||
|
||||
if(_impl != nullptr)
|
||||
{
|
||||
JniMethodInfo methodInfo;
|
||||
if (JniHelper::getStaticMethodInfo(methodInfo,
|
||||
JCLS_DOWNLOADER,
|
||||
"cancelAllRequests",
|
||||
"(" JARG_DOWNLOADER ")V"))
|
||||
{
|
||||
methodInfo.env->CallStaticVoidMethod(
|
||||
methodInfo.classID,
|
||||
methodInfo.methodID,
|
||||
_impl
|
||||
);
|
||||
methodInfo.env->DeleteLocalRef(methodInfo.classID);
|
||||
}
|
||||
sDownloaderMap.erase(_id);
|
||||
JniHelper::getEnv()->DeleteGlobalRef(_impl);
|
||||
}
|
||||
DLLOG("Destruct DownloaderAndroid: %p", this);
|
||||
}
|
||||
|
||||
IDownloadTask *DownloaderAndroid::createCoTask(std::shared_ptr<const DownloadTask>& task)
|
||||
{
|
||||
return nullptr;
|
||||
DownloadTaskAndroid *coTask = new DownloadTaskAndroid;
|
||||
coTask->task = task;
|
||||
|
||||
JniMethodInfo methodInfo;
|
||||
if (JniHelper::getStaticMethodInfo(methodInfo,
|
||||
JCLS_DOWNLOADER,
|
||||
"createTask",
|
||||
"(" JARG_DOWNLOADER "I" JARG_STR JARG_STR")V"))
|
||||
{
|
||||
jstring jstrURL = methodInfo.env->NewStringUTF(task->requestURL.c_str());
|
||||
jstring jstrPath= methodInfo.env->NewStringUTF(task->storagePath.c_str());
|
||||
methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, _impl, coTask->id, jstrURL, jstrPath);
|
||||
methodInfo.env->DeleteLocalRef(jstrURL);
|
||||
methodInfo.env->DeleteLocalRef(jstrPath);
|
||||
methodInfo.env->DeleteLocalRef(methodInfo.classID);
|
||||
}
|
||||
|
||||
DLLOG("DownloaderAndroid::createCoTask id: %d", coTask->id);
|
||||
_taskMap.insert(make_pair(coTask->id, coTask));
|
||||
return coTask;
|
||||
}
|
||||
|
||||
void DownloaderAndroid::_onProcess(int taskId, int64_t dl, int64_t dlNow, int64_t dlTotal)
|
||||
{
|
||||
DLLOG("DownloaderAndroid::onProgress(taskId: %d, dl: %lld, dlnow: %lld, dltotal: %lld)", taskId, dl, dlNow, dlTotal);
|
||||
auto iter = _taskMap.find(taskId);
|
||||
if (_taskMap.end() == iter)
|
||||
{
|
||||
DLLOG("DownloaderAndroid::onProgress can't find task with id: %d", taskId);
|
||||
return;
|
||||
}
|
||||
DownloadTaskAndroid *coTask = iter->second;
|
||||
function<int64_t(void*, int64_t)> transferDataToBuffer;
|
||||
onTaskProgress(*coTask->task, dl, dlNow, dlTotal, transferDataToBuffer);
|
||||
}
|
||||
|
||||
void DownloaderAndroid::_onFinish(int taskId, int errCode, const char *errStr)
|
||||
{
|
||||
DLLOG("DownloaderAndroid::_onFinish(taskId: %d, errCode: %d, errStr: %s)", taskId, errCode, (errStr)?errStr:"null");
|
||||
auto iter = _taskMap.find(taskId);
|
||||
if (_taskMap.end() == iter)
|
||||
{
|
||||
DLLOG("DownloaderAndroid::_onFinish can't find task with id: %d", taskId);
|
||||
return;
|
||||
}
|
||||
DownloadTaskAndroid *coTask = iter->second;
|
||||
string str = (errStr ? errStr : "");
|
||||
vector<unsigned char> data;
|
||||
onTaskFinish(*coTask->task,
|
||||
(errCode ? DownloadTask::ERROR_IMPL_INTERNAL : DownloadTask::ERROR_FILE_OP_FAILED),
|
||||
errCode,
|
||||
str,
|
||||
data
|
||||
);
|
||||
coTask->task.reset();
|
||||
_taskMap.erase(iter);
|
||||
}
|
||||
}
|
||||
} // namespace cocos2d::network
|
||||
|
||||
static void _nativeOnProgress(JNIEnv *env, jclass clazz, jint id, jint taskId, jlong dl, jlong dlnow, jlong dltotal)
|
||||
{
|
||||
DLLOG("_nativeOnProgress(id: %d, taskId: %d, dl: %lld, dlnow: %lld, dltotal: %lld)", id, taskId, dl, dlnow, dltotal);
|
||||
auto iter = sDownloaderMap.find(id);
|
||||
if (sDownloaderMap.end() == iter)
|
||||
{
|
||||
DLLOG("_nativeOnProgress can't find downloader by key: %p for task: %d", clazz, id);
|
||||
return;
|
||||
}
|
||||
cocos2d::network::DownloaderAndroid *downloader = iter->second;
|
||||
downloader->_onProcess((int)taskId, (int64_t)dl, (int64_t)dlnow, (int64_t)dltotal);
|
||||
}
|
||||
|
||||
static void _nativeOnFinish(JNIEnv *env, jclass clazz, jint id, jint taskId, jint errCode, jstring errStr)
|
||||
{
|
||||
DLLOG("_nativeOnFinish(id: %d, taskId: %d)", id, taskId);
|
||||
auto iter = sDownloaderMap.find(id);
|
||||
if (sDownloaderMap.end() == iter)
|
||||
{
|
||||
DLLOG("_nativeOnFinish can't find downloader id: %d for task: %d", id, taskId);
|
||||
return;
|
||||
}
|
||||
cocos2d::network::DownloaderAndroid *downloader = iter->second;
|
||||
if (errStr)
|
||||
{
|
||||
const char *nativeErrStr = nullptr;
|
||||
env->GetStringUTFChars(errStr, JNI_FALSE);
|
||||
downloader->_onFinish((int)taskId, (int)errCode, nativeErrStr);
|
||||
env->ReleaseStringUTFChars(errStr, nativeErrStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
downloader->_onFinish((int)taskId, (int)errCode, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static JNINativeMethod sMethodTable[] = {
|
||||
{ "nativeOnProgress", "(IIJJJ)V", (void*)_nativeOnProgress },
|
||||
{ "nativeOnFinish", "(III" JARG_STR ")V", (void*)_nativeOnFinish },
|
||||
};
|
||||
|
||||
static bool _registerNativeMethods(JNIEnv* env)
|
||||
{
|
||||
jclass clazz = env->FindClass(JCLS_DOWNLOADER);
|
||||
if (clazz == NULL)
|
||||
{
|
||||
DLLOG("_registerNativeMethods: can't find java class:%s", JARG_DOWNLOADER);
|
||||
return false;
|
||||
}
|
||||
if (JNI_OK != env->RegisterNatives(clazz, sMethodTable, sizeof(sMethodTable) / sizeof(sMethodTable[0])))
|
||||
{
|
||||
DLLOG("_registerNativeMethods: failed");
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include "network/CCIDownloaderImpl.h"
|
||||
|
||||
class _jobject;
|
||||
|
||||
namespace cocos2d { namespace network
|
||||
{
|
||||
class DownloadTaskAndroid;
|
||||
|
@ -39,9 +41,13 @@ namespace cocos2d { namespace network
|
|||
|
||||
virtual IDownloadTask *createCoTask(std::shared_ptr<const DownloadTask>& task) override;
|
||||
|
||||
// designed called by internal
|
||||
void _onProcess(int taskId, int64_t dl, int64_t dlNow, int64_t dlTotal);
|
||||
void _onFinish(int taskId, int errCode, const char *errStr);
|
||||
protected:
|
||||
class Impl;
|
||||
std::shared_ptr<Impl> _impl;
|
||||
int _id;
|
||||
_jobject* _impl;
|
||||
std::unordered_map<int, DownloadTaskAndroid*> _taskMap;
|
||||
};
|
||||
|
||||
}} // namespace cocos2d::network
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#define DownloaderImpl DownloaderApple
|
||||
|
||||
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
#include <unordered_map> // temp
|
||||
|
||||
#include "network/CCDownloader-android.h"
|
||||
#define DownloaderImpl DownloaderAndroid
|
||||
|
||||
|
@ -107,14 +107,15 @@ namespace cocos2d { namespace network {
|
|||
std::vector<unsigned char>& data)
|
||||
{
|
||||
if (DownloadTask::ERROR_NO_ERROR != errorCode)
|
||||
|
||||
{
|
||||
if (onTaskError)
|
||||
{
|
||||
onTaskError(task, errorCode, errorCodeInternal, errorStr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// success callback
|
||||
if (task.storagePath.length())
|
||||
{
|
||||
if (onFileTaskSuccess)
|
||||
|
|
|
@ -25,7 +25,7 @@ THE SOFTWARE.
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
#include "base/CCConsole.h"
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
package org.cocos2dx.lib;
|
||||
|
||||
import com.loopj.android.http.AsyncHttpClient;
|
||||
import com.loopj.android.http.FileAsyncHttpResponseHandler;
|
||||
import com.loopj.android.http.RequestHandle;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
class FileTaskHandler extends FileAsyncHttpResponseHandler {
|
||||
int _id;
|
||||
File _finalFile;
|
||||
|
||||
private long _initFileLen;
|
||||
private long _lastBytesWritten;
|
||||
private Cocos2dxDownloader _downloader;
|
||||
|
||||
void LogD(String msg) {
|
||||
android.util.Log.d("Cocos2dxDownloader", msg);
|
||||
}
|
||||
|
||||
public FileTaskHandler(Cocos2dxDownloader downloader, int id, File temp, File finalFile) {
|
||||
super(temp, true);
|
||||
_finalFile = finalFile;
|
||||
_downloader = downloader;
|
||||
_id = id;
|
||||
_initFileLen = getTargetFile().length();
|
||||
_lastBytesWritten = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(long bytesWritten, long totalSize) {
|
||||
//LogD("onProgress(bytesWritten:" + bytesWritten + " totalSize:" + totalSize);
|
||||
long dlBytes = bytesWritten - _lastBytesWritten;
|
||||
long dlNow = bytesWritten + _initFileLen;
|
||||
long dlTotal = totalSize + _initFileLen;
|
||||
_downloader.onProgress(_id, dlBytes, dlNow, dlTotal);
|
||||
_lastBytesWritten = bytesWritten;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
_downloader.onStart(_id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// onFinish called after onSuccess/onFailure
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int i, Header[] headers, Throwable throwable, File file) {
|
||||
LogD("onFailure(i:" + i + " headers:" + headers.toString() + " throwable:" + throwable + " file:" + file);
|
||||
_downloader.onFinish(_id, i, throwable.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(int i, Header[] headers, File file) {
|
||||
LogD("onSuccess(i:" + i + " headers:" + headers.toString() + " file:" + file);
|
||||
String errStr = null;
|
||||
do {
|
||||
// rename temp file to final file
|
||||
// if final file exist, remove it
|
||||
if (_finalFile.exists()) {
|
||||
if (_finalFile.isDirectory()) {
|
||||
errStr = "Dest file is directory:" + _finalFile.getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
if (false == _finalFile.delete()) {
|
||||
errStr = "Can't remove old file:" + _finalFile.getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
File tempFile = getTargetFile();
|
||||
tempFile.renameTo(_finalFile);
|
||||
} while (false);
|
||||
_downloader.onFinish(_id, 0, errStr);
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadTask {
|
||||
|
||||
DownloadTask() {
|
||||
handle = null;
|
||||
fileHandler = null;
|
||||
resetStatus();
|
||||
}
|
||||
|
||||
void resetStatus() {
|
||||
finished = false;
|
||||
bytesReceived = 0;
|
||||
totalBytesReceived = 0;
|
||||
totalBytesExpected = 0;
|
||||
data = null;
|
||||
}
|
||||
|
||||
RequestHandle handle;
|
||||
FileTaskHandler fileHandler;
|
||||
|
||||
boolean finished;
|
||||
|
||||
// progress
|
||||
long bytesReceived;
|
||||
long totalBytesReceived;
|
||||
long totalBytesExpected;
|
||||
byte[] data;
|
||||
|
||||
}
|
||||
|
||||
public class Cocos2dxDownloader {
|
||||
private int _id;
|
||||
private AsyncHttpClient _httpClient = new AsyncHttpClient();
|
||||
private String _tempFileNameSufix;
|
||||
private HashMap _taskMap = new HashMap();
|
||||
|
||||
void onProgress(final int id, final long downloadBytes, final long downloadNow, final long downloadTotal) {
|
||||
DownloadTask task = (DownloadTask)_taskMap.get(id);
|
||||
if (null != task) {
|
||||
task.bytesReceived = downloadBytes;
|
||||
task.totalBytesReceived = downloadNow;
|
||||
task.totalBytesExpected = downloadTotal;
|
||||
}
|
||||
Cocos2dxHelper.runOnGLThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nativeOnProgress(_id, id, downloadBytes, downloadNow, downloadTotal);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onStart(int id) {
|
||||
DownloadTask task = (DownloadTask)_taskMap.get(id);
|
||||
if (null != task) {
|
||||
task.resetStatus();
|
||||
}
|
||||
}
|
||||
|
||||
public void onFinish(final int id, final int errCode, final String errStr) {
|
||||
DownloadTask task = (DownloadTask)_taskMap.get(id);
|
||||
if (null == task) return;
|
||||
_taskMap.remove(id);
|
||||
Cocos2dxHelper.runOnGLThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nativeOnFinish(_id, id, errCode, errStr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Cocos2dxDownloader createDownloader(int id, int timeoutInSeconds, String tempFileNameSufix) {
|
||||
Cocos2dxDownloader downloader = new Cocos2dxDownloader();
|
||||
downloader._id = id;
|
||||
|
||||
downloader._httpClient.setEnableRedirects(true);
|
||||
if (timeoutInSeconds > 0) {
|
||||
downloader._httpClient.setTimeout(timeoutInSeconds * 1000);
|
||||
}
|
||||
// downloader._httpClient.setMaxRetriesAndTimeout(3, timeoutInSeconds * 1000);
|
||||
downloader._httpClient.allowRetryExceptionClass(javax.net.ssl.SSLException.class);
|
||||
|
||||
downloader._tempFileNameSufix = tempFileNameSufix;
|
||||
return downloader;
|
||||
}
|
||||
|
||||
public static void createTask(final Cocos2dxDownloader downloader, int id_, String url_, String path_) {
|
||||
final int id = id_;
|
||||
final String url = url_;
|
||||
final String path = path_;
|
||||
|
||||
Cocos2dxHelper.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DownloadTask task = new DownloadTask();
|
||||
do {
|
||||
File tempFile = new File(path + downloader._tempFileNameSufix);
|
||||
if (tempFile.isDirectory()) break;
|
||||
|
||||
File parent = tempFile.getParentFile();
|
||||
if (!parent.isDirectory() && !parent.mkdirs()) break;
|
||||
|
||||
File finalFile = new File(path);
|
||||
if (tempFile.isDirectory()) break;
|
||||
|
||||
task.fileHandler = new FileTaskHandler(downloader, id, tempFile, finalFile);
|
||||
Header[] headers = null;
|
||||
long fileLen = tempFile.length();
|
||||
if (fileLen > 0) {
|
||||
// continue download
|
||||
List<Header> list = new ArrayList<Header>();
|
||||
list.add(new BasicHeader("Range", "bytes=" + fileLen + "-"));
|
||||
headers = list.toArray(new Header[list.size()]);
|
||||
}
|
||||
task.handle = downloader._httpClient.get(Cocos2dxHelper.getActivity(), url, headers, null, task.fileHandler);
|
||||
//task.handle = downloader._httpClient.get(url, task.fileHandler);
|
||||
} while (false);
|
||||
|
||||
if (null == task.handle) {
|
||||
final String errStr = "Can't create DownloadTask for " + url;
|
||||
Cocos2dxHelper.runOnGLThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
downloader.nativeOnFinish(downloader._id, id, 0, errStr);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
downloader._taskMap.put(id, task);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void cancelAllRequests(final Cocos2dxDownloader downloader) {
|
||||
Cocos2dxHelper.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
//downloader._httpClient.cancelAllRequests(true);
|
||||
Iterator iter = downloader._taskMap.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) iter.next();
|
||||
//Object key = entry.getKey();
|
||||
DownloadTask task = (DownloadTask)entry.getValue();
|
||||
if (null != task.handle) {
|
||||
task.handle.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
native void nativeOnProgress(int id, int taskId, long dl, long dlnow, long dltotal);
|
||||
native void nativeOnFinish(int id, int taskId, int errCode, String errStr);
|
||||
}
|
|
@ -26,5 +26,5 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile fileTree(dir: '../java/libs', include: ['*.jar'])
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ void DownloaderBaseTest::progressCallback(const network::DownloadTask& task,
|
|||
|
||||
void DownloaderBaseTest::successCallback(const network::DownloadTask& task)
|
||||
{
|
||||
cocos2d::log("download finished: %s", task.storagePath.c_str());
|
||||
cocos2d::log("download successed: %s", task.storagePath.c_str());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue