关于跨平台:macos下编译openSSLlibhv编译

参考:https://juejin.cn/post/7265042701065961508 export OPENSSL_CRYPTO_LIBRARY=/usr/local/Cellar/openssl@1.1/1.1.1texport OPENSSL_INCLUDE_DIR=/usr/local/Cellar/openssl@1.1/1.1.1t/includeexport OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl@1.1/1.1.1texport LDFLAGS="/usr/local/Cellar/openssl@1.1/1.1.1t/lib/libssl.a /usr/local/Cellar/openssl@1.1/1.1.1t/lib/libcrypto.a"export PKG_CONFIG_PATH=/usr/local/Cellar/openssl@1.1/1.1.1t环境变量参数设置为这些才行

August 31, 2023 · 1 min · jiezi

关于跨平台:Flutter-多引擎支持-PlatformView-以及线程合并解决方案

作者:字节挪动技术-李皓骅 摘要本文介绍了 Flutter 多引擎下,应用 PlatformView 场景时不能绕开的一个线程合并问题,以及它最终的解决方案。最终 Pull Request 曾经 merge 到 Google 官网 Flutter 仓库: https://github.com/flutter/en... 本文关键点: 线程合并,实际上指的并不是操作系统有什么高级接口,能够把两个 pthread 合起来,而是 flutter 引擎中的四大 Task Runner 里,用一个 Task Runner 同时生产解决两个 Task Queue 中排队的工作。线程合并问题,指的是 Flutter 引擎四大线程(Platform 线程、UI 线程、Raster 线程、IO 线程)其中的 Platform 线程和 Raster 线程在应用 PlatformView 的场景时须要合并和拆散的问题。之前的官网的线程合并机制,只反对一对一的线程合并,但多引擎场景就须要一对多的合并和一些相干的配套逻辑。具体请看下文介绍。对于 Flutter 引擎的四大 Task Runner 能够参考官网 wiki 中的 Flutter Engine 线程模型 : https://github.com/flutter/fl...本文介绍的线程合并操作(也就实现了一个 looper 生产两个队列的音讯的成果),见如下的示意图,这样咱们能够有个初步的印象: 背景介绍什么是 PlatformView?首先,介绍下 PlatformView 是什么,其实它简略了解成——平台相干的 View 。也就是说,在Android 和 iOS 平台原生有这样的控件,然而在Flutter的跨平台控件库里没有实现过的一些Widget,这些控件咱们能够应用Flutter提供的PlatformView的机制,来做一个渲染和桥接,并且在下层能够用Flutter的办法去创立、管制这些原生View,来保障两端跨平台接口对立。 比方WebView,地图控件,第三方广告SDK等等这些场景,咱们就必须要用到PlatformView了。 举一个例子,下图就是 Android 上应用 PlatformView 机制的 WebView 控件和 Flutter控件的混合渲染的成果: ...

September 16, 2021 · 8 min · jiezi

用JS开发跨平台桌面应用从原理到实践

