<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Isaac Olukunle's Blog]]></title><description><![CDATA[Hi, I'm Isaac an Android developer at Square. This is hopefully where I'll be posting my learnings about android and software development in general.]]></description><link>https://isaaco.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 01:07:58 GMT</lastBuildDate><atom:link href="https://isaaco.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Lessons Learnt Migrating to Koin 3]]></title><description><![CDATA[Wow, I haven’t written an article in 4 years! It was a combination of procrastination and just feeling like I didn’t have anything worth writing an article about (likely not true). But here we are, it’s good to be back!
Ok now, for what the article i...]]></description><link>https://isaaco.dev/lessons-learnt-migrating-to-koin-3</link><guid isPermaLink="true">https://isaaco.dev/lessons-learnt-migrating-to-koin-3</guid><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><category><![CDATA[Kotlin]]></category><category><![CDATA[koin]]></category><category><![CDATA[software development]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Isaac]]></dc:creator><pubDate>Mon, 03 Feb 2025 00:02:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738541505804/a9cd70fc-5379-40a5-9074-d00ad0825418.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Wow, I haven’t written an article in 4 years! It was a combination of procrastination and just feeling like I didn’t have anything worth writing an article about (likely not true). But here we are, it’s good to be back!</p>
<p>Ok now, for what the article is about, Koin! I have a project where I use Koin for dependency injection (or service location for the DI snobs), and I’ve been on version 2 for a long time, just because I didn’t feel like migrating. But this week I finally had the time, and decided lets go to Koin 3, fix any problems that pop up, and then after that we’ll go to Koin 4.</p>
<p>So I updated my grade dependencies to v3, synced, and built the app. A lot of problems appeared as expected. But overall they could be categorized into a few main problem groups.</p>
<h2 id="heading-koincreaterootscope-no-longer-exists">koin.createRootScope() no longer exists</h2>
<p>Usually in your application subclass, you have a bunch of setup code in your <code>startKoin</code> block, something like this:</p>
<pre><code class="lang-kotlin">startKoin {
    androidLogger()
    androidContext(<span class="hljs-keyword">this</span>)
    <span class="hljs-keyword">val</span> modules = <span class="hljs-comment">// Some list of modules</span>
    koin.loadModules(modules)
    koin.createRootScope()
}
</code></pre>
<p>In Koin 3, this method was removed, and we no longer need to create a root scope in our <code>Application</code> class. So you can simply delete that line.</p>
<h2 id="heading-get-is-no-longer-available-as-a-top-level-import">get() is no longer available as a top-level import</h2>
<p>In a lot of classes that extend KoinComponent. I was using the top-level get() function to satisfy dependencies.</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SomeClass</span>: <span class="hljs-type">KoinComponent {</span></span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">saveSomeState</span><span class="hljs-params">()</span></span> {
           <span class="hljs-keyword">get</span>&lt;LocalRepository&gt;().saveSomeState(SomeState())
    }
}
</code></pre>
<p>The recommended way to fix this is to replace such usages by using <code>inject()</code> to provide the dependencies, as you always have in other situations.</p>
<pre><code class="lang-kotlin"> SomeClass: KoinComponent {
   <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> localRepository: LocalRepository <span class="hljs-keyword">by</span> inject()
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">saveSomeState</span><span class="hljs-params">()</span></span> {
           localRepository.saveSomeState(SomeState())
    }
}
</code></pre>
<p>If that is not possible though, as it wasn’t for me in some situations, you can still get access to <code>get()</code> by using getKoin().get() in any subclass of KoinComponent. So now you have something like<br /><code>getKoin().get().saveOnboardingState(OnboardingState())</code></p>
<h2 id="heading-changes-to-how-to-provide-a-viewmodel">Changes to how to provide a ViewModel</h2>
<p>Honestly, this was the biggest one based on how many places in the codebase needed to be refactored. The app uses the MVVM architecture with one ViewModel per activity. It is sort of enforced with a BaseActivity like this:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseActivity</span>&lt;<span class="hljs-type">VM : BaseViewModel</span>&gt; : <span class="hljs-type">AppCompatActivity</span></span>() {
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">lateinit</span> <span class="hljs-keyword">var</span> viewModel: VM
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">val</span> viewModelClass: KClass&lt;VM&gt;

    <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">provideViewModel</span><span class="hljs-params">()</span></span>: VM = getViewModel(viewModelClass)
}
</code></pre>
<p>And I can hear a few of you booing from behind. Pardon me, this is from back when inheritance in Android development was cool. The point is though, you need to provide a viewModel class and we use <code>getViewModel()</code> to provide it for you if it’s a regular viewModel with no runtime or assisted parameters. If you have parameters however you can just override <code>provideViewModel()</code>.</p>
<h3 id="heading-getviewmodel-is-deprecated">getViewModel() is deprecated.</h3>
<p>Ok, this brings us to the first change <code>getViewModel()</code> is deprecated. Instead prefer using Koin’s <code>viewModel()</code> delegate. Something like:<br /><code>private val viewModel: AnnouncementViewModel by viewModel()</code></p>
<p>or if you have parameters<br /><code>private val viewModel: AnnouncementViewModel by viewModel { parametersOf(param1, param2) }</code></p>
<p>Pretty easy, which is the reason why people love Koin in the first place!</p>
<p>Ok, but there’s a problem here, this doesn’t really fit in with the existing structure and architecture here, with a BaseActivity where I don’t know the exact class yet. So in order to fix this, we rely on our good friend <code>getKoin()</code>. And basically all usages of <code>getViewModel(viewModelClass)</code> become</p>
<pre><code class="lang-kotlin">getKoin().<span class="hljs-keyword">get</span>&lt;viewModelClass&gt;() <span class="hljs-comment">// If you have no parameters or </span>

