首页>>后端>>Golang->golanginit

golanginit

时间:2023-12-12 本站 点击:2

Golang中的init函数

init函数在包中定义,通常用来

除了下面讨论的一些差异之外,init函数中可以放任何常规函数可以放的东西

要使用导入的包,需前蚂正要先将包初始化,初始化及顺序问题由Golang的 running system 完成。

Go里面的一个包可以包含多个文件。分布在众多包中的众多文件,变量和init函数执行的顺序应该是什么样子呢?之前的 文章 提到了变量的初始化顺序。完成此操作后,需要决定文件 a.go 或 z.go 中的变量初始化谁更应该早执行。 这取决于传递给编译器的文件顺序。 如果 z.go 首先由构建系统传递,则 z.go 先执行,然后在 a.go 的再执行。 这同样适用于init 函数的触发。 语言规范建议始终使用相同的顺序并按字典顺序从包中传递文件名:

不过依赖文件名初始化顺序的程序十分罕见,让我们来看这样子慧悔的例子

运行后,程序会这样输出

init 函数不需要参数也不返回任何值。和main不同,标物丛识符init未声明,所以不能被引用

在编译时,程序返回

同一个包或文件中可以有很多个init函数,在不同文件中定义的init函数如下按照字母顺序执行,同一个文件按声明顺序执行,举例

输出

init函数的最常见用途,就是用来给那些不能通过表达式初始化的变量初始化,如:

表达式中可不能使用for循环,所以通过init函数来解决这个问题

Go对于未使用的引用非常严格。有些场景,你导入一个包,只为了执行其中的init函数(如mysql的driver)。

golang中包循环依赖问题

一、go中为什么不允许循环依赖

二、如何解决循环依赖

循环依赖就是A引用B,B又引用A,形成了一个包引用的闭环。要解决循环引用,就是打破这个闭环,让A引用B,B不能引用A。看下面的例子:

包结构如下:

执行main函数报错:

报错的原因是 我们在执行bagA.PrintA()的时候,引用了A包,A包又引用了B包,B包又引用了A包,形成了循环依赖。那我们打破依赖就可以了。

那么该怎么打破呢?

我们发现A包引用B包,是因为A包需要调用B包的bagB.GetName()方法;同则激样的,B包引用A包,是因为B包需要调用A包的bagA.GetName()方法。那么,我们有没有不需要引包就能使B包可以调用A包的方法呢?

当然是有的。看下面:

我们在B包里定义了一个方法变量AHandler,并且提供了为这个方法变量赋值的方法没盯灶Register(),然后在A包里的init()方法里,调用B包的Register()方法,将A包的GetName方法复赋值给了AHandler变量。 这样,在B包执行方法AHandler是不是就相当于调用了A包的GetName方法呢?看执行结果:

总结:

上述解决办法的核心逻辑就是,B包使用一个方法变量来替代A中的方法(来完成B不引用A),A来为该变量赋值(因为A引用B,A可以调用B的方法来完成赋值)。 解决循环依赖问题,思想就是打破包的循环依赖,以不导包的方式调用其他包的方法。所以,采用接口的形式也可以解决循环依赖(B定义一个接口,A中你想枯扮要调用的方法实现了该接口,A中完成接口变量赋值,B来调用接口方法,有时间再补充例子吧)

如何Golang开发Android应用

环境配置好复杂,我不得不唠叨几句。

需要下载golang1.4rc版,下载ndk,然后编译。 然后用go get 下载gobind这个工具, 然后,将写好的代码用gobind转化下,然后使用特殊的编译命令,将代码编译成.so文件,将生成的相关文件,放到android studio的项目中。然后java代码中,利用jni调用引用的代码。

... 好,接带宏着往下看吧。

环境准备

一台Linux 64的机器

一个带有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)

}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Golang/25547.html