Golang 开发跨平台SDK(iOS, Android),Part 2
上一篇简单的介绍了下Golang Mobile的工作原理,这一篇讲介绍如何使用Golang 做一个跨平台的RSS SDK。
RSS和HTTP
RSS是基于XML标准,在互联网上被广泛采用的内容包装和投递协议,主要用来描述和同步网站内容。既然RSS是一种网络协议,那我们就需要使用Golang的http
模块了。整个流程可以归结为,RSS的SDK提供一个方法去请求RSS源获得数据,然后通过callback获得数据,而这些数据最好是Android/iOS都可以直接使用的model
集合。
作为一个http
请求在移动端最好是在子进程中执行,通过异步回调返回给主进程或UI 进程,这其中带来的明显好处就是我们的http
任务不会阻塞整个UI,用户在请求RSS源获取最新网站内容的同时依然可以继续对应用进行操作。Golang 本身并没有提供一个异步执行http
的库,但是我们可以利用Golang routine
简单的封装一个异步执行http
的库。
func (t *Task) Runtask() {
r := t.create() // 这里create是创建channel
go func() {
res, err := http.Get(t.Url)
if err != nil {
r.err <- err
} else {
buf, err := ioutil.ReadAll(res.Body)
if err != nil {
r.err <- err
} else {
r.data <- buf
}
}
}()
}
上面的代码段摘自我实现的库go-async,其原理就是将http任务在 golang routine中去执行,当执行结束是通过channel 返回。
接口
通过简单的继承上面的go-async我们就可以实现一个简单的RSS http请求。
func RSS() string {
return "http://rss.cnbeta.com/rss"
}
type Listener interface {
OnSuccess(buf []byte)
OnFailure(err string)
OnEnd()
}
type CnBeta struct {
listener Listener
}
func (cb *CnBeta) Run() {
t := async.NewTask(RSS(), cb)
t.Runtask()
}
func (cb *CnBeta) Success(buf []byte) {
}
func (cb *CnBeta) Failure(err error) {
}
因为Golang现在不支持倒出constant类型的数据结构,所以我们需要定义一个function,然后利用function的返回值取得这个常量。Success
和Failure
是从go-async继承的方法,当异步http callback的时候就会执行其中一个方法。Listener
是定义的一个外部接口,当然在Java/swift/Objective-C中实现这个接口在应用中拿到异步callback的数据。
Model
上面我实现了请求RSS源并且异步获取了byte数据,但是我们的目标是设计一个SDk,只提供方法并不能满足一个完整的SDK的需要,SDK需要接口返回的是Model或者Model的集合。
func NewRssXml() *RssXml {
return &RssXml{
Channel: &RssFeed{},
}
}
type RssXml struct {
Version string
Channel *RssFeed
}
type RssFeed struct {
Title string
Link string
Description string
Language string
Copyright string
PubDate string
Items []*RssItem
}
type RssItem struct {
Title string
Link string
Description string
PubDate string
}
func (rss *RssXml) Feed() *RssFeed {
return rss.Channel
}
上面就是使用Golang实现的RSS的model,由于Golang mobile 本身的限制只能export基本的数据类型,所以我只export需要的类型,但是我也想要直接export Items []*RssItem
,这时我们可以通过实现一个类型方法来实现
type RssFeed struct {
Title string
Link string
Description string
Language string
Copyright string
PubDate string
items []*RssItem
}
func (feed *RssFeed) Length() int64 {
return int64(len(feed.items))
}
func (feed *RssFeed) Item(p int64) *RssItem {
if p >= feed.Length() {
return &RssItem{}
}
return feed.items[p]
}
通过 rssfeed.Length()
我们获得总共有多少RssItem
,而通过rssfeed.Item(1)
我们变可以得到第二个RssItem
,变相的实现了直接使用rssfee.Items[1]
。
整合
实现了Rss Model这时我们便可以将接口改写下
type Listener interface {
OnSuccess(rss *RssXml)
OnFailure(err string)
OnEnd()
}
func (cb *CnBeta) Success(buf []byte) {
doc := xmlx.New()
if err := doc.LoadBytes(buf, nil); err != nil {
cb.Failure(err)
return
}
cb.listener.OnSuccess(parser(doc))
cb.listener.OnEnd()
}
func (cb *CnBeta) Failure(err error) {
cb.listener.OnFailure(err.Error())
cb.listener.OnEnd()
}
这是一整个SDK 便是完成了,但是最关键的部分是编译后在应用开发时引入,以iOS为例通过前一篇的方法我们得到了rss.framework
,这时我们只要通过XCode将这个rss.framework
引入到项目中便可以使用了。
func loadRss() {
let cnbeta = GoCbNewCnBeta(self)
cnbeta.run()
}
// MARK: - cnbeta lisener interface
func onEnd() {
}
func onFailure(err: String!) {
}
func onSuccess(rss: GoCbRssXml!) {
cbRss = rss
dispatch_async(dispatch_get_main_queue(), {
if (self.refreshControl!.refreshing) {
self.refreshControl!.endRefreshing()
}
self.tableView.reloadData()
})
}
下面的截图便是我写的一个Demo
结论
作为一个跨平台的语言,Golang拥有其特有的优势,编译成机器码带来的高效的性能,而且作为一个编写SDK或者数据处理层的工具,其高效的开发效率也是值得考虑的。至于能不能用在production的项目上,我想说Google都在用。