2014年12月24日 星期三

使用golang 开发的 andriod应用

最近在捣鼓一个东东,就是使用golang开发andriod应用.说起来简单操作起来还挺麻烦,中间又学习了很多东西.比如ubuntu,docker,angular,ionic,jquery mobile,amazeui,avalon,andriod studio.对每样都东西都有个了解后,才完成使用golang 开发andriod应用的任务.虽然有几个技术项目没有用,但毕竟选择合适的需要对比.
这次捣鼓收获很多,对上面几个技术都有一个不错的了解,不说精通,但至少入门了.
下面我就把这次主要的体会写下来,给大家分享.
  1. 第一使用docker编译go的so库.为什么要使用docker呢,因为配置环境很复杂.golang mobile官方源码库里提供了dockerfile,可以很方便的生成已经配置好开发坏境的docker镜像.
  2. 第二,使用ionic做html5界面库.ionic 界面很漂亮,文档很齐全,速度比JQM 快了不少,并且使用了最著名的angular.
  3. 第三,使用golang 做http服务,由于http服务要使用很多静态资源,所以需要使用go-bindata生成嵌入式的资源文件.
  4. 第四,使用了andirod studio作为开发工具,由于墙的原因,配置开发环境很坑嗲,为此我还专门购买了一个坑爹的VPN.我使用最新版的,总体来讲比半年前好用多了.至于为什么要使用andirid studio呢,毕竟是官方推荐的,现在直接用,免得以后再换.
随便画的图,这是什么图呢?难道就是传说中的构架图?
最后,我放几张图,让大家感性的感受一下:
这是运行的最终界面
项目目录结构
关键性代码
最后送上,apk demo,才3.08MB,还是挺小的,大家可以安装感受一下流畅度
再最后送上andriod studio项目源码,你可以直接导入运行.你可能会问这种开发模式ionic,phonegap都已经提供,并且跨平台的,你这样有什么优势.我想说的是,项目源码里没有.so库的源码.你有本事就去破解吧.

2014年12月23日 星期二

快速删除.git文件

在使用go get github.com的代码过程中,同时也会把.git文件,搞过来,其占用的空间不少,最关键的是看着不舒适.
所以就想把他们删除.以绝后患.
下面是删除的命令:
find . -name '.git' | sed -e 's/^/rm -rf /g'| /bin/sh
这是删除前空间大小:
171 MB (180,234,769 字节)
这是删除后空间大小
154 MB (161,707,177 字节)
 
虽然小了才20M,但是看着舒服了.
 
删除.SVN
find . -name '.svn' | sed -e 's/^/rm -rf /g'| /bin/sh

2014年12月19日 星期五

sublime 3065 破解,主要注册码就可以了

本以为这个版本也像以前的版本一样,要修改程序代码再加上注册码才行呢. 晚上搜索了一箩筐,终于找到一个注册码,没想到直接输入就行了. 下面我就贡献注册码: —– BEGIN LICENSE —– Andrew Weber Single User License EA7E-855605 813A03DD 5E4AD9E6 6C0EEB94 BC99798F 942194A6 02396E98 E62C9979 4BB979FE 91424C9D A45400BF F6747D88 2FB88078 90F5CC94 1CDC92DC 8457107A F151657B 1D22E383 A997F016 42397640 33F41CFC E1D0AE85 A0BBD039 0E9C8D55 E1B89D5D 5CDB7036 E56DE1C0 EFCC0840 650CD3A6 B98FC99C 8FAC73EE D2B95564 DF450523 —— END LICENSE ——

2014年12月19日 星期五

Sublime 3在Ubuntu无法输入中文方法,亲测

本经验介绍如何解决在Ubuntu14.04下Sublime Text 3无法输入中文的问题

工具/原料

  • Ubuntu14.04
  • 搜狗输入法 for Linux
  • Sublime text 3

已知前置条件

  1. 1
    本经验目前在Ubuntu14.04环境下,已有搜狗输入法 for Linux和Sublime Text 3的情况下安装成功。
    Ubuntu下Sublime Text 3解决无法输入中文的方法
    END

解决方法步骤2

  1. 保存下面的代码到文件sublime_imfix.c(位于~目录) #include <gtk/gtkimcontext.h> void gtk_im_context_set_client_window (GtkIMContext *context,          GdkWindow    *window) {  GtkIMContextClass *klass;  g_return_if_fail (GTK_IS_IM_CONTEXT (context));  klass = GTK_IM_CONTEXT_GET_CLASS (context);  if (klass->set_client_window)    klass->set_client_window (context, window);  g_object_set_data(G_OBJECT(context),"window",window);    if(!GDK_IS_WINDOW (window))    return;  int width = gdk_window_get_width(window);  int height = gdk_window_get_height(window);  if(width != 0 && height !=0)    gtk_im_context_focus_in(context); }
    Ubuntu下Sublime Text 3解决无法输入中文的方法
  2. 将上一步的代码编译成共享库libsublime-imfix.so,命令 cd ~ gcc -shared -o libsublime-imfix.so sublime_imfix.c  `pkg-config --libs --cflags gtk+-2.0` -fPIC
    Ubuntu下Sublime Text 3解决无法输入中文的方法
  3. 然后将libsublime-imfix.so拷贝到sublime_text所在文件夹 sudo mv libsublime-imfix.so /opt/sublime_text/
    Ubuntu下Sublime Text 3解决无法输入中文的方法
  4. 修改文件/usr/bin/subl的内容 sudo gedit /usr/bin/subl#!/bin/sh exec /opt/sublime_text/sublime_text "$@" 修改为 #!/bin/sh LD_PRELOAD=/opt/sublime_text/libsublime-imfix.so exec /opt/sublime_text/sublime_text "$@" 此时,在命令中执行 subl 将可以使用搜狗for linux的中文输入
    Ubuntu下Sublime Text 3解决无法输入中文的方法
  5. 为了使用鼠标右键打开文件时能够使用中文输入,还需要修改文件sublime_text.desktop的内容。 命令 sudo gedit /usr/share/applications/sublime_text.desktop   将[Desktop Entry]中的字符串 Exec=/opt/sublime_text/sublime_text %F 修改为 Exec=bash -c "LD_PRELOAD=/opt/sublime_text/libsublime-imfix.so exec /opt/sublime_text/sublime_text %F"   将[Desktop Action Window]中的字符串 Exec=/opt/sublime_text/sublime_text -n 修改为 Exec=bash -c "LD_PRELOAD=/opt/sublime_text/libsublime-imfix.so exec /opt/sublime_text/sublime_text -n"   将[Desktop Action Document]中的字符串 Exec=/opt/sublime_text/sublime_text --command new_file 修改为 Exec=bash -c "LD_PRELOAD=/opt/sublime_text/libsublime-imfix.so exec /opt/sublime_text/sublime_text --command new_file"   注意: 修改时请注意双引号"",否则会导致不能打开带有空格文件名的文件。 此处仅修改了/usr/share/applications/sublime-text.desktop,但可以正常使用了。 opt/sublime_text/目录下的sublime-text.desktop可以修改,也可不修改。
    Ubuntu下Sublime Text 3解决无法输入中文的方法
  6. 经过以上步骤我们能在Sublime中输入中文了。
    Ubuntu下Sublime Text 3解决无法输入中文的方法

2014年12月19日 星期五

andriod studio 项目在应用golang 编译的so库

经过我2天的奋战,终于,在ubuntu下使用golang 编译.so库文件,然后复制到windosw上,在andriod studio下,建一个项目引用.so,并且成功啦!
界面如下:
点击[获取数据],就在控制台打印 Go says  chengziqing
下面是项目源码结构:
其中最麻烦的是安装andriod studio 开发环境了.光下载 andriod studio用一晚上,又下载andriod sdk,用了一上午,andriod studio 很垃圾,卡
的要死.下载东西要各种翻墙.
点击事件的代码:
其中
Go.init(getApplicationContext())很关键,一定要调用.
上面是golang so 库 Saygo方法.里面开启了一个http服务.由于要访问网络,要给
AndroidManifest.xml 加上权限:
<uses-permission android:name="android.permission.INTERNET"/>
如果不加权限会打印出 Go says chengziqing ,加上权限后,会被阻塞,就不会打印出这句话.
既然这样,那我有一个设想,就是使用golang 做http服务,做成网站,然后使用andriod 的webview,嵌入进去,这样就可以做离线的andriod app了.
下面是我是测试的界面:
这是一个http file server.
这会不会是html5崛起的开始?golang 以后还会增加编译apple 手机使用的so库,那样就可以跨平台啦,

2014年12月17日 星期三

golang开发android 环境部署

第一步先使用VMware安装64位最新ubuntu,这一步骤略过 第二步安装docker容器,官网有详细的步骤,不过最关键的是下面两个命令直接执行就可以了: 下载已经脚本直接执行
$ curl -sSL https://get.docker.com/ubuntu/ | sudo sh
验证是否成功:
$ sudo docker run -i -t ubuntu /bin/bash
第三步 使用golang 官方提供的 Dockerfile 创建 golang mobile 开发环境.具体步骤如下: 把https://github.com/golang/mobile/blob/master/Dockerfile 下载下来,然后在下载的目录里执行:
sudo docker build -t mobile
由于脚本里有google 的资源,下载会失败,再执行之前最好安装vpn. 第四步 进入 mobile 容器 /bin/sh 执行下面命令:
go get -d golang.org/x/mobile/...
cd /src/golang.org/x/mobile/example/basic
./make.bash
cd bin
这样就可以看到你生成的apk了

