用Go语言写Android应用 (1) - 用Go写本地应用

简介: 如何用Go语言写跨平台的用于手机上的应用,我们以Android为例分析本地应用的写法。

用Go语言写Android应用 (1) - 从看个小例子开始

下载安装gomobile工具

首先安装1.5以上版本的Go语言环境,这个大家都应该有了。
然后需要通过科学上网方法去下载gomobile命令:

go get golang.org/x/mobile/cmd/gomobile

接着需要通过gomobile init去下载Android NDK的部分toolchain,执行下面的命令:

gomobile init

可以去android网站上下载一份最新的NDK以备用,将来遇上缺什么文件的话,就从NDK里面复制就是了。
下载地址在:https://developer.android.com/ndk/downloads/index.html

一切就绪了之后,先运行个例子试一试吧:

gomobile build -target=android golang.org/x/mobile/example/basic

成功之后,就生成了basic.apk。
可以通过gomobile install命令安装这个apk:

gomobile install golang.org/x/mobile/example/basic

当然啦,apk都生成了,直接用adb install就是了。
basic例子的功能很简单,红底上画一个绿色渐变的三角形,随着手指的点击事件,三角形的直角顶点的位置跟着一起走。

basic例子分析

纯用Go写的Android代码,与Android NDK用C++写的代码异曲同工,都是用OpenGL/ES的命令直接作画的方式。
我们来看下这个不长的例子,涉及到Go语言相关或者是OpenGL相关的不理解的不要紧,后面我们都会介绍,我们先过几个例子找找感觉:

引用包

第一步是引用了一堆要用的包,跟app相关,event相关,openGL相关。

package main

import (
    "encoding/binary"
    "log"

    "golang.org/x/mobile/app"
    "golang.org/x/mobile/event/lifecycle"
    "golang.org/x/mobile/event/paint"
    "golang.org/x/mobile/event/size"
    "golang.org/x/mobile/event/touch"
    "golang.org/x/mobile/exp/app/debug"
    "golang.org/x/mobile/exp/f32"
    "golang.org/x/mobile/exp/gl/glutil"
    "golang.org/x/mobile/gl"
)

定义变量

基本是OpenGL相关的几个值,还有颜色,X和Y的位置。

var (
    images   *glutil.Images
    fps      *debug.FPS
    program  gl.Program
    position gl.Attrib
    offset   gl.Uniform
    color    gl.Uniform
    buf      gl.Buffer

    green  float32
    touchX float32
    touchY float32
)

主函数

先向app.Main中注册我们的主函数。

func main() {
    app.Main(func(a app.App) {
        var glctx gl.Context
        var sz size.Event

下面就是一个典型的消息驱动的循环,app将事件传给我们的主函数,我们根据事件来处理。GUI系统的代码的框架都差不多哈。

        for e := range a.Events() {
            switch e := a.Filter(e).(type) {
            case lifecycle.Event:
                switch e.Crosses(lifecycle.StageVisible) {
                case lifecycle.CrossOn:
                    glctx, _ = e.DrawContext.(gl.Context)
                    onStart(glctx)
                    a.Send(paint.Event{})
                case lifecycle.CrossOff:
                    onStop(glctx)
                    glctx = nil
                }
            case size.Event:
                sz = e
                touchX = float32(sz.WidthPx / 2)
                touchY = float32(sz.HeightPx / 2)
            case paint.Event:
                if glctx == nil || e.External {
                    // As we are actively painting as fast as
                    // we can (usually 60 FPS), skip any paint
                    // events sent by the system.
                    continue
                }

                onPaint(glctx, sz)
                a.Publish()
                // Drive the animation by preparing to paint the next frame
                // after this one is shown.
                a.Send(paint.Event{})
            case touch.Event:
                touchX = e.X
                touchY = e.Y
            }
        }
    })
}

后面基本上就需要一些OpenGL的知识了

func onStart(glctx gl.Context) {
    var err error

通过glutil.CreateProgram创建一个程序,OpenGL里程序是将顶点着色器和片段着色器绑定在一起的实体。

    program, err = glutil.CreateProgram(glctx, vertexShader, fragmentShader)
    if err != nil {
        log.Printf("error creating GL program: %v", err)
        return
    }

    buf = glctx.CreateBuffer()
    glctx.BindBuffer(gl.ARRAY_BUFFER, buf)
    glctx.BufferData(gl.ARRAY_BUFFER, triangleData, gl.STATIC_DRAW)

    position = glctx.GetAttribLocation(program, "position")
    color = glctx.GetUniformLocation(program, "color")
    offset = glctx.GetUniformLocation(program, "offset")

    images = glutil.NewImages(glctx)
    fps = debug.NewFPS(images)
}

func onStop(glctx gl.Context) {
    glctx.DeleteProgram(program)
    glctx.DeleteBuffer(buf)
    fps.Release()
    images.Release()
}

