Xavi Rigau

null

Leveling Up: From AsyncTask to RxJava

| Comments

Hey! It’s been a while since I wrote the previous post, but as I said these have been busy days so I guess that’s my excuse.

Last night I worked again on the app, I did something I’ve been thinking of for the last couple of weeks: I’ve moved from AsyncTask to RxJava!

Why?

Well, it’s not that for the project I need it (for now, there’re not many asynchronous events, API calls, etc.) but I think it’s something cool to learn and probably something useful, since for more complex apps it seems a really good solution.

So when I was in Droidcon Italy, I had a chat with Jamie and Mathias from SoundCloud about RxJava and how awesome it is, so I decided to try myself: I converted the Renderscripting demo into RxJava, result here and I think it went pretty well!

How?

So first I had to read some resources from the Internet about RxJava and Reactive Programming. I found it hard to understand in the beginning and also, IMO the namings used (Observable, Observer, Subscription, etc.) are a bit confusing when you’re not used to them. I’m still not familiar enough to explain myself in detail how RP works with RxJava, but there’s a bunch of information on the Internet about it (see the list of links at the end of the post).

I guess if you’re familiar with Functional Programming, the learning curve should be flatter, which in my case doesn’t really apply (although I started learning FP, I never spent enough time so it’s still in my To Do list).

Show me teh codez

What I did is still not completed and it’s not stable enough, so I created a branch for it and I’ll keep working on it until I think it’s as stable as it’s now in the master branch. You can check the code here and see the diff with master here.

I think one interesting point here is that I almost didn’t need to modify any Task-related class – in fact, it got simplified in the app module, since we don’t need to do funky stuff because of an AsyncTask.

Another good point is that the Tasks are still testable, since the only thing I had to do is build the Observables around them, which means, you can run a Task synchronously or just get an Observable that executes that task. I think you can even execute that Observable in the current Thread if you wish to test it! This means Tasks can still be simple, for example:

This is how the SummaryTask (the task that gets a Summary from a post) looks like:

SummaryTask.javaView in Github
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SummaryTask implements Task<Summary> {

    private final String url;

    public SummaryTask(String url) {
        this.url = url;
    }

    @Override
    public Summary execute(Services services) {
        return services.summary(url);
    }

}

And so my TaskFactory just needs to create an Observable that executes this task:

TaskFactory.javaView in Github
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TaskFactory {

    public static Observable<Summary> fetchSummary(String url) {
        return observableFrom(summaryTask(url));
    }

    private static Task<Summary> summaryTask(String url) {
        return new SummaryTask(url);
    }

    private static <T> Observable<T> observableFrom(final Task<T> task) {
        return Observable.create(new Observable.OnSubscribe<T>() {
            @Override
            public void call(Subscriber<? super T> subscriber) {
                try {
                    subscriber.onNext(new TaskExecutor(new UrlConnectionClient()).execute(task));
                    subscriber.onCompleted();
                } catch (Throwable error) {
                    subscriber.onError(error);
                }
            }
        });
    }
}

Simple, isn’t it? Last step is execute it and create an Observer that will get the output sequence from this Observable (which in this case is just one item) and update the UI:

NewsDetailsFragment.javaView in Github
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class NewsDetailsFragment extends Fragment {

    // ... Other stuff...

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // ... Other stuff

        loadSummary();
    }

    private void loadSummary() {
        fetchSummary(news.getUrl())
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Summary>() {
                    @Override
                    public void onNext(Summary summary) {
                        displaySummary(summary);
                    }

                    @Override
                    public void onCompleted() {
                        hideLoading();
                    }

                    @Override
                    public void onError(Throwable e) {
                        log("Error downloading news summary", e);
                        hideLoading();
                        toast(R.string.generic_error_oops);
                    }
                });
    }
}

That’s it! Thanks to the RxJava-Android library we can use the AndroidSchedulers.mainThread() Scheduler in order to get the callbacks in the UI Thread :)

A decision I took is not to use Retrofit’s built in RxJava support, since I don’t feel there’s a need for it at this pont. I want to learn how to use RxJava properly, so I want to write all the bits myself as much as I can. This doesn’t mean I don’t like the idea of having it already built in Retrofit, I’ll probably use it in the future.

What’s next?

As I said, this is still far from perfect. The biggest issue is orientation changes. There’s no built in support for that, but I guess the cache Observable operator can be used for caching and emiting the sequence of results after rotation.

