RSSAmplifier

Blog

Guowei Lv

Recent content on Guowei Lv

lvguowei.meRSS feed ↗291 posts

Latest posts

Flow exception handling (Part 1)

You may have heard that “No try/catch inside Flow” or “Only use catch() operator”. But why? Let’s explore from the beginning. Here is a very simple setup: fun main() = runBlocking { val worker = Worker() val scope = CoroutineScope(EmptyCoroutineContext) val flow = flow { emit(1) emit(2) emit(3) } scope.launch { try { flow.collect { println(worker.doWork(it)) } } catch…

What is a Coroutine?

The first question we should ask when learning Kotlin Coroutine is: what is a Coroutine after all? Let’s go back to the Thread world and ask the same question, what is a Thread? Here is code that will create a simple Thread and start it: val thread = thread { } So the answer seems obvious, a Thread is just the Thread object returned. Easy. Can we say the same in the Coroutine world?

Pointfree Ep2 Side Effects

The title of this episode is called “Side effects”, but in my opinion, it’s all about how to move side-effects out of the function and make the function composable. Side effects in the body of the function If there is some side-effects in the body of the function, we can move the side-effect into the return of the function, and let the caller deal with it. func…

Monthly Favourite Songs

When I was very young, like in primary school, I got a cassette of anime songs, I was so shocked and immediately fallen in love with. It’s a collection of songs from Macross 7. To this day, I have never watched that anime, but the songs are so touching I have to post it here. I found this amazing live concert that contains all the familiar melodies:

Pointfree Ep1 Functions

I&rsquo;m starting a new series, mostly just me taking notes while I go through the PointFree videos. The pipe operator The |> operator is defined as: precedencegroup ForwardApplication { associativity: left } infix operator |>: ForwardApplication func |> <A, B>(x: A, f: (A) -> B) -> B { return f(x) } It can be used as:: func incr(_ x: Int) -> Int { return x + 1 } func square(_ x: Int) -> Int {…

Bites of Compose 16

In previous post we talked about how to do animation in Compose using animateXXXAsState() function. And also see that though easy to use, it has limited customization capabilities. Now, let&rsquo;s take a look at a lower level(and more powerful) way: Animatable. @Composable fun AnimatableDemo() { val coroutineScope = rememberCoroutineScope() val anim = remember { Animatable(48.dp,…

Bites of Compose 15

Let&rsquo;s take a look at the simplest way to implement animation in Compose, which is using animateXXXAsState() set of functions. Let&rsquo;s say we want to increase the size of a Box by 10.dp every time we click on it. We already know how to do it without animation: @Preview @Composable fun AnimationDemo() { var target by remember { mutableStateOf(48.dp) } Box( modifier = Modifier .size(target)…

Bites of Compose 14

In previous post, we discussed the mental model of multiple LayoutModifiers. Now let&rsquo;s talk about the mental model of the combination of LayoutModifier and DrawModifier. First of all, we need to know that everything on screen is drawn using some DrawModifier: Text, Image, Background, etc. Let me give you the mental model directly. Given the following code:…

Bites of Compose 13

When we have multiple LayoutModifiers, how do we think about them? In this post, I will introduce a useful mental model. First we need to understand that LayoutModifier determines size and position of a component. The model is simple, let&rsquo;s say we have the following code: Text( text = 'Hello', modifier = Modifier .padding(20.dp) .background(Color.Green) // ignore this for now, only for…

Bites of Compose 12

LayoutModifier is a very important type of Modifier in Compose. It can decorate its component&rsquo;s layout. So let&rsquo;s get an idea of what this means by implementing padding using it. Let&rsquo;s try to add a 10dp padding around a Text. class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent {…

Bites of Compose 11

Let&rsquo;s see an example of how to use LookAheadLayout to implement shared view transition animation. Here is the code: @Composable fun Avatar(modifier: Modifier = Modifier) { Box( modifier = modifier .size(100.dp) .background(Color.Green) ) } @OptIn(ExperimentalComposeUiApi::class) @Composable fun CustomLookAheadLayout3() { var flag by remember { mutableStateOf(false) } var lookaheadOffset by…

Bites of Compose 10

Let&rsquo;s see an example of how to do custom layout in compose using Layout(). @Composable fun CustomLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) { Layout(modifier = modifier, content = content) { measurables, constraints -> var width = 0 var height = 0 val offset = 10.dp.toPx().roundToInt() val placeables = measurables.map { measurable ->…

Bites of Compose 9

Let&rsquo;s see an example of using the Android&rsquo;s native Canvas in Compose. One of the things that is impossible to do in Compose is doing 3D rotation. Let&rsquo;s see how to get hold of the native Canvas and do it the old way. @Composable fun MyView() { val image = ImageBitmap.imageResource(R.drawable.avatar) val paint by remember { mutableStateOf(Paint()) } val animatable = remember {…

Bites of Compose 8

Today&rsquo;s topic is rememberCoroutineScope. Situation 1 How to launch a Coroutine in Compose? Can we &ldquo;just do it&rdquo; ? class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { lifecycleScope.launch { } } } } Answer Apparently, no. Android studio gives an error saying &ldquo;Calls to launch should…

Bites of Compose 7

Let&rsquo;s talk about side effects in Compose. Situation 1 What will happen when the button is clicked? @Composable fun Test() { var flag by remember { mutableStateOf(false) } Column { Button(onClick = { flag = !flag }) { Text('change') } Text(flag.toString()) Log.d('test', 'flag is $flag') } } Answer The Text on the screen will change, also there will be a log entry in logcat. This logging…

Bites of Compose 6

In previous post we talked about the companion object Modifier and how it&rsquo;s implementing the interface Modifier. Now it is time to go deeper and see Modifier&rsquo;s internals. Situation 1 What is Modifier after all? Answer Well, what if I tell you Modifier is just a binary tree data structure. Let&rsquo;s dive into the source code to prove this. First let&rsquo;s take a look at the class…

Bites of Compose 5

Let&rsquo;s talk about Modifier. Situation 1 What is the simplest Modifier? Answer Modifier What is it? a class? an object? It is a companion object, which implements the Modifier interface. companion object : Modifier { override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = initial override fun…

Bites of Compose 4

This time we are focusing on derivedStateOf and how it is different from remember. Let&rsquo;s look at this simple example: Situation 1 What will happen when user clicks on the Text? @Composable private fun Situation1() { var name by remember { mutableStateOf('guowei') } val uppercase by remember { derivedStateOf { name.uppercase() } } Text(uppercase, modifier = Modifier.clickable { name = 'hello'…

Bites of Compose 3

Let&rsquo;s do a bit of recap first. (I highly suggest you go through previous post if not already done so) Situation 1 Will clicking the button trigger the recompose of UserPage? data class User(var name: String) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val user1 = User('Guowei Lv') val user2 = User('Guowei…

Bites of Compose 2

Situation 1 Take a look at the Composable below, what will happen if the button is clicked? @Composable fun Situation1() { val names by remember { mutableStateOf(mutableListOf('Bob', 'Tom')) } Column { names.forEach { Text(it) } Button(onClick = { names.add('Jane') }) { Text('Add Jane!') } } } Answer The list will still consist of Bob and Tom. Jane will not be added. The reason is that for mutable…

Bites of Compose 1

I&rsquo;m planning to write a series of short articles about Android&rsquo;s Jetpack Compose UI framework. You can use it to test your understanding or maybe learn a few things along the way. Situation 1 Take a look at the Composable below, what will happen if the button is clicked? @Composable fun Situation1() { var name = 'Guowei' Column { Text(name) Button(onClick = { name = 'Hello' }) {…

Demystify RxJava (2)

In this article, we focus on how the dispose system works in RxJava. First, let&rsquo;s take a look at the Disposable interface. public interface Disposable { void dispose(); boolean isDisposed(); } Not much going on here, basically it says a Disposable can be disposed. First example we are going to examine is: Observable.interval(1, TimeUnit.SECONDS) If you click through the code, the…

Demystify RxJava (1)

Let&rsquo;s see what makes RxJava tick. RxJava is complex, so I will have to (overly) simplify things at places. All of this is just trying to help you to get a better picture of how RxJava is implemented. Let&rsquo;s go. Let&rsquo;s start with the Single, it&rsquo;s just an interface with one method: public interface Single<T> { void subscribe(SingleObserver<T> observer); } So a Single is just a…

I wrote a game again - 24!

It all started from a message sent by my cousin in our family group chat one day. He posted some of those harder 24 problems, and I couldn&rsquo;t solve any of them. (If you don&rsquo;t know what is this 24 game is all about, here you can read it https://en.wikipedia.org/wiki/24_game) So, I decided to write a program to help me. And that program turned into a mobile game eventually. You can find…

Separating Program Evaluation From Description

5.3 Separating program evaluation from description This is the title of Chapter 5.3 from the book Functional Programming in Kotlin. Everyone knows that a program is a list of instructions that will be executed/evaluated in the order they are written in. fun exec() { doThis() doThat() doMore() } So the description decides the evaluation. What does it mean to separate them? I mean, can they be…

Handmade NestedScrollView

I&rsquo;m learning how the nested scrolling machanism works on Android, but couldn&rsquo;t really find any in-depth material. So I turned into the Chinese community, and found this incredible article. It is very long and detailed to death, I don&rsquo;t really have time to go through it all. I find the first half, a handmade SimpleNestedScrollView to be quite interesting, let me put that part in…

Understand Android View&#39;s Touch Events

How the view system in Android handles touch events? Let&rsquo;s try to understand it by designing it from scratch ourselves! (This is not my original but a summary of this https://juejin.cn/post/6844903761052188679) Let&rsquo;s do this by coming up a series of requirements (from naive to sophisticated) and see how we can design the logic to fulfill them. Requirement 1 In a nested view hierachy,…

WTF Livedata?! (or Kotlin)

LiveData is convenient, powerful and super easy to use. But, there are still some quirks that could possibly leads to some heads banging against walls. How many times can I observe? In theory, we can observe livedata with multiple observers, just like the good old pub-sub pattern, right? Well, let&rsquo;s test this out. Let&rsquo;s create a super simple livedata. class MainViewModel : ViewModel()…

Fun Facts About OnMeasure() and OnLayout()

Let&rsquo;s understand more about Android view&rsquo;s measure and layout process, and have some fun along the way. Let&rsquo;s say we have this: <LinearLayout xmlns:android='http://schemas.android.com/apk/res/android' xmlns:app='http://schemas.android.com/apk/res-auto' xmlns:tools='http://schemas.android.com/tools' android:layout_width='match_parent' android:layout_height='match_parent'…

How to Really Understand ViewModel in Android

Yes, there is documentation and tons of sample apps out there. But they are all in a top down approach, meaning they give you the final result of how to use it first, and never tells you why and what is behind the scenes. I will fix that in this article, and present a bottom up approach which leads to much better and deeper understanding. First, at the bottom of things, we need to define what is…

How to implement an ExpandableLayout

It is quite common in Android that we need some expandable widget to show and hide information. This is my first attempt, note that this is only a &ldquo;sketch&rdquo;, and I intentionally leave some room for improvement. One interesting detail worth mentioning is how the animation is done. I used the reverse() function to play animation backwards in order to achieve a smooth and continuous feel.…

How by Lazy Works

by lazy is implemented by using the &ldquo;property delegation&rdquo; in Kotlin. But if you look into the source code and trying to understand what is going on, it can be confusing, because it is full of locks and generics and where&rsquo;s the `getValue()`` function they say that the delegation must implement?? In the handmade spirit (best way to learn is by doing it yourself), let&rsquo;s do a…

Kotlin Noinline and Crossinline

Compile time constant const val NAME = 'Guowei' fun main() { tv.text = NAME } After compile it will (almost) look like this: // Imaginary code fun main() { tv.text = 'Guowei' } inline function We can do similar things to functions, by adding keyword inline. inline fun hello() { println('hello') } fun main() { hello() } So at compile time, the hello() function will be copied to the calling place:

View Binding Internals

I was bored at night so decided to peek into the guts of how Android&rsquo;s View Binding works. To my suprise the generated code is extremely simple. Imagine you have a list_item.xml file and it looks like this: <LinearLayout> <ImageView android:id='@+id/icon' /> <TextView android:id='@+id/name' /> </LinearLayout> Then the generated class will be like this: public final class ListItemBinding…

Next Job Oriented Programming

Rant alert I&rsquo;ve been having this idea for a while, and when having dinner with my other programmer friend yesterday, I jokingly said that &ldquo;Have you noticed there is a very popular programming paradigm that&rsquo;s been adopted everywhere but no one is aware of it?&rdquo; Yes, it is what I call Next Job Oriented Programming(NJOP). Don&rsquo;t get me wrong, I&rsquo;m not against adopting…

Android Custom View 102 (Part 19)

In this post let&rsquo;s take a deeper look at some of the more advanced uses of ObjectAnimator. KeyFrame First example is the usage of KeyFrames. Here is the final result: Let&rsquo;s look at the code val imageView = findViewById<ImageView>(R.id.imageView) /** In total we want to move 300dp. 0% of time passed, it has moved 0dp. 30% of time passed, it has moved 100dp. 60% of time passed, it has…

Android Custom View 102 (Part 18)

In this post let&rsquo;s look at how to do this cool animation using ObjectAnimator. This is built on top of previous post class CameraView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private val imageSize = dp2px(200) private val leftPadding = dp2px(100)…

Android Custom View 102 (Part 17)

Let&rsquo;s look at how to use camera in canvas transformation. First this is what we want to implement: The trick is to draw the upper and lower part separately. And we are using this &ldquo;reverse drawing&rdquo; technique. Upper part override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.save() canvas.drawBitmap(image, leftPadding, topPadding, paint) canvas.restore() } override fun…

Android Custom View 102 (Part 16)

In this post I want to go into details on canvas transformations, especially if we want to combine them. Let&rsquo;s take the simple example of translation + rotation. The end result is like this: We can easily see that the image has been translated to (300, 200) and then rotated 45 degrees. class TransformView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr:…

Android Custom View 102 (Part 15)

Let&rsquo;s use Xfermode to draw a circled avatar. The original avatar image is this: We will implement a custom view that clips it into a circle. class Avatar @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val WIDTH = dp2px(300) private val PADDING = dp2px(50) val paint = Paint(Paint.ANTI_ALIAS_FLAG)…

Android Custom View 102 (Part 14)

In this tutorial let&rsquo;s see how to draw a piechart. class PieChart @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) val bounds: RectF = RectF() val RADIUS = dp2px(150) val OFFSET = dp2px(50) var offsetIndex = 2 val angles = arrayOf(60.0f, 120.0f, 30.0f,…

Android Custom View 102 (Part 13)

Today let&rsquo;s draw a dashboard meter. I have drawn the blueprint this time: Here is the code: class Dashboard @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val BOTTOM_ANGLE = 120 private val RADIUS = dp2px(150) private val ARM_LENGTH = dp2px(120) private val DASH_WIDTH = dp2px(2) private val…

SICP Goodness - The Y Combinator

Do you think Computer Science equals building websites and mobile apps? Are you feeling that you are doing repetitive and not so intelligent work? Are you feeling a bit sick about reading manuals and copy-pasting code and keep poking around until it works all day long? Do you want to understand the soul of Computer Science? If yes, read SICP!!! I rewatched the Lecture 7A again and found that I…

Back to Basics - App Bar

We often hear 3 words: app bar, action bar and tool bar. Let&rsquo;s make clear of them first: app bar: the name of the UI element/bar at the top of the app. action bar: the previous implementation of app bar, comes with some themes by default. But should not really be used anymore. tool bar: the current implementation of app bar. Should be used in replacement of action bar. Let&rsquo;s create an…

Dependency Injection in Android With Dagger2 (14)

Here is one clean solution of how to combine Dagger + ViewModel + SavedStateHandle. The only difference now is that inside our ViewModel has access to SavedStateHandle. We capture this through a abstract class: abstract class SavedStateViewModel: ViewModel() { abstract fun init(savedStateHandle: SavedStateHandle) } And the ViewModel now looks like this: class MyViewModel @Inject constructor(…

Dependency Injection in Android With Dagger2 (13)

Previously, we created a centralized ViewModelFactory which can create all ViewModels in the app: class ViewModelFactory @Inject constructor( private val myViewModelProvider: Provider<MyViewModel>, private val myViewModel2Provider: Provider<MyViewModel2> ): ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return when(modelClass) { MyViewModel::class.java…

Dependency Injection in Android With Dagger2 (12)

It is a bit cumbersome to create a Factory for each ViewModel we have. Actually, we can just create one Factory that is responsible for creating all ViewModels in the app. The Factory is very straightforward: class ViewModelFactory @Inject constructor( private val myViewModelProvider: Provider<MyViewModel>, private val myViewModel2Provider: Provider<MyViewModel2> ): ViewModelProvider.Factory {…

Dependency Injection in Android With Dagger2 (11)

The main advantage of using ViewModel is: it can survive configuration change. That means if you associate a ViewModel with an Activity, then after configuration change, the activity will get the same instance of that ViewModel. Of course that comes with some setup work to do on developers side. But it is not that bad, basically the activity gets its ViewModel through ViewModelProvider. If your…

Dependency Injection in Android With Dagger2 (10)

Let&rsquo;s take a look at the ViewMvcFactory: class ViewMvcFactory @Inject constructor( private val layoutInflaterProvider: LayoutInflater, private val imageLoaderProvider: ImageLoader ) One thing worth paying attention is that this is a factory. Factory is used to create objects. So this means that its dependencies: LayoutInflater and ImageLoader will be reused to create objects everytime. We…

Dependency Injection in Android With Dagger2 (9)

This is a simple one. No more explanation, just paste the code here. @UiThread @Module class AppModule { @Provides @AppScope @Retrofit1 fun retrofit1(urlProvider: UrlProvider): Retrofit { return Retrofit.Builder() .baseUrl(urlProvider.baseUrl1()) .addConverterFactory(GsonConverterFactory.create()) .build() } @Provides @AppScope @Retrofit2 fun retrofit2(): Retrofit { return Retrofit.Builder()…