Some notes on mobile dev stuff
Android | Kotlin
Автор: @vla_dos
Post #27
1.24K
EV @loops_everlasting
This post (sticker, poll or similar) has no web preview. Open in Telegram
@JvmStatic, а при комплишене заодно подставляется INSTANCE или Companion. Ну или имя companion’а, если вдруг его решили как-то назвать, так тоже можно





screenOrientation targetSdk = 36android.jar, в котором лежит только публичный api, без имплементаций (если вы натыкались на throw new RuntimeException("Stub!”), это как раз оно). Но для того, чтобы скомпилироваться, нам этого достаточноcompileSdk = 35, нам недоступны api, которые появились в новой бете андроида (36) – в нашем android.jar их просто нетcompileSdk поднимать безопасно, – давайте так и сделаем!minSdk. Какая разница, что метод доступен только в 16 api, когда у нас поддержка начинается с 21?compileSdk безопасно? Так вот, всё дело в targetSdktargetSdk = 22, то пермишены выдаются автоматически (и были даже кое-какие известные приложения, которые этим пользовались)
/**
* Return true if we are in RTL compatibility mode (either before Jelly Bean MR1 or
* RTL not supported)
*/
private boolean isRtlCompatibilityMode() {
final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
return targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1 || !hasRtlSupport();
}
targetSdk, с которым можно опубликовать приложениеpublic несет дополнительную смысловую нагрузку
abstract class Animal {
protected abstract fun say(): String
}
abstract class Fox : Animal() {
public override fun say(): String {
return "Ring-ding-ding-ding-dingeringeding!"
}
}
class ArcticFox : Fox() {
// тут public уже не нужен
override fun say(): String {
return "Wa-pa-pa-pa-pa-pa-pow!"
}
}
class FennecFox : Fox() {
// тут тоже
override fun say(): String {
return "Chacha-chacha-chacha-chow!"
}
}
finalpublic, и final здесь выполняют одну и ту же функцию – нивелируют предыдущую эксплицитную декларацию: public можно использовать для того, чтобы перекрыть protected / internal, а final – для open / abstract
open class RedFox : Fox() {
final override fun say(): String {
return "A-hee-ahee ha-hee!"
}
}
class EuropeanRedFox : RedFox() {
// ошибка – “’say' in 'RedFox' is final and cannot be overridden”
override fun say(): String {
return "A-oo-oo-oo-ooo! Woo-oo-oo-ooo!"
}
}
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
public inline fun <T> mutableListOf(): MutableList<T> = ArrayList()
publicpublic по умолчанию?@RequiresOptIn. Она дает возможность сообщить пользователю, что api is not meant to be public. Например, так это выглядит в корутинах:
@Retention(value = AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, AnnotationTarget.PROPERTY)
@RequiresOptIn(
level = RequiresOptIn.Level.ERROR, message = "This is an internal kotlinx.coroutines API that " +
"should not be used from outside of kotlinx.coroutines. No compatibility guarantees are provided. " +
"It is recommended to report your use-case of internal API to kotlinx.coroutines issue tracker, " +
"so stable API could be provided instead"
)
public annotation class InternalCoroutinesApi
@InternalCoroutinesApi
public fun tryResume(value: T, idempotent: Any? = null): Any?
tryResume, нам нужно явно добавить либо @OptIn(InternalCoroutinesApi::class) (если мы хотим "проглотить" ошибку), либо @InternalCoroutinesApi (если хотим пробросить ее дальше). В обоих случаях мы по сути подписываемся, что согласны на нестабильное api – то есть, технически все эти сущности публичные, но есть нюансpublic может быть полезен, даже когда вам не нужно предоставлять внешнее apiEverlasting Loops LinkageError На днях готовил к релизу наш SDK для Unity. И всё должно было быть просто, потому что это обертка над нативными, самое сложное позади А на нативном SDK для Android у нас есть UI-фича, которая сделана на Compose И вот при взаимодействии с ней…
lifecycle-viewmodel), а для конкретного таргета уже выбирается зависимость с соответствующим суффиксом. В итоге некоторые транзитивные зависимости зарезолвились как-то так:
+--- androidx.compose.material3:material3-desktop:{strictly 1.3.0} -> 1.3.0 (c)
+--- androidx.lifecycle:lifecycle-viewmodel-compose-desktop:{strictly 2.8.0} -> 2.8.0 (c)
+--- androidx.compose.ui:ui-jvmstubs:{strictly 1.7.2} -> 1.7.2 (c)
-android.
+--- androidx.compose.material3:material3-android:{strictly 1.3.0} -> 1.3.0 (c)
+--- androidx.lifecycle:lifecycle-viewmodel-compose-android:{strictly 2.8.3} -> 2.8.3 (c)
+--- androidx.compose.ui:ui-android:{strictly 1.7.2} -> 1.7.2 (c)
-desktop/-jvmstubs–артефактах –> в рантайме был крэш с LinkageError.java.lang.NoClassDefFoundError, в рантайме почему-то не нашлось класса, который был при компиляции. Это частный случай java.lang.LinkageError – обычно такие ситуации возникают, когда dependencyA уже ранее была скомпилирована с dependencyB, но в рантайме оказалась другая версия dependencyB, с несовместимым API, либо её вовсе нет. А так как всё уже было скомпилировано ранее, ошибка происходит в рантайме
public class SomeJavaClass {
interface Listener {
void onEvent(String event);
}
private HashSet<Listener> listeners = new HashSet<>();
void addListener(Listener listener) {
listeners.add(listener);
}
void removeListener(Listener listener) {
listeners.remove(listener);
}
}
class SomeKotlinClass {
private val eventListenerLambda = { eventId: String ->
println(eventId)
}
fun testLambdas(someJavaClass: SomeJavaClass) {
someJavaClass.addListener(eventListenerLambda)
someJavaClass.removeListener(eventListenerLambda)
}
}
(String) -> Unit в качестве параметра типа Listener, компилятор либо использует method reference, либо, когда Java 6, заворачивает в адаптер, и в обоих случаях инстанс итогового Listener каждый раз получается разный
public final class SomeKotlinClass {
@NotNull
private final Function1 eventListenerLambda = SomeKotlinClass::eventListenerLambda$lambda$0;
public static final int $stable;
public final void testLambdas(@NotNull SomeJavaClass someJavaClass) {
Intrinsics.checkNotNullParameter(someJavaClass, "someJavaClass");
Function1 var2 = this.eventListenerLambda;
someJavaClass.addListener(SomeKotlinClass::testLambdas$lambda$1);
var2 = this.eventListenerLambda;
someJavaClass.removeListener(SomeKotlinClass::testLambdas$lambda$2);
}
private static final Unit eventListenerLambda$lambda$0(String eventId) {
Intrinsics.checkNotNullParameter(eventId, "eventId");
System.out.println(eventId);
return Unit.INSTANCE;
}
private static final void testLambdas$lambda$1(Function1 $tmp0, String event) {
$tmp0.invoke(event);
}
private static final void testLambdas$lambda$2(Function1 $tmp0, String event) {
$tmp0.invoke(event);
}
}
someJavaClass.removeListener(eventListenerLambda) содержимое listeners не изменится