Android Studio Workarounds, and Command-Line Gradle Builds
I got a new phone. (Not something that happens often.)
Fun, right? Well, partly, but also something I'd been dreading. I had a feeling that my ancient RSS reader, FeedViewer, which I use daily to read all my news feeds, probably wouldn't work under a modern Android (I wrote it for KitKat and it was last updated under Marshmallow). And that was correct. Aside from Google's propensity to change the allowed storage locations with every new Android release, Android 10 and later no longer have a dedicated menu button. Lord knows why not, since they have a dedicated strip of real estate under all non-fullscreen apps for the Back and Home buttons; but they removed the Menu button from that strip. Without a menu button, FeedViewer would need to show its own three-line button (called the "hamburger") or three-dots button (I learned this is called a "kebab"). Which meant that I had to recompile FeedViewer.
(Brief aside: after much web searching, I have been unable to find any guidelines as to when to use the hamburger vs. when to use a kebab. I use a couple of apps that have both, and I can never predict which item will be in one menu vs. the other. I had long suspected that somewhere there must be an interface guideline document explaining the difference -- but if there is, I haven't been able to find it. For FeedViewer I ended up using a kebab menu, mostly because Android Studio had a template for it.)
New (well, to me) Build System
Recompiling: easier said than done.
The old FeedViewer was built using a relatively
simple command called ant. But I tried to rebuild it a year or so ago
and discovered that ant builds no longer work. Instead, command-line
builds are done via gradle. Just for fun, I tried
apt install gradle
: even with apt-recommends and
apt-suggests disabled, it wanted to install 110 packages -- even
though I already had the Java development kit installed.
I opted instead to download Android Studio from Google. It needed about 8 Gb, but I could put it all on an external disk and not clutter my main system disk.
Although I dislike IDEs (integrated development environments) in general, I started with Studio. Might as well try to do things right. It offered an option to import an old project, but the importer apparently can't handle ant projects, so instead, I started a new project from Studio's "Basic Activity" template, got a basic application building, and then started moving code piece by piece from the old project.
Invisible Window Blues
Things were going well until I shut down for the evening, then fired up Android Studio again the next morning. It brought up a tips window, which I read and dismissed, and then ... nothing. The program was still running, but there was no window. I found reports like can't restore Android Studio window after minimise and Activating minimized window doesn't bring it to the foreground that were clearly the same bug, but the only solution anyone had found involved changing something in the preferences dialog, which is hard to do when you don't have a window. (The preference wouldn't have helped anyway; I tried it later.)
Apparently Android Studio is based on something called "JetBrains Intellij IDEA", which has a bug that sometimes makes the program start with the main window iconified. Probably it doesn't happen in Windows or under Gnome, but it's a showstopper in a window manager like Openbox that doesn't show iconified windows.
Fortunately, once you know what's happening, the workaround isn't too bad: if you don't see a window, go to Openbox's Desktops menu and click the workspace you're on to see a list of all windows in that workspace. Iconified windows will show up in parentheses; click on the parenthesized entry to bring up the Android Studio window.
I have to go through this roughly ever other time I start Android Studio. If there's a way to get Intellij IDEA to draw its window instead of starting iconified, I haven't found it.
Design View Blues
Moving the old FeedViewer code went surprisingly smoothly. I only moved a little at a time. Nearly half of the old FeedViewer code was crufty hacks to work around the many bugs in the Android WebView component in KitKat and Marshmallow. I wanted to see if Android 10 and 11's WebView was any better -- and was pleasantly surprised to find that hardly any of those hacks were needed any more. The new code is much cleaner and smaller without all those workarounds.
Setting up the user interface wasn't quite so easy, even though FeedViewer's interface is extremely simple. I thought one advantage of the IDE was supposed to be that you can drag and drop components into a preview window to see what it's going to look like. Right?
With Android, not so much. The previews in Android Studio aren't even remotely the same sizes, colors or layouts that you see when you run the app in the emulator or on a real phone. And if you try to drag things around, the handles all scrunch together in crazy unintuitive ways. Eventually I gave up on the design panes of the IDE and just edited the XML source.
Release-signing Blues
I'd been building my app in debug mode and testing it in the emulator. But to install it on my phone, I wanted a signed release build.
Android Studio offers Build > Generate Signed Bundle/APK. I ran through it, creating a new keystore and key. It prompted me for a keyring password. Keyring is a Gnome thing that I don't have, so I canceled the dialog. But apparently Studio can't work without it; when I tried to build, it died every time with
Entry name 'res/color/material_on_surface_disabled.xml' collidedThere is no "material_on_surface_disabled.xml" file in my project.
I found lots of pages where people ask about that; apparently Studio gets easily confused about its cache. Sometimes deleting the build and release directories, followed by a Build > Rebuild Project, fixes it ... but not in my case.
I never found a way past this error. But I knew I eventually wanted to be able to build from the command line using gradle. This seemed like a good time to try.
Command-Line Gradle Builds
Gradle builds turned out to be super easy. Studio had
set up everything already. For debug builds,
gradlew assembleDebug
was all I needed.
Signed builds were only a little harder. Create a ~/.gradle/gradle.properties file containing:
RELEASE_STORE_FILE=/path/to/keystore RELEASE_STORE_PASSWORD=keystore-password RELEASE_KEY_ALIAS=key0 RELEASE_KEY_PASSWORD=key-password
Then run: gradlew assembleRelease
That's it: no hunting for invisible windows, no wrestling with cache collisions.
That means that I can develop without needing to run Studio at all.
Running the Emulator
I did still want to run the emulator, even when not using studio. That requires finding outwhich AVD you have installed:
$ emulator -list-avds Pixel_3a_API_30_x86
Then run the emulator using that AVD:
$ emulator @Pixel_3a_API_30_x86 &
ADB Tricks
You can install to the emulator using adb -- but I found I had to use the adb that came with the Android build tools, not the adb from Ubuntu that I'd been using to talk to my phone. I ended up making an alias to adjust my PATH when I'm going to be working on Android development, since there are so many different path components to add:
androidbuild () { ANDROID_BUILD_HOME=/ssd/Android export ANDROID_HOME=$ANDROID_BUILD_HOME/android-sdk-linux export ANDROID_SDK=$ANDROID_HOME export JAVA_HOME=$ANDROID_BUILD_HOME/android-studio/jre/ PATH=$ANDROID_BUILD_HOME/android-sdk-linux/platform-tools:$PATH PATH=$PATH:$ANDROID_SDK/emulator PATH=$PATH:$ANDROID_BUILD_HOME/android-studio/bin PATH=$PATH:$ANDROID_BUILD_HOME/gradle/gradle-6.7.1/bin PATH=$PATH:$ANDROID_BUILD_HOME/android-sdk-linux/tools PATH=$PATH:$ANDROID_BUILD_HOME/android-sdk-linux/tools/bin PATH=$PATH:$ANDROID_BUILD_HOME/android-studio/jre/bin export PATH }
Then you can install your debug builds to the emulator:
adb install -r app/build/outputs/apk/debug/app-debug.apk
You can also run the app from adb, but you need to know the activity name,
not just the app name.
adb shell dumpsys package your.package.name
will show you a bunch of options, and in my case the relevant activity was
com.shallowsky.feedviewer/.MainActivity so I can start the app
running with:
adb shell am start -n com.shallowsky.feedviewer/.MainActivity
Of course, you can also use adb logcat to debug, and all the other adb commands.
A nice thing about this setup is that Studio still works. It's not like the old Android setup, where you could have a source directory that worked with Eclipse, or one that worked with ant, but it was almost impossible to switch between them in the same source directory. In this case, Studio uses Gradle under the hood, making it easy to switch back and forth. If I want to look at a user interface preview (though why I would since it doesn't look anything like the actual app), or use Studio's autocomplete of function names or auto-include-finding magic, I can. If I just want to tweak something quick and dash off a new build with gradle, I have that option too.
[ 17:49 Dec 19, 2020 More programming | permalink to this entry | ]