2014年12月03日 星期三

USING 在mysql 删除重复数据只保留一条记录的应用

DELETE FROM tb_users USING tb_users,( SELECT DISTINCT MIN(username) AS username,user_id FROM tb_users GROUP BY username HAVING COUNT(1) > 1 ) AS t2 WHERE tb_users.username = t2.username AND tb_users.user_id <> t2.user_id

2014年10月24日 星期五

golang里 bufio包详解

bufio包提供了带有缓冲功能的Reader和Writer, 应用了装饰者模式. bufio.Reader是对io.Reader的包装, 并且实现了io.Reader接口. 类似的, bufio.Writer是对io.Writer的包装, 并且实现了io.Writer接口.

Reader

bufio.Reader的定义如下:
type Reader struct {
    // 缓冲区
    buf          []byte
    // 底层Reader
    rd           io.Reader
    // buffer中有效数据的边界
    r, w         int
    // read过程中出现的error
    err          error
    lastByte     int
    lastRuneSize int
}
lastByte和lastRuneSize字段是为了支持unread操作而存在的. lastByte表示读取的最后一个字节数据, -1表示不可用状态. 注意lastByte的类型为int, 如果lastByte的类型为byte, -1是合法的字节数据值, 无法使用-1表示不可用状态. 将lastByte的类型设定为int则可以规避这个问题, byte强转为int之后, 取值范围是[0-255], 此时-1不再是合法值, 因此可以用来表示不可用状态.

创建*Reader对象

可以通过以下2个function创建*bufio.Reader对象:
// 指定缓冲区大小
func NewReaderSize(rd io.Reader, size int) *Reader
// 使用默认缓冲区大小, 相当于调用NewReaderSize(rd, defaultBufSize)
func NewReader(rd io.Reader) *Reader
有趣的是, 在NewReaderSize中, 会判断rd是否已经是一个*bufio.Reader对象了:
b, ok := rd.(*Reader)
if ok && len(b.buf) >= size {
    return b
}
如果rd已经是一个*bufio.Reader对象了, 且缓冲区大小也满足条件, 则直接返回rd自身, 以防止不必要的包装.

Read

func (b *Reader) Read(p []byte) (n int, err error)
*bufio.Reader实现了io.Reader接口, 就是因为*bufio.Reader具有Read方法. 如果缓冲区中没有数据:
if b.w == b.r {
    if b.err != nil {
        return 0, b.readErr()
    }
    if len(p) >= len(b.buf) {
        // 读取数据量超过缓冲区大小时, 直接从底层的io.Reader对象读取并返回数据, 避免[]byte的copy
        // 注意, 这部分数据是没有进入缓冲区的
        n, b.err = b.rd.Read(p)
        if n > 0 {
            b.lastByte = int(p[n-1])
            b.lastRuneSize = -1
        }
        return n, b.readErr()
    }
    // 试图填满缓冲区
    b.fill()
    // 此时如果缓冲区仍然没有数据, 则说明肯定出现了error
    if b.w == b.r {
        return 0, b.readErr()
    }
}
接下来, 返回缓冲区中的数据:
// 最多从缓冲区中取出n个字节
if n > b.w-b.r {
    n = b.w - b.r
}
copy(p[0:n], b.buf[b.r:])
// 改变标记字段
b.r += n
b.lastByte = int(b.buf[b.r-1])
b.lastRuneSize = -1
return n, nil
从代码可以看出, Read最多调用一次底层Reader的Read方法, 因此返回的n是有可能小于len(p)的.

Buffered

返回当前缓冲区中的有效字节数.
func (b *Reader) Buffered() int { return b.w - b.r }

ReadSlice

func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
读取并返回byte数据, 直到读取到指定的delim, 或者发生error, 或者缓冲区已满. 如果缓冲区中的数据满足条件, 则直接返回:
if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 {
    line1 := b.buf[b.r : b.r+i+1]
    b.r += i + 1
    return line1, nil
}
否则, 将数据读取到缓冲区后再进行判断:
for {
    if b.err != nil {
        line := b.buf[b.r:b.w]
        b.r = b.w
        return line, b.readErr()
    }

    // 记录当前缓冲区中的字节数
    n := b.Buffered()
    b.fill()

    // 在新读取的数据中搜索
    if i := bytes.IndexByte(b.buf[n:b.w], delim); i >= 0 {
        line := b.buf[0 : n+i+1]
        b.r = n + i + 1
        return line, nil
    }

    // 判断缓冲区是否已满
    if b.Buffered() >= len(b.buf) {
        b.r = b.w
        return b.buf, ErrBufferFull
    }
}

