发布网友 发布时间:2024-09-29 19:33
共1个回答
热心网友 时间:2024-11-04 14:05
Golang中的init函数init函数在包中定义,通常用来
除了下面讨论的一些差异之外,init函数中可以放任何常规函数可以放的东西
要使用导入的包,需前蚂正要先将包初始化,初始化及顺序问题由Golang的runningsystem完成。
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,然后编译。然后用goget下载gobind这个工具,然后,将写好的代码用gobind转化下,然后使用特殊的编译命令,将代码编译成.so文件,将生成的相关文件,放到androidstudio的项目中。然后java代码中,利用jni调用引用的代码。
...好,接带宏着往下看吧。
环境准备
一台Linux64的机器
一个带有AndroidStudioIDE的开发机器
因为环境配置实在复杂,所以我们引入的docker。
dockerpullcodeskyblue/docker-goandroid
dockerrun--rm-ticodeskyblue/docker-goandroidbash
cdexample;echo"viewexampleprojects
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
我已经写了一个例子,先直接搞下来
编译下,试试行不行(就算不行问题应该也不大,因为大问题都被我消灭了)
cdGoHello/app
./make.bash
../gradlewbuild
一切顺利的话在build/outputs/apk下应该可以看到app-debug.apk这个文件。(剧透下,这个文件只有800多K)
编译兆行磨好的我放到qiniu上了,可以点击下载看看
下面可以尝试改改,我抛砖引玉说下
打开hi.go这个文件
hi.go的内容,比较简单,我们写Go代码主要就是这部分
//Packagehiprovidesafunctionforsayinghello.
packagehi
import"fmt"
funcHello(namestring){
fmt.Printf("Hello,%s!\n",name)
return"(Go)World"
}
文件末尾添加下面这行代码
funcWelcome(namestring)string{
returnfmt.Sprintf("Welcome%stothegoworld",name)
}
使用./make.bash重新编译下
打开MainActivity.java修改下OnClickListener事件
button.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
Stringmessage=Hi.Welcome("yourname");
Toast.makeText(MainActivity.this,message,Toast.LENGTH_LONG).show();
}
});
编译运行下,把生成的apk安装到手机上试试。
原理解读(有兴趣的接着看)
首先说下gobind这个工具。
go_hi/go_hi.go这个文件时通过gobind这个工具生成的,用来配合一个简单的程序,生成.so文件
//go_hi.go
packagego_hi
import(
"golang.org/x/mobile/bind/seq"
"example/hi"
)
funcproxy_Hello(out,in*seq.Buffer){
param_name:=in.ReadUTF16()
hi.Hello(param_name)
}
funcinit(){
seq.Register("hi",1,proxy_Hello)
}
这个简单的程序内容是这样的
//main.go
packagemain
import(
"golang.org/x/mobile/app"
_"golang.org/x/mobile/bind/java"
_"example/hi/go_hi"
)
funcmain(){
app.Run(app.Callbacks{})
}
src/MyActivity.java文件内容是这样的
import...
importgo.Go;//引入Go这个包
importgo.hi.Hi;//gobind生成的代码
publicclassMainActivityextendsActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
Go.init(getApplicationContext());//初始化两个线程
Hi.Hello("world");
}
}
其中有一句Go.init(...)这里再看go.Go这个包是什么样子的
publicfinalclassGo{
//initloadslibgojni.soandstartstheruntime.
publicstaticvoidinit(Contextcontext){
...判断该函数是否该执行的代码--省略--
System.loadLibrary("gojni");//gojni需要这句
newThread("GoMain"){
publicvoidrun(){
Go.run();//run()是一个native方法
}
}.start();
Go.waitForRun();//这个也是一个native方法
//这部分可以理解为,启动了一个后台线程不断的接收结果到缓存中。
newThread("GoReceive"){
publicvoidrun(){Seq.receive();}
}.start();
}
privatestaticbooleanrunning=false;
privatestaticnativevoidrun();
privatestaticnativevoidwaitForRun();
}
MyActivity.java中还有段代码是Hi.Hello("world");,打开Hi.java路径在src/go/hi/Hi.java,这个文件也是gobind生成的,是用来给java方便的调用.so文件
//Hi.java
//Fileisgeneratedbygobind.Donotedit.
packagego.hi;
importgo.Seq;
publicabstractclassHi{
privateHi(){}//uninstantiable
publicstaticvoidHello(Stringname){
go.Seq_in=newgo.Seq();
go.Seq_out=newgo.Seq();
_in.writeUTF16(name);
Seq.send(DESCRIPTOR,CALL_Hello,_in,_out);//下面接着说
}
privatestaticfinalintCALL_Hello=1;
privatestaticfinalStringDESCRIPTOR="hi";
}
Seq.send这部分实际上最终调用的是一段go代码
funcSend(descriptorstring,codeint,req*C.uint8_t,reqlenC.size_t,res**C.uint8_t,reslen*C.size_t){
fn:=seq.Registry[descriptor][code]
in:=new(seq.Buffer)
ifreqlen0{
in.Data=(*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen]
}
out:=new(seq.Buffer)
fn(out,in)
seqToBuf(res,reslen,out)
}