上一篇简单的介绍了下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的返回值取得这个常量。SuccessFailure是从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 demo

结论

    作为一个跨平台的语言,Golang拥有其特有的优势,编译成机器码带来的高效的性能,而且作为一个编写SDK或者数据处理层的工具,其高效的开发效率也是值得考虑的。至于能不能用在production的项目上,我想说Google都在用。

refs