用Go语言写Android应用 (2) - 从Android的Java调用Go代码

简介: 激动人心的时刻到了,从这一刻开始,我们可以通过Android的Java来调用go写的代码了!

用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 hello

import "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:assembleDebug

BUILD SUCCESSFUL

Total time: 56.107 secs

Build成功之后,apk就生成在GOROOTsrcgolang.orgxmobileexamplebindandroidappbuildoutputsapk下面。
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 testCounter

type 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开始讲起吧。

目录
相关文章
|
29天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
77 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
2天前
|
监控 编译器 Go
1 行命令引发的Go应用崩溃
这篇文章分析了Go编译时插桩工具导致go build -race竞态检测产生崩溃的原因。
|
2月前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
161 1
|
2月前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
2月前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
2月前
|
SQL 监控 算法
为Go应用无侵入地添加任意代码
这篇文章旨在提供技术深度和实践指南,帮助开发者理解并应用这项创新技术来提高Golang应用的监控与服务治理能力。在接下来的部分,我们将通过一些实际案例,进一步展示如何在不同场景中应用这项技术,提供更多实践启示。
|
3天前
|
缓存 前端开发 Android开发
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
|
7天前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
1月前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
70 19
|
1月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
74 14

热门文章

最新文章

  • 1
    app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
  • 2
    【03】优雅草央千澈详解关于APP签名以及分发-上架完整流程-第三篇安卓APP上架华为商店后面的步骤-华为应用商店相对比较麻烦一些-华为商店安卓上架
  • 3
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 4
    【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 5
    Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
  • 6
    java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
  • 7
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 8
    Java快速入门之数组、方法
  • 9
    【Bug合集】——Java大小写引起传参失败,获取值为null的解决方案
  • 10
    FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel