Skip to content

Commit 7c27113

Browse files
committed
Finish up Android example
1 parent fcf4544 commit 7c27113

6 files changed

Lines changed: 300 additions & 5 deletions

File tree

example-android/app/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,9 @@ dependencies {
3030
testImplementation 'junit:junit:4.12'
3131
androidTestImplementation 'com.android.support.test:runner:1.0.1'
3232
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
33+
34+
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
35+
implementation 'io.reactivex.rxjava2:rxjava:2.1.8'
36+
37+
implementation 'com.akaita.java:rxjava2-debug:1.2.2'
3338
}

example-android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package="com.akaita.java.rxjava2debug.exampleandroid">
44

55
<application
6+
android:name=".ExampleApplication"
67
android:allowBackup="true"
78
android:icon="@mipmap/ic_launcher"
89
android:label="@string/app_name"
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2018 akaita
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.akaita.java.rxjava2debug.exampleandroid
18+
19+
import android.app.Application
20+
import android.util.Log
21+
import com.akaita.java.rxjava2debug.RxJava2Debug
22+
23+
/**
24+
* It is good practice to enable RxJava2Debug in the app's Application class, so it is enabled as soon as possible
25+
*/
26+
class ExampleApplication : Application() {
27+
28+
override fun onCreate() {
29+
super.onCreate()
30+
31+
enableCrashlytics()
32+
33+
// Enable RxJava2Debug in Application
34+
// Make sure it is enabled RIGHT AFTER setting up any Crash reporting mechanism such as Crashlytics
35+
// Provide a list of the root packages of your code, so RxJava2Debug can point you to the source of the stream
36+
RxJava2Debug.enableRxJava2AssemblyTracking(MY_CODE_PACKAGES)
37+
}
38+
39+
/**
40+
* This method just replicates Crashlytics' behaviour:
41+
*
42+
* It sets up the same system that Crashlytics does, but instead of sending the report to Fabric it will print it to LogCat
43+
*/
44+
private fun enableCrashlytics() {
45+
val defaultUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler()
46+
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
47+
Log.e("UnhandledException", "Error", throwable)
48+
defaultUncaughtHandler.uncaughtException(thread, throwable)
49+
}
50+
}
51+
52+
53+
companion object {
54+
55+
/**
56+
* This array assumes that the code I wrote for this app is contained under three root packages
57+
*
58+
* This specific example only needs "com.akaita.java.rxjava2debug.exampleandroid". I just added two more in order to show how you could do it
59+
*/
60+
val MY_CODE_PACKAGES = arrayOf(
61+
"com.akaita.java.rxjava2debug.exampleandroid",
62+
"com.akaita.java.rxjava2debug.module1",
63+
"com.akaita.java.rxjava2debug.module2"
64+
)
65+
}
66+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2018 akaita
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.akaita.java.rxjava2debug.exampleandroid
18+
19+
/**
20+
* You don't care about this file.
21+
* This file just contains utilities for the sake of UI prettiness, nothing directly related to the usage of RxJava2Debug
22+
*/
23+
24+
fun Throwable.toHtml(): String {
25+
val sb = StringBuilder()
26+
27+
var t: Throwable? = this
28+
.let {
29+
sb.append("<font color=\"red\">${it.javaClass.canonicalName}: ${it.message}</font><br>")
30+
sb.append(it.stackTrace.toHtml())
31+
it.cause
32+
}
33+
34+
while (t != null) {
35+
sb.append("Caused by: ${t.javaClass}<br>")
36+
sb.append(t.stackTrace.toHtml())
37+
t = t.cause
38+
}
39+
40+
return sb.toString()
41+
}
42+
43+
fun Array<StackTraceElement>.toHtml(): String {
44+
val sb = StringBuilder()
45+
46+
this.forEach { element ->
47+
sb.append("\tat $element<br>")
48+
}
49+
50+
return sb.toString()
51+
}
52+
53+
fun String.highlight(patterns: Array<String>): String {
54+
var result = this
55+
patterns.forEach { pattern ->
56+
result = result.highlight(pattern)
57+
}
58+
return result
59+
}
60+
61+
fun String.highlight(pattern: String): String {
62+
val sb = StringBuilder()
63+
this.split("<br>")
64+
.forEach { line ->
65+
if (line.contains(pattern)) {
66+
sb.append("<font color=\"blue\">$line</font><br>")
67+
} else {
68+
sb.append("$line<br>")
69+
}
70+
}
71+
return sb.toString()
72+
}
Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,98 @@
1+
/*
2+
* Copyright 2018 akaita
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package com.akaita.java.rxjava2debug.exampleandroid
218

3-
import android.support.v7.app.AppCompatActivity
419
import android.os.Bundle
20+
import android.support.v7.app.AppCompatActivity
21+
import android.text.Html
22+
import android.util.Log
23+
import com.akaita.java.rxjava2debug.RxJava2Debug
24+
import io.reactivex.Single
25+
import io.reactivex.android.schedulers.AndroidSchedulers
26+
import io.reactivex.functions.Consumer
27+
import io.reactivex.schedulers.Schedulers
28+
import kotlinx.android.synthetic.main.activity_main.*
529

630
class MainActivity : AppCompatActivity() {
731

832
override fun onCreate(savedInstanceState: Bundle?) {
933
super.onCreate(savedInstanceState)
1034
setContentView(R.layout.activity_main)
35+
36+
toggleAssembly.setOnCheckedChangeListener({ _, isChecked -> toggleRxJava2Debug(isChecked) })
37+
handledException.setOnClickListener { generateHandledException() }
38+
unhandledException.setOnClickListener { generateUnhandledException() }
39+
}
40+
41+
/**
42+
* Toggle RxJava2Debug on and off.
43+
*/
44+
private fun toggleRxJava2Debug( enable: Boolean ) {
45+
if (enable) {
46+
RxJava2Debug.enableRxJava2AssemblyTracking(ExampleApplication.MY_CODE_PACKAGES)
47+
} else {
48+
RxJava2Debug.disableRxJava2AssemblyTracking()
49+
}
50+
}
51+
52+
/**
53+
* Generates an exception in a computation thread, by inserting a `null` event into a stream
54+
*
55+
* By handling this exception in `onError`, it prevents the app from crashing.
56+
*
57+
* It handles the exception by printing it in LogCat and the screen, so the developer can fix it
58+
*/
59+
private fun generateHandledException() {
60+
Single.just("event")
61+
.subscribeOn(Schedulers.computation())
62+
.doOnEvent { _, _ -> Log.i("HandledException", "Start") }
63+
.map { null }
64+
.observeOn(AndroidSchedulers.mainThread())
65+
.subscribe(
66+
{ Log.i("HandledException", "Subscribe") },
67+
{ t: Throwable ->
68+
val enhancedStackTrace = RxJava2Debug.getEnhancedStackTrace(t)
69+
Log.e("HandledException", "Error", enhancedStackTrace)
70+
displayStackTrace(enhancedStackTrace)
71+
}
72+
)
73+
}
74+
75+
/**
76+
* Generates an exception in a computation thread, by inserting a `null` event into a stream
77+
*
78+
* No implementation for `onError` is provided, so the exception will be thrown and it will eventually crash the app O_o
79+
*/
80+
private fun generateUnhandledException() {
81+
Single.just("event")
82+
.subscribeOn(Schedulers.computation())
83+
.doOnEvent { _, _ -> Log.i("HandledException", "Start") }
84+
.map { null }
85+
.observeOn(AndroidSchedulers.mainThread())
86+
.subscribe(Consumer { Log.i("UnhandledException", "Subscribe") })
87+
}
88+
89+
/**
90+
* Obtain an HTML version of the stacktrace, so it looks nice on the screen
91+
*/
92+
private fun displayStackTrace(throwable: Throwable?) {
93+
textView.text = Html.fromHtml(
94+
throwable
95+
?.toHtml()
96+
?.highlight(ExampleApplication.MY_CODE_PACKAGES) ?: "Something horrible happened!") //
1197
}
1298
}

