Jasper Ji

开口不在舌头上

0%

JNI(Java Native Interface)意为Java本地接口,虽然使用Java有段时间了,但对这个并不太了解。这个是基于网上老外的一篇文章的总结,环境是Mac OS

1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorldJNI {
static {
System.loadLibrary("native");
}

public static void main(String[] args) {
new HelloWorldJNI().sayHello();
}

// Declare a native method sayHello() that receives no arguments and returns void
private native void sayHello();
}

使用命令生成头文件

1
javac -h . HelloWorldJNI.java

生成如下的com_jasper_ji_maven_test_HelloWorldJNI.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jasper_ji_maven_test_HelloWorldJNI */

#ifndef _Included_com_jasper_ji_maven_test_HelloWorldJNI
#define _Included_com_jasper_ji_maven_test_HelloWorldJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jasper_ji_maven_test_HelloWorldJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_jasper_ji_maven_test_HelloWorldJNI_sayHello
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

我们再创建一个同名cpp文件

1
2
3
4
5
6
7
8
9
10
11
12
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "com_jasper_ji_maven_test_HelloWorldJNI.h"
#include <iostream>
/*
* Class: com_jasper_ji_maven_test_HelloWorldJNI
* Method: sayHello
* Signature: ()V
*/
void JNICALL Java_com_jasper_ji_maven_test_HelloWorldJNI_sayHello (JNIEnv *env, jobject obj)
{
std::cout << "Hello from C++ !!" << std::endl;
}

编译文件,生成com_jasper_ji_maven_test_HelloWorldJNI.o的文件

1
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_jasper_ji_maven_test_HelloWorldJNI.cpp -o com_jasper_ji_maven_test_HelloWorldJNI.o

生成动态库

1
g++ -dynamiclib -o libnative.dylib com_jasper_ji_maven_test_HelloWorldJNI.o -lc

运行Java,这里需要注意的是-Djava.library.path路径我是把libnative.dylib这个文件路径放在com的根目录下的,也就是当前目录。

1
java -cp . -Djava.library.path=. com.jasper.ji.maven.test.HelloWorldJNI

总结

平时不太写C/C++的代码,所以有点生,不过之前对C的研究还是起了不少的帮助。JNI虽然提供了调用其他语言的功能,但是这是比较麻烦的,不同的平台需要编译对应的库。早前在做安卓的时候,倒是有简单的运行过NDK的例子,不过那会并没有专门研究下JNI。

参考

Guide to JNI (Java Native Interface)

记录一些实际工作中MySQL的使用问题和新的。

给新增字段设置为NULL的问题

字段不为NULL,原来的表新增一个字段,但这个字段又是不能为空,如果设置字段不为NULL,因为之前的是空,所以会报错。

最近找一款开源仓储系统,最好使用Java开发,发现Jeewms还不错,也有演示Demo。Gitee上大概有3.2K的Star,Github虽然也放了一个,但我看好像没有什么人关注。

Java

主要遇到的坑是JDK版本一定要是Java 8,我本地因为安装最新的Eclispe时安装了Java 11,结果编译后报各种错,一直没有跑起来,主要是Java 11去掉了某些库。最后设置JAVA_HOME为Java 8后就好了。

数据库

这个项目在数据库的设计上字段名称上有些使用英文命名,业务部分的字段是拼音命名,好在不是简写的,另外也有个数据库设计文件可以参考,字段上也有注释,对于习惯了使用英文命名的人来说可能会想着修改,但是还是有难度的,不建议重构。

也使了用视图,知道这个东西,但实际项目中并没有使用过,如果没有用过的需要注意下。

也使用了触发器的东西,也需要注意下,如果是直接拿项目里面的sql文件还原的话问题不大,但如果是重新导出sql的时候一定要注意,触发器这些设置也要一并导出,像我使用的MySQL Workbench默认是没有勾选的,而我也没有太注意是否使用了触发器,结果本地好着了,正式服务器却发现有问题,一查看是一个字段没有更新的缘故,习惯性的在代码里面查找哪里有修改,结果没有发现,查看二进制日志后,想到应该是触发器的问题,果然是通过触发器更新的,而服务器上的问题是因为导出的sql没有勾选触发器的缘故。

架构方面

这个使用Tomcat+Spring MVC,并不是现在流行的SpringBoot,所以对于只接触过SpringBoot的人需要注意下。操作数据方面使用的是Hibernat,反正没有用过,只使用过Mybatis,感觉还是需要学习下的。视图方面使用的是JSP,之前只使用Thymeleaf。

