The creation of this channel was inspired by @themishkun. You can subscribe to him
Telegram
Голос из-под шторки | Миша Левченко
Продуктивность разработки, вайб-инжиниринг, обзоры книжечек, мемы и жиза от @themishkun
❤3
Наконец добрался до канала, зато с новостями
На досуге сделал плагин для Intellij, решил написать об этом статью, и ее опубликовали в ProAndroidDev! Claps or any feedback appreciated, а сам плагин можно посмотреть тут
В ближайшее время постараюсь сделать перевод для хабра
На досуге сделал плагин для Intellij, решил написать об этом статью, и ее опубликовали в ProAndroidDev! Claps or any feedback appreciated, а сам плагин можно посмотреть тут
В ближайшее время постараюсь сделать перевод для хабра
Medium
Better Kotlin–Java Auto-Complete: How to Improve Code Completion Suggestions in Mixed Projects
If you write in Kotlin, you may know that for data classes the compiler generates lots of stuff for us, including componentN() functions…
👍5
а вот, кстати, и перевод
пока переводил, обнаружил, что в оригинале удалили гифку при публикации. видимо, решили, что в серьезных статьях не место каким-то там гифкам, но получилось так, что драматическая пауза не была соблюдена, мы сразу перешли к спойлеру
на хабре гифка локализована; оригинал – в первом комментарии
пока переводил, обнаружил, что в оригинале удалили гифку при публикации. видимо, решили, что в серьезных статьях не место каким-то там гифкам, но получилось так, что драматическая пауза не была соблюдена, мы сразу перешли к спойлеру
на хабре гифка локализована; оригинал – в первом комментарии
Хабр
Улучшаем автокомплит в смешанных Java-Kotlin проектах
Честно говоря, не знаю, нужно ли ставить тэг «перевод» на собственную статью. Ну ок, поставил. Убрал для Технотекста – надеюсь, не зря! Всем привет! Недавно я наткнулся на статью , где...
🔥4
Kotlin function types
ответил в комментариях в Android Good Reads, но решил и тут написать, вдруг кому-то будет интересно
допустим, у нас есть такой Java-класс
и такой Kotlin-класс
чтобы передать лямбду типа
и если запустить, можно увидеть, что после выполнения
напишите в комменты, кому это было очевидно!
ответил в комментариях в Android Good Reads, но решил и тут написать, вдруг кому-то будет интересно
допустим, у нас есть такой Java-класс
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);
}
}
и такой Kotlin-класс
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 не изменитсянапишите в комменты, кому это было очевидно!
🔥3❤1
LinkageError
На днях готовил к релизу наш SDK для Unity. И всё должно было быть просто, потому что это обертка над нативными, самое сложное позади
А на нативном SDK для Android у нас есть UI-фича, которая сделана на Compose
И вот при взаимодействии с ней в Unity-проекте всё крэшилось с
На нативном SDK при этом всё хорошо; другие пакеты в Unity не привносят конфликтующие androidx-зависимости
Если кто-то догадался, что произошло, пишите в комменты
На днях готовил к релизу наш SDK для Unity. И всё должно было быть просто, потому что это обертка над нативными, самое сложное позади
А на нативном SDK для Android у нас есть UI-фича, которая сделана на Compose
И вот при взаимодействии с ней в Unity-проекте всё крэшилось с
java.lang.NoClassDefFoundError, в рантайме почему-то не нашлось класса, который был при компиляции. Это частный случай java.lang.LinkageError – обычно такие ситуации возникают, когда dependencyA уже ранее была скомпилирована с dependencyB, но в рантайме оказалась другая версия dependencyB, с несовместимым API, либо её вовсе нет. А так как всё уже было скомпилировано ранее, ошибка происходит в рантаймеНа нативном SDK при этом всё хорошо; другие пакеты в Unity не привносят конфликтующие androidx-зависимости
Если кто-то догадался, что произошло, пишите в комменты
❤2
Everlasting Loops
LinkageError На днях готовил к релизу наш SDK для Unity. И всё должно было быть просто, потому что это обертка над нативными, самое сложное позади А на нативном SDK для Android у нас есть UI-фича, которая сделана на Compose И вот при взаимодействии с ней…
Разгадка
Дело в том, что гугл не отстает и тоже переводит свои библиотеки на KMP – таким образом, у артефакта есть какой-то общий id (например,
Только суффикс должен был быть
Напишите, кстати, в комменты, кто сталкивался с таким. Кажется, что это не очень тривиально – практически все пишут на котлине, и когда мы подключаем Kotlin Gradle plugin в модуль, зависимости резолвятся с ожидаемым суффиксом:
Бридж, который связывает слой нативного андроида и Unity, был написан на Java; Kotlin-плагина в проекте не было вообще.
Ну а дальше всё просто: каких-то сущностей не оказалось в
P.S. Начиная с AGP 8.4 такой проблемы больше нет. На проекте был 8.3
Дело в том, что гугл не отстает и тоже переводит свои библиотеки на KMP – таким образом, у артефакта есть какой-то общий id (например,
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.Напишите, кстати, в комменты, кто сталкивался с таким. Кажется, что это не очень тривиально – практически все пишут на котлине, и когда мы подключаем Kotlin Gradle plugin в модуль, зависимости резолвятся с ожидаемым суффиксом:
+--- 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)
Бридж, который связывает слой нативного андроида и Unity, был написан на Java; Kotlin-плагина в проекте не было вообще.
Ну а дальше всё просто: каких-то сущностей не оказалось в
-desktop/-jvmstubs–артефактах –> в рантайме был крэш с LinkageError.P.S. Начиная с AGP 8.4 такой проблемы больше нет. На проекте был 8.3
👍2🔥1
кто бы мог подумать, что картинку спойлерить можно, а monospace и code block – нельзя
👍3
public class String
Вы могли видеть такую функцию:
Или такую:
Что у них общего? Они обе на котлине, и обе объявлены с модификатором
🤔 Зачем это нужно в лаконичном языке, где сущности и так
В Kotlin 1.4 появился опциональный флаг, который обязывает везде указывать модификатор доступа, а для публичного api еще и возвращаемый тип
Эта фича нужна разработчикам библиотек, чтобы лучше контролировать этот самый публичный api. Если вы нигде не можете пропустить модификатор(код попросту не скомпилируется) , скорее всего вы укажете тот доступ, который и планировали. В результате будет гораздо меньше мест, где изменения могут что-то сломать у текущих клиентов
Кстати, про то, как никому ничего не сломать, на днях был доклад у @dolgo_polo_dev. И там на секции вопросов подняли тему, что делать, если у нас несколько модулей
Тут нет идеального варианта даже внутри котлина, но есть компромиссный
У нас нет никакого модификатора, который был бы internal внутри library group, но есть специальная аннотация
Теперь, чтобы вызвать(если мы хотим "проглотить" ошибку) , либо (если хотим пробросить ее дальше) . В обоих случаях мы по сути подписываемся, что согласны на нестабильное api – то есть, технически все эти сущности публичные, но есть нюанс
При этом кейворд
Но об этом уже в следующем посте
🎃 Everlasting Loops
Вы могли видеть такую функцию:
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()
Что у них общего? Они обе на котлине, и обе объявлены с модификатором
public🤔 Зачем это нужно в лаконичном языке, где сущности и так
public по умолчанию?В Kotlin 1.4 появился опциональный флаг, который обязывает везде указывать модификатор доступа, а для публичного api еще и возвращаемый тип
Эта фича нужна разработчикам библиотек, чтобы лучше контролировать этот самый публичный api. Если вы нигде не можете пропустить модификатор
Кстати, про то, как никому ничего не сломать, на днях был доклад у @dolgo_polo_dev. И там на секции вопросов подняли тему, что делать, если у нас несколько модулей
Тут нет идеального варианта даже внутри котлина, но есть компромиссный
У нас нет никакого модификатора, который был бы internal внутри library group, но есть специальная аннотация
@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 При этом кейворд
public может быть полезен, даже когда вам не нужно предоставлять внешнее apiНо об этом уже в следующем посте
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥5❤1
public final override
Помимо разработки библиотек, есть еще один кейс, где эсплицитный
С точки зрения синтаксиса котлин такое позволяет, и тулинг не подсвечивает его как redundant
А еще в котлине есть ключевое слово
Концептуально это очень похоже на предыдущий кейс, ибо и
@loops_everlasting
Помимо разработки библиотек, есть еще один кейс, где эсплицитный
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!"
}
}
С точки зрения синтаксиса котлин такое позволяет, и тулинг не подсвечивает его как redundant
А еще в котлине есть ключевое слово
finalКонцептуально это очень похоже на предыдущий кейс, ибо и
public, и 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!"
}
}
@loops_everlasting
Telegram
Everlasting Loops
public class String
Вы могли видеть такую функцию:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val…
Вы могли видеть такую функцию:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val…
👍6🔥5🥱2🦄1
