Capacitor is the spiritual successor to Cordova and Phonegap. Unfortunately they took this literally and left out actual instructions on how to compile the app to a native android app, because who needs those… At time of writing, Vue recommends either Capacitor or NativeScript, but NativeScript requires prior knowledge about native apps I do not have. If you can work with that, their installation guide is a lot clearer than Capacitor’s is, and possibly a better fit for you.
Credit to the following resources for providing snippets to get this guide working: Install Android SDK CLI Ubuntu 20.04 WSL2 (Work in Progress) (by steveclarke), the documentation of NativeScript and a guide to building a react native app in WSL2 by bergmannjg.
Table of Contents
- Prerequisites and goals
- Creating a new Vue app and installing Capacitor
- Installing the Android SDK
- Other dependencies
- Running a dev build
- Building for production
Prerequisites and goals
At the end of this blog you will be able to:
- Set up a Vue 3 app with Capacitor
- Be able to run a test build on a real device from WSL2
- Be able to build a version of your app that can be deployed to the Play Store
Notable things that I will not be touching upon are how to make this work with an emulator, and how to debug the app on a real device.
I expect you have already installed node (I have 16.13.0) and npm (I have 8.1.0). If not, go do that using your favourite method. I also assume you use bash (you can check with
ps -p $$). If you do not use bash, there may be slight differences in syntax. I also expect you to have an android device you can connect to via usb, and where you already have enabled the USB debugger.
Creating a new Vue app and installing Capacitor
Added here for completeness sake. Chances are you already pulled this from some other guide.
vue create your-app cd your-app npm install @capacitor/core @capacitor/cli npm install @capacitor/android npx cap init "My beautiful app" com.something.my_beautiful_app --web-dir=dist npx cap add android
Installing the Android SDK
After these initial steps the Capacitor documentation stops. You can run an debug build via the following command:
npx cap run android
This will likely yield the error
[error] native-run failed with error ERR_SDK_NOT_FOUND: No valid Android SDK root found. More details for this error may be available online: https://github.com/ionic-team/native-run/wiki/Android-Errors
Command line tools
We will tackle this problem one step at a time. First we will install the Android SDK command line tools. Obtain the most up-to-date download link for the command line tools only from the official android studio download page. Alternatively just install the version I used at time of writing (November 2021).
# Install unzip if it is not already installed sudo apt install unzip # We will install this in the home directory cd $HOME mkdir -p Android/cmdline-tools wget https://dl.google.com/android/repository/commandlinetools-linux-7302050_latest.zip unzip commandlinetools-linux-7302050_latest.zip -d Android/cmdline-tools # This location is important, because otherwise you will get an error that the SDK root could not be determined mv Android/cmdline-tools/cmdline-tools Android/cmdline-tools/latest # And clean up after ourselves rm commandlinetools-linux-7302050_latest.zip
While we have the command line tools, other tools like Capacitor relying on it do not know where to find it. For this we need to set the
ANDROID_HOME environment variable. We also need to update our
PATH environment variable so the command line knows where to find the sdkmanager for example.
# These changes last until the end of the current bash session. We will add a more permanent solution later export ANDROID_HOME=$HOME/Android export PATH="$ANDROID_HOME/cmdline-tools/latest:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH"
Installing some build tools and platforms
If you would to run the emulator now (
npx cap run android), you’ll notice it at least doesn’t complain that it can’t find an Android SDK root. It still doesn’t work though. To fix this we will run the sdkmanager with some commands. If the sdkmanager complains with an error like
Error: Could not determine SDK root. make sure that it is in the correct location. In my version it needs to be in a folder
cmdline-tools/latest/bin to automatically detect where the root is.
# Make sure we have the latest information sdkmanager --update # We are installing version 31. To figure out what options *you* have, see below # You'll have to accept licenses by pressing y. Ignore the clause where you sell your soul. That's normal. sdkmanager "build-tools;31.0.0" "platform-tools" "platforms;android-31" "tools"
If you want to find out what other platforms you can use, you can list the options with
sdkmanager --list | grep tools
Last but not least you need to accept any remaining licenses that did not pop up during the initial install.
Making our changes permanent
If all of the above went as expected, we can make our changes to our environment variables more permanent. Otherwise figure out what you needed to change, and persist those changes instead.
~/.bashrc file with an editor of your choice, then add the following lines to the bottom.
export ANDROID_HOME=$HOME/Android # Note: I am exporting these from back to front from our previous command export PATH=$ANDROID_HOME/tools/bin:$PATH export PATH=$ANDROID_HOME/tools:$PATH export PATH=$ANDROID_HOME/emulator:$PATH export PATH=$ANDROID_HOME/platform-tools:$PATH export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$PATH export PATH=$ANDROID_HOME/cmdline-tools/latest:$PATH
You will need to restart your shell for these changes to take effect. Go do that now. Exit out of the shell (ctrl+d) and start it again. Then test if your path and android home variable are set by echo-ing them in the shell.
# should show a line with something like /home/youruser/Android echo $ANDROID_HOME # should show a loooong line, with our Android paths at the start echo $PATH
Last but not least we need to make sure that gradle is installed. Gradle is used to build our source code into a nice apk that can be loaded on a device.
At time of writing the version of gradle in the Ubuntu repository is woefully out of date, so remove it if it is installed.
sudo apt remove gradle
Then install it from a different repository
sudo add-apt-repository ppa:cwchien/gradle sudo apt-get update sudo apt install gradle # And check if it is higher than v4 gradle -v
You’ll eventually also need the java jdk. You can check if one is already installed for you.
sudo apt list --installed | grep jdk
The easiest way to install the latest working version for you is probably installing default-jdk-headless, which installed openjdk-11-jdk-headless for me.
sudo apt install default-jdk-headless
To eventually be able to create a signed apk you can put in the Google Play Store you will need to have zipalign installed.
sudo apt install zipalign
Similar to zipalign, we will need the apksigner to… sign… the apk… that we want to put in the Google Play Store.
sudo apt install apksigner
Running a dev build
With the android build tools installed, everything will work fine… right? Right?
Unfortunately when running
npx cap run android, we quickly figure out that the emulator is not working. We get the following error.
[error] native-run failed with error ERR_UNSUITABLE_API_INSTALLATION: No suitable API installation found. Use --sdk-info to reveal missing packages and other issues. More details for this error may be available online: https://github.com/ionic-team/native-run/wiki/Android-Errors
The proposed switch doesn’t reveal anything, and the –verbose flag the link suggests doesn’t do anything either. I have not been able to figure out how to get an emulator working on WSL2. You may be able to accomplish this by running adb on Windows instead by following the instructions on bergmannjg’s gist or an article by Adrien Pellegrini showing how to use the Android emulator in Windows 10 with WSL2. What I did figure out is how to make the command deploy a test build to a connected device. Unfortunately this too takes some work.
Make usb devices visible to WSL
The first road block to overcome is to make usb devices visible to WSL2. If you run
lsusb in WSL, you will notice that there are no usb devices listed.
To solve this problem we need to create a bridge service that sends usb data from Windows to WSL2. It’s probably best to follow the instructions from the article that introduces it directly: Connecting USB devices to WSL by Ben McMorran. In short: install a service called usbipd-win on Windows, two libraries called linux-tools-5.4.0-77-generic and hwdata on WSL2 and finally add /usr/lib/linux-tools/5.4.0-77-generic to secure_path in the /etc/sudoers file. Finally restart the computer so the service starts.
Connecting a phone to WSL2
This section largely follows what cjshearer commented on a github issue about adb and uses some information in the article linked in the previous paragraph. If anything is unclear, you may want to consult these posts.
First, open Powershell on Windows as administrator. Then open a WSL2 command line window. This makes sure that Ubuntu is actually running. Next plug in your Android test device, and run the following command in Powershell.
usbipd wsl list
In my case there is a whole list of usb devices, but the one I want is marked as
1-6 SAMSUNG Android ADB Interface, SAMSUNG Mobile USB Modem, ... Not attached
1-6 is the bus id of my phone, so I will bridge it to WSL2 with the following command in Powershell
usbipd wsl attach --busid 1-6
Enter your WSL2 sudo password.
You should now see your device in WSL2 if you execute the
lsusb command. Remember that you need to enable this bridge whenever you remove the usb cable from the device and put it back in. It will not remember to bridge the usb (and this is probably for the best).
Giving adb permissions to the phone
When you now do the
adb devices command, you’ll notice that it shows you a device number and something like
no permissions (missing udev rules? user is in the plugdev group); see [http://developer.android.com/tools/device.html]
So… we have to set up the udev rules for this device. Izzy does a very good job in their answer android.stackexchange.com explaining what to do, so I recommend just following that answer. In short, make note of the device id in the output of
lsusb, craft a line with it in a specific rule file, then restart udev and reload the udevadm service, and replug the device. The group you select for this device should be one you are in. Adb suggests
plugdev as te group name. You can check your own groups with the
sudo service udev restart sudo udevadm control --reload
You may need to do this after every computer restart.
You can now deploy your app for testing purposes to the connected device. Make sure that you authorize the computer on the phone.
adb devices should show you the device id followed by “device”. If it shows unauthorized, you still need to authorize it on the phone. If it shows no permissions you may need to restart the services listed above, and you may need to replug your phone.
Building for production
Eventually you probably want to publish your app in the Google Play Store. To do this you will need to generate a signed apk. Since we don’t have a GUI in WSL2, we will have to do this from the command line as well. Use the official documentation on building your app from the command line as a guideline.
Preparing a key to sign
To sign an apk with a key, we first need… a key. This key needs to be constant between releases, so generate it once and make sure to save it to your repository. First, navigate to the android subfolder of your project.
Then generate a key. We use the alias “release”, since aliases can be enumerated anyway with the keystore’s password so there is no value in making it obscure. Store the password of the keystore securely somewhere outside the repository.
keytool -genkey -v -keystore release.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release
Assemble and sign your build
We first want to assemble an apk that is ready for release, but still needs to be signed. To do this, we just run gradle.
cd /path/to/your/project/android gradle assembleRelease
Your apk will be generated somewhere deep in the
app/build folder. You can quickly get the path with
find . -type f -name '*.apk' or hope it is in
./app/build/outputs/apk/release/app-release-unsigned.apk like it is for me.
If you run into problems with an out-of-date version of gradle, scroll up to Other dependencies > Gradle.
Next we zipalign the entire thing:
zipalign -v -p 4 ./app/build/outputs/apk/release/app-release-unsigned.apk ./app/build/outputs/apk/release/app-release-unsigned-aligned.apk
Finally sign the apk. Use the password that you securely stored somewhere several paragraphs ago.
apksigner sign --ks release.jks --out ./app/build/outputs/apk/release/app-release-signed.apk ./app/build/outputs/apk/release/app-release-unsigned-aligned.apk
And finally verify that everything went well.
apksigner verify ./app/build/outputs/apk/release/app-release-signed.apk
Finally copy that file somewhere where you will be able to access it to put it in the Google Play Store.