业务规则

这块并没有详细的文档说明,如果没有做个仓储系统的,需要修改一些东西还是会有点摸不着头绪,需要好好的厘清下。最好还是找一些仓储方面的资料对照着理解项目。

总结

整个项目时可用的,但要重构架构的话可能比较有难度,因为并不是最新的技术,不是很适合拿来学习,如果不了解之前的这类项目的开发技术的话,倒也是一种参考。

ZooKeeper没有直接用过,所以想研究下,本来读《ZooKeeper:分布式过程协同技术详解》,感觉还是很模糊,虽然大概明白分布式会需要这样一个东西,现在使用的Spring微服务中Eureka跟ZooKeeper有相似的地方。纸上得来终归浅,所以觉得跑一些实际的例子。

安装

我的环境是Mac,使用Brew安装。

可视化界面

ZooKeeper自带的zkCli命令行工具,其实也能用。也有可视化的界面工具ZKUI,最后使用的ZKUI。

客户端

使用的Java跑的例子,有点类似数据的增删改查了,当然也有监听。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Main {
// 连接地址及端口号
private static final String SERVER_HOST = "127.0.0.1:2181";

// 会话超时时间
private static final int SESSION_TIME_OUT = 2000;

public static void main(String[] args) throws Exception {
// 参数一:服务端地址及端口号
// 参数二:超时时间
// 参数三:监听器
ZooKeeper zooKeeper = new ZooKeeper(SERVER_HOST, SESSION_TIME_OUT, new Watcher() {
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
// 获取事件的状态
Event.KeeperState state = event.getState();
// 判断是否是连接事件
if (Event.KeeperState.SyncConnected == state) {
Event.EventType type = event.getType();
if (Event.EventType.None == type) {
System.out.println("zk客户端已连接...");
}
}
}
});
zooKeeper.create("/go", "Hello World".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("新增ZNode成功");
zooKeeper.close();
}
}

更高级的可以使用Curator,出了基本的客户端功能外,还提供了锁、选举、屏障、缓存、持久化节点、队列这样的功能。

参考:
ZooKeeper入门,看这篇就够了!

书籍

目前的书籍不多,在省图找到了这本《ZooKeeper:分布式过程协同技术详解》,此书不太适合一点经验都没有的人阅读,
相对来说偏实战了。此书还在读,目前来看结构清晰,推荐。吐槽下《ZooKeeper:Distributed Process Coordination》的中文版翻译为《ZooKeeper:分布式过程协同技术详解》,把“Process”翻译为“过程”确实没有“进程”或者“程序”好。ZooKeeper官方的说明是一种协调分布式服务的应用程序,这个其实很贴合意思。

总结

我还是习惯了从代码理解原理,这么跑下来,至少从代码的角度是理解了。初看有点简单,不就是一些节点数据么,但这样也很容易陷入技术细节,所以还是要看几个应用场景的实际例子,从整体上理解。

参考:

原创ZooKeeper入门实战教程,这个博主系列文章非常不错,浅显明了。

Zookeeper 应用实例,入门级的例子。

左晖这个人要不是最近因为去世,我估计也不太知道是谁。首次听到链家,那会觉得是一家新起来的互联网的网站,感觉好像很是风口,因为挖了不少技术的牛人过去。自己那会打算买房时也专门下载了链家的APP,不过感觉那上面的房子好像偏贵。而贝壳呢?好像那会广告打的比较凶。近一两年发现,发现附近多了好多叫德佑的房产中介,最近跟一位卖房的中介聊天时才发现德佑跟贝壳的关系。作为技术人我非常好奇他是如何实现传统行业加互联网的逆袭的?因为我最近两年一直在传统企业做相关的系统,但是成效很低,所以非常想一睹左晖的思路。

《详谈左晖》是本不到180页的32开小册子,我大概用了2个多小时读完了此书。主要是17年和20年对左晖的访谈内容。左晖虽然从事的是传统行业,实际上他是计算机专业毕业,只不过人家没有做技术,首先做过保险,后来因为政策的缘故,改做卖房中介,链家其实很早了,2001年就创建了,虽然早期也有网站,但实际主要做线下了,链家实际是一家有着IT基因的公司,这点很关键。另一家公司就是京东,一开始是摆柜台,后来借助互联网,终成巨头。左晖一直强调传统行业在基础方面的薄弱,主要体现在战略、组织、信息化、IT方面。个人深有体会,传统的中小企业重业务,流程上比较灵活,信息化还停留在表格的阶段。