onPaint的过程,是个标准的OpenGL绘制三角形的流程:

func onPaint(glctx gl.Context, sz size.Event) {
    glctx.ClearColor(1, 0, 0, 1)
    glctx.Clear(gl.COLOR_BUFFER_BIT)

    glctx.UseProgram(program)

    green += 0.01
    if green > 1 {
        green = 0
    }
    glctx.Uniform4f(color, 0, green, 0, 1)

    glctx.Uniform2f(offset, touchX/float32(sz.WidthPx), touchY/float32(sz.HeightPx))

    glctx.BindBuffer(gl.ARRAY_BUFFER, buf)
    glctx.EnableVertexAttribArray(position)
    glctx.VertexAttribPointer(position, coordsPerVertex, gl.FLOAT, false, 0, 0)
    glctx.DrawArrays(gl.TRIANGLES, 0, vertexCount)
    glctx.DisableVertexAttribArray(position)

    fps.Draw(sz)
}

下面是三角形的三个顶点的坐标:

var triangleData = f32.Bytes(binary.LittleEndian,
    0.0, 0.4, 0.0, // top left
    0.0, 0.0, 0.0, // bottom left
    0.4, 0.0, 0.0, // bottom right
)

const (
    coordsPerVertex = 3
    vertexCount     = 3
)

最后是我们创建顶点着色器时所使用的GLSL:

const vertexShader = `#version 100
uniform vec2 offset;

attribute vec4 position;
void main() {
    // offset comes in with x/y values between 0 and 1.
    // position bounds are -1 to 1.
    vec4 offset4 = vec4(2.0*offset.x-1.0, 1.0-2.0*offset.y, 0, 0);
    gl_Position = position + offset4;
}`
const fragmentShader = `#version 100
precision mediump float;
uniform vec4 color;
void main() {
    gl_FragColor = color;
}`

对照下C++所写的native activity

我们来对照下,Android用NDK来写这种native的activity都需要做什么吧。总的代码比Go版本的还是长不少,我们就挑个Android的主函数看一下:

void android_main(struct android_app* state) {
    struct engine engine;

    // Make sure glue isn't stripped.
    app_dummy();

    memset(&engine, 0, sizeof(engine));
    state->userData = &engine;
    state->onAppCmd = engine_handle_cmd;
    state->onInputEvent = engine_handle_input;
    engine.app = state;

...

    if (state->savedState != NULL) {
        // We are starting with a previous saved state; restore from it.
        engine.state = *(struct saved_state*)state->savedState;
    }

    // loop waiting for stuff to do.

    while (1) {
        // Read all pending events.
        int ident;
        int events;
        struct android_poll_source* source;

        // If not animating, we will block forever waiting for events.
        // If animating, we loop until all events are read, then continue
        // to draw the next frame of animation.
        while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events,
                (void**)&source)) >= 0) {

            // Process this event.
            if (source != NULL) {
                source->process(state, source);
            }

...

            // Check if we are exiting.
            if (state->destroyRequested != 0) {
                engine_term_display(&engine);
                return;
            }
        }

        if (engine.animating) {
            // Done with events; draw next animation frame.
            engine.state.angle += .01f;
            if (engine.state.angle > 1) {
                engine.state.angle = 0;
            }

            // Drawing is throttled to the screen update rate, so there
            // is no need to do timing here.
            engine_draw_frame(&engine);
        }
    }
}
//END_INCLUDE(all)

Android例子中用的OpenGL比上面Go的例子要简单一些:

