Go for Android
Go is the language from Google and Android is the mobile OS from Google. The bond between them are just born with. So there should be no surprise that we can use Go to write Android programs, though some important restricts for now:
- There is no Android SDK for Go, so no system API nor GUI for Go program on Android.
- Go doesn’t support JNI for now. So Go programs have to be compiled as separate executables, wrapped as assets in apk and called within Java based apps.
- Maybe cross-compiling is really hard. We cannot build Android applications which use Cgo(C bridge for Go) on our desktop computer for now, as shown below.
But it’s not just a toy because of the following advantages:
- Go is fast and all C programmers just like it.
- Go language is bundled with high quality libraries, especially suitable for special tasks.
- Go is from Google! It does have a future! Well, at least let’s hope so.
Go is a very young language, thus there’s few guides or tutorials out there. But it also makes earlier investion provide more return. My friend and colleague @rarnu also likes the idea of writing Android program using Go and has written a short tutorial. I’d like to rewrite it here and fill all lost details which he was too lazy to include.
Prerequisites
- C Tools
- GCC, C standard library, bison, GNU Make and Awk
- Mercurial
hg
- Android SDK
adb
Preparing Go Language
Firstly some environment variables need to be set (better be added to .bashrc
or .zshrc
etc.):
export GOROOT="$HOME/Code/Go/Home"
export GOBIN="$GOROOT/bin"
export PATH="$PATH:$GOBIN:"
GOROOT
should be the folder where Go source is in and GOBIN
should be in the system PATH
. Double check them and issue these commands in terminal:
hg clone -u release https://go.googlecode.com/hg/ $GOROOT
cd $GOROOT/src
export GOOS=linux
export GOARCH=arm
./all.bash
Now check out $GOBIN
folder, three commands go
godoc
gofmt
(go
command line tools) and a folder linux_arm
(cross-compiling tools for Linux/ARM targets) should be in there. Line 2-5 can be run multiple times by setting different $GOOS
and $GOARCH
combinations to support amd64
386
and arm
instruction sets all in one install base. See “Installing Go from source” for detail.
Hello Android from Go!
Now create Go source files for testing. Let’s start with the most favorite ‘Hello world!’ one.
package main
func main() {
println("Hello Android from Go!")
}
Compile it and generate executable:
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o hello-arm hello.go
The last command will generate an executable named hello-arm
which targets Linux/ARM system (i.e. Android). Now connect to an debuggable Android device and issue in the same folder:
adb push hello-arm /data/local/
adb shell
/data/local/hello-arm
If all things go right it will generate output as expected. Using the same routine showed above, all code examples on Go’s homepage can be verified working well on Android devices.
Real World Example
The following code example is borrowed from rarnu’s blog post and modified a bit.
package main
import ("net/http"; "flag"; "os"; "strconv"; "encoding/json"; "io/ioutil")
func main() {
flag.Parse()
if flag.NArg() != 1 {
os.Stdout.WriteString("\n")
return
}
version,_ := strconv.Atoi(flag.Arg(0))
res,_ := http.Get("http://rarnu.7thgen.info/api/query_software_update.php")
if res.StatusCode != 200 {
os.Stdout.WriteString("\n")
return
}
buf,_ := ioutil.ReadAll(res.Body)
var f interface{}
json.Unmarshal(buf, &f)
m := f.(map[string]interface{});
v,_ := m["lastver"];
sver,_ := strconv.Atoi(v.(string))
if sver > version {
fn,_ := m["url"]
os.Stdout.WriteString(fn.(string) + "\n")
return
}
os.Stdout.WriteString("\n")
}
Handling JSON data returned from web services in Go is pretty neat and clean, right? Build it as above:
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o cu-arm checkupdate.go
adb push cu-arm /data/local/
adb shell
/data/local/cu-arm 20
When running it on my Galaxy Nexus (ARM v7 dual-core 1.2 GHz Cortex-A9, Android 4.0.2 with kernel 3.0.8) it crashed on line 14 - the http.Get
call failed. Because of some issues in the cross-compiling system, all calls using Cgo will cause failure like that, at least for now. Unfortunately the net
package depend on Cgo and nearly all useful applications need net
package.
Call Go Code in Android Apps
Exactly the same as calling any native executables in Android apps, which fully explained in this guide. I’ll list a more general and accurate solution below for convenience.
The following Java class shows how to invoke a process in Android apps (actually it is used by Google itself), which is the base of the following guide:
public class CommandResult {
public String result = "";
public String error = "";
public static runCommand(String command, boolean root) {
Log.e("runCommand", command);
Process process = null;
DataOutputStream os = null;
DataInputStream stdout = null;
DataInputStream stderr = null;
CommandResult ret = new CommandResult();
try {
StringBuffer output = new StringBuffer();
StringBuffer error = new StringBuffer();
if (root) {
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(command + "\n");
os.writeBytes("exit\n");
os.flush();
} else {
process = Runtime.getRuntime().exec(command);
}
stdout = new DataInputStream(process.getInputStream());
String line;
while ((line = stdout.readLine()) != null) {
output.append(line).append('\n');
}
stderr = new DataInputStream(process.getErrorStream());
while ((line = stderr.readLine()) != null) {
error.append(line).append('\n');
}
process.waitFor();
ret.result = output.toString().trim();
ret.error = error.toString().trim();
} catch (Exception e) {
ret.result = "";
ret.error = e.getMessage();
} finally {
try {
if (os != null) {
os.close();
}
if (stdout != null) {
stdout.close();
}
if (stderr != null) {
stderr.close();
}
process.destroy();
} catch (Exception e) {
ret.result = "";
ret.error = e.getMessage();
}
}
return ret;
}
}
To run a native executable (whether written in Go or not) in Android apps:
- Include the binary
go-exec
in theassets
folder. - Use
getAssets().open("go-exec")
to get anInputStream
. - Write it to
/data/data/app-package-name/
, where the app has access to write files and make it executable. - Make it executable using the code above, i.e.
CommandResult.runCommand("/system/bin/chmod 744 /data/data/app-package-name/go-exec", 0)
- Run
/data/data/app-package-name/go-exec
using the code above.
Updates
Go 1
is now live! Article updated to match changes in Go 1
. And the Go 1
release note is a must read.