<?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" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Aaron He's blog]]></title><description><![CDATA[It might sound silly.]]></description><link>https://aaronhe.org/</link><image><url>https://aaronhe.org/favicon.png</url><title>Aaron He&apos;s blog</title><link>https://aaronhe.org/</link></image><generator>Ghost 5.23</generator><lastBuildDate>Sat, 22 Aug 2026 20:28:36 GMT</lastBuildDate><atom:link href="https://aaronhe.org/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Using git filter-repo to rewrite commit history]]></title><description><![CDATA[<p><code>git filter-repo</code> is a neat tool to rewrite commit history.</p><p>A recent use case required me to move an entire repository to another repository while <strong>preserving history</strong>. A simple <code>git subtree</code> would do the move, but one little problem is after the commit history moved over, the commit messages would</p>]]></description><link>https://aaronhe.org/git-filter-repo-to-rewrite-commit-history/</link><guid isPermaLink="false">6362048b3e46391ee7d51e71</guid><category><![CDATA[git]]></category><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Wed, 26 Oct 2022 04:30:32 GMT</pubDate><content:encoded><![CDATA[<p><code>git filter-repo</code> is a neat tool to rewrite commit history.</p><p>A recent use case required me to move an entire repository to another repository while <strong>preserving history</strong>. A simple <code>git subtree</code> would do the move, but one little problem is after the commit history moved over, the commit messages would have references to the pull request number from the original repo, then Github would create a link using the PR number <em>but based on the new repo</em>, which would point to a wrong PR in the new repo.</p><p>To avoid that, I&apos;d need to rewrite commit history. But since there were many many commits, I had to figure out doing this in a scriptable way. This is where <code>git filter-repo</code> comes in handy, which is the replacement of the old <code>filter-branch</code>.</p><p>To do that, <code>filter-repo</code> has a <a href="https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html#_filtering_of_commit_messages_see_also_message_callback">filter for commit messages</a>. The filter also has a <code>--message-callback</code> option, which would be followed by a Python code body to process commit message. The <a href="https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html#CALLBACKS">callback</a> would give you the original commit message as the standard input and use the output as the new commit message, both in <em>byte strings</em>. In this case, a simple regular expression would do the trick:</p><p><code>git filter-repo --message-callback &apos;return re.sub(br&quot;(#(\d{1,3}))\n&quot;, br&quot;(https://github.com/FooOrg/BarRepo/pull/\1)&quot;, message)&apos;</code></p><p>Another consideration is I only wanted to rewrite the commits from the original repo, but <code>filter-repo</code> by default tries to rewrite all the commits in the repo. <code>--refs</code>argument is exactly for this purpose where you can specify a range of commits. For example, I have all the commits in my local branch <code>ahe/foo</code>, then I will just tell <code>filter-repo</code> to only rewrite commit messages for the diffs between the <code>main</code> branch and <code>ahe/foo</code> branch: <code>--refs main..ahe/foo</code>.</p>]]></content:encoded></item><item><title><![CDATA[Updating Ghost]]></title><description><![CDATA[<p>Every time I update Ghost, there is always some problems taking down the site. A lot of the times it&apos;s just because things like I forgot to update nodejs before updating Ghost CLI. Here I&apos;m writing the steps down so next time I can follow them</p>]]></description><link>https://aaronhe.org/updating-ghost/</link><guid isPermaLink="false">6362048b3e46391ee7d51e70</guid><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Fri, 16 Oct 2020 06:43:59 GMT</pubDate><content:encoded><![CDATA[<p>Every time I update Ghost, there is always some problems taking down the site. A lot of the times it&apos;s just because things like I forgot to update nodejs before updating Ghost CLI. Here I&apos;m writing the steps down so next time I can follow them exactly, and hopefully they will be useful for other folks. </p><p>Just a little background, the instance is running on Ubuntu 18.04 LTS at <a href="https://m.do.co/c/cbe00087c4c2">DigitalOcean</a>.</p><p>The first step is to make sure npm and nodejs is updated to the latest version supported by Ghost. Go to <a href="https://ghost.org/faq/node-versions/">https://ghost.org/faq/node-versions/</a> to find the recommneded nodejs version.</p><pre><code># update npm to the latest
npm install -g npm@latest