从0到1

左晖认为线上从0到1比较难,从1到N则相对简单。而线下则是从0到1相对简单,但是从1到N则比较难。刚好传统公司和互联网公司都待过,虽然都是些不出名的一般公司。传统公司看重的是业务,而业务很大程度跟业务员有关系,往往是一个业务模式换个打法,所以很难形成标准化的东西,或者说根本就无法形成,因为这类公司是以盈利为第一目的,所以很少长期考虑,有什么做什么,缺乏标准化的输出,这也是做大比较难的原因。而线上公司一开始就是想着一个大目标去的,所以能不能验证商业模式的成功很关键,不是线下这种有一笔业务就算一笔的事,线上的一般会比较强调标准化,而商业模式的验证则比较难,也就是所谓的从0到1。一单验证模式可行,那么规模就容易上。

总结

传统行业的互联化一定要紧密建立在业务环节,并且要能够利用数字以及互联网来优化或者抽象化一些细节流程,这样才能有所作为,当然也很容易变成信息化与流程的脱节问题,关键是在于落实。

听闻WebAssembly已经有些年了,不过一直没有跑个例子尝试下。主要是使用Rust来编译生成WebAssembly的目标文件,虽然可以使用C/C++,但想想我的Rust学习一直进展不大,不知道用在哪里,刚好Rust对WebAssembly支持也是比较详细。

Rust问题

安装wasm-pack时提示没有安装rustup,虽然我已经哟给Brew安装了rust,最后安装rustup后就好了。

执行cargo install cargo-generate时,加载Crates非常慢,最后使用国内的镜像后顺利完成,这是国内镜像的配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
# 指定镜像
replace-with = 'sjtu'

# 清华大学
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"

# 中国科学技术大学
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"

# 上海交通大学
[source.sjtu]
registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index"

# rustcc社区
[source.rustcc]
registry = "https://code.aliyun.com/rustcc/crates.io-index.git"

总结

我在iOS的Safari试了下,也可以。虽然之前也有其他语言编译为JS的做法,所以主要解决的是如何构建大型web应用的工程问题,现在ES6的普及,好像这类方案渐渐的也不怎么提了。其他语言编译成WebAssembly的做法,主要是为解决性能的问题,把一些跟性能有关系使用WebAssembly,另外WebAssembly可以解决原来JS的老是暴露的问题。理论上其他语言都可以编译WebAssembly,但是主流的还是C/C++、Go、Rust之类的编译为WebAssembly的方式了,不过Web开发我不是全职的那种,有需要了就写写。感觉一般Web开发者也不大会用到,有性能要求的应用可能会用到,但估计写WebAssembly估计也是领域专业的人员。

参考资料

Rust 🦀 and WebAssembly 🕸 ,例子主要按这个来跑的。

WebAssembly Mozilla开发者网站关于WebAssembly的介绍,比较详细。

GTK之前有看过,还专门购买了一本关于GTK的书籍,那是主要是为了学习C的面向对象实现。现在又看GTK是因为准备购买PinePhone,主要看中了PinePhone可以运行多个Linux发行版本。现在无论是iOS还是Android都不能自由的使用喜欢的语言。作为一个曾经的iOS开发者,iOS的运行速度还真的没得说。Android因为使用Java缘故,总还是没有办法更快似乎,所以一直想着整一台Linux手机自己定制UI,不过找一台Linux手机实在是太难了,主要是目前手机都是这些公司定制的硬件,所以驱动是个大问题。

为什么选择GTK呢?我也看了下QT,之前也简单的使用了下,确实非常适合开发UI程序,不过授权方面还是有点问题,GTK没有这个问题。另外无论C/C++对我来说都不是太大的问题,所以还是用GTK了。

考虑到主要是用来开发Linux的,所以直接在Ubuntu下跑个例子看下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdnoreturn.h>
#include <stdatomic.h>
#include <assert.h>
#include <gtk/gtk.h>
#include <glib/gprintf.h>

#ifndef var
#define var __auto_type
#endif

static void PrintMsg(GtkWidget *widget, gpointer window) {
if(window != NULL)
g_printf("Button clicked\n");

var dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, u8"按钮已被按下!");
gtk_window_set_title(GTK_WINDOW(dialog), u8"请注意");

gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}

int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
var window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GTK Test");
gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);

var button = gtk_button_new_with_label("Button");
gtk_widget_set_halign(button, GTK_ALIGN_START);
gtk_widget_set_valign(button, GTK_ALIGN_START);
gtk_widget_set_tooltip_text(button, "This is a button widget");
g_signal_connect(button, "clicked", G_CALLBACK(PrintMsg), window);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
static_assert(sizeof(int) == 4, "Not compatible architecture!");
puts("Will enter GTK runloop...");
gtk_main();
puts("Program terminated!");
return 0;
}

问题

1
/usr/include/pango-1.0/pango/pango-coverage.h:28:10: fatal error: 'hb.h' file not found

解决方案运行pkgconf --cflags harfbuzz

1
/usr/bin/ld: cannot find -lBlocksRuntime

解决方案运行sudo apt-get install libblocksruntime-dev

编译

1
clang main.c -std=gnu11 -fblocks -lBlocksRuntime -I/usr/include/glib-2.0/ -I/usr/include/atk-1.0/ -I/usr/include/gdk-pixbuf-2.0/ -I/usr/include/cairo/ -I/usr/include/pango-1.0/ -I/usr/lib/x86_64-linux-gnu/glib-2.0/include/ -I/usr/include/gtk-3.0/ -I /usr/include/harfbuzz/ -L/usr/lib/x86_64-linux-gnu/ -lgtk-3 -lgobject-2.0 -lpangocairo-1.0 -lgio-2.0 -latk-1.0 -lgdk-3 -lglib-2.0    -o gtk-test

总结

GTK开发确实比较麻烦,后续还的配置个好的IDE环境,不过想想要求的OC开发,其实对于C还是蛮亲切的。其实一旦涉及真实项目,更多都是对框架的学习了,先挖个坑。

缘起

没有去天童寺之前我就在网上阅读了《如净和尚语录》,后来就想着有没有纸质版的,刚好去天童寺的时候,我请了一本,是线装繁体竖排本,发现很多字不知道读音,有点阅读困难,另外携带也不太方便,因为是16开的。后来在孔夫子网上淘到了一本应该自己印刷的带拼音的简体版本,而且还是32开的。那段时间经常拿着在地铁上读,确实让我读进去了一些内容,不过不足的是发现里面居然缺少了大概两页的内容,另外有些无法打出来的字体直接用问号显示。于是就有想自己编辑一本带拼音的繁体版本,虽然不到一百页的内容,实际整理的时间也差不多有半个月时间,前后又反复的阅读修正了一些错误。当时想着再印刷出来,不过实际上到现在也没有印刷出来,这个版本的PDF非常适合在手机上阅读,所以也觉得没有必要印刷出来了。

《如净和尚语录》注音版我在Github上建了个项目,并把PDF文件放在了方面,有兴趣的可以拉下来读读。项目地址

后记

后来那本32开的小册子在去年去郑州出差的时候,走的太急,落在了场子里面,希望有缘人能得到。我自己整理的这个PDF注音版本现在也经常在手机偶尔看看。

缘起

17年的时候为了练习Swift,也想验证下自己对于产品方面的想法,当时流行的应用都是通过爬虫抓取内容网站的数据,清洗数据后再通过推荐引擎展示给用户,当时我们公司也做的是这类应用,只不过后来没有成功。作为练手的业余项目,我并没有打算也做类似的项目,那会我主要做iOS客户端,对于服务端也不是很在行,而且当时这类应用有个问题是,你需要搜集大量的内容网站,可这一定是用户喜欢的吗?当时想关于用户喜欢什么样的内容让用户自己去找吧,而我只提供一个工具,最后选定了RSS阅读器。在App Store上找了一些RSS应用调研,很多应用是国外人开发的,交互上不太符合国人的习惯,另外不少应用需要注册后才能使用,当然有些还是付费的。觉得还是有改进的空间的,目标是开发一款简单的应用,首先不需要登录注册之类的,用户添加自己的RSS源,然后拉取数据,展示详情,最后这是一款免费的应用。很快这款应用就被开发出来上线了,过了段时间我看了下数据,有不少用户评价,之前的应用用户很少评价,我一直觉得用户能评价应用很难,至少我自己都很少评价了。用户多半是通过搜索下载的,这让我想到了长尾效应,即使如此小众的应用,总有它的用户群体。

瓶颈

