I Hate you Android
08 Feb 2015Problem: you want to download a large file from an application.
For WikipOff, I want to let users download large (a few Gbs) files from the application. Thoses files are going to be hosted on any kind of website, mostly on my own server, through https:// links.
I’m a lazy developer, which means I want to write as less code as possible. So I ask Google how to download large files the Android-way.
Turns out I have a few options, from tons of code to write, to almost none :
- Implement a
Service
- Code an
AsyncTask
- Use the
DownloadManager
service
DownloadManager Service
Cool, Android comes with a download manager, let’s try to use that.
Fail number one
I want my app to support 2.3.x platforms and up. Unfortunately the DownloadManager class won’t let you download https:// links.
### Fail number two
I have no interest in paying for certificates that are trusted by Google, my certificates are self-signed. The problem is DownloadManager
will only check against the device’s global certificate store.
A bug calling against this has been open since 2009, and Google’s only answer is to add your own certs in the system store.
Unfortunately adding your own certificates in Android is a pain in the ass.
To add a certificate in your device’s store, you must either push the certificate in DER format through USB, or use an app and enter manually the URL to an SSL service that presents the cert.
Once that’s done, Android KitKat will keep threatening you with crazy messages saying that all your communications may be monitored.
### Okay, let’s code instead
FUCK IT I’ll do my own download manager
AsyncTask
AsyncTask is a weird kind of class you can implement to have work done in the background of your app, and get updates while it’s being done.
Why it’s weird:
- Your class has to extend
AsyncTask<T, TT, TTT>
. That’s three different generic types where you can put any kind of stupid shit you want. - The methods you interact with take varargs, so if you’re concerned with doing one job you have this ugly code everywhere :
protected Long doInBackground(T... stuff) {
T myArg = stuff[0];
doStuff(myArf);
}
protected void onProgressUpdate(TT... progress) {
setProgressPercent(progress[0]);
}
- You can’t move this weird class in its own file. It has to stick inside the calling code, because it has no callback functionality.
setProgressPercent
method, which may update a ProgressBar, has to be around.
That’s ugly shit, let’s see about Services
Service
This class, and its simple implentation IntentService
looks very Android-y. You implement a Service
, register it, and communicate with it with Intents
. It handles queing new tasks and the code is clean.
Unfortunately, the service will stop when your app gets paused and you have no way to cleanly stop a task once it’s started. It will also get killed if your app gets Paused, for example when the screen locks…
A way to prevent this is to code a whole new Service
from scratch, and make it run in the foreground, using notifications…. Does that looks familiar? Yes ! that’s exactly what DownloadManager
is. Do I want to recode all that from scratch ?
Back to HTTP
OKAY GOOGLE
THANK YOU FOR MAKING SIMPLE STUFF TOO SIMPLE TO BE USEFUL
This is my third branch, I’ve refactored all the download managing code three times. I’m tired of trying to circumvent Android deficiencies. I’m defeated (again), I’ll play with your stupid rules.