导读使用Electron开发客户端程序已经有一段时间了,整体感觉还是非常不错的,其中也遇到了一些坑点,本文是从【运行原理】到【实际应用】对Electron进行一次系统性的总结。【多图,长文预警~】 本文所有实例代码均在我的github electron-react上,结合代码阅读文章效果更佳。另外electron-react还可作为使用Electron + React + Mobx + Webpack 技术栈的脚手架工程。 一、桌面应用程序 桌面应用程序,又称为 GUI 程序(Graphical User Interface),但是和 GUI 程序也有一些区别。桌面应用程序 将 GUI 程序从GUI 具体为“桌面”,使冷冰冰的像块木头一样的电脑概念更具有 人性化,更生动和富有活力。我们电脑上使用的各种客户端程序都属于桌面应用程序,近年来WEB和移动端的兴起让桌面程序渐渐暗淡,但是在某些日常功能或者行业应用中桌面应用程序仍然是必不可少的。 传统的桌面应用开发方式,一般是下面两种: 1.1 原生开发直接将语言编译成可执行文件,直接调用系统API,完成UI绘制等。这类开发技术,有着较高的运行效率,但一般来说,开发速度较慢,技术要求较高,例如: 使用C++ / MFC开发Windows应用使用Objective-C开发MAC应用1.2 托管平台一开始就有本地开发和UI开发。一次编译后,得到中间文件,通过平台或虚机完成二次加载编译或解释运行。运行效率低于原生编译,但平台优化后,其效率也是比较可观的。就开发速度方面,比原生编译技术要快一些。例如: 使用C# / .NET Framework(只能开发Windows应用)Java / Swing不过,上面两种对前端开发人员太不友好了,基本是前端人员不会涉及的领域,但是在这个【大前端????】的时代,前端开发者正在想方设法涉足各个领域,使用WEB技术开发客户端的方式横空出世。 1.3 WEB开发使用WEB技术进行开发,利用浏览器引擎完成UI渲染,利用Node.js实现服务器端JS编程并可以调用系统API,可以把它想像成一个套了一个客户端外壳的WEB应用。 在界面上,WEB的强大生态为UI带来了无限可能,并且开发、维护成本相对较低,有WEB开发经验的前端开发者很容易上手进行开发。 本文就来着重介绍使用WEB技术开发客户端程序的技术之一【electron】 二、Electron Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。 Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的。 2.1 使用Electron开发的理由:1.使用具有强大生态的Web技术进行开发,开发成本低,可扩展性强,更炫酷的UI2.跨平台,一套代码可打包为Windows、Linux、Mac三套软件,且编译快速3.可直接在现有Web应用上进行扩展,提供浏览器不具备的能力4.你是一个前端????????~当然,我们也要认清它的缺点:性能比原生桌面应用要低,最终打包后的应用比原生应用大很多。 2.2 开发体验兼容性 虽然你还在用WEB技术进行开发,但是你不用再考虑兼容性问题了,你只需要关心你当前使用Electron的版本对应Chrome的版本,一般情况下它已经足够新来让你使用最新的API和语法了,你还可以手动升级Chrome版本。同样的,你也不用考虑不同浏览器带的样式和代码兼容问题。 Node环境 这可能是很多前端开发者曾经梦想过的功能,在WEB界面中使用Node.js提供的强大API,这意味着你在WEB页面直接可以操作文件,调用系统API,甚至操作数据库。当然,除了完整的 Node API,你还可以使用额外的几十万个npm模块。 跨域 你可以直接使用Node提供的request模块进行网络请求,这意味着你无需再被跨域所困扰。 强大的扩展性 借助node-ffi,为应用程序提供强大的扩展性(后面的章节会详细介绍)。 2.3 谁在用Electron 现在市面上已经有非常多的应用在使用Electron进行开发了,包括我们熟悉的VS Code客户端、GitHub客户端、Atom客户端等等。印象很深的,去年迅雷在发布迅雷X10.1时的文案: 从迅雷X 10.1版本开始,我们采用Electron软件框架完全重写了迅雷主界面。使用新框架的迅雷X可以完美支持2K、4K等高清显示屏,界面中的文字渲染也更加清晰锐利。从技术层面来说,新框架的界面绘制、事件处理等方面比老框架更加灵活高效,因此界面的流畅度也显著优于老框架的迅雷。至于具体提升有多大?您一试便知。你可以打开VS Code,点击【帮助】【切换开发人员工具】来调试VS Code客户端的界面。 三、Electron运行原理 ...

June 10, 2019 · 8 min · jiezi

深入理解Flutter多线程