static void engine_draw_frame(struct engine* engine) {
    if (engine->display == NULL) {
        // No display.
        return;
    }

    // Just fill the screen with a color.
    glClearColor(((float)engine->state.x)/engine->width, engine->state.angle,
            ((float)engine->state.y)/engine->height, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    eglSwapBuffers(engine->display, engine->surface);
}

小结一下

上面的例子,除去了OpenGL的知识以外,我们不需要知道任何跟Android系统相关的知识。
别小看了这段小小的代码,在gomobile的支持下,它可是跨平台的哟。

下一期,我们讨论更加激动人心的话题,如何从Android的Java代码调用Go的代码。

目录
相关文章
|
1天前
|
缓存 测试技术 持续交付
Golang深入浅出之-Go语言中的持续集成与持续部署(CI/CD)
【5月更文挑战第5天】本文介绍了Go语言项目中的CI/CD实践,包括持续集成与持续部署的基础知识,常见问题及解决策略。测试覆盖不足、版本不一致和构建时间过长是主要问题,可通过全面测试、统一依赖管理和利用缓存优化。文中还提供了使用GitHub Actions进行自动化测试和部署的示例,强调了持续优化CI/CD流程以适应项目需求的重要性。
30 1
|
1天前
|
Kubernetes Cloud Native Go
Golang深入浅出之-Go语言中的云原生开发:Kubernetes与Docker
【5月更文挑战第5天】本文探讨了Go语言在云原生开发中的应用,特别是在Kubernetes和Docker中的使用。Docker利用Go语言的性能和跨平台能力编写Dockerfile和构建镜像。Kubernetes,主要由Go语言编写,提供了方便的客户端库与集群交互。文章列举了Dockerfile编写、Kubernetes资源定义和服务发现的常见问题及解决方案,并给出了Go语言构建Docker镜像和与Kubernetes交互的代码示例。通过掌握这些技巧,开发者能更高效地进行云原生应用开发。
30 1
|
1天前
|
负载均衡 监控 Go
Golang深入浅出之-Go语言中的服务网格(Service Mesh)原理与应用
【5月更文挑战第5天】服务网格是处理服务间通信的基础设施层,常由数据平面(代理,如Envoy)和控制平面(管理配置)组成。本文讨论了服务发现、负载均衡和追踪等常见问题及其解决方案,并展示了使用Go语言实现Envoy sidecar配置的例子,强调Go语言在构建服务网格中的优势。服务网格能提升微服务的管理和可观测性,正确应对问题能构建更健壮的分布式系统。
18 1
|
2天前
|
消息中间件 Go API
Golang深入浅出之-Go语言中的微服务架构设计与实践
【5月更文挑战第4天】本文探讨了Go语言在微服务架构中的应用,强调了单一职责、标准化API、服务自治和容错设计等原则。同时,指出了过度拆分、服务通信复杂性、数据一致性和部署复杂性等常见问题,并提出了DDD拆分、使用成熟框架、事件驱动和配置管理与CI/CD的解决方案。文中还提供了使用Gin构建HTTP服务和gRPC进行服务间通信的示例。
14 0
|
2天前
|
Prometheus 监控 Cloud Native
Golang深入浅出之-Go语言中的分布式追踪与监控系统集成
【5月更文挑战第4天】本文探讨了Go语言中分布式追踪与监控的重要性,包括追踪的三个核心组件和监控系统集成。常见问题有追踪数据丢失、性能开销和监控指标不当。解决策略涉及使用OpenTracing或OpenTelemetry协议、采样策略以及聚焦关键指标。文中提供了OpenTelemetry和Prometheus的Go代码示例,强调全面可观测性对微服务架构的意义,并提示选择合适工具和策略以确保系统稳定高效。
16 5
|
2天前
|
监控 算法 Go
Golang深入浅出之-Go语言中的服务熔断、降级与限流策略
【5月更文挑战第4天】本文探讨了分布式系统中保障稳定性的重要策略:服务熔断、降级和限流。服务熔断通过快速失败和暂停故障服务调用来保护系统;服务降级在压力大时提供有限功能以保持整体可用性;限流控制访问频率,防止过载。文中列举了常见问题、解决方案,并提供了Go语言实现示例。合理应用这些策略能增强系统韧性和可用性。
20 0
|
2天前
|
负载均衡 算法 Go
Golang深入浅出之-Go语言中的服务注册与发现机制
【5月更文挑战第4天】本文探讨了Go语言中服务注册与发现的关键原理和实践,包括服务注册、心跳机制、一致性问题和负载均衡策略。示例代码演示了使用Consul进行服务注册和客户端发现服务的实现。在实际应用中,需要解决心跳失效、注册信息一致性和服务负载均衡等问题,以确保微服务架构的稳定性和效率。
14 3
|
2天前
|
中间件 Go
Go语言中的中间件设计与实现
【5月更文挑战第4天】Go语言中的中间件在HTTP请求处理中扮演重要角色,提供了一种插入逻辑层的方式,便于实现日志、认证和限流等功能,而不增加核心代码复杂性。中间件遵循`http.Handler`接口,通过函数组合实现。常见问题包括错误处理(确保中间件能正确处理并传递错误)和请求上下文管理(使用`context.Context`共享数据以避免并发问题)。通过理解中间件机制和最佳实践,可以构建更健壮的Web应用。
19 0
|
2天前
|
移动开发 前端开发 Android开发
构建高效Android应用:探究Kotlin协程的优势
【5月更文挑战第4天】 在移动开发领域,尤其是对于Android开发者而言,编写响应迅速且高效的应用程序至关重要。Kotlin作为一种现代的编程语言,其提供的协程特性为异步编程带来了革命性的改变。本文将深入探讨Kotlin协程在Android开发中的应用优势,并通过实例代码展示如何利用协程简化异步任务处理,提高应用性能和用户体验。
|
2天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能对比
【5月更文挑战第4天】在移动开发的世界中,性能一直是衡量应用质量的重要指标。随着Kotlin的兴起,许多Android开发者开始考虑是否应该从传统的Java迁移到Kotlin。本文通过深入分析两者在Android平台上的性能差异,帮助开发者理解Kotlin在实际项目中的表现,并提供选择编程语言时的参考依据。
17 5

热门文章

最新文章

推荐镜像

更多