├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml └── vcs.xml ├── app ├── .DS_Store ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── .DS_Store │ └── main │ ├── .DS_Store │ ├── AndroidManifest.xml │ ├── cpp │ ├── .DS_Store │ ├── CMakeLists.txt │ ├── ConcurrenceQueue.h │ ├── CoroutineDespatcher.cpp │ ├── CoroutineDespatcher.h │ ├── OSCoroutine.cpp │ ├── OSCoroutine.h │ ├── OSThread.cpp │ ├── OSThread.h │ ├── log.h │ └── native-lib.cpp │ ├── java │ └── com │ │ └── kymjs │ │ ├── coroutine │ │ ├── Coroutine.java │ │ └── MainActivity.kt │ │ └── thread │ │ └── LiteThread.java │ └── res │ ├── .DS_Store │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── values-night │ └── themes.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/assetWizardSettings.xml 45 | .idea/dictionaries 46 | .idea/libraries 47 | # Android Studio 3 in .gitignore file. 48 | .idea/caches 49 | .idea/modules.xml 50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 51 | .idea/navEditor.xml 52 | 53 | # Keystore files 54 | # Uncomment the following lines if you do not want to check your keystore files in. 55 | #*.jks 56 | #*.keystore 57 | 58 | # External native build folder generated in Android Studio 2.2 and later 59 | .externalNativeBuild 60 | .cxx/ 61 | 62 | # Google Services (e.g. APIs or Firebase) 63 | # google-services.json 64 | 65 | # Freeline 66 | freeline.py 67 | freeline/ 68 | freeline_project_description.json 69 | 70 | # fastlane 71 | fastlane/report.xml 72 | fastlane/Preview.html 73 | fastlane/screenshots 74 | fastlane/test_output 75 | fastlane/readme.md 76 | 77 | # Version control 78 | vcs.xml 79 | 80 | # lint 81 | lint/intermediates/ 82 | lint/generated/ 83 | lint/outputs/ 84 | lint/tmp/ 85 | # lint/reports/ -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/AndroidCoroutine/0899aa345abd22429d3f992664981c0987902853/app/.DS_Store -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdk 30 6 | 7 | defaultConfig { 8 | applicationId "com.kymjs.coroutine" 9 | minSdk 21 10 | targetSdk 30 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | externalNativeBuild { 15 | cmake { 16 | cppFlags '-std=c++11' 17 | // arguments "-DANDROID_ARM_NEON=TRUE" 18 | } 19 | } 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | externalNativeBuild { 33 | cmake { 34 | path file('src/main/cpp/CMakeLists.txt') 35 | version '3.10.2' 36 | } 37 | } 38 | buildFeatures { 39 | viewBinding true 40 | } 41 | } 42 | 43 | dependencies { 44 | implementation 'androidx.appcompat:appcompat:1.2.0' 45 | implementation 'com.google.android.material:material:1.3.0' 46 | implementation "androidx.core:core-ktx:1.3.2" 47 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 48 | } 49 | repositories { 50 | mavenCentral() 51 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/AndroidCoroutine/0899aa345abd22429d3f992664981c0987902853/app/src/.DS_Store -------------------------------------------------------------------------------- /app/src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/AndroidCoroutine/0899aa345abd22429d3f992664981c0987902853/app/src/main/.DS_Store -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/cpp/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/AndroidCoroutine/0899aa345abd22429d3f992664981c0987902853/app/src/main/cpp/.DS_Store -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.2) 2 | project("coroutine") 3 | 4 | #ENABLE_LANGUAGE(ASM) 5 | 6 | add_library( 7 | coroutine 8 | SHARED 9 | native-lib.cpp 10 | OSCoroutine.cpp 11 | OSThread.cpp 12 | CoroutineDespatcher.cpp) 13 | 14 | find_library( 15 | log-lib 16 | log) 17 | 18 | target_link_libraries( 19 | coroutine 20 | ${log-lib}) -------------------------------------------------------------------------------- /app/src/main/cpp/ConcurrenceQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhangtao on 2022/2/15. 3 | // 4 | 5 | #ifndef COROUTINE_CONCURRENCEQUEUE_H 6 | #define COROUTINE_CONCURRENCEQUEUE_H 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | template 15 | class ConcurrenceQueue { 16 | private: 17 | // data_queue访问信号量 18 | mutable std::mutex mut; 19 | mutable std::condition_variable data_cond; 20 | using queue_type = std::deque; 21 | queue_type data_queue; 22 | public: 23 | using value_type = typename queue_type::value_type; 24 | 25 | ConcurrenceQueue() = default; 26 | 27 | ConcurrenceQueue(const ConcurrenceQueue &) = delete; 28 | 29 | ConcurrenceQueue &operator=(const ConcurrenceQueue &) = delete; 30 | 31 | /* 32 | * 使用迭代器为参数的构造函数,适用所有容器对象 33 | * */ 34 | template 35 | ConcurrenceQueue(_InputIterator 36 | first, 37 | _InputIterator last 38 | ) { 39 | for (auto itor = first; itor != last; ++itor) { 40 | data_queue.push(*itor); 41 | } 42 | } 43 | 44 | /* 45 | * 使用初始化列表为参数的构造函数 46 | * */ 47 | ConcurrenceQueue(std::initializer_list list) : ConcurrenceQueue(list.begin(), list.end()) { 48 | } 49 | 50 | /* 51 | * 将元素加入队列 52 | * */ 53 | void push(const value_type &new_value) { 54 | std::lock_guard lk(mut); 55 | data_queue.push_back(std::move(new_value)); 56 | data_cond.notify_one(); 57 | } 58 | 59 | /* 60 | * 将元素加入队列头 61 | * */ 62 | void push_front(const value_type &value1, const value_type &value2) { 63 | std::lock_guard lk(mut); 64 | data_queue.push_front(std::move(value1)); 65 | data_queue.push_front(std::move(value2)); 66 | data_cond.notify_one(); 67 | } 68 | 69 | void push_front(const value_type &new_value) { 70 | std::lock_guard lk(mut); 71 | data_queue.push_front(std::move(new_value)); 72 | data_cond.notify_one(); 73 | } 74 | 75 | /* 76 | * 从队列中弹出一个元素,如果队列为空就阻塞 77 | * */ 78 | value_type pop() { 79 | std::unique_lock lk(mut); 80 | data_cond.wait(lk, [this] { return !this->data_queue.empty(); }); 81 | auto value = std::move(data_queue.front()); 82 | data_queue.pop_front(); 83 | return value; 84 | } 85 | 86 | /* 87 | * 从队列尾弹出一个元素,如果队列为空就阻塞 88 | * */ 89 | value_type pop_back() { 90 | std::unique_lock lk(mut); 91 | data_cond.wait(lk, [this] { return !this->data_queue.empty(); }); 92 | auto value = std::move(data_queue.front()); 93 | data_queue.pop_back(); 94 | return value; 95 | } 96 | 97 | /* 98 | * 从队列中弹出一个元素,如果队列为空返回false 99 | * */ 100 | bool try_pop(value_type &value) { 101 | std::lock_guard lk(mut); 102 | if (data_queue.empty()) 103 | return false; 104 | value = std::move(data_queue.front()); 105 | data_queue.pop_front(); 106 | return true; 107 | } 108 | 109 | /* 110 | * 返回队列是否为空 111 | * */ 112 | auto empty() const -> decltype(data_queue.empty()) { 113 | std::lock_guard lk(mut); 114 | return data_queue.empty(); 115 | } 116 | 117 | /* 118 | * 返回队列中元素数个 119 | * */ 120 | auto size() const -> decltype(data_queue.size()) { 121 | std::lock_guard lk(mut); 122 | return data_queue.size(); 123 | } 124 | }; 125 | 126 | #endif //COROUTINE_CONCURRENCEQUEUE_H 127 | -------------------------------------------------------------------------------- /app/src/main/cpp/CoroutineDespatcher.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhangtao on 2022/2/11. 3 | // 4 | 5 | #include "CoroutineDespatcher.h" 6 | 7 | CoroutineDespatcher::CoroutineDespatcher(JNIEnv *env, bool mainThread) { 8 | env->GetJavaVM(&(this->jvm)); 9 | this->mainThread = mainThread; 10 | this->env = env; 11 | } 12 | 13 | CoroutineDespatcher::~CoroutineDespatcher() { 14 | jvm->DetachCurrentThread(); 15 | } 16 | 17 | void CoroutineDespatcher::despatchInMainThread(OSCoroutine *pCoroutine) { 18 | if (pCoroutine != nullptr) { 19 | pCoroutine->async(env); 20 | delete pCoroutine; 21 | } 22 | } 23 | 24 | void CoroutineDespatcher::despatch() { 25 | while (!mainThread) { 26 | if (suspendQueue.empty()) { 27 | fightCoroutine = fightQueue.pop(); 28 | if (fightCoroutine->suspendState == NONE) { 29 | LOGD("====fightQueue->执行协程%lld", fightCoroutine->coroutineId); 30 | fightCoroutine->async(env); 31 | if (fightCoroutine->suspendState == FINISHED) { 32 | delete fightCoroutine; 33 | } 34 | } 35 | } else { 36 | if (suspendQueue.try_pop(fightCoroutine)) { 37 | if (fightCoroutine->suspendState < RESUMED) { 38 | LOGD("====suspendQueue->执行协程%lld", fightCoroutine->coroutineId); 39 | fightCoroutine->async(env); 40 | } 41 | 42 | if (fightCoroutine->suspendState == FINISHED) { 43 | delete fightCoroutine; 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | void CoroutineDespatcher::join(jobject jobj) { 51 | if (mainThread) { 52 | despatchInMainThread(new OSCoroutine(env, jobj)); 53 | } else { 54 | if (fightCoroutine != nullptr) { 55 | fightCoroutine->suspendState = SUSPEND; 56 | suspendQueue.push_front(fightCoroutine, new OSCoroutine(env, jobj)); 57 | } else { 58 | suspendQueue.push_front(new OSCoroutine(env, jobj)); 59 | } 60 | } 61 | } 62 | 63 | void CoroutineDespatcher::attachCoroutine(jobject jobj) { 64 | if (mainThread) { 65 | despatchInMainThread(new OSCoroutine(env, jobj)); 66 | } else { 67 | fightQueue.push(new OSCoroutine(env, jobj)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/cpp/CoroutineDespatcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhangtao on 2022/2/11. 3 | // 4 | 5 | #ifndef COROUTINE_THREAD_H 6 | #define COROUTINE_THREAD_H 7 | 8 | #include 9 | #include "ConcurrenceQueue.h" 10 | #include "pthread.h" 11 | #include "OSCoroutine.h" 12 | #include "log.h" 13 | 14 | class CoroutineDespatcher { 15 | private: 16 | ConcurrenceQueue fightQueue; 17 | ConcurrenceQueue suspendQueue; 18 | OSCoroutine *fightCoroutine; 19 | bool mainThread; 20 | JNIEnv *env; 21 | 22 | void despatchInMainThread(OSCoroutine *pCoroutine); 23 | 24 | public: 25 | JavaVM *jvm; 26 | 27 | ~CoroutineDespatcher(); 28 | 29 | CoroutineDespatcher(JNIEnv *env, bool mainThread); 30 | 31 | void despatch(); 32 | 33 | void join(jobject jobj); 34 | 35 | void attachCoroutine(jobject jobj); 36 | }; 37 | 38 | #endif //COROUTINE_THREAD_H 39 | -------------------------------------------------------------------------------- /app/src/main/cpp/OSCoroutine.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhangtao on 2022/2/14. 3 | // 4 | 5 | #include "OSCoroutine.h" 6 | 7 | OSCoroutine::OSCoroutine(JNIEnv *env, jobject jobj) { 8 | env->GetJavaVM(&(this->jvm)); 9 | this->jCoroutine = env->NewGlobalRef(jobj); 10 | jclass jCoroutineClass = env->GetObjectClass(jCoroutine); 11 | jfieldID jCoroutineId = env->GetFieldID(jCoroutineClass, "coroutineId", "J"); 12 | this->coroutineId = env->GetLongField(jCoroutine, jCoroutineId); 13 | } 14 | 15 | OSCoroutine::~OSCoroutine() { 16 | } 17 | 18 | void OSCoroutine::async(JNIEnv *env) { 19 | if (suspendState == NEED_RESUME) { 20 | resume(env); 21 | } else { 22 | suspendState = NONE; 23 | if (jvm->AttachCurrentThread(&env, NULL) != 0) { 24 | LOGD("faile to attach"); 25 | } 26 | 27 | jclass jCoroutineClass = env->GetObjectClass(jCoroutine); 28 | jmethodID runId = env->GetMethodID(jCoroutineClass, "run", "()Ljava/lang/Object;"); 29 | if (runId != nullptr) { 30 | suspendAwait(env, env->CallObjectMethod(jCoroutine, runId)); 31 | } else { 32 | LOGD("No run method found in Coroutine"); 33 | } 34 | } 35 | } 36 | 37 | void OSCoroutine::suspendAwait(JNIEnv *env, jobject result) { 38 | if (suspendState == SUSPEND) { 39 | if (setjmp(suspendPoint)) { 40 | await(env, result); 41 | } else { 42 | suspendState = NEED_RESUME; 43 | LOGD("挂起协程:%lld", coroutineId); 44 | } 45 | } else if (suspendState == NONE) { 46 | await(env, result); 47 | } 48 | } 49 | 50 | void OSCoroutine::await(JNIEnv *env, jobject result) { 51 | jclass jCoroutineClass = env->GetObjectClass(jCoroutine); 52 | jmethodID onAwaitMethod = env->GetMethodID(jCoroutineClass, "onAwait", "(Ljava/lang/Object;)V"); 53 | if (onAwaitMethod != nullptr) { 54 | env->CallVoidMethod(jCoroutine, onAwaitMethod, result); 55 | } 56 | suspendState = FINISHED; 57 | LOGD("协程:%lld,执行完成", coroutineId); 58 | env->DeleteGlobalRef(jCoroutine); 59 | } 60 | 61 | void OSCoroutine::delay(jlong millis) { 62 | 63 | } 64 | 65 | void OSCoroutine::resume(JNIEnv *env) { 66 | LOGD("恢复协程%lld", coroutineId); 67 | suspendState = RESUMED; 68 | longjmp(suspendPoint, 1); 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/cpp/OSCoroutine.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhangtao on 2022/2/14. 3 | // 4 | 5 | #ifndef COROUTINE_OSCOROUTINE_H 6 | #define COROUTINE_OSCOROUTINE_H 7 | 8 | 9 | #include 10 | #include "log.h" 11 | #include 12 | 13 | const int NONE = 0; 14 | const int SUSPEND = 1; 15 | const int NEED_RESUME = 2; 16 | const int RESUMED = 3; 17 | const int FINISHED = 4; 18 | 19 | class OSCoroutine { 20 | private: 21 | jmp_buf suspendPoint; 22 | public: 23 | JavaVM *jvm; 24 | jobject jCoroutine; 25 | jlong coroutineId; 26 | int suspendState = NONE; 27 | 28 | ~OSCoroutine(); 29 | 30 | OSCoroutine(JNIEnv *env, jobject thiz); 31 | 32 | void async(JNIEnv *env); 33 | 34 | void await(JNIEnv *env, jobject result); 35 | 36 | void suspendAwait(JNIEnv *env, jobject result); 37 | 38 | void delay(jlong millis); 39 | 40 | void resume(JNIEnv *env); 41 | }; 42 | 43 | 44 | #endif //COROUTINE_OSCOROUTINE_H 45 | -------------------------------------------------------------------------------- /app/src/main/cpp/OSThread.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhangtao on 2022/2/11. 3 | // 4 | 5 | #include "log.h" 6 | #include "OSThread.h" 7 | 8 | OSThread::OSThread(JNIEnv *env, long threadId) { 9 | this->threadId = threadId; 10 | this->despatcher = new CoroutineDespatcher(env, threadId == 1); 11 | } 12 | 13 | OSThread::~OSThread() { 14 | delete despatcher; 15 | } 16 | 17 | void OSThread::start() { 18 | if (threadId == 1) { 19 | LOGD("在主线程执行协程"); 20 | despatcher->despatch(); 21 | } else { 22 | pthread_t tid; 23 | pthread_attr_t Attr; 24 | pthread_attr_init(&Attr); 25 | pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); 26 | if (pthread_create(&tid, &Attr, &OSThread::thread_entry_function, this->despatcher) != 0) { 27 | LOGD("线程创建失败"); 28 | return; 29 | } 30 | LOGD("启动C线程 tid=%ld", tid); 31 | pthread_attr_destroy(&Attr); 32 | } 33 | } 34 | 35 | void *OSThread::thread_entry_function(void *args) { 36 | auto *despatcher = static_cast(args); 37 | despatcher->despatch(); 38 | delete despatcher; 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/cpp/OSThread.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhangtao on 2022/2/11. 3 | // 4 | 5 | #ifndef COROUTINE_OSTHREAD_H 6 | #define COROUTINE_OSTHREAD_H 7 | 8 | 9 | #include "CoroutineDespatcher.h" 10 | 11 | class OSThread { 12 | public: 13 | long threadId; 14 | CoroutineDespatcher *despatcher; 15 | 16 | OSThread(JNIEnv *env, long threadId); 17 | 18 | ~OSThread(); 19 | 20 | void start(); 21 | 22 | static void *thread_entry_function(void *args); 23 | }; 24 | 25 | 26 | #endif //COROUTINE_OSTHREAD_H 27 | -------------------------------------------------------------------------------- /app/src/main/cpp/log.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LOG_H 2 | #define CORE_LOG_H 3 | 4 | #define APP_NAME "com.kymjs.coroutine" 5 | #define DEBUG 1 6 | 7 | #ifdef __ANDROID__ 8 | 9 | #include 10 | #include 11 | 12 | #ifdef DEBUG 13 | #define LOGV(...) __android_log_print(2, APP_NAME, __VA_ARGS__) 14 | #define LOGD(...) __android_log_print(3, APP_NAME, __VA_ARGS__) 15 | #define LOGI(...) __android_log_print(4, APP_NAME, __VA_ARGS__) 16 | #define LOGW(...) __android_log_print(5, APP_NAME, __VA_ARGS__) 17 | #define LOGE(...) __android_log_print(6, APP_NAME, __VA_ARGS__) 18 | #else 19 | #define LOGV(...) {} 20 | #define LOGD(...) {} 21 | #define LOGI(...) {} 22 | #define LOGW(...) {} 23 | #define LOGE(...) {} 24 | #endif 25 | 26 | #else // __ANDROID__ 27 | 28 | ///================= package define ===================== 29 | #define ARG_COUNT_PRIVATE(\ 30 | _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, \ 31 | _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, \ 32 | _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, \ 33 | _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ 34 | _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, \ 35 | _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, \ 36 | _60, _61, _62, _63, _64, N, ...) N 37 | 38 | #define ARG_COUNT(...) ARG_COUNT_PRIVATE(0, __VA_ARGS__,\ 39 | 64, 63, 62, 61, 60, \ 40 | 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ 41 | 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ 42 | 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ 43 | 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ 44 | 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ 45 | 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 46 | 47 | #define FUN_COUNT_GLUE(M, count) M##count 48 | #define FUN_JOIN_COUNT(M, count) FUN_COUNT_GLUE(M,count) 49 | #define FUN_JOIN_ARGS(x, y) x y 50 | 51 | #define CallSomeOne(fn, ...) FUN_JOIN_ARGS(FUN_JOIN_COUNT(fn, ARG_COUNT(__VA_ARGS__)), (__VA_ARGS__)) 52 | 53 | #if defined QS_LOG 54 | #include "QsLog.h" 55 | #define PR QLOG_INFO() // QsLog 输出(一个用Qt封装的日志类,挺好用的,在此推荐一下) 56 | #define ENDL "" 57 | #elif defined QT_CORE_LIB // Qt 标准输出 58 | #include 59 | #define PR qDebug() 60 | #define ENDL "" 61 | #elif defined __cplusplus 62 | 63 | #include 64 | 65 | using namespace std; 66 | #define PR std::cout 67 | #define ENDL std::endl 68 | #endif 69 | 70 | #define OUT_RED "\033[0;31;1m" 71 | #define OUT_GREEN "\033[0;32;1m" 72 | #define OUT_YELLOW "\033[0;33;1m" 73 | #define OUT_BLUE "\033[0;34;1m" 74 | #define OUT_END "\033[0m" 75 | 76 | #define FILE_INFO "[" << __FILE__ << '@' << __FUNCTION__ << '#' << __LINE__ << "]" 77 | 78 | #define param1(a) #a":" << a 79 | #define param2(a, b) #a":" << a << ", "#b":" << b 80 | #define param3(a, b, c) #a":" << a << ", "#b":" << b << ", "#c":" << c 81 | #define param4(a, b, c, d) #a":" << a << ", "#b":" << b << ", "#c":" << c << ", "#d":" << d 82 | 83 | #define pr0() "null param out" 84 | #define pr1(...) param1(__VA_ARGS__) 85 | #define pr2(...) param2(__VA_ARGS__) 86 | #define pr3(...) param3(__VA_ARGS__) 87 | #define pr4(...) param4(__VA_ARGS__) 88 | #define pr5(a, b, c, d, e) pr3(a,b,c) << ", " << param2(d,e) 89 | #define pr6(a, b, c, d, e, f) pr3(a,b,c) << ", " << param3(d,e,f) 90 | #define pr7(a, b, c, d, e, f, g) pr4(a,b,c,d) << ", " << param3(e,f,g) 91 | #define pr8(a, b, c, d, e, f, g, h) pr4(a,b,c,d) << ", " << param4(e,f,g,h) 92 | #define pr9(a, b, c, d, e, f, g, h, i) pr8(a,b,c,d,e,f,g,h) << ", " << param1(i) 93 | #define pr10(a, b, c, d, e, f, g, h, i, j) pr9(a,b,c,d,e,f,g,h,i) << ", " << param1(j) 94 | //.... 有兴趣可以继续扩充 95 | 96 | #define LOGV(x) PR << FILE_INFO << x << ENDL // 原样输出,无需格式化 97 | 98 | #define LOGD(...) PR << "" << "DEBUG " << FILE_INFO << CallSomeOne(pr, __VA_ARGS__) << ENDL 99 | #define LOGI(...) PR << OUT_GREEN << "INFO " << FILE_INFO << CallSomeOne(pr, __VA_ARGS__) << OUT_END << ENDL 100 | #define LOGW(...) PR << OUT_YELLOW << "WARN " << FILE_INFO << CallSomeOne(pr, __VA_ARGS__) << OUT_END << ENDL 101 | #define LOGE(...) PR << OUT_RED << "ERROR " << FILE_INFO << CallSomeOne(pr, __VA_ARGS__) << OUT_END << ENDL 102 | #endif // ANDROID_H 103 | 104 | #endif //CORE_LOG_H 105 | 106 | -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "CoroutineDespatcher.h" 5 | #include "OSThread.h" 6 | #include "OSCoroutine.h" 7 | #include "log.h" 8 | #include "ConcurrenceQueue.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | static std::map threads; 14 | 15 | JNIEXPORT void JNICALL 16 | Java_com_kymjs_thread_LiteThread_startNative(JNIEnv *env, jobject thiz) { 17 | } 18 | 19 | JNIEXPORT void JNICALL 20 | Java_com_kymjs_thread_LiteThread_yield(JNIEnv *env, jobject thiz) { 21 | LOGD("calling yield operation"); 22 | sched_yield(); 23 | } 24 | 25 | JNIEXPORT void JNICALL 26 | Java_com_kymjs_thread_LiteThread_sleep(JNIEnv *env, jclass clazz, jlong millis, jint thread_id) { 27 | } 28 | 29 | JNIEXPORT jboolean JNICALL 30 | Java_com_kymjs_thread_LiteThread_isInterrupt(JNIEnv *env, jobject thiz) { 31 | LOGD("getting intterrupt flag"); 32 | return 0; 33 | } 34 | 35 | JNIEXPORT void JNICALL 36 | Java_com_kymjs_thread_LiteThread_interrupt(JNIEnv *env, jobject thiz) { 37 | LOGD("getting intterrupt flag"); 38 | } 39 | 40 | JNIEXPORT void JNICALL 41 | Java_com_kymjs_thread_LiteThread_resume(JNIEnv *env, jobject thiz) { 42 | LOGD("getting intterrupt flag"); 43 | } 44 | 45 | JNIEXPORT void JNICALL 46 | Java_com_kymjs_coroutine_Coroutine_init(JNIEnv *env, jobject thiz, jlong jThreadId) { 47 | auto *pThread = threads.find(jThreadId)->second; 48 | if (pThread == nullptr) { 49 | pThread = new OSThread(env, jThreadId); 50 | pThread->start(); 51 | threads.insert(std::map::value_type(pThread->threadId, pThread)); 52 | } 53 | } 54 | 55 | JNIEXPORT void JNICALL 56 | Java_com_kymjs_coroutine_Coroutine_async(JNIEnv *env, jobject thiz) { 57 | jclass jCroutineClass = env->GetObjectClass(thiz); 58 | jfieldID jThreadId = env->GetFieldID(jCroutineClass, "threadId", "J"); 59 | jlong threadId = env->GetLongField(thiz, jThreadId); 60 | threads.find(threadId)->second->despatcher->attachCoroutine(thiz); 61 | } 62 | 63 | JNIEXPORT void JNICALL 64 | Java_com_kymjs_coroutine_Coroutine_delay(JNIEnv *env, jobject thiz, jlong millis) { 65 | } 66 | 67 | JNIEXPORT void JNICALL 68 | Java_com_kymjs_coroutine_Coroutine_join(JNIEnv *env, jobject thiz) { 69 | jclass jCroutineClass = env->GetObjectClass(thiz); 70 | jfieldID jThreadId = env->GetFieldID(jCroutineClass, "threadId", "J"); 71 | jlong threadId = env->GetLongField(thiz, jThreadId); 72 | threads.find(threadId)->second->despatcher->join(thiz); 73 | } 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/kymjs/coroutine/Coroutine.java: -------------------------------------------------------------------------------- 1 | package com.kymjs.coroutine; 2 | 3 | import java.util.concurrent.atomic.AtomicLong; 4 | 5 | public class Coroutine { 6 | private static final AtomicLong count = new AtomicLong(); 7 | public final long coroutineId; 8 | public final long threadId; 9 | 10 | public Coroutine() { 11 | this(Thread.currentThread().getId()); 12 | } 13 | 14 | public Coroutine(long id) { 15 | coroutineId = count.incrementAndGet(); 16 | threadId = id; 17 | init(threadId); 18 | } 19 | 20 | public void onAwait(T result) { 21 | } 22 | 23 | public Object run() { 24 | return null; 25 | } 26 | 27 | private native void init(long threadId); 28 | 29 | public native void async(); 30 | 31 | public native void delay(long millis); 32 | 33 | public native void join(); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/kymjs/coroutine/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kymjs.coroutine 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.widget.TextView 6 | import com.kymjs.coroutine.MainActivity.TestThread 7 | import com.kymjs.coroutine.MainActivity.TestCoroutine 8 | import com.kymjs.thread.LiteThread 9 | import com.kymjs.coroutine.Coroutine 10 | import com.kymjs.coroutine.databinding.ActivityMainBinding 11 | import java.lang.Exception 12 | import java.net.URL 13 | 14 | class MainActivity : AppCompatActivity() { 15 | companion object { 16 | init { 17 | System.loadLibrary("coroutine") 18 | } 19 | } 20 | 21 | private var binding: ActivityMainBinding? = null 22 | override fun onCreate(savedInstanceState: Bundle?) { 23 | super.onCreate(savedInstanceState) 24 | binding = ActivityMainBinding.inflate(layoutInflater) 25 | setContentView(binding!!.root) 26 | val tv = binding!!.sampleText 27 | tv.text = "test ndk" 28 | binding!!.button1.setOnClickListener { TestThread().start() } 29 | binding!!.button2.setOnClickListener { TestCoroutine().async() } 30 | binding!!.button3.setOnClickListener { TestCoroutine(2).async() } 31 | binding!!.button4.setOnClickListener { 32 | val a1 = TestCoroutine(2) 33 | val a2 = TestCoroutine(2) 34 | a1.async() 35 | a2.join() 36 | } 37 | } 38 | 39 | internal class TestThread : LiteThread() { 40 | override fun run() { 41 | super.run() 42 | println(Thread.currentThread().name) 43 | println("test in java despatch method") 44 | var i = 0 45 | while (true) { 46 | println("i=" + i++) 47 | sleep(1000, threadId) 48 | println("当前中断状态:$isInterrupt") 49 | if (i > 5) { 50 | if (isInterrupt) { 51 | resume() 52 | println("恢复") 53 | } else { 54 | interrupt() 55 | println("中断") 56 | } 57 | i = 0 58 | } 59 | } 60 | } 61 | } 62 | 63 | internal class TestCoroutine : Coroutine { 64 | constructor() : super() {} 65 | constructor(id: Long) : super(id) {} 66 | 67 | override fun run(): Any { 68 | super.run() 69 | println("========run::" + hashCode()) 70 | try { 71 | return URL("https://baidu.com").readText() 72 | } catch (e: Exception) { 73 | e.printStackTrace() 74 | } 75 | return "test in java" 76 | } 77 | 78 | override fun onAwait(result: T) { 79 | super.onAwait(result) 80 | println("========onAwait::" + hashCode()) 81 | println("========output::$result") 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kymjs/thread/LiteThread.java: -------------------------------------------------------------------------------- 1 | package com.kymjs.thread; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | public class LiteThread { 6 | private static final AtomicInteger count = new AtomicInteger(); 7 | public final int threadId; 8 | 9 | public LiteThread() { 10 | threadId = count.incrementAndGet(); 11 | } 12 | 13 | public void run() { 14 | } 15 | 16 | public void start() { 17 | startNative(); 18 | } 19 | 20 | private native void startNative(); 21 | 22 | public native void yield(); 23 | 24 | public static native void sleep(long millis, int threadId); 25 | 26 | public native boolean isInterrupt(); 27 | 28 | public native void interrupt(); 29 | 30 | public native void resume(); 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/res/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/AndroidCoroutine/0899aa345abd22429d3f992664981c0987902853/app/src/main/res/.DS_Store -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 |