该文章属于<简书 — 刘小壮>原创,转载请注明:<简书 — 刘小壮> https://www.jianshu.com/p/54da18ed1a9e Flutter默认是单线程任务处理的,如果不开启新的线程,任务默认在主线程中处理。 事件队列和iOS应用很像,在Dart的线程中也存在事件循环和消息队列的概念,但在Dart中线程叫做isolate。应用程序启动后,开始执行main函数并运行main isolate。 每个isolate包含一个事件循环以及两个事件队列,event loop事件循环,以及event queue和microtask queue事件队列,event和microtask队列有点类似iOS的source0和source1。 event queue:负责处理I/O事件、绘制事件、手势事件、接收其他isolate消息等外部事件。microtask queue:可以自己向isolate内部添加事件,事件的优先级比event queue高。 这两个队列也是有优先级的,当isolate开始执行后,会先处理microtask的事件,当microtask队列中没有事件后,才会处理event队列中的事件,并按照这个顺序反复执行。但需要注意的是,当执行microtask事件时,会阻塞event队列的事件执行,这样就会导致渲染、手势响应等event事件响应延时。为了保证渲染和手势响应,应该尽量将耗时操作放在event队列中。 async、await在异步调用中有三个关键词,async、await、Future,其中async和await需要一起使用。在Dart中可以通过async和await进行异步操作,async表示开启一个异步操作,也可以返回一个Future结果。如果没有返回值,则默认返回一个返回值为null的Future。 async、await本质上就是Dart对异步操作的一个语法糖,可以减少异步调用的嵌套调用,并且由async修饰后返回一个Future,外界可以以链式调用的方式调用。这个语法是JS的ES7标准中推出的,Dart的设计和JS相同。 下面封装了一个网络请求的异步操作,并且将请求后的Response类型的Future返回给外界,外界可以通过await调用这个请求,并获取返回数据。从代码中可以看到,即便直接返回一个字符串,Dart也会对其进行包装并成为一个Future。 Future<Response> dataReqeust() async { String requestURL = 'https://jsonplaceholder.typicode.com/posts'; Client client = Client(); Future<Response> response = client.get(requestURL); return response;}Future<String> loadData() async { Response response = await dataReqeust(); return response.body;}在代码示例中,执行到loadData方法时,会同步进入方法内部进行执行,当执行到await时就会停止async内部的执行,从而继续执行外面的代码。当await有返回后,会继续从await的位置继续执行。所以await的操作,不会影响后面代码的执行。 下面是一个代码示例,通过async开启一个异步操作,通过await等待请求或其他操作的执行,并接收返回值。当数据发生改变时,调用setState方法并更新数据源,Flutter会更新对应的Widget节点视图。 class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); }}FutureFuture就是延时操作的一个封装,可以将异步任务封装为Future对象。获取到Future对象后,最简单的方法就是用await修饰,并等待返回结果继续向下执行。正如上面async、await中讲到的,使用await修饰时需要配合async一起使用。 ...

April 26, 2019 · 2 min · jiezi

灵活高效的跨平台打字练习软件Klavaro

输入法方案云云近期我将双拼输入从自然码转回小鹤双拼了,这得多亏了Rime这个跨平台的输入法可自定义挂载小鹤音形。从不同平台换用过不同的双拼方案,搜狗双拼,微软双拼,永码,自然码,以及最终换回小鹤双拼。由于最初小鹤双拼只支持Windows平台的,我用IOS系统和Linux系统后就不得改用搜狗(iso最早自带的双拼方案)和自然码了,但是这些双拼方案都只有音,缺失了形。高频的键位错误我基本上都是盲打输入的,但是由于一些手指的灵活度远远不够,导致一些按键错字频率较高。(小拇指弱弱哭泣)打字练习所以我要选择一个输入软件来练习一些灵活不够手指。以此来提高击键的准确率以及降低回退率。当然有了输入法之间的惨痛经历,我选择软件尽量都选择垮平台优先。(GUN/Linux平台是最优考虑的)介绍Klavaro是免费的键盘打字指导和练习软件,从打字的指法教学、基本练习到加快速度均帮你安排好课程,练好英文打字,不管你之后想学哪一种中文输入法,熟练键盘之后,文书编辑、游戏聊天相信都能够得心应手使用方法:下载并完成安装开启 Klavaro 后,便可依据介面的指示来挑选适合自己程度的课程建议不管初学还是进阶,都应该先看 Introduction 来学习软体的操作步骤初学与进阶可在1 ~ 4 功能来进行选择每次练习结束,均会显示所用时间、错误率等资讯,需要历史的练习纪录也可点击[Progress]来查看下载地址Klavaro - Touch Typing Tutor 这个官网是不好看,但是软件设计还是OK的。可以自己上传练习文章。但是段落只能支持10行。所以上传代码段落就不推荐了。todo补充图片和使用感受。

