bad internet
old phones
best practices
Jacques Smuts
Android Developer
Nomanini
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9883656/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918526/Android.png)
Section | Estimate |
---|---|
Nomanini/Environment | 2:00 |
Bad Internet | 12:00 |
Unexpected Intermission | 1:00 |
Old Phones | 10:00 |
Theme | 6:00 |
Questions | 8:00 |
environment
informal merchants in Africa
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9883667/africa_map.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9883671/pasted-from-clipboard.png)
Abidjan, Cote d'Ivoire
Dar Es Salaam, Tanzania
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885092/pasted-from-clipboard.png)
Tanzania, further out
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885095/pasted-from-clipboard.png)
Tanzania, further out
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885106/pasted-from-clipboard.png)
Example Phone
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/10019845/pasted-from-clipboard.png)
bad internet
Many Suggestions linked at the end
- increase timeout
OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
.build()
bad internet
- increase timeout
OkHttpClient.Builder()
.connectTimeout(serverConfig.timeout, TimeUnit.SECONDS)
.writeTimeout(serverConfig.timeout, TimeUnit.SECONDS)
.readTimeout(serverConfig.timeout, TimeUnit.SECONDS)
.build()
bad internet
- increase timeout
- aggressive retry policy?
bad internet
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
...
companion object {
/**
* How many redirects and auth challenges should we attempt?
*/
private const val MAX_FOLLOW_UPS = ?
}
}
OkHttp Default
- increase timeout
- aggressive retry policy?
bad internet
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
...
companion object {
/**
* How many redirects and auth challenges should we attempt?
*/
private const val MAX_FOLLOW_UPS = 20
}
}
OkHttp Default
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
IDEMPOTENT
Running the same operation N times
always gives the same output*
HTTP Method | Idempotent? |
---|---|
GET |
YES |
DELETE | YES |
PUT | YES |
POST | NO |
HTTP Method | Idempotent? |
---|---|
GET | YES |
DELETE | YES |
PUT | YES |
POST | YES |
- aggressive retry policy?
bad internet
api.sellAirtime(
userId = "Biko46",
amount = 300,
productId = "MTN123",
)
- aggressive retry policy!
bad internet
api.sellAirtime(
userId = "Biko46",
amount = 300,
productId = "MTN123",
sourceReference =
generateSourceReference(
"Biko46",
300,
"MTN123"
)
)
- aggressive retry policy!
bad internet
fun generateSourceReference(
userId: String,
amount: Int,
productId: String
) {
val time = System.currentTimeMillis()
return HashUtil.sha1(
"$userId$amount$productId$time"
)
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889540/cell_tower.png)
- increase timeout
- aggressive retry policy!
bad internet
- asynchronous architecture
(offline first)
bad internet
- asynchronous architecture
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889166/cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9885828/Nomanini_logo_mark_128__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9889143/cellphone.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918125/database.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918152/circle.png)
Repository
Database
Cloud/Server
UI / Viewmodel
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918526/Android.png)
WorkManager,
PushService,
other
Practice without theory is blind.
Theory without practice is sterile.
- Karl Marx
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918241/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918263/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918276/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918287/pasted-from-clipboard.png)
You need to actually use your app in bad circumstances.
Old Phones
minSdk=15
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918345/pasted-from-clipboard.png)
minSdk=15
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918453/minsdk15.png)
minSdk=15
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9918453/minsdk15.png)
minSdk=15
- Various Libraries Require minSdk19/21
- Compose
- OkHttp 4
- Coil
- Firebase 20+
- Many More
Development Implications
minSdk=15
SECURITY CONCERNS
minSdk=15
- Slow CPU
- Low Memory
- Low Storage Space
- Small Screens
- Unresponsive screen input
- Unexpected bugs
Phone Implications
Slow CPU
minSdk=15
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9971447/pasted-from-clipboard.png)
Low Memory
minSdk=15
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9971473/pasted-from-clipboard.png)
And LeakCanary!
Small Screens
minSdk=15
Design well, please
Unresponsive Touchscreens
minSdk=15
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/9971626/pasted-from-clipboard.png)
Chonky Design
Unexpected Bugs
minSdk=15
Slow rollout
Track Crashes
What works for me
- Kotlin
- Coroutines + Flow + Turbine
- Dagger
- SqlDelight
- OkHttp (+ Retrofit + Moshi)
- Repository Pattern
- MVVM/MVI (or similar)
- Flowbinding
Practice without theory is blind.
Theory without practice is sterile.
- Karl Marx
Follow Best Practices,
but harsher
How do I prepare my code
for harsh environments?
How to find best practices?
developer.android.com recommendations
Following open source contributors on social media
Open Source Projects
Coding personal projects
Questions
Further Reading & Tips@
JacquesSmuts.com
![](https://s3.amazonaws.com/media-p.slid.es/uploads/2233486/images/10019880/pasted-from-clipboard.png)