<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Hunga1ok's blog</title>
 <link href="https://hunga1ok.github.io/blog/atom.xml" rel="self"/>
 <link href="https://hunga1ok.github.io/blog/"/>
 <updated>2023-09-25T17:15:46+02:00</updated>
 <id>https://hunga1ok.github.io</id>
 <author>
   <name>hunga1ok</name>
   <email>hunga1ok@gmail.com</email>
 </author>

 
 <entry>
   <title>Android logging with Timber</title>
   <link href="https://hunga1ok.github.io/blog/2023/09/24/android-logging-with-timber"/>
   <updated>2023-09-24T00:00:00+02:00</updated>
   <id>https://hunga1ok.github.io/2023/09/24/android-logging-with-timber</id>
   <content type="html">&lt;h1 id=&quot;android-logging-with-timber&quot;&gt;Android logging with Timber&lt;/h1&gt;

&lt;h2 id=&quot;triển-khai-timber&quot;&gt;Triển khai Timber&lt;/h2&gt;
&lt;p&gt;Logging là một vấn đề không còn xa lạ với các lập trình viên. Hôm nay mình cũng sẽ trình bày về cách implement logging sử dụng thư viện Timber.&lt;/p&gt;

&lt;p&gt;Đầu tiên thì Timber là một API cho class Log của Android. Nó bổ trợ và nâng cấp cho chức năng log của Android. Ta làm việc đó bằng cách trồng (plant) một đối tượng Tree. Để sử dụng Timber, ta khai báo việc implement timber vào build.gradle&lt;/p&gt;