April 10, 2019 · 1 min · jiezi

Electron 跨平台应用开发入门

本文由 Deguang 发表于 码路-技术博客Tips:Electron 介绍Electron 环境搭建进程通信调用系统 APIWrite once, run anywhere. Sun 公司 Java 介绍词。端的跨平台实现方案有哪些?Web(浏览器)移动端设备:Hybrid(混合)、React Native、Weex、Flutter桌面端:NW.js、Electron、Flutter(~1.0)Electron1. 什么是 ElectronElectron 是由 Github 开发,用 HTML,CSS 和 JavaScript 来构建跨平台桌面应用程序的一个开源库。 Electron 通过将 Chromium 和 Node.js 合并到同一个运行时环境中,并将其打包为 Mac,Windows 和 Linux 系统下的应用来实现这一目的。开发者只要使用 Web 技术完成业务部分即可,这就是对前端开发者最友好的地方。2. Electron 应用结构Elctron 应用运行时,分为 主进程 和 渲染进程。主进程 (Main Process)package.json 中定义的 main 脚本运行的进程,被定义为 主进程,一个 Electron 应用有且只有一个主进程。主进程可以创建 Web 页面窗口,并传入 URL 加载网页作为图形界面。渲染进程 (Renderer Process)Electron 的每个页面都有它自己的进程,叫做渲染进程。每一个渲染进程都是独立的,只关心它所运行的页面;进程通信(IPC, Inter-Process Comminication)Electron 不允许渲染进程调用系统 GUI 的原生 API,例如打开文件选择之类的系统操作,这种对系统 API 的调用只允许存在与主进程中。渲染进程,也就是页面调用 系统 API,需要由主进程担任桥梁的作用,来完成操作并得到返回结果。这里有两种方式可以实现进程通信:使用 ipcRenderer 和 ipcMain 模块通信ipcRendereripcRenderer 是从渲染进程到主进程的异步通信,可以使用它提供的一些方法从渲染进程发送同步或异步的消息ipcMainipcMain 是从主进程到渲染进程的异步通信,处理从渲染进程发送出来的异步和同步信息,// renderer processconst {ipcRenderer} = requier(’electron’)// asyncipcRenderer.on(‘PICK_FILE_CALLBACK’, (event, arg) => { console.log(arg) // files})ipcRenderer.send(‘PICK_FILE’)// syncipcRenderer.sendSync(‘PICK_FILE_SYNC’) // files// main processconst {ipcMain} = require(’electron’)// asyncipcMain.on(‘PICK_FILE’, (event, data) => { // do something … event.sender.send(‘PICK_FILE_CALLBACK’, ‘files’)})// syncipcMain.on(‘PICK_FILE_SYNC’, (event, data) => { // do something event.returnValue = ‘files’})more: ipcRenderer、 ipcMainipcMain、ipcRenderer 又是什么?ipcMain 和 ipcRenderer 都是 EventEmitter 类的一个实例。 EventEmitter 类是由 NodeJS 中的 events 模块定义、导出的。EventEmitter 类是 NodeJS 事件的基础,实现了事件模型需要的接口, 包括 addListener,removeListener, emit 及其它工具方法. 同原生 JavaScript 事件类似, 采用了发布/订阅(观察者)的方式, 使用内部 _events 列表来记录注册的事件处理器。使用 remote 进行 RPC 通信在渲染进程中使用主系统模块。// 渲染进程中const { BrowserWindow } = require(’electron’).remotelet win = new BrowserWindow({width: 600, height: 400})win.loadURL(‘https://github.com’)remote 对象remote 模块返回的对象表示主进程中的一个对象,调用远程对象、远程函数时,相当于发送同步进程信息。let win = new BrowserWindow({width: 600, height: 400}),相当于在主进程中创建了一个 BrowserWindow 对象,然后在渲染进程中返回了相应的远程对象。more: remote3. APIElectron APIElectron 提供了大量 API 以优化桌面应用开发体验。Electron API 都有指派进程类型(文档中标记):只能用于主进程(BrowserWindow)、只能用于渲染进程(remote) 和 两种进程中均可使用。Node.js APIElectron 同时在主进程和渲染进程中对Node.js 暴露了所有的接口原生 API// Native APIconst fs = require(‘fs’)fs.readFile(…)第三方 Node.js 模块npm install -save axiosconst axios = require(‘axios’)axios.interceptors.request(…)4. 第一个 Electron 应用环境依赖:Node.js && Npm (Yarn)Node.js: 安装参考 https://nodejs.orgYarn: npm i -g yarn推荐使用 yarn 构建 Electron,使用 npm 大概率出现依赖安装失败的情况起步0.0.1 创建 demo 目录,执行 npm init,配置启动脚本 “start”: “electron .”// package.json{ “name”: “demo”, “version”: “1.0.0”, “description”: “”, “main”: “main.js”, “scripts”: { “start”: “electron .” }, “author”: “”, “license”: “ISC”}0.0.2 创建 main.js && index.html➜ demo tree.├── index.html├── main.js└── package.json0.0.3 安装 Electronnpm install –save-dev electron0.0.4 创建一个窗口:// main.jsconst { app, BrowserWindow } = require(’electron’)let winfunction createWindow() { win = new BrowserWindow({ width: 600, height: 400}) win.loadFile(‘index.html’)}app.on(‘ready’, createWindow)<!– index.html –><body> <h1>Hello, Electron.</h1> Node.js: <script>document.write(process.versions.node)</script>; Chrome: <script>document.write(process.versions.chrome)</script>; Electron: <script>document.write(process.versions.electron)</script>.</body>0.0.5 执行 npm run start,即可打开一个窗口,显示 Hello, Electron.。0.0.6 更新main.js,尝试更多功能:// main.jsconst { app, BrowserWindow } = require(’electron’)let winfunction createWindow() { win = new BrowserWindow({ width: 600, height: 400}) win.loadFile(‘index.html’) win.webContents.openDevTools() win.on(‘close’, () => { // 关闭窗口、清空 win 对象 win = null }) app.on(‘window-all-closed’, () => { // macOS 应用通常关闭窗口还是保活的,只有使用 cmd + q 强制退出 if (process.platform !== ‘darwin’) { app.quit() } }) app.on(‘activate’, () => { // macOS 点击 dock 图标并且没有其他窗口打开时,重新创建一个窗口 if (win === null) { createWindow() } })}app.on(‘ready’, createWindow)0.0.7 重新执行 npm run start 启动应用。more: 更多 Electron API 使用,可以尝试官方 electron-api-demos打包应用electron-packager# 安装 electron-packagernpm install electron-packager –save-dev# 基本命令electron-packager <sourcedir> <appname> –platform=<platform> –arch=<arch> [optional flags…]# 例如:# electron-packager ./ demo –out ./dist –app-version 1.0.0 –overwrite参数说明:sourcedir:项目路径appname:应用名称platform:构建平台的应用(Windows、Mac 还是 Linux)arch:x86、 x64 还是两个架构都用optional options:可选选项为了更方便的构建,在 package.json 增加构建脚本"package": “electron-packager ./ demo –out ./dist –app-version 1.0.0 –overwrite"执行 npm run packagemore: electron-packagerelectron-builder# 安装 electron-buildernpm install electron-builder –save-dev// package.json 增加如下配置"build”: { “productName”: “xxx”, “appId”: “com.xxx.xxx”},“scripts”: { “dist”: “electron-builder”}执行 npm run dist,完成打包more: electron-builderps:mac 跨平台 打包 win32 需要 wine客户端数据库Electron 应用可以在本地使用客户端数据库,来维护本地数据,进行相应的 CURD 操作,下面是常用的一些轻量级数据库:nedbsqliteRxdb…参考文档:Electron 文档 ...