# update nodejs to recommended version, e.g., v12
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs</code></pre><p>Once you have npm and nodejs updated, run <code>sudo npm install -g ghost-cli@latest</code> to update to the latest Ghost CLI.</p><p>Next, go to the directory where the instance is running, then switch to the user who owns the directory.</p><p>Be sure to check the <a href="https://ghost.org/faq/upgrades/">Upgrading Ghost</a> page to find out if additional instruction is needed, especially when there&apos;s a major version upgrade.</p><p>Always back up the <code>content</code> directory! A simple <code>cp -r content ~/backup-dir</code> would do the trick.</p><p>Finally, it&apos;s time to run <code>ghost update</code> to actually update Ghost.</p><p>Well, I lied. <code>ghost update</code> sometimes may not be the final step. In case of failures, try running <code>ghost update --force</code> to do the update for the 2nd time, which might just work the 2nd time. If the update is successful, but the blog still isn&apos;t up and running, <code>reboot</code>.</p><p>If the update still fails, <code>ghost doctor</code> might give you some information. If you lose the patience by now, <code>ghost update --rollback</code> could be handy to revert back to the previous stable version of Ghost. </p><p>Lastly, if still nothing works, and you still want to proceed with the update and you don&apos;t want to spend too much time dealing with it anymore, just reinstall Ghost and restore from the backup.</p><p>You can hate WordPress however you want it, but it&apos;s never a pain to upgrade(if the environment is set up correctly).</p>]]></content:encoded></item><item><title><![CDATA[Moshi Kotlin Gotchas]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://github.com/square/moshi#kotlin-support">Moshi 1.5</a> added <code>@Json</code> annotation support for Kotlin classes. It&apos;s a great addition for Kotlin projects and has been working really well for me. I&apos;d just like to share three tips when working with Moshi Kotlin.</p>
<h2 id="nogenericssupportyet">No generics support yet</h2>
<p>Resovling generics types is still</p>]]></description><link>https://aaronhe.org/three-tips-for-using-moshi-kotlin-adapter/</link><guid isPermaLink="false">6362048b3e46391ee7d51e6a</guid><category><![CDATA[Android]]></category><category><![CDATA[Kotlin]]></category><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Sun, 17 Dec 2017 08:44:34 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://github.com/square/moshi#kotlin-support">Moshi 1.5</a> added <code>@Json</code> annotation support for Kotlin classes. It&apos;s a great addition for Kotlin projects and has been working really well for me. I&apos;d just like to share three tips when working with Moshi Kotlin.</p>
<h2 id="nogenericssupportyet">No generics support yet</h2>
<p>Resovling generics types is still <a href="https://github.com/square/moshi/issues/309#issuecomment-325500576">working in progress</a>. <a href="https://github.com/google/auto/tree/master/value">AutoValue</a> and its <a href="https://github.com/rharter/auto-value-moshi">Moshi extension</a> is the best alternative if you have such a need.</p>
<h2 id="proguard">ProGuard</h2>
<p>If it&apos;s unfortunate that your project has to use ProGuard in the release apk, there might be times a misconfiguration will surprise you, like some fields just gone missing. There are already many <a href="https://github.com/square/moshi/issues?utf8=%E2%9C%93&amp;q=is%3Aissue%20Kotlin%20proguard%20">issues</a> opened with people posting different workarounds. The rule stated in the README has worked for me until I bumped up Kotlin version to 1.50. And by the end of the day, we basically had to keep everything from <code>kotlin.Metadata</code> and all the model classes. :(</p>
<pre><code>-keep class kotlin.Metadata { *; }
-keep class org.aaronhe.api.** { *; }
</code></pre>
<p>And if you are using Kotlin reflect 1.2, there&apos;s little extra <a href="https://github.com/square/moshi/issues/402#issuecomment-348886666">configraution</a> you may need to add: <code>-keep public class kotlin.reflect.jvm.internal.impl.builtins.* { public *; }</code>.</p>
<h2 id="adaptercreationisexpensiveforthefirsttime">Adapter creation is expensive(for the first time)</h2>
<p>Moshi Kotlin has a depdency on <code>kotlin-reflect</code> because it needs some extra information about the class, like what fields are being annotated, what&apos;s value of the <code>@Json</code> annotation etc. This transitive depedency will increase the apk size a little bit. If you are developing for Android, you might also need to be aware of when to call <code>Moshi#adapter</code> for kotlin classes. In order to get the information Moshi needs, Moshi has to get a hold of the primary constutor. However that&apos;s an expensive call because <code>KClass#primaryConstructor</code> will in-turn call <code>getResourcesAsStream</code> which could take 2 seconds on a Nexus 4 device running KitKat. Fortunately, Moshi <a href="https://publicobject.com/2016/03/24/reflection-machines/">caches</a> generated adapters, so only the first time creation takes a performance hit.</p>
<p>I discovered this with the help from <a href="https://nimbledroid.com">NimbleDroid</a>. It was actually caused by a class having an adapter as its member variable and being injected at the application class. So the application startup time surffered from this. Making the adapter lazily initlaized was the easist fix.</p>
<p>There&apos;s also an interesting project called <a href="https://github.com/ansman/kotshi">kotshi</a> which uses annotation processing to generate Moshi adapters for Kotlin data classes. I haven&apos;t used this in production yet. But it looks very promising.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[A little trick to add WebP "transparency" support on pre-4.2 devices]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>WebP is great, but transparency WebP is only <a href="https://developer.android.com/guide/topics/media/media-formats.html#image-formats">supported</a> natively on Android since 4.2.1. Even though everyone should have <code>minSdk</code> set to at least 19 by <a href="https://twitter.com/minSdkVersion/status/795702388774567936">now</a>, for apps that sill have a large amount of users on pre-4.2 devices, there&apos;s a little trick maybe</p>]]></description><link>https://aaronhe.org/a-little-trick-to-add-webp-transparency-support-on-pre/</link><guid isPermaLink="false">6362048b3e46391ee7d51e68</guid><category><![CDATA[Android]]></category><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Mon, 14 Aug 2017 05:50:08 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>WebP is great, but transparency WebP is only <a href="https://developer.android.com/guide/topics/media/media-formats.html#image-formats">supported</a> natively on Android since 4.2.1. Even though everyone should have <code>minSdk</code> set to at least 19 by <a href="https://twitter.com/minSdkVersion/status/795702388774567936">now</a>, for apps that sill have a large amount of users on pre-4.2 devices, there&apos;s a little trick maybe apply to you for adding WebP &quot;transparency&quot; support.</p>
<p>The <a href="https://developers.google.com/speed/webp/docs/cwebp"><code>cwebp</code></a> CLI provides an option called <code>-blend_alpha</code> which allows you to blend the alpha channel using a solid color. What that means is if you have a large PNG image with transparency area but always lays on top of a known background color, say white. Then, when you convert that PNG image into a WebP image, you can use <code>-blend_alpha 0xffffff</code> option to blend the alpha channel with white color. This will not only reduces the image size, but also works on pre-4.2 devices.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Get rid of `get` prefix for Kotlin property getters]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>One of the nice things Kotlin provides is interoperability with Java. Existing Java code can just call Kotlin classes/methods as if they were Java classes/methods. However, when it comes to property getters, the equivalent Java method would have a <code>get</code> prefix. This is can be a little bit</p>]]></description><link>https://aaronhe.org/get-rid-of-get-prefix-for-kotlin-property-getters/</link><guid isPermaLink="false">6362048b3e46391ee7d51e67</guid><category><![CDATA[Kotlin]]></category><category><![CDATA[Android]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Thu, 01 Jun 2017 00:09:40 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>One of the nice things Kotlin provides is interoperability with Java. Existing Java code can just call Kotlin classes/methods as if they were Java classes/methods. However, when it comes to property getters, the equivalent Java method would have a <code>get</code> prefix. This is can be a little bit annoying.</p>
<p>Let&apos;s say we have this <code>Experiment</code> class has two getters <code>started</code> and <code>ended</code>:</p>
<pre><code class="language-kotlin">class Experiment {
  val started get() = // fancy logic
  val ended get() = // another fancy logic
}
</code></pre>
<p>When calling those two getters from Java, it&apos;d be <code>experiment.getStarted()</code> and <code>experiment.getEnded()</code>. The extra <code>get</code> prefix is a little redundant. There&apos;s a small trick we can do to avoid that is to use <code>@JvmName</code> annotation. Just annotate the property getter like this:</p>
<pre><code class="language-kotlin">class Experiment {
  val started @JvmName(&quot;started&quot;) get() = // fancy logic
  val ended @JvmName(&quot;ended&quot;) get() = // another fancy logic
}
</code></pre>
<p>Then, in Java, we now are be able to just call <code>experiment.started()</code>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Retrofit 2 dynamic header]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In Retrofit 2, when we need to add additional headers, we would just add an implementation of the  <a href="https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/Interceptor.java"><code>Interceptor</code></a>.</p>
<p>But sometimes, we only need to add a particular header for one API call, and the value is set dynamically at runtime. It&apos;s a bit  tedious to add another</p>]]></description><link>https://aaronhe.org/retrofit-2-dynamic-header/</link><guid isPermaLink="false">6362048b3e46391ee7d51e66</guid><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Mon, 30 Jan 2017 07:53:56 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In Retrofit 2, when we need to add additional headers, we would just add an implementation of the  <a href="https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/Interceptor.java"><code>Interceptor</code></a>.</p>
<p>But sometimes, we only need to add a particular header for one API call, and the value is set dynamically at runtime. It&apos;s a bit  tedious to add another interceptor just for that call.</p>
<p>The <code>@Header</code> annotation is really handy for this case. So we can pass a parameter annotated with <code>@Header</code> to the API call method. And Retrofit will take care of it.</p>
<p>Let&apos;s take look at an example:</p>
<pre><code class="language-java">@GET(&quot;/weather&quot;)
Observable&lt;WeatherInfo&gt; getWeather(@Header(&quot;foo&quot;) String value, Location location);
</code></pre>
<p>That would allow us to pass a <code>String</code> value as a request header for the <code>getWeather</code> API call. If the <code>value</code> is <code>null</code>, the <code>foo</code> header will just be omitted from the request.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Use JDK 8 to prevent OOM during Gradle builds]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Recently, I&apos;ve run into <code>OutOfMemoryError: PermGen space</code> issue when building Gradle projects, especially with Kotlin.</p>
<p>A common way to fix that is to increase the heap size for Gradle JVM, like adding <code>org.gradle.jvmargs=-XX:MaxPermSize=512m</code> to <code>grade.properties</code>.</p>
<p>Actually, there&apos;s another simple way</p>]]></description><link>https://aaronhe.org/use-jdk-8-to-prevent-oom-during-builds/</link><guid isPermaLink="false">6362048b3e46391ee7d51e63</guid><category><![CDATA[Android]]></category><category><![CDATA[Java]]></category><category><![CDATA[Kotlin]]></category><category><![CDATA[Gradle]]></category><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Thu, 28 Jan 2016 05:48:55 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Recently, I&apos;ve run into <code>OutOfMemoryError: PermGen space</code> issue when building Gradle projects, especially with Kotlin.</p>
<p>A common way to fix that is to increase the heap size for Gradle JVM, like adding <code>org.gradle.jvmargs=-XX:MaxPermSize=512m</code> to <code>grade.properties</code>.</p>
<p>Actually, there&apos;s another simple way to fix the issue which is using JDK 8 because it eliminates the PermGen space. More details can be found at <a href="http://javaeesupportpatterns.blogspot.co.uk/2013/02/java-8-from-permgen-to-metaspace.html">Java 8: From PermGen to Metaspace</a> or this <a href="http://stackoverflow.com/a/22509753/620138">StackOverflow answer</a>.</p>
<p>Notes to Android developers, even though JDK 8 hasn&apos;t been supported by Android, but you can still use JDK 8 to build Android projects as long as you set compatibility level to 6 or 7 depends on <code>minSDKVersion</code>. Then you will get benefits like what we mentioned earlier.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Cleanup when a subscriber unsubscribes]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Often times, I need to clean up stuff after a <code>subscriber</code> unsubscribes. There&apos;s a handy methods just for that: <code>Subscriptions.create(Action0)</code>. The parameter <code>Action0</code> is a function passed in to do things after a <code>subscriber</code> unsubscribes.</p>
<p>For example, if a callback is set on an instance when</p>]]></description><link>https://aaronhe.org/cleanup-when-subscriber-unsubscribes/</link><guid isPermaLink="false">6362048b3e46391ee7d51e62</guid><category><![CDATA[RxJava]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Sun, 24 Jan 2016 08:37:41 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Often times, I need to clean up stuff after a <code>subscriber</code> unsubscribes. There&apos;s a handy methods just for that: <code>Subscriptions.create(Action0)</code>. The parameter <code>Action0</code> is a function passed in to do things after a <code>subscriber</code> unsubscribes.</p>
<p>For example, if a callback is set on an instance when a <code>subscriber</code> subscribes to a stream, so the instance can emit items whenever the callback gets called. After the <code>Subscriber</code> unsubscribes, we need to free the listener from the instance to avoid side effects.<br>
Or if the observable is a network call, you may want to <a href="(https://github.com/square/retrofit/blob/master/retrofit-adapters/rxjava/src/main/java/retrofit2/RxJavaCallAdapterFactory.java#L100)">cancel it when a <code>subscriber</code> unsubscribes</a>.</p>
<p>Let&apos;s look at some code.</p>
<pre><code class="language-java">public static Observable&lt;Integer&gt; textLengthChanges(final Text text) {
  return Observable.create(subscriber -&gt; {
    Text.OnLengthChangedListener listener = length -&gt; {
      if (!subscriber.isUnsubscribed()) {
        subscriber.onNext(length);
      }
    };

    text.setOnLengthChangedListener(listener);
  });
}
</code></pre>
<p>This <code>textLengthChanges</code> observable emits the length of the <code>text</code> whenever it&apos;s changed.</p>
<p>A subscriber at some point would unsubscribe to the stream after it has done the work. Then we need to release the <code>Text.OnLengthChangedListener</code> from the <code>text</code> instance. It can be done by simply adding:</p>
<pre><code class="language-java">...
subscriber.add(Subscriptions.create(() -&gt; text.setOnLengthChangedListener(null)));
...
</code></pre>
<p>Under the hood, <code>Subscriptions.create(Action0)</code> creates a <code>BooleanSubscription</code> which checks if it&apos;s got unsubscribed and if so, it will invoke the <code>Action0</code>.</p>
<p>I must say the naming <code>Subscriptions.create(Action0)</code> isn&apos;t that intuitive to me (<a href="https://github.com/ReactiveX/RxJava/issues/2967">and someone agrees on that</a>). But because <code>Subscription</code> is <a href="https://github.com/ReactiveX/RxJava/issues/173#issuecomment-14545484">equivalent</a> to <code>Disposable</code> in Rx.Net, the naming makes some sense. It&apos;s also worth mentioning that it seems RxJava 2.0 is going with <a href="https://github.com/ReactiveX/RxJava/tree/2.x/src/main/java/io/reactivex/disposables"><code>Disposable</code></a> instead of <code>Subscription</code>.</p>
<p>Last but not least, don&apos;t be confused with <a href="http://reactivex.io/documentation/operators/using.html"><code>Observable.using</code></a>. This operator is useful when you have full control over lifespan of an object, so you can do some cleanups of the object when the <code>Observable</code> terminates.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Enable ProGuard for Android Project]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Lately we have to deal with Android 65k method limit, so enabling ProGuard to remove some unreferenced methods might be a good way to <strong>temporarily</strong> solve this problem.</p>
<p><strong>Enable ProGuard</strong><br>
It&apos;s really easy to enable ProGuard. If using Maven, just add the following code in your pom file</p>]]></description><link>https://aaronhe.org/enable-proguard-for-android-project/</link><guid isPermaLink="false">6362048b3e46391ee7d51e60</guid><category><![CDATA[Android]]></category><dc:creator><![CDATA[Aaron He]]></dc:creator><pubDate>Sun, 07 Dec 2014 23:36:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Lately we have to deal with Android 65k method limit, so enabling ProGuard to remove some unreferenced methods might be a good way to <strong>temporarily</strong> solve this problem.</p>
<p><strong>Enable ProGuard</strong><br>
It&apos;s really easy to enable ProGuard. If using Maven, just add the following code in your pom file under Android Maven Plugin&apos;s configuration:</p>
<pre><code class="language-xml">&lt;proguard&gt;
    &lt;skip&gt;false&lt;skip&gt;
    &lt;config&gt;proguard-project.txt&lt;/config&gt;