example-android/app/src/main/res/layout/activity_main.xml

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,78 @@
66
android:layout_height="match_parent"
77
tools:context="com.akaita.java.rxjava2debug.exampleandroid.MainActivity">
88

9+
<ToggleButton
10+
android:id="@+id/toggleAssembly"
11+
android:layout_width="wrap_content"
12+
android:layout_height="wrap_content"
13+
android:layout_marginEnd="8dp"
14+
android:layout_marginStart="8dp"
15+
android:layout_marginTop="8dp"
16+
android:checked="true"
17+
android:textAllCaps="false"
18+
android:textOff="RxJava2Debug OFF"
19+
android:textOn="RxJava2Debug ON"
20+
app:layout_constraintEnd_toEndOf="parent"
21+
app:layout_constraintStart_toStartOf="parent"
22+
app:layout_constraintTop_toTopOf="parent" />
23+
24+
<Button
25+
android:id="@+id/handledException"
26+
android:layout_width="wrap_content"
27+
android:layout_height="wrap_content"
28+
android:layout_marginLeft="8dp"
29+
android:layout_marginStart="8dp"
30+
android:layout_marginTop="16dp"
31+
android:text="Handled exception"
32+
app:layout_constraintStart_toStartOf="parent"
33+
app:layout_constraintTop_toBottomOf="@+id/toggleAssembly" />
34+
35+
<Button
36+
android:id="@+id/unhandledException"
37+
android:layout_width="wrap_content"
38+
android:layout_height="wrap_content"
39+
android:layout_marginEnd="8dp"
40+
android:layout_marginRight="8dp"
41+
android:backgroundTint="@android:color/holo_red_light"
42+
android:text="Unhandled exception"
43+
app:layout_constraintBottom_toBottomOf="@+id/handledException"
44+
app:layout_constraintEnd_toEndOf="parent"
45+
app:layout_constraintTop_toTopOf="@+id/handledException" />
46+
947
<TextView
48+
android:id="@+id/stackTraceHeader"
1049
android:layout_width="wrap_content"
1150
android:layout_height="wrap_content"
12-
android:text="Hello World!"
51+
android:layout_marginTop="24dp"
52+
android:text="Stack Trace"
53+
android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
54+
app:layout_constraintEnd_toEndOf="parent"
55+
app:layout_constraintStart_toStartOf="parent"
56+
app:layout_constraintTop_toBottomOf="@+id/handledException" />
57+
58+
<ScrollView
59+
android:layout_width="0dp"
60+
android:layout_height="0dp"
61+
android:layout_marginBottom="8dp"
62+
android:layout_marginEnd="8dp"
63+
android:layout_marginStart="8dp"
64+
android:layout_marginTop="8dp"
1365
app:layout_constraintBottom_toBottomOf="parent"
14-
app:layout_constraintLeft_toLeftOf="parent"
15-
app:layout_constraintRight_toRightOf="parent"
16-
app:layout_constraintTop_toTopOf="parent" />
66+
app:layout_constraintEnd_toEndOf="parent"
67+
app:layout_constraintStart_toStartOf="parent"
68+
app:layout_constraintTop_toBottomOf="@+id/stackTraceHeader">
69+
70+
<HorizontalScrollView
71+
android:layout_width="wrap_content"
72+
android:layout_height="wrap_content">
73+
74+
<TextView
75+
android:id="@+id/textView"
76+
android:layout_width="wrap_content"
77+
android:layout_height="wrap_content"
78+
android:text="Generate a 'Handled exception' to see the Stack Trace" />
79+
80+
</HorizontalScrollView>
1781

82+
</ScrollView>
1883
</android.support.constraint.ConstraintLayout>

0 commit comments

Comments
 (0)