SubwayTooter-Android-App/anko/src/main/java/org/jetbrains/anko/Async.kt

210 lines
6.7 KiB
Kotlin

/*
* Copyright 2016 JetBrains s.r.o.
*
* 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.
*/
@file:Suppress("unused")
package org.jetbrains.anko
import android.app.Activity
import android.content.Context
import android.os.Handler
import android.os.Looper
import androidx.fragment.app.Fragment
import java.lang.ref.WeakReference
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
/**
* Execute [f] on the application UI thread.
*/
fun Context.runOnUiThread(f: Context.() -> Unit) {
if (Looper.getMainLooper() === Looper.myLooper()) f() else ContextHelper.handler.post { f() }
}
/**
* Execute [f] on the application UI thread.
*/
inline fun Fragment.runOnUiThread(crossinline f: () -> Unit) {
requireContext().runOnUiThread { f() }
}
class AnkoAsyncContext<T>(val weakRef: WeakReference<T>)
/**
* Execute [f] on the application UI thread.
* If the [doAsync] receiver still exists (was not collected by GC),
* [f] gets it as a parameter ([f] gets null if the receiver does not exist anymore).
*/
fun <T> AnkoAsyncContext<T>.onComplete(f: (T?) -> Unit) {
val ref = weakRef.get()
if (Looper.getMainLooper() === Looper.myLooper()) {
f(ref)
} else {
ContextHelper.handler.post { f(ref) }
}
}
/**
* Execute [f] on the application UI thread.
* [doAsync] receiver will be passed to [f].
* If the receiver does not exist anymore (it was collected by GC), [f] will not be executed.
*/
fun <T> AnkoAsyncContext<T>.uiThread(f: (T) -> Unit): Boolean {
val ref = weakRef.get() ?: return false
if (Looper.getMainLooper() === Looper.myLooper()) {
f(ref)
} else {
ContextHelper.handler.post { f(ref) }
}
return true
}
/**
* Execute [f] on the application UI thread if the underlying [Activity] still exists and is not finished.
* The receiver [Activity] will be passed to [f].
* If it is not exist anymore or if it was finished, [f] will not be called.
*/
fun <T : Activity> AnkoAsyncContext<T>.activityUiThread(f: (T) -> Unit): Boolean {
val activity = weakRef.get() ?: return false
if (activity.isFinishing) return false
activity.runOnUiThread { f(activity) }
return true
}
fun <T : Activity> AnkoAsyncContext<T>.activityUiThreadWithContext(f: Context.(T) -> Unit): Boolean {
val activity = weakRef.get() ?: return false
if (activity.isFinishing) return false
activity.runOnUiThread { activity.f(activity) }
return true
}
@JvmName("activityContextUiThread")
fun <T : Activity> AnkoAsyncContext<AnkoContext<T>>.activityUiThread(f: (T) -> Unit): Boolean {
val activity = weakRef.get()?.owner ?: return false
if (activity.isFinishing) return false
activity.runOnUiThread { f(activity) }
return true
}
@JvmName("activityContextUiThreadWithContext")
fun <T : Activity> AnkoAsyncContext<AnkoContext<T>>.activityUiThreadWithContext(f: Context.(T) -> Unit): Boolean {
val activity = weakRef.get()?.owner ?: return false
if (activity.isFinishing) return false
activity.runOnUiThread { activity.f(activity) }
return true
}
@Deprecated(message = "Use support library fragments instead. Framework fragments were deprecated in API 28.")
fun <T : Fragment> AnkoAsyncContext<T>.fragmentUiThread(f: (T) -> Unit): Boolean {
val fragment = weakRef.get() ?: return false
if (fragment.isDetached) return false
val activity = fragment.activity ?: return false
activity.runOnUiThread { f(fragment) }
return true
}
@Deprecated(message = "Use support library fragments instead. Framework fragments were deprecated in API 28.")
fun <T : Fragment> AnkoAsyncContext<T>.fragmentUiThreadWithContext(f: Context.(T) -> Unit): Boolean {
val fragment = weakRef.get() ?: return false
if (fragment.isDetached) return false
val activity = fragment.activity ?: return false
activity.runOnUiThread { activity.f(fragment) }
return true
}
private val crashLogger = { throwable: Throwable -> throwable.printStackTrace() }
/**
* Execute [task] asynchronously.
*
* @param exceptionHandler optional exception handler.
* If defined, any exceptions thrown inside [task] will be passed to it. If not, exceptions will be ignored.
* @param task the code to execute asynchronously.
*/
fun <T> T.doAsync(
exceptionHandler: ((Throwable) -> Unit)? = crashLogger,
task: AnkoAsyncContext<T>.() -> Unit,
): Future<Unit> {
val context = AnkoAsyncContext(WeakReference(this))
return BackgroundExecutor.submit {
return@submit try {
context.task()
} catch (thr: Throwable) {
val result = exceptionHandler?.invoke(thr)
result ?: Unit
}
}
}
fun <T> T.doAsync(
exceptionHandler: ((Throwable) -> Unit)? = crashLogger,
executorService: ExecutorService,
task: AnkoAsyncContext<T>.() -> Unit,
): Future<Unit> {
val context = AnkoAsyncContext(WeakReference(this))
return executorService.submit<Unit> {
try {
context.task()
} catch (thr: Throwable) {
exceptionHandler?.invoke(thr)
}
}
}
fun <T, R> T.doAsyncResult(
exceptionHandler: ((Throwable) -> Unit)? = crashLogger,
task: AnkoAsyncContext<T>.() -> R,
): Future<R> {
val context = AnkoAsyncContext(WeakReference(this))
return BackgroundExecutor.submit {
try {
context.task()
} catch (thr: Throwable) {
exceptionHandler?.invoke(thr)
throw thr
}
}
}
fun <T, R> T.doAsyncResult(
exceptionHandler: ((Throwable) -> Unit)? = crashLogger,
executorService: ExecutorService,
task: AnkoAsyncContext<T>.() -> R,
): Future<R> {
val context = AnkoAsyncContext(WeakReference(this))
return executorService.submit<R> {
try {
context.task()
} catch (thr: Throwable) {
exceptionHandler?.invoke(thr)
throw thr
}
}
}
internal object BackgroundExecutor {
@Suppress("MemberVisibilityCanBePrivate")
var executor: ExecutorService =
Executors.newScheduledThreadPool(2 * Runtime.getRuntime().availableProcessors())
fun <T> submit(task: () -> T): Future<T> = executor.submit(task)
}
private object ContextHelper {
val handler = Handler(Looper.getMainLooper())
}