&lt;/proguard&gt;
</code></pre>
<p><code>proguard-project.txt</code> is the configuration file for ProGuard. I&apos;ll explain it later. If using Gradle as your build system, just add following code in <code>buildTypes</code><a href="#gradle-alt-setting"><sup>1</sup></a>&#xFF1A;</p>
<pre><code class="language-groovy">runProguard true
proguardFiles file(&apos;proguard-project.txt&apos;)
</code></pre>
<p>After enabling ProGuard, whenever the project is built, ProGuard will generate several text files: dumpt.txt, mapping.txt, seeds.txt and usage.txt.</p>
<ul>
	<li>dump.txt: Internal structure of all the classes in the final .apk.</li>
	<li>mapping.txt: If obfuscation is enabled( by default it is), this file will list the mapping between original classes/methods/fields name and obfuscated one.</li>
	<li>seeds.txt: Lists classes and members are not obfuscated.</li>
	<li>usage.txt: Lists all the code removed from the final .apk.</li>
</ul>
<strong>Config ProGuard</strong>
There are two recommended configuration files for Android project from Google. They are located in tools/proguard under Android SDK directory. I would pick the one without optimization becasue I am not so sure about what kind of optimization ProGuard will do. Full usage of ProGuard can be found <a href="http://proguard.sourceforge.net/manual/usage.html" target="_blank">here</a>. Here are several rules I used.
<ul>
	<li>Warning: can&#x2019;t find superclass/interface/referenced class: Generally, this might be the first or the only warnings you would encounter. The reason why this warning happens is really simple: some jars are missing, so you need to specify the locations of those jars. But for an Android project, in most cases just use <code>-dontwarn</code> to turn off those warnings because during Android build process, it will <a href="http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass" target="_blank">automatically feed you all referenced jars.</a></li>
	<li><code>-dontobfuscate</code>: I just don&apos;t feel the need for code obfuscation yet. And it will make debug a little bit harder. ProGuard will generate a mapping file between original class names and obfuscated ones. By using <a href="https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/retrace/introduction.html" target="_blank">retrace tools</a>, it will make life easire. I am sure there&apos;s an automated way to do that. For Crashlytics, it supports reading the mapping file, so you don&apos;t need the extra step to using the retace tool.</li>
	<li>Automated Instrumentation Testing: If the Android project has some instrumentation tests, <code>-keep public class * extends android.app.Application</code> is a must. There will be an class generated extends <code>android.app.Application</code> class for each test project but only referenced in AndroidManifest.xml, so ProGuard just thinks that Application class is unreferenced. Lost a whole day of productivity because of this. :-(</li>
	<li>Play Services: This gigantic library contains about 20k-30k methods. Even though it&apos;s generally considered a must-have in Android development, lots of classes/methods might just sit there doing nothing. Google has its own <a href="https://developer.android.com/google/play-services/setup.html#Proguard" target="_blank">recommended configuration</a> for this library. Just include those rules. Good news is, <a href="http://android-developers.blogspot.com/2014/11/google-play-services-65.html" target="_blank">the next release of Play Services</a>, v6.5, will be modularized.</li>
	<li>Reflection: Anything using reflection should be kept.</li>
</ul>
<strong>Android 65k</strong>
So what is <a href="https://developer.android.com/tools/building/multidex.html" target="_blank">Android 65k problem</a> exactly? <a href="http://en.wikipedia.org/wiki/Dalvik_%28software%29" target="_blank">Davlik</a> is the rumtime of Android. Its instruction set has a variable called <code>method reference index</code>. It defines the maximum methods you can invoke. Unfortunately, the size of that variable has never changed since the birth of Android, and it&apos;s a 16 bit integer, 65536. Except using ProGuard to get around this problem, another popular solution is using multi-dex.
<p><img src="https://aaronhe.org/content/images/2015/10/65k-meme.jpg" alt="Ambitious New Android Developer" loading="lazy"></p>
<p><strong>Multi-dex</strong><br>
Mobile app is really a thing now, there still might be a chance your project references more than 65536 methods even after ProGuard is enabled. Some guys come up with an idea: compile code to multiple dex files, one of those is the primary, custom loading dex files when needed. Well, before ART is released, the new runtime for Android, this is very cumbersome, inefficient and error prone. As ART is released alone with Lollipop, multi-dex gets native support. Multiple dex files will be compiled to one .oat file, executable for ART. For pre-ART Android, Google also has a support library for that. More details for setting it up could be found here: <a href="https://developer.android.com/tools/building/multidex.html#mdex-gradle" target="_blank">Configuring Your App for Multidex with Gradle</a>. It seems solved the problem a bit nicer, but still there are several <a href="https://developer.android.com/tools/building/multidex.html#limitations" target="_blank">limitations</a>, like doesn&apos;t work well for pre-4.0 devices, adds compiling overhead, etc.</p>
<p>The real problem for 65k looks very simple though. It might just be a really hard bug for Google to fix. On the other hand, mobile apps are doing more and more things, there should be a perfect solution for this.<hr></p>
<ol>
	<li id="gradle-alt-setting">There are other ways to configure ProGuard for Gradle. <code>getDefaultProguardFile(&apos;proguard-android.txt&apos;),&apos;proguard-rules.pro&apos;</code> looks neat. First find the default configuration file from Android SDK then merge it with your project specific config. This is something Android Maven Plugin cannot do yet.</li>
</ol><!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>