经过一年多的更新,应用基本上算是稳定了,18年底后就没有再更新过了,一方面工作有些变化,之前公司的新闻类应用已经不再运营了。而我也做了一年多的Unity游戏应用,iOS开发实际上很少了,之后离开了原来的公司。短暂的停留后,开始到现在的公司做服务器方面的开发,iOS的开发者账号也在到期后没有再续,一方面我想试下,如果账号不续费,应用还在商店吗?当然最主要的是,工作太忙iOS已经很久都不弄了,对于RSS阅读器应用,也没有更好的想法。最后每年花着钱,却没有一分的收入,这可能对于做一款免费应用者或多或少的有点失落吧,毕竟需要生活。苹果账号没有续费后,应用结果就被下架了。大概一年的时间,我没有再想关于苹果开发的事。

重启

21年四月份的时候,我又把苹果账号给续费了,在做企业应用的一年里,发现相比行业的复杂性,个人应用似乎更能好把握些吧。另外作为一个软件开发者,很大程度是依赖于工作的,工作上我们可以沉迷于技术的细节,但从宏观上来看,至少这么些年做的应用都是商业上的不成功吧,当然作为开发者很难去左右这个结果。而作为App Store的独立开发者可以很大程度上让自己在整个从产品的构想到设计,开发,到最后的营销及运营,都能有很大的锻炼。对于开发人员而言,App Store依旧施展自己创意和想法的最好平台。

于是我决定继续RSS阅读器的开发,iOS 13启用夜间模式后,之前的应用确实一些问题,新版本主要是适配夜间模式,之前有用户也提到是否可以开发夜间模式的显示,不过一直没有启动。因为界面比较少很快也就适配完了,内容显示那块我也学习到了如何使用CSS适配夜间模式。

另外做了一个决定新版本将只支持iOS 13以上的版本,夜间模式和Swift UI都只能在iOS 13以上的版本使用,只想专注在功能的开发上,而不是兼容上。我自己的iPhone 6s都已经升级到iOS 14了,所以这个决定也是有一定的依据的。

未来

关于RSS阅读器,我依旧没有更好的想法,实际上在新版本中我又去掉了几个已经失效的RSS源,而有些RSS源已经很久没有更新了。我一度想再找一些不错的源,可发现真的很难,很多都是好几年前的帖子,所以RSS阅读器的前景并不乐观,这是一块正在极速萎缩的市场,而很多开发者估计会放弃,更不可能成为一款公司的产品存在。难怪早在2012的时候谷歌就关闭了RSS阅读应用Google Reader。

升级其他软件的时候居然把Ruby版本升级到了3.0,而Octopress依赖的Jekyll的版本是2.0,对Ruby版本有限制,新版本用不了。本来想换个静态博客的框架来着,想着自己好歹也是全栈工程师,自己升级下吧。

Octopress其实是基于Jekyll的定制静态博客框架,生成静态内容主要依赖于Jekyll了。看了下Jekyll已经到4.0了,干脆直接升级到4.0了。不过发现一些问题,4.0后放弃了对RDiscount、Pygments的支持,默认的Markdown解析用的是Kramdown。Octopress分别使用了RDiscount、Pygments,所以这块只能弃用了,改用Kramdown。

升级后发现代码高亮有点问题,4.0因为弃用了Pygments,所以只能使用默认的Rouge。Octopress自己实现了一个代码高亮的样式,但是有兼容问题,最后直接弃用。使用Rouge生成Github的样式,最后修改了下,最终变成现在的样子,总之还是不太喜欢之前的暗背景的样式。

生成文章方面,因为Jekyll的一些变更,遇到三个问题,首先是文章列表是生序排序的,修改了下也好了。其次发现首页没有文章,主要是没有安装jekyll-paginate的缘故。最后就是生成的文章没有时间,Octopress自定义了一个时间格式的钩子,但是没有起作用,最后直接使用原生的方式解决了。

博客预览使用rake preview会报错,最后直接在Public目录下起了个Python的Web服务器,这个并不影响使用。

总结

现在还在用Octopress人确实不多了,多半都是不更新的僵尸博客了。其实这些静态博客解决方案原理大同小异的,之前有人吐槽Octopress生成博客文章慢,实际上主要是Jekyll慢了,不过现在都4.0了,目前我觉得速度还是能接受的。

既然Octopress作者都不维护了,想着后续问题修复的差不多了,把修改的版本放出了,也算是尽一份力吧。

参考:
Jekyll 4.0.0 Released