Thursday, March 16, 2017

Lint stuck after upgrading gradle plugin to 2.3.0

app:lint hangs after I upgraded the gradle plugin to 2.3.0.

Unexpected failure during lint analysis (this is a bug in lint or one of the libraries it depends on)

It complains about ProblemReporter.isClassPathCorrect( and other places in ProblemReporter, but I was not able to pinpoint the problem.

Turns out this is caused by libraries that uses old lint APIs, in my case Timber and Butterknife. Once I updated those dependencies, lint finishes.

Thanks Josh Burton for sharing his solution with me!

Monday, February 13, 2017

Constraint Layout beta5 lint error

Constraint Layout beta 5 is the release candidate, and added a lint to deprecate older versions.

The easiest way to get rid of the lint error is to press Alt-Enter and choose the first option to upgrade.

However, I encountered a bug, which forces me to stay with beta4 until the next version comes out with the fix. I still want to get rid of the lint error so my continuous build does not fail. I didn't want to change lintOptions abortOnError to false in build.gradle because I still want my build to catch other fatal lint errors.

I tried to get Android Studio to fix this lint error for me by choosing Disable inspection from Alt-Enter. Alas, that only changed the local settings. Turns out that I need to go to Settings → Editor → Inspections to undo that.

The option I wanted is Suppress: Add tools:ignore="MissingConstraints" attribute, which modifies the xml for me to add the appropriate lint suppression.

<!-- Cannot upgrade to beta5 due to -->

Finally, I added a comment to remind me why I suppress the lint error.

More info: Suppress Lint Warnings.

Monday, January 16, 2017

Java compatibility in build.gradle

I got an email from Google asking me to update Fit Cat for Android Wear 2.0, so I tried to compile that app after I haven't touched it for a few months.

Since Android Studio evolves so quickly, I wasn't exactly surprised when I got an error:

Error converting bytecode to dex:
Cause: Dex cannot parse version 52 byte code.
This is caused by library dependencies that have been
compiled using Java 8 or above.

It asked me to add sourceCompatitbility and targetCompatitbility to Java submodules. What does that mean? After a lot of searching plus trial and error I found the answer.

In your top-level build.gradle, add this:

allprojects {
  tasks.withType(JavaCompile) {
    sourceCompatibility = JavaVersion.VERSION_1_7
    targetCompatibility = JavaVersion.VERSION_1_7

Thursday, December 1, 2016

Elite Worship

Yesterday I read an article called In a world..., which talked about the problem of elite worship in the iOS community. I'm an Android developer, but I see the same problems in our community as well.

Here is how I see it:

  • People who tweet, blog, speak and open source are considered elites
  • Elites are held above the rest of the community, making it unwelcome for others

Does being loud make you a better developer?

Or, flipping the question, are people who don't share their work inherently worse? No, of course not. Unfortunately, unseen work is just that: unseen. Without external evidence, there is no way to tell if a developer is good or not, so we assume they are average. Not better, not worse.

I, too, would like to believe that we live in a meritocracy where good work is automatically recognized. But how? How do people know what you are doing if you don't tell them?

This encourages people to run up a hill and yell on top of their voice. And the people who are comfortable doing that are rewarded, are seen as "better".

However, this is not automatically lead to elite worship. Yes, there will always be some people who are more visible in the community. But that does not mean it has to be a small group who are revered above all else.

What you can do

When you see good work, point it out

One problem of elite worship is that we ended up comparing ourselves with people with more Twitter followers, more mentions in the industry newsletter, more conference talks etc and feel defeated. We can counter that by pointing out the good work we see that is not enshrined on the internet.

Here is a concrete thing you can do: When you do a code review, don't just point out the things to fix. Remark on the good parts as well.

When something takes longer than expected, write it down

We need more voices in our community. Blogging is great, because there is no gatekeeper to decide who gets to publish and who doesn't. But there is still one hurdle: What to write about?

Pay attention to what you do day to day. If you spent more time figuring out how to do something than you thought you would, it is worth writing down.

Don't worry about looking stupid because others must know how to do it already. You don't have to push the boundaries of human knowledge. That's PhD theses, not blog posts.

Think of it as notes to your future self, a place to put codes and commands in monospace font so you can come back in 3 months to copy and paste them.

Just because some people share more doesn't mean there is no place for you to do it. Everyone has a different experience, and we want to hear from you.

Speak at local meetups

In the original article, the author laments that there is no diversity at conferences. Always the same faces, always the same topics. As a conference organizer, I can tell you that speaker selection is hard. There were so many people that very much deserved a speaking slot but didn't get one, because we had a very limited number. You can read more about how we try to have a balanced speaker roster at 360|AnDev.

That said, we don't have to let conferences be the only place where technical talks are given. There are numerous meetups happening all over the world on any given week. And they are always, always looking for speakers. Sure, it does not come with the prestige of conference speaking, but does that make them worthless?

If you think the local audience is too small to worth your time, record your talk and post it on the internet. I wrote a guide on how to record your screen and voice with QuickTime. Would love it if someone write one for Open Broadcaster Software (OBS)!

Give talks yourself, and encourage those around you to do it. That is how we get new voices. Don't let the conference speaking circuit dictate who gets to speak and who doesn't.

Sharing != Elite Worship

People are lazy. If we see the same name over and over, we think that person must know something. This is fundamentally what leads to elite worship. We can't change human nature, but we can spread the love. Point out each other's good work, encourage each other to make it visible. Make the elite circle so large that it is no longer elite.

Thursday, October 13, 2016

Dennis Ritchie Dies Again

Yesterday many people on my Twitter timeline sharing an article about Dennis Ritchie's death:

I clicked through to read the article, which was from 2011. Why is this circulating again?

My guess is that some people were retweeting without bothering to read the article, and thought he died recently.

This makes me think of a reaction to a tweet I posted.

I was furious. Why would I post something that I'm not sure about? I even backed it up with a slide. Why are you doubting me?

Took me a while to remember that that's what other people do. Say things even when they are only 60% sure. I look back at my career and cringe to think of all the times when other people were regarded more knowledgeable because they open their mouths even when they are not sure.

This time, I am happy that I checked the details before passing it on.

Saturday, October 8, 2016

Constraint Layout: Icon Label Text

I am making one of those classic layouts: an icon with two lines of text. I would like to use vector drawable for the icon, and scale it according to the text sizes. I want the top edge of the icon to line up with the top edge of the first line of text, and the bottom edge of the icon to line up with the bottom edge of the second line.

How would I do that? With ConstraintLayout!


The width and the height of the ImageView is 0dp. This tells ConstraintLayout to compute them by the constraints. In this case, the height of the ImageView is determined by these constraints:


The width is the same as the height.


The rest of the constraints are for positioning.

With that, the image scales up as the text sizes increase. It stays sharp because it is a vector.

Layout Editor

I tried to make this layout with the Layout Editor, but could not figure out how to create the constraint app:layout_constraintTop_toTopOf="@+id/label" for the ImageView. I was hovering my cursor around the top edge but not sure how to drag it to link the two views. So I added up playing with the editor a bit to deduce the XML attributes, and switched to editing the XML directly. I hope to use the layout editor in my next attempt to use Constraint Layout.

Follow-up Twitter discussion:

Read the whole Twitter thread.

Source code

Click on either TextView to increase its size. Click on the image to reset.

Friday, September 16, 2016

TransactionTooLargeException crashes on Nougat

I was testing my app on Android Nougat, and it crashed when I try to move from one Activity to another. I saw this in the log: java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 700848 bytes.

Actually, the platform has been printing warning log about this for a while, but let's be honest, who has time to read all the logs? Nougat (API 24) throws TransactionTooLargeExceptions as RuntimeExceptions, so we cannot ignore that any more.


Turns out I was saving a big list of search results during onSaveInstanceState to persist them over rotation. Time to move that to a Loader!

But what is the limit?

But now I'm curious: What is the limit? I wrote a sample app to find out.

public class MainActivity extends Activity {
  protected void onCreate(Bundle savedInstanceState) {

    Intent intent = new Intent(this, AnotherActivity.class);

  protected void onSaveInstanceState(Bundle outState) {

    int length = 1000;
    Uri uri = getIntent().getData();
    if (uri != null) {
      try {
        length = Integer.parseInt(uri.getLastPathSegment());
      } catch (NumberFormatException e) {

    byte[] data = new byte[length];
    outState.putByteArray("data", data);

To try different sizes, I start the app like this:

adb shell am start \
  -a android.intent.action.View \
  -n com.sqisland.tutorial.transcation_too_large/.MainActivity \
  -d 700000

This launches MainActivity, which immediately goes to AnotherActivity. When that happens, the system calls onSaveInstanceState, which tries to stash away a byte array of the length specified in the adb command, retrieved by getIntent().getData(). This way, I can try different numbers without recompiling and redeploying the app.

I did a binary search on a Nougat emulator and my Nexus 9. The limit is slightly different, but it hovers around 500000. That is not a small number, but not too hard to exceed if you try to store data rather than state.