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

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.

hello.go
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.

checkupdate.go
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:

CommandResult.java
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 the assets folder.
  • Use getAssets().open("go-exec") to get an InputStream.
  • 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.

Neo Lee's Picture

About Neo Lee

An Internet architect, product designer, entrepreneur, lifetime game and music lover.

Shanghai, China http://soulhacker.me