March 29, 2019 · 3 min · jiezi

使用Rust + Electron开发跨平台桌面应用 ( 二 )

前言在上一篇文章使用Rust + Electron开发跨平台桌面应用 ( 一 )中,我们将Rust + Electron结合起来,使用Rust编写核心业务逻辑,并编译成node库提供给Electron的UI界面调用,但是在上一篇文章中发现遇到了很多问题,尤其是Electron 的版本和 Rust编译出来的版本必须要一致,否则会无法调用成功,这就很坑了,所以为了改变这一情况,今天我们将使用另一种方式将Rust的代码提供给Js进行调用,这就是FFI。FFI是什么FFI(Foreign Function Interface)是用来与其它语言交互的接口,由于现实中很多程序是由不同编程语言写的,必然会涉及到跨语言调用,这时一般有两种解决方案:1、将函数做成一个服务,通过进程间通信(IPC)或网络协议通信(RPC, RESTful等);2、直接通过 FFI 调用。前者需要至少两个独立的进程才能实现,而后者直接将其它语言的接口内嵌到本语言中,所以调用效率比前者高。Rust作为系统级编程语言,也是对FFI提供了完善的支持。mangle由于rust支持重载,所以函数名会被编译器进行混淆,就像c++一样。因此当你的函数被编译完毕后,函数名会带上一串表明函数签名的字符串。这样的函数名为ffi调用带来了困难,因此,rust提供了#[no_mangle]属性为函数修饰。 对于带有#[no_mangle]属性的函数,rust编译器不会为它进行函数名混淆, 如:#[no_mangle]pub extern fn test() {}下面我们来编写一个thread_count.rs,其实跟寻常的rust代码没有什么区别:#[no_mangle]pub extern fn threadcount(x: i32) -> i32 { let result: i32 = num_cpus::get() as i32; return result * x;}指定库类型rust默认编译成rust自用的rlib格式库,要让rust编译成动态链接库或者静态链接库,需要显示指定,一共有三种方式,我这里采用的是直接在Cargo.Toml文件中指定,如下:[lib]name = “thread_count"crate-type = [“dylib”]需要注意的是name,必须符合rust的包结构,能够在src目录下找到。我们执行cargo build命令,可以看到,在/target/debug目录下生成了我们需要的文件libthread_count.dylibJS使用rust的动态链接库那么我们要如何在JS中调用rust生成dylib呢?答案就是ffi-napi,我们使用ffi-napi这个包来在js中调用ffi,话不多说,直接看代码let ffi = require(‘ffi-napi’);let path = require(‘path’);let threadCount = ffi.Library(path.join(__dirname, ‘./target/debug/libthread_count’), { threadcount: [‘int’, [‘int’]]});let result = threadCount.threadcount(12);console.log(“thead_count: " + result);结果如下:好了,到此为止,我们就成功的将rust编译成动态链接库给JS调用了,这种方式是我觉得比较好的一种方式,虽然引入函数的方式比较丑,但是我们不用担心node版本的问题。结语虽然FFI是一种我认为比较好的方式,但是它也不是完美无缺的,例如,在跨越FFI的过程中,我们会丢失rust的类型信息,从而引发安全性问题,当然这也不是没有解决办法,我们可以使用rust的Box来包装我们的类型,这个可以单独开一篇文章来讲述,就不展开了(先挖个坑,哪天想起来再填)

January 31, 2019 · 1 min · jiezi