ReadLine

func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
读取一行数据, 如果某行太长(超过缓冲区大小), 先返回开头部分, 并设置isPrefix为true, 其余部分将在随后的ReadLine中返回. 先调用ReadSlice得到初步数据:
line, err = b.ReadSlice('\n')
如果err是ErrBufferFull(缓冲区已满), 处理数据后返回给调用者:
if err == ErrBufferFull {
    // 处理\r\n的情形
    if len(line) > 0 && line[len(line)-1] == '\r' {
        if b.r == 0 {
            // should be unreachable
            panic("bufio: tried to rewind past start of buffer")
        }
        // 将\r放回缓冲区, 并将其从line中删除, 以便下次调用ReadLine时一并处理'\r\n'
        b.r--
        line = line[:len(line)-1]
    }
    // 返回的isPrefix为true, 表明该行太长, line只是该行的一部分数据
    return line, true, nil
}
如果line的最后一个字节确实是'\n', 则将'\n'或者'\r\n'从line中删除后返回:
if line[len(line)-1] == '\n' {
    drop := 1
    if len(line) > 1 && line[len(line)-2] == '\r' {
        drop = 2
    }
    line = line[:len(line)-drop]
}

ReadBytes

func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
ReadBytes和ReadSlice的区别在于, ReadBytes不会因为缓冲区已满就停止搜索, 而是继续下去, 直到读取到指定的delim, 或者发生了error(通常是io.EOF).
var frag []byte
var full [][]byte
err = nil

for {
    var e error
    frag, e = b.ReadSlice(delim)
    if e == nil { // 读取到指定的delim
        break
    }
    if e != ErrBufferFull { // 发生了ErrBufferFull之外的error
        err = e
        break
    }

    // 处理err为ErrBufferFull的情形
    // 将frag中的数据copy一份, 保存在full中.
    buf := make([]byte, len(frag))
    copy(buf, frag)
    full = append(full, buf)
}
如此以来, 数据就存储在full和frag中了, 接下来需要将2者中存储的数据合并到一个buffer中:
// 计算buf所需的length
n := 0
for i := range full {
    n += len(full[i])
}
n += len(frag)

// 将数据从full和frag中copy到buf中, 注意copy的顺序
buf := make([]byte, n)
n = 0
for i := range full {
    n += copy(buf[n:], full[i])
}
copy(buf[n:], frag)
return buf, err

ReadString

ReadString和ReadBytes类似, 区别在于ReadString返回的是string:
func (b *Reader) ReadString(delim byte) (line string, err error) {
    bytes, e := b.ReadBytes(delim)
    return string(bytes), e
}
可以调用ReadString('\n')代替ReadLine, 此时不需要处理烦人的isPrefix标记, 因为对于ReadString('\n')来说, 不管行有多长, 都只会一次性返回.

Writer

bufio.Writer的定义如下:
type Writer struct {
    err error
    // 缓冲区
    buf []byte
    // buf中有效数据个数
    n   int
    // 底层Writer
    wr  io.Writer
}

创建*Writer对象

可以通过以下2个function创建*bufio.Writer对象:
// 指定缓冲区大小
func NewWriterSize(wr io.Writer, size int) *Writer
// 使用默认缓冲区大小, 相当于调用NewWriterSize(wr, defaultBufSize)
func NewWriter(wr io.Writer) *Writer

Flush

将缓冲区中的数据写入相应的底层io.Writer:
func (b *Writer) Flush() error {
    if b.err != nil {
        return b.err
    }
    // 缓冲区中没有数据
    if b.n == 0 {
        return nil
    }
    // 将缓冲区中的数据写入io.Writer
    n, e := b.wr.Write(b.buf[0:b.n])
    if n < b.n && e == nil {
        e = io.ErrShortWrite
    }
    if e != nil {
        // 写入了n个字节时, 将写入的部分从缓冲区中删除
        if n > 0 && n < b.n {
            copy(b.buf[0:b.n-n], b.buf[n:b.n])
        }
        // 设置相关标记
        b.n -= n
        b.err = e
        return e
    }
    // 清空缓冲区
    b.n = 0
    return nil
}

Available

