Android App development under FreeBSD
I started using FreeBSD around May 2020. Don't know why, maybe just got a bit tired with Linux, and needed something new (I've been using Linux for ~15 years, at 10th year, I made a video about my Linux daily usage).
So far I'm quite happy about FreeBSD experience. At one point, it was painful and I asked myself why are you doing this, man? I was about to give up, but then I take a deep breath and tried again, since deep down my heart I kind of know why I'm painful: it's because I mixed the FreeBSD Quarterly version and the Latest version. The second time, I sticked with Quarterly.
All in all, FreeBSD is very stable! I loved it when I found the laptop (ThinkPad T440p) can sleep and wakeup reliably – on the other hand, X Window on Linux often fail to wake up, forcing me to restart the desktop session and losing all the open windows.
1 Android App development under FreeBSD
Android App development under FreeBSD is very painful, but not impossible. After a lot of Googling, I have come through a relatively smooth method.
TL;DR:
- Install Linux Binary Compatibility
Install Linux version of Android Studio, make it run with LBC
Need decompile and recompile a certain Android jar file.
- Downgrade adb on FreeBSD
- Updated lsp-intellij and lsp-intellij-server, so that I can use Emacs for Android App development
- A workaround for using both Emacs (lsp-intellij) and AndroidStudio for App development
1.1 Install Linux Binary Compatibility
You'd better have read the LBC document, and have a basic understanding about how it works (see below).
1.2 Hacking android jars and android studio
- Find a simple Android App source code (for e.g., copy from a Linux system).
- Get the android sdk downloaded through gradle (by running
./gradlew aR
), then it fails, complaining about unknown platform: FreeBSD - patch the sdk common.jar (
find ~/.gradle/ -name 'common*.jar'|grep android
) according to http://zewaren.net/android-1.html, but with jd-gui:- open the jar file with jd-gui
- save the SdkConstants.java (package:
com.android
) to ~/ - javac ~/SdkConstants.java -cp $(find ~/.gradle/ -name 'common*.jar'|grep android)
- mkdir com/android -p;
- mv ~/SdkConstants.class ./com/android/;
- jar uf $(find ~/.gradle/ -name 'common*.jar'|grep android) com/android/SdkConstants.class
Work around an mkdir error in aapt2:
sudo ln -s /home/ /compat/linux/home
This is found out through running aapt2 with truss: aapt2 will
mkdir("/home")
and fail, so, I just desparately made a symlink for it./compat/linux
has a filesystem union for linux binaries, but, sometimes the union seems to be broken, for e.g., if you run this command:PATH=/compat/linux/bin:/compat/linux/usr/bin/:$PATH bash -c 'ls /dev'
You will see nothing except
/dev/shm
. But if you run:PATH=/compat/linux/bin:/compat/linux/usr/bin/:$PATH bash -c 'ls /dev/null'
Then, you can see that
/dev/null
is output. This is confusing for some programs: If youls /dev
,/dev/null
is not there; but if youls /dev/null
, it is there! The reason aapt2 try to mkdir("/home") is kind of like that too, I think.run
linux-env ./gradlew aR
again, and this time, it works.linux-env
is a simple script I wrote:#!/usr/bin/env bash
set -e
PATH=/compat/linux/bin:$PATH
"$@"Now, I can also run AndroidStudio, can generate an Android App Project.
- export JAVA_HOME=/usr/local/openjdk8/
Gradle sync will fail, complaining:
> Process 'command '/home/bhj/android-studio/jre/bin/java'' finished with non-zero exit value 1
To fix this, inside AndroidStudio, set the jre that gradle uses to the same as /usr/local/openjdk8.
(Project Structure -> SDK Location -> JDK Location).
Then, gradle will fail again, complaining
Unknown platform 'FreeBSD'
. But we already know how to fix this: start AndroidStudio withlinux-env ~/android-studio/bin/studio.sh
.
1.3 Fix adb devices go missing
Here the problem is that my phone's adb connection will suddenly be gone when doing adb push or adb install (which involes pushing the apk file first).
But another tool that I'm using, won't make adb device disappear when installing apk. The tool's C++ adb client code is copied from adb version 1.0.39, and the current freebsd adb
version is 1.0.41, so I doubted that maybe it's because of the higher adb version is not compatible with my phone.
In other words:
- If I use the adb binary directly, that is, 1.0.41 adb client with 1.0.41 adb server (adb fork-server), adb push will disconnect my adb.
- If I use my tool's 1.0.39 adb client with 1.0.41 adb server, adb push won't disconnect my adb.
Seems the bug must be in the adb 1.0.41 client/server protocol, so I decided to downgrade my adb binary.
After some googling, I found that I can install older version of adb with portdowngrade, so I googled for freebsd's adb port revision for adb-1.0.39, and downgraded it. One problem when compiling: I must replace android_pubkey.c with current version to fix a build error:
sudo cp \
/usr/ports/devel/android-tools-adb/work/platform_system_core-platform-tools-29.0.4/libcrypto_utils/android_pubkey.c \
/usr/ports/adb-1.0.39/android-tools-adb/work/platform_system_core-android-8.1.0_r2/adb/../libcrypto_utils/android_pubkey.c
After that, my adb device connection is quite stable now.
1.4 Update lsp-intellij and lsp-intellij-server
I use Emacs for almost everything.
There is a project lsp-intellij that allow me to use Emacs together with AndroidStudio (which is based on Intellij), but it has long been unmaintained: it supported lsp-mode version 4.1, and can't be used in current version anymore.
I verified using old version of everything (thanks to github!), and saw that lsp-intellij does work in that version. That gave me confidence to make it work under current version of lsp-mode (6.2.1).
It's very dirty for now, but it does work (auto-completion, syntax error hint, etc). Maybe I will clean it up later.
For details check out the source code:
1.5 Work around for using both AndroidStudio and Emacs
Once I start lsp, the AndroidStudio window for the App project will be hidden – I can't open it again, unless I kill AndroidStudio and restart it, but then, I can't use lsp in Emacs unless I restart lsp, which will close the AndroidStudio window.
Lsp-intellij just formid you to edit a single file in parallel.
For now, I can only think of a workaround for this:
Copy the project files with a hardlink:
cp AppProject AppProject.shadow -avl
- Open AppProject.shadow in AndroidStudio
- When I need to import a class, for e.g., this is not supported by lsp-intellij, so I switch to the AndroidStudio AppProject.shadow, and do the import there, then I switch back to Emacs.
2 Further plan
- Clean up lsp-intellij and lsp-mode code changes, find a way that is standard with the lsp-mode people.
- Study whether I can add more lsp feature to lsp-intellij, such as managing imports, code refactory, minor fixes, and so on.