如何Golang开发Android应用

发布网友 发布时间:2022-04-23 22:56

我来回答

1个回答

热心网友 时间:2022-04-14 07:11

环境配置好复杂,我不得不唠叨几句。
  需要下载golang1.4rc版,下载ndk,然后编译。 然后用go get 下载gobind这个工具, 然后,将写好的代码用gobind转化下,然后使用特殊的编译命令,将代码编译成.so文件,将生成的相关文件,放到android studio的项目中。然后java代码中,利用jni调用引用的代码。
  ... 好,接着往下看吧。
  环境准备
  一台Linux 的机器
  一个带有AndroidStudioIDE的开发机器
  因为环境配置实在复杂,所以我们引入的docker。
  docker pull codeskyblue/docker-goandroid
  docker run --rm -ti codeskyblue/docker-goandroid bash

  cd example; echo "view example projects

  docker起来之后,什么就都配置好了,NDK啦,java啦,GO的环境变量了,等等,并且还预装了vim,gradle,tmux,git,syncthing,svn
  开始写代码
  写代码之前,先约定下目录结构
  go的代码都放在src/golib下,编译使用make.bash编译脚本,看下这个文件树
  .
  |-- app.iml
  |-- build.gradle
  |-- libs/armeabi-v7a # go编译生成的so文件
  | `-- libgojni.so
  |-- main.go_tmpl # 一个模板文件,先不用管它
  |-- make.bash # 编译脚本,用来生成.so和Java代码
  `-- src
  |-- golib
  | |-- hi
  | | |-- go_hi�0�2�0�2�0�2 # 自动生成的代码
  | | | `-- go_hi.go
  | | `-- hi.go # 需要编写的代码
  | `-- main.go
  `-- main
  |-- AndroidManifest.xml
  |-- java
  | |-- go # 自动生成的代码
  | | |-- Go.java
  | | |-- Seq.java
  | | `-- hi
  | | `-- Hi.java
  | `-- me/shengxiang/gohello # 主要的逻辑代码
  | `-- MainActivity.java
  `-- res

  我已经写了一个例子,先直接搞下来
  
  编译下,试试行不行(就算不行问题应该也不大,因为大问题都被我消灭了)
  cd GoHello/app
  ./make.bash
  ../gradlew build

  一切顺利的话在build/outputs/apk下应该可以看到app-debug.apk这个文件。(剧透下,这个文件只有800多K)
  编译好的我放到qiniu上了,可以点击下载看看
  下面可以尝试改改,我抛砖引玉说下
  打开hi.go这个文件
  hi.go的内容,比较简单,我们写Go代码主要就是这部分
  // Package hi provides a function for saying hello.
  package hi

  import "fmt"

  func Hello(name string) {
  fmt.Printf("Hello, %s!\n", name)
  return "(Go)World"
  }

  文件末尾添加下面这行代码
  func Welcome(name string) string {
  return fmt.Sprintf("Welcome %s to the go world", name)
  }

  使用./make.bash重新编译下
  打开MainActivity.java 修改下OnClickListener事件
  button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
  String message = Hi.Welcome("yourname");
  Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
  }
  });

  编译运行下,把生成的apk安装到手机上试试。
  原理解读(有兴趣的接着看)
  首先说下gobind这个工具。
  go_hi/go_hi.go这个文件时通过gobind这个工具生成的,用来配合一个简单的程序,生成.so文件
  // go_hi.go
  package go_hi

  import (
  "golang.org/x/mobile/bind/seq"
  "example/hi"
  )

  func proxy_Hello(out, in *seq.Buffer) {
  param_name := in.ReadUTF16()
  hi.Hello(param_name)
  }

  func init() {
  seq.Register("hi", 1, proxy_Hello)
  }

  这个简单的程序内容是这样的
  // main.go
  package main

  import (
  "golang.org/x/mobile/app"

  _ "golang.org/x/mobile/bind/java"
  _ "example/hi/go_hi"
  )

  func main() {
  app.Run(app.Callbacks{})
  }

  src/MyActivity.java文件内容是这样的
  import ...
  import go.Go; // 引入Go这个包
  import go.hi.Hi; // gobind生成的代码

  public class MainActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Go.init(getApplicationContext()); // 初始化两个线程
  Hi.Hello("world");
  }
  }

  其中有一句Go.init(...)这里再看go.Go这个包是什么样子的
  public final class Go {
  // init loads libgojni.so and starts the runtime.
  public static void init(Context context) {
  ... 判断该函数是否该执行的代码 -- 省略 --
  System.loadLibrary("gojni"); // gojni需要这句

  new Thread("GoMain") {
  public void run() {
  Go.run(); // run()是一个native方法
  }
  }.start();

  Go.waitForRun(); // 这个也是一个native方法

  // 这部分可以理解为,启动了一个后台线程不断的接收结果到缓存中。
  new Thread("GoReceive") {
  public void run() { Seq.receive(); }
  }.start();
  }

  private static boolean running = false;

  private static native void run();
  private static native void waitForRun();
  }

  MyActivity.java中还有段代码是 Hi.Hello("world");,打开Hi.java路径在src/go/hi/Hi.java,这个文件也是gobind生成的,是用来给java方便的调用.so文件
  // Hi.java
  // File is generated by gobind. Do not edit.
  package go.hi;

  import go.Seq;

  public abstract class Hi {
  private Hi() {} // uninstantiable

  public static void Hello(String name) {
  go.Seq _in = new go.Seq();
  go.Seq _out = new go.Seq();
  _in.writeUTF16(name);
  Seq.send(DESCRIPTOR, CALL_Hello, _in, _out); // 下面接着说
  }

  private static final int CALL_Hello = 1;
  private static final String DESCRIPTOR = "hi";
  }

  Seq.send这部分实际上最终调用的是一段go代码
  func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) {
  fn := seq.Registry[descriptor][code]
  in := new(seq.Buffer)
  if reqlen > 0 {
  in.Data = (*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen]
  }
  out := new(seq.Buffer)
  fn(out, in)
  seqToBuf(res, reslen, out)
  }

  转载仅供参考,版权属于原作者。祝你愉快,满意请采纳哦

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com