返回缓冲区中的空闲字节数:
func (b *Writer) Available() int { return len(b.buf) - b.n }

Buffered

返回缓冲区中的有效字节数:
func (b *Writer) Buffered() int { return b.n }

Write

*bufio.Writer具有Write方法, 因此*bufio.Writer实现了io.Writer接口.
func (b *Writer) Write(p []byte) (nn int, err error) {
    for len(p) > b.Available() && b.err == nil {
        var n int
        if b.Buffered() == 0 {
            // 需要写入大量数据, 且缓冲区中没有有效数据时, 直接将p写入底层io.Writer中
            n, b.err = b.wr.Write(p)
        } else {
            // 先将部分数据(b.Available()个字节)写入缓冲区中, 然后再刷新缓存
            n = copy(b.buf[b.n:], p)
            b.n += n
            // Flush时如果出错, 会将error存储在b.err中
            b.Flush()
        }
        nn += n
        // 截去已写入的部分
        p = p[n:]
    }
    if b.err != nil {
        return nn, b.err
    }
    // 将剩余部分写入缓冲区, 此时len(p) <= b.Available(), 即缓冲区肯定能够容纳p中的数据
    n := copy(b.buf[b.n:], p)
    b.n += n
    nn += n
    return nn, nil
}

WriteByte

写入单个字节数据.
func (b *Writer) WriteByte(c byte) error {
    if b.err != nil {
        return b.err
    }
    // 缓冲区中没有可用空间时, 尝试执行Flush操作, 如果Flush失败, 直接返回错误
    if b.Available() <= 0 && b.Flush() != nil {
        return b.err
    }
    // 将c写入缓冲区
    b.buf[b.n] = c
    b.n++
    return nil
}

WriteString

写入string数据. 从源码可以看出, 相当于调用了Write([]byte(s)).

2014年10月22日 星期三

How to setup Ngrok with a self-signed SSL cert

Intro

The plan is to create a pair of executables (ngrok and ngrokd) that are connected with a self-signed SSL cert. Since the client and server executables are paired, you won't be able to use any other ngrokto connect to this ngrokd, and vice versa.

DNS

Add two DNS records: one for the base domain and one for the wildcard domain. For example, if your base domain is domain.com, you'll need a record for that and for *.domain.com.

Different Operating Systems

If the OS on which you'll be compiling ngrok (that's the server section below) is different than the OS on which you'll be running the client, then you will need to set the GOOS and GOARCH env variables. I run Linux everywhere, so I don't know how to do that. Please Google it or see the discussion here. If you know how to do this and want to add GOOS/GOARCH instructions here, please let me know.

On Server

MAKE SURE YOU SET NGROK_DOMAIN BELOW. Set it to the base domain, not the wildcard domain.
NGROK_DOMAIN="my.domain.com"
git clone https://github.com/inconshreveable/ngrok.git
cd ngrok

openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pem
openssl genrsa -out device.key 2048
openssl req -new -key device.key -subj "/CN=$NGROK_DOMAIN" -out device.csr
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 5000

cp rootCA.pem assets/client/tls/ngrokroot.crt
# make clean
make release-server release-client
Copy bin/ngrok to whatever computer you want to connect from. Then start the server:
bin/ngrokd -tlsKey=device.key -tlsCrt=device.crt -domain="$NGROK_DOMAIN" -httpAddr=":8000" -httpsAddr=":8001"

On Client

MAKE SURE YOU SET NGROK_DOMAIN BELOW. Set it to the base domain, not the wildcard domain.
NGROK_DOMAIN="my.domain.com"
echo -e "server_addr: $NGROK_DOMAIN:4443\ntrust_host_root_certs: false" > ngrok-config
./ngrok -config=ngrok-config 80
Or for SSH forwarding: ./ngrok -config=ngrok-config --proto=tcp 22 https://gist.github.com/lyoshenka/002b7fbd801d0fd21f2f

2014年10月21日 星期二

项目 特币宝 - 快乐世界杯(bitcup)

让2014的巴西世界杯更加欢乐!投注简单快捷、信息公开透明、中奖金额源地址返回! 今天世界杯期间,闲着无聊,就做了这个项目.项目很简单,使用比特币投注,然后根据比赛结果,猜对的人可以获得比特币奖金.主要思想是利用比特币的匿名特性,奖金自动返还到投资的比特币地址.界面采用bootstrap,紫色系,紫色有一种神秘特性.框架采用目前最有名的 revel.没有采用数据库. 项目源码托管在GITHUB,有兴趣的可以去看看.没多少技术含量,主要是对那几天工作的一种记录. https://github.com/chengziqing/bitcup 项目快照: