用Go语言写Android应用 (2) - 从Android的Java调用Go代码
来源:互联网 发布:足彩基本面分析软件 编辑:程序博客网 时间:2024/06/10 00:43
用Go语言写Android应用 (2) - 从Android的Java调用Go代码
上一篇我们讲到,Go在Android中的作用,就相当于NDK中的C/C++。上节我们学习了参照NDK的方式用纯Go语言来写应用。
但是,也正如在Android中,C/C++主要是通过JNI的方式被Java代码调用,本节我们就学习如何使用Java代码来调用Go代码。
Java调Go的JNI例子
Java部分
我们首先来看这个简单得不能再简单的Java部分的代码,只有一个TextView,然后调用Go写的Hello.Greetings函数返回一个字符串,显示在这个TextView上。
package org.golang.example.bind;import android.app.Activity;import android.os.Bundle;import android.widget.TextView;import go.hello.Hello;public class MainActivity extends Activity { private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.mytextview); // Call Go function. String greetings = Hello.Greetings("Android and Gopher"); mTextView.setText(greetings); }}
布局也没什么好说的,就只有一个TextView:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/mytextview" /></RelativeLayout>
Go代码
我们再看看Go代码,也是简单到不能再简单了,把收到的字符串前面加上”Hello, “,然后再返回给Java就完事了。
package helloimport "fmt"func Greetings(name string) string { return fmt.Sprintf("Hello, %s!", name)}
编译和运行
于是,见证奇迹的时刻来了,我们把这个bind的例子编译运行一下吧。
按照Android的惯例,编译是通过gradle脚本来完成的,我们首先要在build.gradle中配一下Go的环境。
修改GOROOT下面的src/golang.org/x/mobile/example/bind/android/hello/build.gradle,配置GOPATH,GO和GOMOBILE的路径:
plugins { id "org.golang.mobile.bind" version "0.2.6"}gobind { /* The Go package path; must be under one of the GOPATH elements or a relative to the current directory (e.g. ../../hello) */ pkg = "golang.org/x/mobile/example/bind/hello" /* GOPATH where the Go package is; check `go env` */ GOPATH = "D:/go/" /* Absolute path to the go binary */ GO = "d:/go/bin/" /* Optionally, set the absolute path to the gomobile binary if the /* gomobile binary is not located in the GOPATH's bin directory. */ GOMOBILE = "d:/go/bin/gomobile"}
下面我们就可能通过gradle来编译这两家伙,以Windows下为例:
执行下面的命令:
gradlew assembleDebug
这时需要科学上网去下载对应版本的gradle,还需要在环境变量里配置好javac的路径。
然后我们就可以看到欢快的build的日志了:
:app:preBuild UP-TO-DATE:app:preDebugBuild UP-TO-DATE:app:checkDebugManifest:app:preReleaseBuild UP-TO-DATE:hello:gobind:app:prepareAndroidHelloUnspecifiedLibrary:app:prepareComAndroidSupportAppcompatV72211Library UP-TO-DATE:app:prepareComAndroidSupportSupportV42211Library UP-TO-DATE:app:prepareDebugDependencies:app:compileDebugAidl UP-TO-DATE:app:compileDebugRenderscript UP-TO-DATE:app:generateDebugBuildConfig UP-TO-DATE:app:mergeDebugShaders UP-TO-DATE:app:compileDebugShaders UP-TO-DATE:app:generateDebugAssets UP-TO-DATE:app:mergeDebugAssets UP-TO-DATE:app:generateDebugResValues UP-TO-DATE:app:generateDebugResources UP-TO-DATE:app:mergeDebugResources UP-TO-DATE:app:processDebugManifest UP-TO-DATE:app:processDebugResources UP-TO-DATE:app:generateDebugSources UP-TO-DATE:app:incrementalDebugJavaCompilationSafeguard UP-TO-DATE:app:compileDebugJavaWithJavac UP-TO-DATE:app:compileDebugNdk UP-TO-DATE:app:compileDebugSources UP-TO-DATE:app:prePackageMarkerForDebug:app:transformClassesWithDexForDebug UP-TO-DATE:app:mergeDebugJniLibFolders UP-TO-DATE:app:transformNative_libsWithMergeJniLibsForDebug:app:processDebugJavaRes UP-TO-DATE:app:transformResourcesWithMergeJavaResForDebug UP-TO-DATE:app:validateDebugSigning:app:packageDebug:app:zipalignDebug:app:assembleDebugBUILD SUCCESSFULTotal time: 56.107 secs
Build成功之后,apk就生成在GOROOT\src\golang.org\x\mobile\example\bind\android\app\build\outputs\apk下面。
adb install安装下就可以看到我们的成果啦~
当然,如果喜欢用IDE的,可以将工程导入到Android Studio里面,hello下面的build.gradle按上面说的改好,通过Android Studio来生成apk也是一样的。
原理
上面我们很欢快地完成了一个例子,但是背后的事情都是gradle插件和脚本帮我们做了。比JNI还要舒服,因为JNI总还有个Javah工具要生成个头文件,还要写Android.mk的。
我们打开生成的apk或者aar文件就可以看到,GoBind帮我们将我们的代码和相关的包装代码编译进了一个叫libgojni.so的库里面。
下面我们揭开这个工程的神秘面纱,看看我们如何也能够实现从Go到Java的映射,我们有一个神奇的工具gomobile bind.
比如说我们写一个最简单的计数器类吧:
package testCountertype Counter struct { Value int}func (c *Counter) Inc() { c.Value++ }func New() *Counter { return &Counter{100} }
然后我们执行gomobile bind命令:
gomobile bind -target=android testCounter
结果,gomobile命令给我们生成了一个testCounter.aar。
这是一个jar包一样的压缩格式,我们把它解开看看。发现,有classes.jar,还有jni,下面是各种指令集下的so库。
我们反编译一下classes.jar,看看我们上面几行的Go代码变成了什么样子。
先看干货吧,我们的Inc()方法,Value的set和get方法都被声明成了JNI的代码。
public final native long getValue(); public final native void setValue(long paramLong); public native void Inc();
下面是完整版的:
import go.Seq;import go.Seq.Proxy;import go.Seq.Ref;import java.util.Arrays;public abstract class TestCounter{ public static void touch() { } private static native void init(); public static native Counter New(); static { Seq.touch(); init(); } public static final class Counter extends Seq.Proxy { private Counter(Seq.Ref paramRef) { super(); } public final native long getValue(); public final native void setValue(long paramLong); public native void Inc(); public boolean equals(Object paramObject) { if ((paramObject == null) || (!(paramObject instanceof Counter))) { return false; } Counter localCounter = (Counter)paramObject; long l1 = getValue(); long l2 = localCounter.getValue(); if (l1 != l2) { return false; } return true; } public int hashCode() { return Arrays.hashCode(new Object[] { Long.valueOf(getValue()) }); } public String toString() { StringBuilder localStringBuilder = new StringBuilder(); localStringBuilder.append("Counter").append("{"); localStringBuilder.append("Value:").append(getValue()).append(","); return "}"; } }}
所以,gomobile bind的秘密很简单,利用Java与C语言的JNI接口,将Go也封装成C的接口,然后完成两者的对接。
为什么不只是接口转化下这么简单,还要写这么复杂的类呢?答案在于GC。不管是Java还是Go都是有GC的,两种带GC的语言碰到一起,是需要小心行事的。具体的细节可以看下这篇文章:https://docs.google.com/document/d/1y9hStonl9wpj-5VM-xWrSTuEJFUAxGOXOhxvAs7GZHE/edit?pref=2&pli=1
当然,这其中还有很多细节值得讲一讲,我们下节就从Go与C语言的接口:cgo开始讲起吧。
- 用Go语言写Android应用 (2) - 从Android的Java调用Go代码
- 用Go语言写Android应用 (1) - 用Go写本地应用
- 用Go语言写Android应用 (1) - 用Go写本地应用
- GO语言学习:go语言实现android应用的步骤
- 用Go写Android程序(3) - Go语言速成
- GO语言如何调用C写的函数
- go语言的代码编写,与命令调用
- Go语言中defer的调用顺序简单测试代码
- Go语言开发Android程序
- 第一行代码,Android Go
- Android 原生应用开发得到Go语言支持
- GO 语言写的加密算法(附 java 版翻译)
- Android Go
- go语言写日志
- Go语言写基数排序
- [Go] --- LiteIDE配置Go语言的代码智能补全
- [Go] --- LiteIDE配置Go语言的代码智能补全
- python调用Go代码
- 15文本编辑器
- js作用域及作用域链概念理解及使用
- 20个常用的Java 功能代码
- android -- 蓝牙 bluetooth (一) 入门
- Ubuntu 12.04下安装MySQL
- 用Go语言写Android应用 (2) - 从Android的Java调用Go代码
- Web容器之Listener初始化顺序
- Flask-db.relationship()参数注释
- TextView加载带有html标签的字符串
- Gt9xx系列------irq的部分
- git on server(ssh)深入剖析,github提供服务原理
- navcat导出数据两种方式
- RxJava 学习笔记(七) --- Filtering 过滤操作
- 怎么实现CorelDRAW中艺术笔工具的快速运用