From 9f79e2505c1f0102c20a92fdbd26abea5e329a14 Mon Sep 17 00:00:00 2001 From: tarsin <43900799+asuka-mio@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:51:31 +0800 Subject: [PATCH] Coroutinize EhClient (#622) * Rename .java to .kt * Coroutinize EhClient to improve performance and clarity * Nullability Compatibility * Refine control flow * Fix nullability --- .../com/hippo/ehviewer/client/EhClient.java | 202 -------------- .../com/hippo/ehviewer/client/EhClient.kt | 260 ++++++++++++++++++ 2 files changed, 260 insertions(+), 202 deletions(-) delete mode 100644 app/src/main/java/com/hippo/ehviewer/client/EhClient.java create mode 100644 app/src/main/java/com/hippo/ehviewer/client/EhClient.kt diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java deleted file mode 100644 index e50be9369..000000000 --- a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2016 Hippo Seven - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hippo.ehviewer.client; - -import android.os.AsyncTask; - -import com.hippo.ehviewer.EhApplication; -import com.hippo.ehviewer.client.exception.CancelledException; -import com.hippo.util.ExceptionUtils; -import com.hippo.util.IoThreadPoolExecutor; -import com.hippo.yorozuya.SimpleHandler; - -import java.io.File; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import okhttp3.Call; -import okhttp3.OkHttpClient; - -public class EhClient { - - public static final String TAG = EhClient.class.getSimpleName(); - - public static final int METHOD_SIGN_IN = 0; - public static final int METHOD_GET_GALLERY_LIST = 1; - public static final int METHOD_GET_GALLERY_DETAIL = 3; - public static final int METHOD_GET_PREVIEW_SET = 4; - public static final int METHOD_GET_RATE_GALLERY = 5; - public static final int METHOD_GET_COMMENT_GALLERY = 6; - public static final int METHOD_GET_GALLERY_TOKEN = 7; - public static final int METHOD_GET_FAVORITES = 8; - public static final int METHOD_ADD_FAVORITES = 9; - public static final int METHOD_ADD_FAVORITES_RANGE = 10; - public static final int METHOD_MODIFY_FAVORITES = 11; - public static final int METHOD_GET_TORRENT_LIST = 12; - public static final int METHOD_GET_PROFILE = 14; - public static final int METHOD_VOTE_COMMENT = 15; - public static final int METHOD_IMAGE_SEARCH = 16; - public static final int METHOD_ARCHIVE_LIST = 17; - public static final int METHOD_DOWNLOAD_ARCHIVE = 18; - public static final int METHOD_VOTE_TAG = 19; - public static final int METHOD_GET_UCONFIG = 20; - private static final OkHttpClient mOkHttpClient = EhApplication.getOkHttpClient(); - private final ThreadPoolExecutor mRequestThreadPool; - - public EhClient() { - mRequestThreadPool = IoThreadPoolExecutor.getInstance(); - } - - @SuppressWarnings("unchecked") - public void execute(EhRequest request) { - if (!request.isCancelled()) { - Task task = new Task(request.getMethod(), request.getCallback()); - task.executeOnExecutor(mRequestThreadPool, request.getArgs()); - request.task = task; - } else { - request.getCallback().onCancel(); - } - } - - public interface Callback { - - void onSuccess(E result); - - void onFailure(Exception e); - - void onCancel(); - } - - @SuppressWarnings("deprecation") - public static class Task extends AsyncTask { - - private final int mMethod; - private final AtomicReference mCall = new AtomicReference<>(); - private final AtomicBoolean mStop = new AtomicBoolean(); - private Callback mCallback; - - public Task(int method, Callback callback) { - mMethod = method; - mCallback = callback; - } - - // Called in Job thread - public void setCall(Call call) throws CancelledException { - if (mStop.get()) { - // Stopped Job thread - throw new CancelledException(); - } else { - mCall.lazySet(call); - } - } - - public void stop() { - if (!mStop.get()) { - mStop.lazySet(true); - - if (mCallback != null) { - // TODO Avoid new runnable - SimpleHandler.getInstance().post(mCallback::onCancel); - } - - Status status = getStatus(); - if (status == Status.PENDING) { - cancel(false); - } else if (status == Status.RUNNING) { - // It is running, cancel call if it is created - Call call = mCall.get(); - if (call != null) { - call.cancel(); - } - } - - // Clear - mCallback = null; - mCall.lazySet(null); - } - } - - @Override - protected Object doInBackground(Object... params) { - try { - return switch (mMethod) { - case METHOD_SIGN_IN -> - EhEngine.signIn(this, mOkHttpClient, (String) params[0], (String) params[1]); - case METHOD_GET_GALLERY_LIST -> - EhEngine.getGalleryList(this, mOkHttpClient, (String) params[0]); - case METHOD_GET_GALLERY_DETAIL -> - EhEngine.getGalleryDetail(this, mOkHttpClient, (String) params[0]); - case METHOD_GET_PREVIEW_SET -> - EhEngine.getPreviewSet(this, mOkHttpClient, (String) params[0]); - case METHOD_GET_RATE_GALLERY -> - EhEngine.rateGallery(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (Float) params[4]); - case METHOD_GET_COMMENT_GALLERY -> - EhEngine.commentGallery(this, mOkHttpClient, (String) params[0], (String) params[1], (String) params[2]); - case METHOD_GET_GALLERY_TOKEN -> - EhEngine.getGalleryToken(this, mOkHttpClient, (Long) params[0], (String) params[1], (Integer) params[2]); - case METHOD_GET_FAVORITES -> - EhEngine.getFavorites(this, mOkHttpClient, (String) params[0]); - case METHOD_ADD_FAVORITES -> - EhEngine.addFavorites(this, mOkHttpClient, (Long) params[0], (String) params[1], (Integer) params[2], (String) params[3]); - case METHOD_ADD_FAVORITES_RANGE -> - EhEngine.addFavoritesRange(this, mOkHttpClient, (long[]) params[0], (String[]) params[1], (Integer) params[2]); - case METHOD_MODIFY_FAVORITES -> - EhEngine.modifyFavorites(this, mOkHttpClient, (String) params[0], (long[]) params[1], (Integer) params[2]); - case METHOD_GET_TORRENT_LIST -> - EhEngine.getTorrentList(this, mOkHttpClient, (String) params[0], (Long) params[1], (String) params[2]); - case METHOD_GET_PROFILE -> EhEngine.getProfile(this, mOkHttpClient); - case METHOD_VOTE_COMMENT -> - EhEngine.voteComment(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (Long) params[4], (Integer) params[5]); - case METHOD_IMAGE_SEARCH -> - EhEngine.imageSearch(this, mOkHttpClient, (File) params[0], (Boolean) params[1], (Boolean) params[2], (Boolean) params[3]); - case METHOD_ARCHIVE_LIST -> - EhEngine.getArchiveList(this, mOkHttpClient, (String) params[0], (Long) params[1], (String) params[2]); - case METHOD_DOWNLOAD_ARCHIVE -> - EhEngine.downloadArchive(this, mOkHttpClient, (Long) params[0], (String) params[1], (String) params[2], (String) params[3], (Boolean) params[4]); - case METHOD_VOTE_TAG -> - EhEngine.voteTag(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (String) params[4], (Integer) params[5]); - case METHOD_GET_UCONFIG -> EhEngine.getUConfig(this, mOkHttpClient); - default -> new IllegalStateException("Can't detect method " + mMethod); - }; - } catch (Throwable e) { - ExceptionUtils.throwIfFatal(e); - return e; - } - } - - @Override - protected void onPostExecute(Object result) { - if (mCallback != null) { - //noinspection StatementWithEmptyBody - if (!(result instanceof CancelledException)) { - if (result instanceof Exception) { - mCallback.onFailure((Exception) result); - } else { - mCallback.onSuccess(result); - } - } else { - // onCancel is called in stop - } - } - - // Clear - mCallback = null; - mCall.lazySet(null); - } - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhClient.kt b/app/src/main/java/com/hippo/ehviewer/client/EhClient.kt new file mode 100644 index 000000000..2ef036e10 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/client/EhClient.kt @@ -0,0 +1,260 @@ +/* + * Copyright 2016 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.hippo.ehviewer.client + +import com.hippo.ehviewer.EhApplication.Companion.okHttpClient +import com.hippo.ehviewer.client.exception.CancelledException +import eu.kanade.tachiyomi.util.lang.launchUI +import eu.kanade.tachiyomi.util.lang.withUIContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import okhttp3.Call +import java.io.File +import java.util.concurrent.atomic.AtomicReference + +class EhClient { + fun execute(request: EhRequest) { + if (!request.isCancelled) { + val task = Task() + val job = scope.launch(Dispatchers.IO, CoroutineStart.LAZY) { + val method = request.method + val params = request.args + val callback: Callback? = request.callback + try { + val result: Any? = when (method) { + METHOD_SIGN_IN -> EhEngine.signIn( + task, + mOkHttpClient, + params[0] as String?, + params[1] as String? + ) + + METHOD_GET_GALLERY_LIST -> EhEngine.getGalleryList( + task, + mOkHttpClient, + params[0] as String? + ) + + METHOD_GET_GALLERY_DETAIL -> EhEngine.getGalleryDetail( + task, + mOkHttpClient, + params[0] as String? + ) + + METHOD_GET_PREVIEW_SET -> EhEngine.getPreviewSet( + task, + mOkHttpClient, + params[0] as String? + ) + + METHOD_GET_RATE_GALLERY -> EhEngine.rateGallery( + task, + mOkHttpClient, + (params[0] as Long), + params[1] as String?, + (params[2] as Long), + params[3] as String?, + (params[4] as Float) + ) + + METHOD_GET_COMMENT_GALLERY -> EhEngine.commentGallery( + task, + mOkHttpClient, + params[0] as String?, + params[1] as String?, + params[2] as String? + ) + + METHOD_GET_GALLERY_TOKEN -> EhEngine.getGalleryToken( + task, + mOkHttpClient, + (params[0] as Long), + params[1] as String?, + (params[2] as Int) + ) + + METHOD_GET_FAVORITES -> EhEngine.getFavorites( + task, + mOkHttpClient, + params[0] as String? + ) + + METHOD_ADD_FAVORITES -> EhEngine.addFavorites( + task, + mOkHttpClient, + (params[0] as Long), + params[1] as String?, + (params[2] as Int), + params[3] as String? + ) + + METHOD_ADD_FAVORITES_RANGE -> @Suppress("UNCHECKED_CAST") EhEngine.addFavoritesRange( + task, + mOkHttpClient, + params[0] as LongArray?, + params[1] as Array?, + (params[2] as Int) + ) + + METHOD_MODIFY_FAVORITES -> EhEngine.modifyFavorites( + task, + mOkHttpClient, + params[0] as String?, + params[1] as LongArray?, + (params[2] as Int) + ) + + METHOD_GET_TORRENT_LIST -> EhEngine.getTorrentList( + task, + mOkHttpClient, + params[0] as String?, + (params[1] as Long), + params[2] as String? + ) + + METHOD_GET_PROFILE -> EhEngine.getProfile(task, mOkHttpClient) + METHOD_VOTE_COMMENT -> EhEngine.voteComment( + task, + mOkHttpClient, + (params[0] as Long), + params[1] as String?, + (params[2] as Long), + params[3] as String?, + (params[4] as Long), + (params[5] as Int) + ) + + METHOD_IMAGE_SEARCH -> EhEngine.imageSearch( + task, + mOkHttpClient, + params[0] as File?, + (params[1] as Boolean), + (params[2] as Boolean), + (params[3] as Boolean) + ) + + METHOD_ARCHIVE_LIST -> EhEngine.getArchiveList( + task, + mOkHttpClient, + params[0] as String?, + (params[1] as Long), + params[2] as String? + ) + + METHOD_DOWNLOAD_ARCHIVE -> EhEngine.downloadArchive( + task, + mOkHttpClient, + (params[0] as Long), + params[1] as String?, + params[2] as String?, + params[3] as String?, + (params[4] as Boolean) + ) + + METHOD_VOTE_TAG -> EhEngine.voteTag( + task, + mOkHttpClient, + (params[0] as Long), + params[1] as String?, + (params[2] as Long), + params[3] as String?, + params[4] as String?, + (params[5] as Int) + ) + + METHOD_GET_UCONFIG -> EhEngine.getUConfig(task, mOkHttpClient) + else -> throw IllegalStateException("Can't detect method $method") + } + withUIContext { callback?.onSuccess(result) } + } catch (e: Exception) { + e.printStackTrace() + if (e !is CancelledException) + withUIContext { callback?.onFailure(e) } + } + request.task = null + request.callback = null + } + task.job = job + task.mCallback = request.callback + request.task = task + job.start() + } else { + request.callback?.onCancel() + } + } + + interface Callback { + fun onSuccess(result: E) + fun onFailure(e: Exception) + fun onCancel() + } + + class Task { + private val mCall = AtomicReference() + internal var mCallback: Callback<*>? = null + internal var job: Job? = null + private val mStop + get() = !(job?.isActive ?: false) + + // Called in Job thread + @Throws(CancelledException::class) + fun setCall(call: Call?) { + if (mStop) { + // Stopped Job thread + throw CancelledException() + } else { + mCall.lazySet(call) + } + } + + fun stop() { + if (!mStop) { + job?.cancel() + mCall.get()?.cancel() + mCallback?.let { + scope.launchUI { it.onCancel() } + } + } + } + } + + companion object { + val scope = CoroutineScope(Dispatchers.IO) + const val METHOD_SIGN_IN = 0 + const val METHOD_GET_GALLERY_LIST = 1 + const val METHOD_GET_GALLERY_DETAIL = 3 + const val METHOD_GET_PREVIEW_SET = 4 + const val METHOD_GET_RATE_GALLERY = 5 + const val METHOD_GET_COMMENT_GALLERY = 6 + const val METHOD_GET_GALLERY_TOKEN = 7 + const val METHOD_GET_FAVORITES = 8 + const val METHOD_ADD_FAVORITES = 9 + const val METHOD_ADD_FAVORITES_RANGE = 10 + const val METHOD_MODIFY_FAVORITES = 11 + const val METHOD_GET_TORRENT_LIST = 12 + const val METHOD_GET_PROFILE = 14 + const val METHOD_VOTE_COMMENT = 15 + const val METHOD_IMAGE_SEARCH = 16 + const val METHOD_ARCHIVE_LIST = 17 + const val METHOD_DOWNLOAD_ARCHIVE = 18 + const val METHOD_VOTE_TAG = 19 + const val METHOD_GET_UCONFIG = 20 + private val mOkHttpClient = okHttpClient + } +} \ No newline at end of file