&lt;div class=&quot;language-gradle highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;repositories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mavenCentral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;implementation&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'com.jakewharton.timber:timber:5.0.1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Việc cài đặt timber rất dễ dàng chúng ta chỉ cần tạo một Timber Tree. Tree ở đây là một dạng cây hỗ trợ việc triển khai cách ghi log vào hệ thống mà ta có thể tự tuỳ chỉnh hoặc dùng tree default là DebugTree. Ví dụ:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Kotlin&quot;&gt;class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()
        Timber.plant(Timber.DebugTree())
    }

    override fun onTerminate() {
        super.onTerminate()
        Timber.uprootAll()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Trên đây là một cách cài đặt đợt giản, chúng ta nên cài đặt Timber sớm nhất có thể. Khi dùng chúng ta có thể dùng như log bình thường:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Timber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;content to log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Timber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;content to log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Timber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;content to log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;tạo-một-cây-tuỳ-chỉnh-với-timber&quot;&gt;Tạo một cây tuỳ chỉnh với Timber&lt;/h2&gt;
&lt;p&gt;Sau đây mình cùng tiến hành tạo một Timber Tree cho mình, ta cần override lại function log :&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LoggingCustom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Timber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Throwable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Not yet implemented&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Dưới đây mình có một ví dụ về lưu custom Tree dùng với logger để lưu log vào file:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;tạo 1 file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FileLoggingCustom.kt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileLoggingCustom&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fileDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sizeLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fileLimitCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;isAppendable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;levelLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Timber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;myLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Logger&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getExternalFilesDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;absolutePath&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;fileHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileHandler&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;levelLog&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handlers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;fileHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sizeLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileLimitCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isAppendable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;fileHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formatter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleFormatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;fileHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handlers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileHandler&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;myLogger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Throwable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;myLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromPriorityToLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fromPriorityToLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;VERBOSE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;FINER&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DEBUG&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;FINE&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;INFO&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;WARN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;WARNING&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ERROR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SEVERE&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ASSERT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SEVERE&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;FINEST&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;data class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;fileDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;LogDir&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;File%g.log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;sizeLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;fileLimitCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;isAppendable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;FileLoggerTree&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;levelLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ALL&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fileDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileDir&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sizeLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sizeLimit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fileLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileLimitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isAppendable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isAppendable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;append&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;levelLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;levelLog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileLoggingCustom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sizeLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileLimitCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isAppendable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;levelLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Khi khởi tạo Tree:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Tree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileLoggingCustom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Timber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ta có thể custom lại các thông số của Logging:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Tree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileLoggingCustom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fileDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;DirFile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file%g.log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CustomTag&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sizeLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000_000_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// ~1MB&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fileLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// create limit 3 file&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;levelLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ALL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// receive all log&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;isAppendable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// true: append log to file, false: create new file&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;Timber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;File log sẽ được lưu vào path&lt;/p&gt;

&lt;p&gt;sdcard/Android/data/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;your package name&lt;/code&gt;/DirFile/file&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;.log&lt;/p&gt;

&lt;p&gt;Trên đây là một cách triển khai Timber và timber log to file.&lt;/p&gt;

&lt;p&gt;Dưới đây là source code: &lt;a href=&quot;https://github.com/hunga1ok/POC-Logging&quot;&gt;Source Code&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Architecture Learning Journey</title>
   <link href="https://hunga1ok.github.io/blog/2023/06/26/architecture-learning-journey"/>
   <updated>2023-06-26T00:00:00+02:00</updated>
   <id>https://hunga1ok.github.io/2023/06/26/architecture-learning-journey</id>
   <content type="html">&lt;h1 id=&quot;architecture-learning-journey&quot;&gt;Architecture Learning Journey&lt;/h1&gt;

&lt;p&gt;In this learning journey you will learn about the Now in Android app architecture: its layers, key classes and the interactions between them.&lt;/p&gt;

&lt;h2 id=&quot;goals-and-requirements&quot;&gt;Goals and requirements&lt;/h2&gt;

&lt;p&gt;The goals for the app architecture are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Follow the &lt;a href=&quot;https://developer.android.com/jetpack/guide&quot;&gt;official architecture guidance&lt;/a&gt; as closely as possible.&lt;/li&gt;
  &lt;li&gt;Easy for developers to understand, nothing too experimental.&lt;/li&gt;
  &lt;li&gt;Support multiple developers working on the same codebase.&lt;/li&gt;
  &lt;li&gt;Facilitate local and instrumented tests, both on the developer’s machine and using Continuous Integration (CI).&lt;/li&gt;
  &lt;li&gt;Minimize build times.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;architecture-overview&quot;&gt;Architecture overview&lt;/h2&gt;

&lt;p&gt;The app architecture has three layers: a &lt;a href=&quot;https://developer.android.com/jetpack/guide/data-layer&quot;&gt;data layer&lt;/a&gt;, a &lt;a href=&quot;https://developer.android.com/jetpack/guide/domain-layer&quot;&gt;domain layer&lt;/a&gt; and a &lt;a href=&quot;https://developer.android.com/jetpack/guide/ui-layer&quot;&gt;UI layer&lt;/a&gt;.&lt;/p&gt;

&lt;center&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/hunga1ok/blog/hung.lv2/images/architecture/architecture-1-overall.png&quot; width=&quot;600px&quot; alt=&quot;Diagram showing overall app architecture&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;The architecture follows a reactive programming model with &lt;a href=&quot;https://developer.android.com/jetpack/guide/ui-layer#udf&quot;&gt;unidirectional data flow&lt;/a&gt;. With the data layer at the bottom, the key concepts are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Higher layers react to changes in lower layers.&lt;/li&gt;
  &lt;li&gt;Events flow down.&lt;/li&gt;
  &lt;li&gt;Data flows up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The data flow is achieved using streams, implemented using &lt;a href=&quot;https://developer.android.com/kotlin/flow&quot;&gt;Kotlin Flows&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;example-displaying-news-on-the-for-you-screen&quot;&gt;Example: Displaying news on the For You screen&lt;/h3&gt;

&lt;p&gt;When the app is first run it will attempt to load a list of news resources from a remote server (when the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prod&lt;/code&gt; build flavor is selected, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;demo&lt;/code&gt; builds will use local data). Once loaded, these are shown to the user based on the interests they choose.&lt;/p&gt;

&lt;p&gt;The following diagram shows the events which occur and how data flows from the relevant objects to achieve this.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/hunga1ok/blog/hung.lv2/images/architecture/architecture-2-example.png&quot; alt=&quot;Diagram showing how news resources are displayed on the For You screen&quot; title=&quot;Diagram showing how news resources are displayed on the For You screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here’s what’s happening in each step. The easiest way to find the associated code is to load the project into Android Studio and search for the text in the Code column (handy shortcut: tap &lt;kbd&gt;⇧ SHIFT&lt;/kbd&gt; twice).&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Step&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Description&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Code &lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1
   &lt;/td&gt;
   &lt;td&gt;On app startup, a &lt;a href=&quot;https://developer.android.com/topic/libraries/architecture/workmanager&quot;&gt;WorkManager&lt;/a&gt; job to sync all repositories is enqueued.
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;Sync.initialize&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;2
   &lt;/td&gt;
   &lt;td&gt;The &lt;code&gt;ForYouViewModel&lt;/code&gt; calls &lt;code&gt;GetUserNewsResourcesUseCase&lt;/code&gt; to obtain a stream of news resources with their bookmarked/saved state. No items will be emitted into this stream until both the user and news repositories emit an item. While waiting, the feed state is set to &lt;code&gt;Loading&lt;/code&gt;.
   &lt;/td&gt;
   &lt;td&gt;Search for usages of &lt;code&gt;NewsFeedUiState.Loading&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;3
   &lt;/td&gt;
   &lt;td&gt;The user data repository obtains a stream of &lt;code&gt;UserData&lt;/code&gt; objects from a local data source backed by Proto DataStore.
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;NiaPreferencesDataSource.userData&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;4
   &lt;/td&gt;
   &lt;td&gt;WorkManager executes the sync job which calls &lt;code&gt;OfflineFirstNewsRepository&lt;/code&gt; to start synchronizing data with the remote data source.
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;SyncWorker.doWork&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;5
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;OfflineFirstNewsRepository&lt;/code&gt; calls &lt;code&gt;RetrofitNiaNetwork&lt;/code&gt; to execute the actual API request using &lt;a href=&quot;https://square.github.io/retrofit/&quot;&gt;Retrofit&lt;/a&gt;.
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;OfflineFirstNewsRepository.syncWith&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;6
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;RetrofitNiaNetwork&lt;/code&gt; calls the REST API on the remote server.
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;RetrofitNiaNetwork.getNewsResources&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;7
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;RetrofitNiaNetwork&lt;/code&gt; receives the network response from the remote server.
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;RetrofitNiaNetwork.getNewsResources&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;8
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;OfflineFirstNewsRepository&lt;/code&gt; syncs the remote data with &lt;code&gt;NewsResourceDao&lt;/code&gt; by inserting, updating or deleting data in a local &lt;a href=&quot;https://developer.android.com/training/data-storage/room&quot;&gt;Room database&lt;/a&gt;.
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;OfflineFirstNewsRepository.syncWith&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;9
   &lt;/td&gt;
   &lt;td&gt;When data changes in &lt;code&gt;NewsResourceDao&lt;/code&gt; it is emitted into the news resources data stream (which is a &lt;a href=&quot;https://developer.android.com/kotlin/flow&quot;&gt;Flow&lt;/a&gt;).
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;NewsResourceDao.getNewsResources&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;10
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;OfflineFirstNewsRepository&lt;/code&gt; acts as an &lt;a href=&quot;https://developer.android.com/kotlin/flow#modify&quot;&gt;intermediate operator&lt;/a&gt; on this stream, transforming the incoming &lt;code&gt;PopulatedNewsResource&lt;/code&gt; (a database model, internal to the data layer) to the public &lt;code&gt;NewsResource&lt;/code&gt; model which is consumed by other layers.
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;OfflineFirstNewsRepository.getNewsResources&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;11
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;GetUserNewsResourcesUseCase&lt;/code&gt; combines the list of news resources with the user data to emit a list of &lt;code&gt;UserNewsResource&lt;/code&gt;s.  
   &lt;/td&gt;
   &lt;td&gt;&lt;code&gt;GetUserNewsResourcesUseCase.invoke&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;12
   &lt;/td&gt;
   &lt;td&gt;When &lt;code&gt;ForYouViewModel&lt;/code&gt; receives the saveable news resources it updates the feed state to &lt;code&gt;Success&lt;/code&gt;.

  &lt;code&gt;ForYouScreen&lt;/code&gt; then uses the saveable news resources in the state to render the screen.
   &lt;/td&gt;
   &lt;td&gt;Search for instances of &lt;code&gt;NewsFeedUiState.Success&lt;/code&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h2 id=&quot;data-layer&quot;&gt;Data layer&lt;/h2&gt;

&lt;p&gt;The data layer is implemented as an offline-first source of app data and business logic. It is the source of truth for all data in the app.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/hunga1ok/blog/hung.lv2/images/architecture/architecture-3-data-layer.png&quot; alt=&quot;Diagram showing the data layer architecture&quot; title=&quot;Diagram showing the data layer architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Each repository has its own models. For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TopicsRepository&lt;/code&gt; has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Topic&lt;/code&gt; model and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NewsRepository&lt;/code&gt; has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NewsResource&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;Repositories are the public API for other layers, they provide the &lt;em&gt;only&lt;/em&gt; way to access the app data. The repositories typically offer one or more methods for reading and writing data.&lt;/p&gt;

&lt;h3 id=&quot;reading-data&quot;&gt;Reading data&lt;/h3&gt;

&lt;p&gt;Data is exposed as data streams. This means each client of the repository must be prepared to react to data changes. Data is not exposed as a snapshot (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getModel&lt;/code&gt;) because there’s no guarantee that it will still be valid by the time it is used.&lt;/p&gt;

&lt;p&gt;Reads are performed from local storage as the source of truth, therefore errors are not expected when reading from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repository&lt;/code&gt; instances. However, errors may occur when trying to reconcile data in local storage with remote sources. For more on error reconciliation, check the data synchronization section below.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example: Read a list of topics&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A list of Topics can be obtained by subscribing to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TopicsRepository::getTopics&lt;/code&gt; flow which emits &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;Topic&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Whenever the list of topics changes (for example, when a new topic is added), the updated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;Topic&amp;gt;&lt;/code&gt; is emitted into the stream.&lt;/p&gt;

&lt;h3 id=&quot;writing-data&quot;&gt;Writing data&lt;/h3&gt;

&lt;p&gt;To write data, the repository provides suspend functions. It is up to the caller to ensure that their execution is suitably scoped.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example: Follow a topic&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Simply call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDataRepository.toggleFollowedTopicId&lt;/code&gt; with the ID of the topic the user wishes to follow and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;followed=true&lt;/code&gt; to indicate that the topic should be followed (use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; to unfollow a topic).&lt;/p&gt;

&lt;h3 id=&quot;data-sources&quot;&gt;Data sources&lt;/h3&gt;

&lt;p&gt;A repository may depend on one or more data sources. For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OfflineFirstTopicsRepository&lt;/code&gt; depends on the following data sources:&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Name&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Backed by&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;TopicsDao
   &lt;/td&gt;
   &lt;td&gt;&lt;a href=&quot;https://developer.android.com/training/data-storage/room&quot;&gt;Room/SQLite&lt;/a&gt;
   &lt;/td&gt;
   &lt;td&gt;Persistent relational data associated with Topics
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;NiaPreferencesDataSource
   &lt;/td&gt;
   &lt;td&gt;&lt;a href=&quot;https://developer.android.com/topic/libraries/architecture/datastore&quot;&gt;Proto DataStore&lt;/a&gt;
   &lt;/td&gt;
   &lt;td&gt;Persistent unstructured data associated with user preferences, specifically which Topics the user is interested in. This is defined and modeled in a .proto file, using the protobuf syntax.
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;NiaNetworkDataSource
   &lt;/td&gt;
   &lt;td&gt;Remote API accessed using Retrofit
   &lt;/td&gt;
   &lt;td&gt;Data for topics, provided through REST API endpoints as JSON.
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h3 id=&quot;data-synchronization&quot;&gt;Data synchronization&lt;/h3&gt;

&lt;p&gt;Repositories are responsible for reconciling data in local storage with remote sources. Once data is obtained from a remote data source it is immediately written to local storage. The  updated data is emitted from local storage (Room) into the relevant data stream and received by any listening clients.&lt;/p&gt;

&lt;p&gt;This approach ensures that the read and write concerns of the app are separate and do not interfere with each other.&lt;/p&gt;

&lt;p&gt;In the case of errors during data synchronization, an exponential backoff strategy is employed. This is delegated to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WorkManager&lt;/code&gt; via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyncWorker&lt;/code&gt;, an implementation of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Synchronizer&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;See the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OfflineFirstNewsRepository.syncWith&lt;/code&gt; for an example of data synchronization.&lt;/p&gt;

&lt;h2 id=&quot;domain-layer&quot;&gt;Domain layer&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.android.com/topic/architecture/domain-layer&quot;&gt;domain layer&lt;/a&gt; contains use cases. These are classes which have a single invocable method (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operator fun invoke&lt;/code&gt;) containing business logic.&lt;/p&gt;

&lt;p&gt;These use cases are used to simplify and remove duplicate logic from ViewModels. They typically combine and transform data from repositories.&lt;/p&gt;

&lt;p&gt;For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetUserNewsResourcesUseCase&lt;/code&gt; combines a stream (implemented using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flow&lt;/code&gt;) of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NewsResource&lt;/code&gt;s from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NewsRepository&lt;/code&gt; with a stream of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserData&lt;/code&gt; objects from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDataRepository&lt;/code&gt; to create a stream of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserNewsResource&lt;/code&gt;s. This stream is used by various ViewModels to display news resources on screen with their bookmarked state.&lt;/p&gt;

&lt;p&gt;Notably, the domain layer in Now in Android &lt;em&gt;does not&lt;/em&gt; (for now) contain any use cases for event handling. Events are handled by the UI layer calling methods on repositories directly.&lt;/p&gt;

&lt;h2 id=&quot;ui-layer&quot;&gt;UI Layer&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://developer.android.com/topic/architecture/ui-layer&quot;&gt;UI layer&lt;/a&gt; comprises:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;UI elements built using &lt;a href=&quot;https://developer.android.com/jetpack/compose&quot;&gt;Jetpack Compose&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.android.com/topic/libraries/architecture/viewmodel&quot;&gt;Android ViewModels&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ViewModels receive streams of data from use cases and repositories, and transforms them into UI state. The UI elements reflect this state, and provide ways for the user to interact with the app. These interactions are passed as events to the ViewModel where they are processed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/hunga1ok/blog/hung.lv2/images/architecture/architecture-4-ui-layer.png&quot; alt=&quot;Diagram showing the UI layer architecture&quot; title=&quot;Diagram showing the UI layer architecture&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;modeling-ui-state&quot;&gt;Modeling UI state&lt;/h3&gt;

&lt;p&gt;UI state is modeled as a sealed hierarchy using interfaces and immutable data classes. State objects are only ever emitted through the transform of data streams. This approach ensures that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the UI state always represents the underlying app data - the app data is the source-of-truth.&lt;/li&gt;
  &lt;li&gt;the UI elements handle all possible states.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example: News feed on For You screen&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The feed (a list) of news resources on the For You screen is modeled using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NewsFeedUiState&lt;/code&gt;. This is a sealed interface which creates a hierarchy of two possible states:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Loading&lt;/code&gt; indicates that the data is loading&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Success&lt;/code&gt; indicates that the data was loaded successfully. The Success state contains the list of news resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feedState&lt;/code&gt; is passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForYouScreen&lt;/code&gt; composable, which handles both of these states.&lt;/p&gt;

&lt;h3 id=&quot;transforming-streams-into-ui-state&quot;&gt;Transforming streams into UI state&lt;/h3&gt;

&lt;p&gt;ViewModels receive streams of data as cold &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html&quot;&gt;flows&lt;/a&gt; from one or more use cases or repositories. These are &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/combine.html&quot;&gt;combined&lt;/a&gt; together, or simply &lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/map.html&quot;&gt;mapped&lt;/a&gt;, to produce a single flow of UI state. This single flow is then converted to a hot flow using &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/state-in.html&quot;&gt;stateIn&lt;/a&gt;. The conversion to a state flow enables UI elements to read the last known state from the flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Displaying followed topics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterestsViewModel&lt;/code&gt; exposes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uiState&lt;/code&gt; as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StateFlow&amp;lt;InterestsUiState&amp;gt;&lt;/code&gt;. This hot flow is created by obtaining the cold flow of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;FollowableTopic&amp;gt;&lt;/code&gt; provided by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetFollowableTopicsUseCase&lt;/code&gt;. Each time a new list is emitted, it is converted into an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterestsUiState.Interests&lt;/code&gt; state which is exposed to the UI.&lt;/p&gt;

&lt;h3 id=&quot;processing-user-interactions&quot;&gt;Processing user interactions&lt;/h3&gt;

&lt;p&gt;User actions are communicated from UI elements to ViewModels using regular method invocations. These methods are passed to the UI elements as lambda expressions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Following a topic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterestsScreen&lt;/code&gt; takes a lambda expression named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;followTopic&lt;/code&gt; which is supplied from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InterestsViewModel.followTopic&lt;/code&gt;. Each time the user taps on a topic to follow this method is called. The ViewModel then processes this action by informing the user data repository.&lt;/p&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.android.com/topic/architecture&quot;&gt;Guide to app architecture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.android.com/jetpack/compose&quot;&gt;Jetpack Compose&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create a SimpleDateFormat</title>
   <link href="https://hunga1ok.github.io/blog/2022/05/12/simple-date-format"/>
   <updated>2022-05-12T00:00:00+02:00</updated>
   <id>https://hunga1ok.github.io/2022/05/12/simple-date-format</id>
   <content type="html">&lt;h2 id=&quot;simpledateformat&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html&quot;&gt;SimpleDateFormat&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;Table content for reference:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Letter&lt;/th&gt;
      &lt;th&gt;Date or Time Component&lt;/th&gt;
      &lt;th&gt;Presentation&lt;/th&gt;
      &lt;th&gt;Examples&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;G&lt;/td&gt;
      &lt;td&gt;Era designator&lt;/td&gt;
      &lt;td&gt;Text&lt;/td&gt;
      &lt;td&gt;AD&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;y&lt;/td&gt;
      &lt;td&gt;Year&lt;/td&gt;
      &lt;td&gt;Year&lt;/td&gt;
      &lt;td&gt;1996; 96&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Y&lt;/td&gt;
      &lt;td&gt;Week year&lt;/td&gt;
      &lt;td&gt;Year&lt;/td&gt;
      &lt;td&gt;2009; 09&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;M&lt;/td&gt;
      &lt;td&gt;Month in year&lt;/td&gt;
      &lt;td&gt;Month&lt;/td&gt;
      &lt;td&gt;July; Jul; 07&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;w&lt;/td&gt;
      &lt;td&gt;Week in year&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;27&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;W&lt;/td&gt;
      &lt;td&gt;Week in month&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;D&lt;/td&gt;
      &lt;td&gt;Day in year&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;189&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;d&lt;/td&gt;
      &lt;td&gt;Day in month&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;10&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;Day of week in month&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;E&lt;/td&gt;
      &lt;td&gt;Day name in week&lt;/td&gt;
      &lt;td&gt;Text&lt;/td&gt;
      &lt;td&gt;Tuesday; Tue&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;u&lt;/td&gt;
      &lt;td&gt;Day number of week (1 = Monday, …, 7 = Sunday)&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;a&lt;/td&gt;
      &lt;td&gt;Am/pm marker&lt;/td&gt;
      &lt;td&gt;Text&lt;/td&gt;
      &lt;td&gt;PM&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;H&lt;/td&gt;
      &lt;td&gt;Hour in day (0-23)&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;k&lt;/td&gt;
      &lt;td&gt;Hour in day (1-24)&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;24&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;K&lt;/td&gt;
      &lt;td&gt;Hour in am/pm (0-11)&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;h&lt;/td&gt;
      &lt;td&gt;Hour in am/pm (1-12)&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;m&lt;/td&gt;
      &lt;td&gt;Minute in hour&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;30&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;s&lt;/td&gt;
      &lt;td&gt;Second in minute&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;55&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;S&lt;/td&gt;
      &lt;td&gt;Millisecond&lt;/td&gt;
      &lt;td&gt;Number&lt;/td&gt;
      &lt;td&gt;978&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;z&lt;/td&gt;
      &lt;td&gt;Time zone&lt;/td&gt;
      &lt;td&gt;General time zone&lt;/td&gt;
      &lt;td&gt;Pacific Standard Time; PST; GMT-08:00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Z&lt;/td&gt;
      &lt;td&gt;Time zone&lt;/td&gt;
      &lt;td&gt;RFC 822 time zone&lt;/td&gt;
      &lt;td&gt;-0800&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;X&lt;/td&gt;
      &lt;td&gt;Time zone&lt;/td&gt;
      &lt;td&gt;ISO 8601 time zone&lt;/td&gt;
      &lt;td&gt;-08; -0800; -08:00&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;##Some code extensions kotlin:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I use &lt;a href=&quot;https://github.com/JodaOrg/joda-time&quot;&gt;JodaTime&lt;/a&gt; library for support handle features&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;formatHour&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;formatHour&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HH:mm:ss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;formatHour&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Invalid Date&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;formatDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Invalid Date&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;formatDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Invalid Date&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parseDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forPattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Android Developer Roadmap</title>
   <link href="https://hunga1ok.github.io/blog/2022/05/05/android-roadmap"/>
   <updated>2022-05-05T00:00:00+02:00</updated>
   <id>https://hunga1ok.github.io/2022/05/05/android-roadmap</id>
   <content type="html">&lt;blockquote&gt;
  &lt;p&gt;A complete roadmap to learn Android App Development. This roadmap will help you in becoming a complete Android Developer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p align=&quot;center&quot;&gt;
&lt;img alt=&quot;Android Developer Roadmap&quot; src=&quot;https://raw.githubusercontent.com/hunga1ok/blog/hung.lv2/images/android_developer_roadmap.png&quot; /&gt;
&lt;/p&gt;

&lt;h2 id=&quot;roadmap-to-learn-android-app-development&quot;&gt;Roadmap to learn Android App Development&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Programming
    &lt;ul&gt;
      &lt;li&gt;Java&lt;/li&gt;
      &lt;li&gt;Kotlin&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Android Studio
    &lt;ul&gt;
      &lt;li&gt;Android Studio IDE Overview&lt;/li&gt;
      &lt;li&gt;Project Structure
 	   * Java/Kotlin
 	   * XML
 	   * .gradle files&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Android Component
    &lt;ul&gt;
      &lt;li&gt;Activity
        &lt;ul&gt;
          &lt;li&gt;Activity Lifecycle&lt;/li&gt;
          &lt;li&gt;Tasks &amp;amp; Back Stack&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Service&lt;/li&gt;
      &lt;li&gt;Broadcast Receiver&lt;/li&gt;
      &lt;li&gt;Content Provider&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Intents
    &lt;ul&gt;
      &lt;li&gt;Types of Intent
        &lt;ul&gt;
          &lt;li&gt;Implicit&lt;/li&gt;
          &lt;li&gt;Explicit&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Intent Filter&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Static User Interface
    &lt;ul&gt;
      &lt;li&gt;View
        &lt;ul&gt;
          &lt;li&gt;Button, ImageView, TextView, EditText etc&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;ViewGroup
     * LinearLayout, RelativeLayout, FrameLayout, ConstraintLayout&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Dynamic User Interface
    &lt;ul&gt;
      &lt;li&gt;RecyclerView&lt;/li&gt;
      &lt;li&gt;ViewPager&lt;/li&gt;
      &lt;li&gt;Spinner&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;CustomView
    &lt;ul&gt;
      &lt;li&gt;Canvas&lt;/li&gt;
      &lt;li&gt;Bitmap&lt;/li&gt;
      &lt;li&gt;Paint&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;UI Resources
    &lt;ul&gt;
      &lt;li&gt;Drawables&lt;/li&gt;
      &lt;li&gt;String&lt;/li&gt;
      &lt;li&gt;Styles&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Fragments
    &lt;ul&gt;
      &lt;li&gt;Fragment Lifecycle&lt;/li&gt;
      &lt;li&gt;Fragment Manager&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Support User Interface
    &lt;ul&gt;
      &lt;li&gt;ProgressBar&lt;/li&gt;
      &lt;li&gt;Dialogs&lt;/li&gt;
      &lt;li&gt;Toast &amp;amp; Snackbar&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Storage
    &lt;ul&gt;
      &lt;li&gt;Shared Preferences&lt;/li&gt;
      &lt;li&gt;File Systems&lt;/li&gt;
      &lt;li&gt;Database
        &lt;ul&gt;
          &lt;li&gt;RoomDB&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Build
    &lt;ul&gt;
      &lt;li&gt;Gradle&lt;/li&gt;
      &lt;li&gt;Debug / Release Configuration&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Threading
    &lt;ul&gt;
      &lt;li&gt;Threads&lt;/li&gt;
      &lt;li&gt;Handler / Looper&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Debugging
    &lt;ul&gt;
      &lt;li&gt;Memory profiling&lt;/li&gt;
      &lt;li&gt;Logging&lt;/li&gt;
      &lt;li&gt;Systrace&lt;/li&gt;
      &lt;li&gt;Exceptions&lt;/li&gt;
      &lt;li&gt;Error Handling&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Memory Leak
    &lt;ul&gt;
      &lt;li&gt;Detecting and Fixing Memory Leaks&lt;/li&gt;
      &lt;li&gt;Context&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;3rd Party Library
    &lt;ul&gt;
      &lt;li&gt;Image Loading
        &lt;ul&gt;
          &lt;li&gt;Glide&lt;/li&gt;
          &lt;li&gt;Picasso&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Dependency Injection
        &lt;ul&gt;
          &lt;li&gt;Dagger&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Networking
        &lt;ul&gt;
          &lt;li&gt;Fast Android Networking Library&lt;/li&gt;
          &lt;li&gt;Retrofit&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;MultiThreading
        &lt;ul&gt;
          &lt;li&gt;RxJava&lt;/li&gt;
          &lt;li&gt;Coroutines&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Data Format
    &lt;ul&gt;
      &lt;li&gt;JSON
        &lt;ul&gt;
          &lt;li&gt;GSON&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Flat Buffer&lt;/li&gt;
      &lt;li&gt;Protocol Buffer&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Android Jetpack
    &lt;ul&gt;
      &lt;li&gt;Foundation Components
        &lt;ul&gt;
          &lt;li&gt;AppCompat&lt;/li&gt;
          &lt;li&gt;Android KTX&lt;/li&gt;
          &lt;li&gt;Multidex&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Architecture Components
        &lt;ul&gt;
          &lt;li&gt;LiveData&lt;/li&gt;
          &lt;li&gt;ViewModel&lt;/li&gt;
          &lt;li&gt;DataBinding&lt;/li&gt;
          &lt;li&gt;Paging&lt;/li&gt;
          &lt;li&gt;Work Manager&lt;/li&gt;
          &lt;li&gt;Navigation&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Behaviour Components
        &lt;ul&gt;
          &lt;li&gt;Download Manager&lt;/li&gt;
          &lt;li&gt;Media Playback&lt;/li&gt;
          &lt;li&gt;Notification&lt;/li&gt;
          &lt;li&gt;Permissions&lt;/li&gt;
          &lt;li&gt;Preference&lt;/li&gt;
          &lt;li&gt;Sharing&lt;/li&gt;
          &lt;li&gt;Slice&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;UI Component
        &lt;ul&gt;
          &lt;li&gt;Animation &amp;amp; Transition&lt;/li&gt;
          &lt;li&gt;Android Auto&lt;/li&gt;
          &lt;li&gt;Emoji&lt;/li&gt;
          &lt;li&gt;Palette&lt;/li&gt;
          &lt;li&gt;Android TV&lt;/li&gt;
          &lt;li&gt;Android Wear&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Architecture
    &lt;ul&gt;
      &lt;li&gt;MVVM&lt;/li&gt;
      &lt;li&gt;MVI&lt;/li&gt;
      &lt;li&gt;MVP&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Unit Testing
	 * Local Unit Testing
	 * Instrumentation Testing&lt;/li&gt;
  &lt;li&gt;Firebase
    &lt;ul&gt;
      &lt;li&gt;FCM&lt;/li&gt;
      &lt;li&gt;Crashlytics&lt;/li&gt;
      &lt;li&gt;Analytics&lt;/li&gt;
      &lt;li&gt;Remote Config&lt;/li&gt;
      &lt;li&gt;App Indexing&lt;/li&gt;
      &lt;li&gt;Dynamic Link&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Security
    &lt;ul&gt;
      &lt;li&gt;Encrypt / Decrypt&lt;/li&gt;
      &lt;li&gt;Proguard&lt;/li&gt;
      &lt;li&gt;R8&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;App Release
    &lt;ul&gt;
      &lt;li&gt;.keystore file&lt;/li&gt;
      &lt;li&gt;App Bundle&lt;/li&gt;
      &lt;li&gt;Playstore&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Keep Learning and Improving&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 

</feed>
