您的位置:首页 > 移动开发 > Android开发

Debugging RxJava on Android

2015-11-13 17:44 691 查看
Skip
to content





Fernando Cejas



About me


Debugging RxJava on
Android

Debugging is the process of finding and resolving bugs or defects that prevent correct operation of computer software (Wikipedia).
Nowadays debugging is not an easy task, specially with all the complexity around current systems:Android is not an exception to this rule and
since we are dealing with asynchronous executions, that becomes way harder.
As you might know, at @SoundCloud,
we are heavily using RxJava as one of our
core components for Android Development, so in this article I am gonna walk you through the way we debug RxObservables and Subscribers.

Give a warm welcome to Frodo

Let me get started by introducing Frodo, but first, if you already watched Matthias
Käppler talk at GOTO Conference (if you haven’t yet, I strongly recommend it), you may have noticed that he talks about someone called Gandalf (minute
41:15). All right, I have to say that in the beginning, Gandalf
was my failed attempt to create an Aspect Oriented Library for Android, fortunately after working hard and receiving useful feedback, it became an Android
Development Kit we use at @SoundCloud.
However, I wanted to have something smaller that solves only one problem, so I decided to extractRxJava Logging
specifics that I have been working on, and give life to Frodo.
Frodo is no more than an Android Library for Logging RxJava Observables
and Subscribers (for now), let’s say Gandalf’s little son or brother. It was actually inspired by Jake
Wharton’s Hugo Library.



Debugging RxJava

First of all, I assume that you have basic knowledge about RxJava and its core components:Observables and Subscribers.
Debugging is a cross cutting concern and we know how frustrating and painful could be. Additionally, many times you have to write
code (that is not part of your business logic) in order todebug stuff, which make things even more complicated, specially when it comes to asynchronous
code execution.
Frodo was born to achieve this and avoid writing code for debugging RxJava objects. It is based on Java Annotations and relies on a Gradle Plugin that
detects when the Debug build type of your application is compiled, and weaves code, which is gonna print RxJava Objects logging informationon the android logcat output. For instance, it
is safe to keep Frodo annotations in your codebaseeven when you are generating Release versions of your Android App. So now, let’s get our hands dirty and have a taste of it.

Using Frodo

To use Frodo the first thing we need to do is to simply apply a Gradle
Plugin to our Android Project like this:

build.gradle

Java

123456789101112buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.3.1' classpath "com.fernandocejas.frodo:frodo-plugin:0.8.1" }} apply plugin: 'com.android.application'apply plugin: 'com.fernandocejas.frodo'
As you can see, we add “com.fernandocejas.frodo:frodo-plugin:0.8.1” to the classpath and afterwards we apply the plugin ‘com.fernandocejas.frodo’.
That should be enough to have access to the Java annotations provided by the Library.

Inspecting @RxLogObservable

The first core functionality of Frodo is to log RxJava Observables through @RxLogObservable Java annotation. Let’s say we have a method that returns an Observable which will emit a list of some sort of DummyClass:ObservableSample.javaJava

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
ObservableSample
{

@RxLogObservable

public
Observable<List<MyDummyClass>>
list()
{

return
Observable.just(buildDummyList());

}

public
List<MyDummyClass>
buildDummyList()
{

return
Arrays.asList(new
MyDummyClass("Batman"),
new
MyDummyClass("Superman"));

}

public
static
final
class
MyDummyClass
{

private
final
String
name;

MyDummyClass(String
name)
{

this.name
=
name;

}

@Override

public
String
toString()
{

return
"Name: "
+
name;

}

}

}

Then we subscribe to our sample observable:

MyClass.java

Java

12345678910final ObservableSample observableSample = new ObservableSample();observableSample.list() .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<List<ObservableSample.MyDummyClass>>() { @Override public void call(List<ObservableSample.MyDummyClass> myDummyClasses) { toastMessage("onNext() List--> " + myDummyClasses.toString()); } });
When compiling and running our application, this is the information we are gonna see on the logcat:LogcatShell

1

2

3

4

5

6

ObservableSample D Frodo
=>
[@Observable
::
@InClass
->
ObservableSample
::
@Method
->
list()]

D Frodo
=>
[@Observable#list
-> onSubscribe() :: @SubscribeOn -> RxNewThreadScheduler-3]

D Frodo
=>
[@Observable#list
-> onNext() -> [Name: Batman, Name: Superman]]

D Frodo
=>
[@Observable#list
-> onCompleted()]

D Frodo
=>
[@Observable#list
-> onTerminate() :: @Emitted -> 1 element :: @Time -> 0 ms]

D Frodo
=>
[@Observable#list
-> onUnsubscribe() :: @ObserveOn -> main]

Basically this means that we subscribed to an Observable returned by the list() method in ObservableSample class. Then we get information
about the emitted items, schedulers and events triggered by the annotated Observable.

Inspecting @RxLogSubscriber

Let’s now explore what @RxLogSubscriber is capable of.

To put an example, let’s create a RxJava dummy Subscriber and annotate it with @RxLogSubscriber.

MySubscriberBackpressure.java

Java