Also testing!! I really want to spend some time writing tests. It also would be nice to set up a Jenkins server and perform some code analysis (PMD, Findbugs, Checkstyle and Lint), I’ll try to spend some time this weekend on this – will share if I do!

A smaller issue is there must be a way to compose the two Observables I use in the NewsDetailsFragment (the one above + the one that parses the Markdown), I just need to sit and spend some time reading the docs, so I consider it a minor issue. But apart from that, the rest looks good to me.

Conclusion

I want to keep learning how to do things ‘the Reactive way’. I know I’m probably doing things wrong and I’ll learn from my mistakes, in fact that’s why I do this in my side project, I don’t mind if I have to spend time rewriting the same code over and over again :P

I have to say though, the first time I tried to do something with RxJava I felt like “OMG WHAT IS THIS?”, which is good because it reminds me that there’s a bunch of stuff I want to learn. That feeling when you do something new for the first time and you’ve got no idea and then you start feeling a bit more confident as you keep working on it, makes you feel so good!

That being said, I’ll go to bed since I’m really tired and I’m probably writing senseless sentences.

RxJava links:

If you want to discuss about RxJava, write your comment below!

Busy Days

| Comments

This has been a busy week so I haven’t done much on the app. On Wednesday we went to the ‘TDD: Back to basics’ organized by the LSCC and the exercise was the Checkout kata (our solution here: https://github.com/xrigau/katas/tree/master/kata-checkout). Thursday and Friday were pub days! :)

Asynchronous Markdown to Spannable conversion

So the first thing I did this week was creating an AsyncTask to convert the Markdown to Spannable so it doesn’t happen in the main thread. Changes here. Even after this changes it still feels slow when loading a news with a lot of text because TextView needs to parse that Spannable in order to render it, so it’s still not perfect, but a bit better than before.

Use android:windowBackground everywhere

I wanted to use android:windowBackground because that’s the first thing that gets loaded when the Activity starts and I wanted to have a white background in the details screen while it’s still loading. So I did it.

This introduced a bug: Because there’s this parallax effect in the news details’ ScrollView, the text would go on top of the ImageView and because the text had a transparent background it was overlayed on top (see image below).

Clipping FTW!

So how I fixed that? Well, the easiest way to fix that would be to set a background color in the TextView that holds the news content, but that’s cheating because we’re adding overdraw (drawing pixels on top of already drawn pixels) and that means a longer rendering time.

In order to avoid overdraw we can clip the area where the ImageView will be drawn so we don’t draw a part of it onscreen. That’s really easy to do because I already had a custom view that was the root view for that layout, so I simply added a few lines of code. You can find a simplified version below:

NewsDetailsParallaxLayout.javaView in Github
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class NewsDetailsParallaxLayout extends FrameLayout implements OnScrollChangedListener {

    private float translationY; // This is the scroll Y position

    // ...

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        float bottom = child.getMeasuredHeight() + translationY;

        canvas.save();
        canvas.clipRect(getLeft(), getTop(), getRight(), bottom);
        boolean result = super.drawChild(canvas, child, drawingTime);
        canvas.restore();

        return result;
    }
}

So the magic happens by just calling Canvas.clipRect(), which will only draw what’s inside the rectangle defined by parameters, see documentation. And this is the result:

So that’s one less thing to think of.

Next days

Next week there’s another meetup on Monday about Wireless Displays and I’m giving a talk about UI testing in Android with Espresso at Droidcon Italy on Friday, so I’m not going to have much spare time. BTW slides for the talk are available in Speaker Deck and code is in Github.

So have a good week and stay tuned for more!

Trying to Add a ViewPager

| Comments

So last night I tried to add a ViewPager to the app I’m working on (the Hacker News client, which I think deserves a better name). And it didn’t go well.

I wanted to have the ViewPager in the details screen (so when you’re reading the news) so that you swipe left and right to see the previous and next news’ content.

Here’s the commit (I know, I should have split that into smaller commits): https://github.com/xrigau/hn-android-client/commit/535c4735082f152df1e80682c7a59eda62b6a5a5

I didn’t manage to get a fully working solution so for now I’ll leave it without the ViewPager since I think it’s not a killer-feature of the app (at least for me).

Failure