<span class="hljs-comment">// If you have some parameters to inject</span>
getKoin().<span class="hljs-keyword">get</span>&lt;viewModelClass&gt;(
        parameters = {
            parametersOf(
                  intent.getStringExtra(COMMUNITY_ID_EXTRA)
            )
        }
}
</code></pre>
<p>The parameter version of the above example was super important because that was a common pattern in the app. If there was a runtime parameter to be injected in an Activity or Fragment. Usually, I override <code>provideViewModel()</code> and use <code>getViewModel(viewModelClass)</code> to provide the param in the actual Activity since it should be available at that point.</p>
<h3 id="heading-getsharedviewmodel-is-deprecated">getSharedViewModel is deprecated.</h3>
<p>I have scenarios in the app where multiple fragments in the same activity are using the activity’s viewModel. In those cases, the viewModel was usually provided using<br /><code>getSharedViewModel(viewModelClass)</code>. That is now deprecated, and I have replaced such usages with <code>getActivityViewModel&lt;viewModelClass&gt;()</code> instead.</p>
<p>Yeah, that was pretty much it! At least for my use case. Overall the migration wasn’t much work, the biggest challenge was finding the information for what is the proper replacement for certain things. Once those were figured out, everything was straightforward! I’ll probably make another post when I migrate to Koin 4, whenever I’m able to finally get to it! 😁</p>
]]></content:encoded></item><item><title><![CDATA[The merge tag, my custom view savior (Improve your custom layout performance on Android)]]></title><description><![CDATA[So, recently I was working on something similar to a “create account” screen, basically a staple of most android apps. The design demanded that the input fields on this screen have a lot of custom behavior and a different look from the default EditTe...]]></description><link>https://isaaco.dev/the-merge-tag-my-custom-view-savior-improve-your-custom-layout-performance-3aee29759cf0</link><guid isPermaLink="true">https://isaaco.dev/the-merge-tag-my-custom-view-savior-improve-your-custom-layout-performance-3aee29759cf0</guid><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Isaac]]></dc:creator><pubDate>Sat, 28 Aug 2021 16:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203942963/2856b45d-2f19-45a7-baaa-a5caa7a03fe6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So, recently I was working on something similar to a “create account” screen, basically a staple of most android apps. The design demanded that the input fields on this screen have a lot of custom behavior and a different look from the default EditText appearance on Android. So my first idea was to make a custom view(layout) for my input field, since this would save me from a lot of code duplication and generally make things cleaner.</p>
<p>So, I set up the custom view/layout like so:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="751ccf20e9d035211b94fc2110751974"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/751ccf20e9d035211b94fc2110751974" class="embed-card">https://gist.github.com/751ccf20e9d035211b94fc2110751974</a></div><div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="7c2d62295c39891829d620872184eec9"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/7c2d62295c39891829d620872184eec9" class="embed-card">https://gist.github.com/7c2d62295c39891829d620872184eec9</a></div><p>Nothing out of the ordinary, right?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203936535/61036fd5-53ba-43eb-be26-1427587d61a0.jpeg" alt /></p>
<p>Now the “create account” screen(or fragment, I use them interchangeably), needed to have about 10 or 11 of these input fields. I lay them out in a Constraint layout like so:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="6837b6e418bd93b1d6dab4cb0213ce70"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/6837b6e418bd93b1d6dab4cb0213ce70" class="embed-card">https://gist.github.com/6837b6e418bd93b1d6dab4cb0213ce70</a></div><p>Then I run the app and notice that it takes about 1.5 seconds to load and display this screen (but why? I ask myself). I also notice that anytime I clicked on the input field and then dismissed the keyboard, it left an ugly blank space where the keyboard used to be before, and it took about a second for the layout to resize and display properly(Now, this I could not forgive).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203937670/fef59585-ba4b-499a-a04b-bccb2e0001d3.png" alt /></p>
<p>So after searching for answers everywhere, a kind man on a discord forum was nice enough to point out that I was nesting(layering) 3 layers of ConstraintLayouts, where one of the layers was really just a wrapper and not needed, and this was possibly what was wrecking my UI performance.</p>
<p>The unnecessary layer in question was the ConstraintLayout in line 7 of layout_input_field.xml above.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203939054/ee759a80-7a6e-4350-883f-60820ea479b2.png" alt /></p>
<p>layout_input_field with superfluous ConstraintLayout</p>
<p>That layout here is just a wrapper, and I don’t need it, because I’m creating an InputField(which extends ConstraintLayout) in the create account fragment XML. And inside this InputField class ‘init’ block on line 17, I’m inflating my custom input field XML whose root is a ConstraintLayout and attaching it to its parent which is “this”(the InputField object).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203940520/094a7b25-8273-4f9e-a25f-ce6c9ba195ec.png" alt /></p>
<p>InputField.kt init block</p>
<p>The above statement becomes clear when we look at the parameter list of DataBindingUtil.inflate()</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203941715/6b5e4a2b-167e-4452-adec-089d278bfe25.png" alt /></p>
<p>Parameter list of DataBindingUtil.inflate()</p>
<p>But, how do we fix this?</p>
<h3 id="heading-so-here-comes-the-tag-to-the-rescue">So here comes the  tag to the rescue.</h3>
<h4 id="heading-what-is-the-merge-tag">What is the merge tag?</h4>
<p>To quote the android documentation:</p>
<blockquote>
<p>The <code>&lt;merge /&gt;</code> tag is a tag used to help eliminate redundant view groups in your view hierarchy when including one layout within another. For example, if your main layout is a vertical <code>[LinearLayout](https://developer.android.com/reference/android/widget/LinearLayout)</code> in which two consecutive views can be re-used in multiple layouts, then the re-usable layout in which you place the two views requires its own root view. However, using another <code>[LinearLayout](https://developer.android.com/reference/android/widget/LinearLayout)</code> as the root for the re-usable layout would result in a vertical <code>[LinearLayout](https://developer.android.com/reference/android/widget/LinearLayout)</code> inside a vertical <code>[LinearLayout](https://developer.android.com/reference/android/widget/LinearLayout)</code>. The nested <code>[LinearLayout](https://developer.android.com/reference/android/widget/LinearLayout)</code> serves no real purpose other than to slow down your UI performance.</p>
</blockquote>
<p>So, to use it, I swapped out the ConstraintLayout for the merge tag, like so:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="d6b80d4eb0155438d94088d916c7615c"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/d6b80d4eb0155438d94088d916c7615c" class="embed-card">https://gist.github.com/d6b80d4eb0155438d94088d916c7615c</a></div><p>Don’t forget to include <strong><em>tools:parentTag</em></strong> in your merge tag, this helps you retain the design preview that you would have with the parent layout, you inflate into.</p>
<p>Doing something as simple as this, reduced the fragment load and display time by more than 50%, it also eliminated the keyboard ugly blank space issue, because the layout could resize faster.</p>
<p><strong><em>The End</em></strong></p>
<p>Can’t believe I’ve spent this much time writing about an XML tag 😂. But sometimes little things can make a big difference, as this did for me, and I’m usually only motivated to write about stuff I figure out after going through a fair bit of trouble to find or fix. Which would explain why it took me over a year to write my second article(that and procrastination). Anyway, it's good to be back.</p>
]]></content:encoded></item><item><title><![CDATA[Custom Listeners In Android]]></title><description><![CDATA[Listeners are a staple of Android development. They are a very popular way of creating asynchronous callbacks. Listeners are generally used to implement the code that runs when an event occurs.
A common usage of listeners that you likely have come ac...]]></description><link>https://isaaco.dev/custom-listeners-in-android-89ebdefe3e99</link><guid isPermaLink="true">https://isaaco.dev/custom-listeners-in-android-89ebdefe3e99</guid><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[Isaac]]></dc:creator><pubDate>Thu, 27 Feb 2020 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203948023/ea2c8fdd-ea6d-4e51-bdba-b1289bc0dc1f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Listeners are a staple of Android development. They are a very popular way of creating asynchronous callbacks. Listeners are generally used to implement the code that runs when an event occurs.</p>
<p>A common usage of listeners that you likely have come across, if you’re a bit experienced in Android development, is the built-in <code>onClickListener</code> of a button. We usually set the <code>onClickListener</code> on a button, with the code that should run when the button is clicked, which is the event in this case. In Java, it usually looks like this:</p>
<p><code>Button button = findViewById(R.id.example);   button.setOnClickListener(new View.OnClickListener(){   @Override   public void onClick(View view) {   //Do some work here   }   });</code></p>
<p>But besides what I described above, we can create our custom listeners with callbacks attached to events that are fired from certain areas of our code.</p>
<p><strong>But why?</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203945142/69c9e86e-b570-4141-adad-eb5585a10046.jpeg" alt /></p>
<p>Here are some cases where we might need to create a custom listener:</p>
<ul>
<li><p>When we need to emit an event up from a fragment to an activity</p>
</li>
<li><p>When we need to emit an event up from within an adapter to an activity or fragment</p>
</li>
</ul>
<p>In general, a listener is useful when you have a “child object” and a “parent object” or handler, where a parent object is an object that creates a new instance of the child object. And we have some work that needs to be done in the parent object, but also needs to be done only when a certain event occurs in the child object. So we have to find a way to communicate the fact that the event in question has occurred in the child object, to the parent object.</p>
<p><strong>So how do we create a custom listener?</strong></p>
<ol>
<li>First, define an interface in the child object (adapter, fragment, POJO). Then define the events that will be fired up to the parent. These events are represented by methods in the interface. An example of how this may look like in Java is:</li>
</ol>
<p><code>public interface CustomListener{</code></p>
<p><code>void onDataReady(Data data);</code></p>
<p><code>void onSubmitForm();</code></p>
<p><code>}</code></p>
<p>In the above example, <code>onDataReady(Data data)</code> and <code>onSubmitForm()</code> are method signatures/callbacks that represent events that may occur in the child object.</p>
<p>2. Next, set up a listener variable to store a particular implementation of the callbacks in our interface. The implementation of the callbacks will be defined by the parent object. So inside the child class, you can make the variable as well as a public setter method which allows the listener callbacks to be defined from the parent, like so:</p>
<p><code>private CustomListener mListener;</code></p>
<p><code>public void setCustomListener(CustomListener listener){   mListener = listener;   }</code></p>
<p>You don’t have to use a setter method, as there are several methods to pass the listener callback implementation into the child object, such as passing it through the constructor or passing it in via a lifecycle event(such as the <code>onAttach()</code> event of a fragment when dealing with activity/fragment communication).</p>
<p>3. Now that we’ve created the listener variable, we can implement the interface on the parent class, override the methods, and put in our implementation of those methods, then set the listener implementation(which is our parent class that implements the interface), on the child object.</p>
<p><code>public class Parent implements Child.CustomListener{</code></p>
<p><code>//Some code</code></p>
<p><code>...</code></p>
<p><code>@Override   public void onDataReady(Data data){   //some fancy implementation   }</code></p>
<p><code>public void onSubmitForm(){   //code we want to run when this event occurs   }</code></p>
<p><code>childObject.setCustomListener(this);</code></p>
<p><code>}</code></p>
<p>The above is a very common way of creating an implementation of the listener in the parent class. Another way of doing the same thing we did above is to create an instance of the custom listener inside the parent class (instead of making the parent class itself implement the interface) and setting that implementation as the custom listener for our child object, like so:</p>
<p><code>Child childObject = new Child();</code></p>
<p><code>Child.CustomListener listener = new Child.CustomListener(){   @Override   public void onDataReady(Data data){   //some fancy implementation   }</code></p>
<p><code>public void onSubmitForm(){   //code we want to run when this event occurs   }</code></p>
<p><code>}</code></p>
<p><code>childObject.setCustomListener(listener);</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738203946139/d07c6b5b-91bd-4f3f-bb3b-ea9955a42143.png" alt /></p>
<p>Just kidding, almost there though.</p>
<p>4. Now the child object can fire up events to the parent using the listener, when they happen, and pass along data if any, to the parent object. For example, in the child object, this can look like:</p>
<p>//The event "onDataReady" has occurred in the child object and we //fire up the event to the parent using the listener</p>
<p><code>public void OnSuccess(Response response){</code></p>
<p><code>Data data = response.getData;</code></p>
<p><code>listener.onDataReady(data);</code></p>
<p><code>}</code></p>
<p>And that’s it, we are done setting up the custom listener! I hope you’re able to start making and using your custom listeners if you haven’t already. Or at least have gained a better understanding of them.</p>
]]></content:encoded></item></channel></rss>