12345678910111213141516171819202122232425@RxLogSubscriberpublic class MySubscriberBackpressure extends Subscriber<Integer> { @Override public void onStart() { request(16); } @Override public void onNext(Integer value) { //empty } @Override public void onError(Throwable throwable) { //empty } @Override public void onCompleted() { if (!isUnsubscribed()) { unsubscribe(); } }}
Forget about the backpressure name of this Subscriber for now, since this topic deserves a whole article. Just know that this Subscriber will only request 16 elements and it is gonna do nothing with the items it receives on the onNext() method. Even though that, we still wanna see what is going on when it subscribes to any Observable which emits Integer values:ObservableSample.javaJava

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public
class
ObservableSample
{

public
Observable<Integer>
numbersBackpressure()
{

return
Observable.create(new
Observable.OnSubscribe<Integer>()
{

@Override

public
void
call(Subscriber<?
super
Integer>
subscriber)
{

try
{

if
(!subscriber.isUnsubscribed())
{

for
(int
i
=
1;
i
<
10000;
i++)
{

subscriber.onNext(i);

}

subscriber.onCompleted();

}

}
catch
(Exception
e)
{

subscriber.onError(e);

}

}

});

}

}

Here is when we subscribe to our SampleObservable:

MyClass.java

Java

123456final ObservableSample observableSample = new ObservableSample();observableSample.numbersBackpressure() .onBackpressureDrop() .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new MySubscriberBackpressure());
Again when we compile and run our application, this is what we get from the logcat output:LogcatShell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

SubscriberBackpressure D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onStart()]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
@Requested
->
40
elements]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
1
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
2
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
3
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
4
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
5
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
6
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
7
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
8
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
9
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
10
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
11
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
12
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
13
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
14
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
15
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onNext()
->
16
::
@ObserveOn
->
main]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
onCompleted()
::
@Received
->
16
elements
::
@Time
->
3
ms]

D Frodo
=>
[@Subscriber
::
MySubscriberBackpressure
->
unSubscribe()]

Information here includes each of the items received, number of elements, schedulers, execution time and events triggered.
As you can see this information is useful in cases of backpressure,
or to see in which thread the items are being emitted or when we wanna se if our Subscriber has subscribed successfully,
thus avoiding memory leaks for example.

Frodo under the hood

In this article, I’m not gonna explain in details how the library internally works, however, if you are curious about it, you
can check an article I wrote last year which includes an example with the same approach I am using for Frodo.
You
can also look into a presentation I prepared as an introduction for both AOP
and the Library or even better, dive into the source code.

Disclaimer: Early stage

Frodo was just born and there is a long way ahead of it. It is still in a very early stage, so you might find issues or things to improve.
Actually, one of the main reasons why it was open source, was to receive feedback/input from the community in order to improve it,
make it better and more useful. I have to say that I’m very excited and I have already used it in 3 different projects without many problems (check the known issues section below for more information). Of
course pull requests are very welcome too.

Known issues

So far, there is a well known issue: since Frodo relies
on a Gradle Plugin (as explained earlier) to detect Android Debug build variant and weave code, if
you make use of Android Library Projects,when you build your Application (even the debug build type), the official Android Gradle Plugin will always generate release
versions of all the Android Library projects included in your solution, thus,this stops Frodo from injecting generated code in annotated methods/classes. Of course this is not gonna
make your app to crash but you won’t see any output on the logcat. There is a workaround for this but be careful if you use it, since you do not wanna ship a release version of your
app with business objects being logged all over the place and exposing critical information.

Just add this flag to the android section in the build.gradle file of you Android Library Project:

build.gradle

Java

1

2

3

android
{

defaultPublishConfig
"debug"

}

Frodo Example Application

The repository includes
a sample app where you can see different use cases, such as Observable errors and other logging information. I
have also enabled Frodo in my Android
Clean Architecture repo if you wanna have a look into it.

Wrapping up

This is pretty much I have to offer in this article, and I hope you have found Frodo useful.

The first version is out and you can find the repository of the project here:

https://github.com/android10/frodo

As always, any feedback is welcome. PRs as well if you wanna contribute. See you soon.

Useful links

Frodo Project website.
Aspect
Oriented Programming in Android.
AO
Programming and Frodo Presentation.
Matthias Käppler Talk at
GOTO Conference: Going Reactive.

Tagged as: android, androiddev, aop, asm, asmdex, aspect
oriented programming, aspectj, cglib, code
injection, development, javassits, programming, reactive, reactive
programming, weaving

Categorized in: Android, Aspect
Oriented Programming, Development, Java, Reactive
Programming

Posted on November
5, 2015 by fcejas


Leave a Reply

You must be logged
in to post a comment.


Post navigation

Architecting
Android…The evolution



Fernando Cejas © 2013 - fernandocejas.com

Search for:


Recent Posts

Debugging RxJava on
Android
Architecting
Android…The evolution
Tasting Dagger 2 on
Android
RxJava
Observable tranformation: concatMap() vs flatMap()
Architecting
Android…The clean way?


Recent Comments


Archives

November 2015
July 2015
April 2015
January 2015
September 2014
August 2014
April 2014
September 2013


Categories

Android
Aspect
Oriented Programming
Development
Java
Reactive
Programming
Software
Architecture
Testing


Meta

Log in
Entries RSS
Comments RSS
WordPress.org
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: