210 lines
6.7 KiB
Kotlin
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())
|
|
}
|