So in the end it didn’t work well. I didn’t have time to do an in-depth debugging to see what were the issues but as I said it’s not a big deal. The main reason is lifecycle. If I want to implement ViewPager I’ll have to redo some code that depends on the Activity and Fragment lifecycle, even Views lifecycle (since I need to use the ViewTreeObserver at some pont).

Anyway, I’m going to leave the branch open and I may eventually come back to it and who knows, maybe I’ll figure out how to do it properly :).

The good

There’s always a good thing when you do something new (even if you fail). I realized many things:

  • I need to improve way asynchronicity is done in the app. I mean for now it works fine but when I tried to add the ViewPager, because it changes the way how Fragments are handled, orientation changes would cause these Fragments to not get the result of a Task. And that sucks.
  • I should think about the UI a bit more because if I want to add a ViewPager, then the fade-in/fade-out of the ActionBar when you scroll the news looks weird. I mean, when you scroll down the ActionBar goes from transparent to opaque as you scroll, then if you swipe to the next news then the ActionBar would jump from opaque to transparent and it just doesn’t feel good.
  • Parsing Markdown as String to Spannable should be done in a background thread. It looks like it’s a tiny thing but it felt slow when swiping to the next/previous page and one of the reasons is that Markdown parsing is expensive!
  • Staying until 3 a.m. just because there’s one more thing I want to try (there’s always one more thing!) is too bad. I felt asleep this morning because of that, so I don’t want to do it again during weekdays.

And on that note, I’m going to bed. Next things I want to do are:

  • Define an MVP (Minimum Viable Product) from where I be confident to ship the app. To do that I’ll need to define functionalities, navigation & UI.
  • Create a Beta testing community on Google+ and let people try the app and improve it with the feedback.
  • Find a good name and create a fancy icon. Maybe also brand it? (I really like what Joaquim Vergès did with the branding of Falcon Pro).

Good night!

Let’s Start Again

| Comments

What does this title mean?

Well, I started my very first blog (more or less) 3 years ago in Wordpress. Then I moved to my own blog (same address as this one) but after a while I quitted. The reason? There was not much to talk about, I was trying to write during my commute time, but that didn’t work well. I think you should only write because you want to, but not just because you have time for it (that was my case back then).

After a while I wanted to write again so I created this Octopress-powered blog, but again, I didn’t write anything apart from my Welcome post (at least I didn’t leave this empty).

It’s been more than 5 months since then. And I’ve decided I want to write again. Why do I think this time is going to be different than the previous ones? Well, I want to imagine that my English is better now so it’ll take me less time to write anything. Plus I have some more spare time than before. Oh, and the most imporant: I really have something to say!

A couple of months ago I started a Hacker News unofficial client for Android. Today I’ve decided I’d share part of the development process in this blog.

So, what?

What I want to do is write a new post every time I write any code in that app. In a perfect world I’d have started writing posts at same time as code and this’d be a really nice (or not) blog. But you know, there has to be always a bad thing!

The difference between writing about random things and writing about this project is that now I have something to say or to share. Because I have to take decisions, find a way to implement this or that, search how this could be done, think whether this should be done one way or the other, etc. So at least this will be kinda self remainder on why I did this or that. And it’d be cool if it distracts anyone reading as well!

Introduction

So just a quick introduction on this project (since I think this is looking long enough and it’s starting to be late). This project is split in two parts:

  1. node.js server: https://github.com/xrigau/hackednews
  2. Android client: https://github.com/xrigau/hn-android-client

The server runs on Heroku and it’s based on the hackednews web API by blackjable. I had to make some changes because it was not working in some cases but it was great to start on that base, since I tried to do all my own and failed. Then I added api that given a URL of a website, returns a JSON with the main contents in that site.

I’m implementing the client from scratch. For now it doesn’t have much functionality. It gets the feed of news from the server and displays them. If you scroll down it will load more news as you scroll through the list. There’s an ugly refresh button that cleans up the list and downloads it again. When you tap on an item in the list it will open in a details or summary screen with (maybe) a fancy picture and the content of the website. Then you can share it or open the original website of the news.

What’s next

I still want to implement more stuff and make it more beautiful. I’d like to improve the way it gets the contents from websites (now it uses Readability’s Parser API) because it fails sometimes and images are not displayed (because HTML is converted to Markdown and the displayed in the app). I want to refactor the code and add more tests. There’s a lot to go. Oh and I need to create an Alpha testing community so I can get some feedback!

But I think it’s enough for now.

So let the hacks begin!