网易首页 > 网易号 > 正文 申请入驻

在IDE插件中接入JCEF

0
分享至

01 项目背景

当前的开发环境存在多种不同语言的 IDE,如 JetBrains 全家桶、Eclipse、Android Studio 和 VS Code 等等。由于每个 IDE 各有其特定的语言和平台要求,因此开发 IDE 插件时,需要投入大量资源才能尽可能覆盖大部分工具。同时,代码复用困难、用户体验不一致等问题又进一步加重了研发资源的负担。

在调研过程中,我们发现如今的大多数开发工具都支持集成 CEF,而 CEF 提供的跨平台解决方案正可以有效解决上述问题。

02 关于 CEF 和 JCEF

CEF(Chromium Embedded Framework)是一个开源项目,它基于 Google Chromium 浏览器引擎,允许开发人员将完整的浏览器功能嵌入到自己的应用程序中。

通过 CEF,开发者可以利用现代 Web 技术来创建强大的桌面应用程序,并实现与 Web 内容的无缝集成。如此一来,开发者便可以利用 CEF 的功能和灵活性,为各种开发工具提供统一的、高质量的插件体验。

JCEF(Java Chromium Embedded Framework)是基于 CEF 的一个特定版本,专门为 Java 应用程序而生。本文内容也主要围绕 JCEF 展开。

JCEF 和其他产品的对比

JCEF vs JxBrowser

JxBrowser 和 JCEF 都允许将 Chromium 浏览器功能嵌入到 Java 应用程序中。其中,JxBrowser 是商业产品,而 JCEF 是开源框架,且商业授权非常友好。

此外,JxBrowser 在独立的本地进程中启动 Chromium,而 JCEF 则是在 Java 进程内启动。JCEF 会快速初始化 Chromium,同时消耗 Java 进程的内存和 CPU;创建多个 Chromium 实例也会占用更多资源。

JCEF vs JavaFX

JavaFX 使用的内置浏览器组件是 WebView,其在不同平台上的实现有所不同。例如,在 macOS 上使用 WebKit,在 Windows 上默认为 Internet Explorer,而新版本的 JavaFX 则默认使用 JCEF。

这种不一致性会增加插件适配的难度,降低整体开发效率。

Java 进程与 JCEF 交互


03 如何在 IDE 插件中接入 JCEF?

下面以 LigaAI JetBrains 插件为例,介绍集成 JCEF 的过程。

1. 在 Java 代码里创建相应的 JcefBrowser。

static JBCefBrowser createBrowser ( Project project ) {
JBCefClient client = JBCefApp . getInstance ( ) . createClient ( ) ;
//CefMessageRouter 用于处理来自 Chromium 浏览器的消息和事件,
//前端代码可以通过innerCefQuery和innerCefQueryCancel发起消息给插件进行处理
CefMessageRouter.CefMessageRouterConfig routerConfig =
new CefMessageRouter.CefMessageRouterConfig ( "innerCefQuery" , "innerCefQueryCancel" ) ;
CefMessageRouter messageRouter = CefMessageRouter . create (routerConfig , new MessageRouterHandler ( ) ) ;
client . getCefClient ( ) . addMessageRouter (messageRouter ) ;
//用于处理以http://inner/开头的请求。用于拦截特定请求,转发请求到本地以获取本地资源
CefApp . getInstance ( )
. registerSchemeHandlerFactory ( "http" , "inner" , new DataSchemeHandlerFactory ( ) ) ;
return new JBCefBrowser (client , "" ) ;
}

2. 加载对应的 URL,渲染页面。

public static void loadURL ( JBCefBrowser browser , String url ) {
//如果不需要设置和浏览器显示相关的,可忽略
browser . getJBCefClient ( )
. addDisplayHandler (settingsDisplayHandler , browser . getCefBrowser ( ) ) ;
browser . loadURL (url ) ;
}

3. Java 进程拦截前端发起的获取静态资源的请求。 如果直接访问外部资源,则不需要做拦截,这一步可忽略。

import com.intellij.liga.web.WebviewClosedConnection ;
import com.intellij.liga.web.WebviewOpenedConnection ;
import com.intellij.liga.web.WebviewResourceState ;
import com.intellij.openapi.vfs.VfsUtil ;
import com.intellij.openapi.vfs.VirtualFile ;
import org.apache.commons.lang.StringUtils ;
import org.cef.callback.CefCallback ;
import org.cef.handler.CefResourceHandler ;
import org.cef.misc.IntRef ;
import org.cef.misc.StringRef ;
import org.cef.network.CefRequest ;
import org.cef.network.CefResponse ;

import java.io.File ;
import java.net.URL ;

//继承 CefResourceHandler 接口,自定义处理 Chromium 浏览器加载的资源(如网页、图像、样式表等)。
//通过实现该接口,可以覆盖默认的资源加载行为,并提供自定义的资源加载逻辑。
public class DataResourceHandler implements CefResourceHandler {

private WebviewResourceState state ;

/**
* 用于处理资源请求,你可以通过该方法获取请求的 URL、请求头部信息,并返回相应的响应结果。
*/
public boolean processRequest ( CefRequest cefRequest , CefCallback cefCallback ) {
String url = cefRequest . getURL ( ) ;
//判断请求是否是用于获取内部静态资源的,如果是则拦截请求,并从项目里对应配置获取对应文件返回
//如果是请求外部资源,则跳过
if ( StringUtils . isNotBlank (url ) && url . startsWith ( "http://inner" ) ) {
String pathToResource = url . replace ( "http://inner" , "/front/inner" ) ;
pathToResource = pathToResource . split ( "\\?" ) [ 0 ] ;
URL resourceUrl = getClass ( ) . getResource (pathToResource ) ;
VirtualFile f = VfsUtil . findFileByURL (resourceUrl ) ;
resourceUrl = VfsUtil . convertToURL (f . getUrl ( ) ) ;
try {
this .state = ( WebviewResourceState ) new WebviewOpenedConnection (resourceUrl . openConnection ( ) ) ;
} catch ( Exception exception ) {
//log output
}
cefCallback.Continue ( ) ;
return true ;
}
return false ;
}

/**
* 用于设置资源响应的头部信息,例如 Content-Type、Cache-Control 等。
*/
public void getResponseHeaders ( CefResponse cefResponse , IntRef responseLength , StringRef redirectUrl ) {
this .state . getResponseHeaders (cefResponse , responseLength , redirectUrl ) ;
}

/**
* 用于读取资源的内容,可以从这个方法中读取资源的数据并将其传递给浏览器
*/
public boolean readResponse ( byte [ ] dataOut , int designedBytesToRead , IntRef bytesRead , CefCallback callback ) {
return this .state . readResponse (dataOut , designedBytesToRead , bytesRead , callback ) ;
}

/**
* 请求取消
*/
public void cancel ( ) {
this .state . close ( ) ;
this .state = ( WebviewResourceState ) new WebviewClosedConnection ( ) ;
}

//定义处理 Chromium Embedded Framework (CEF) 中的 Scheme(协议)请求
public class DataSchemeHandlerFactory implements CefSchemeHandlerFactory {
public CefResourceHandler create ( CefBrowser cefBrowser , CefFrame cefFrame , String s , CefRequest cefRequest ) {
return new DataResourceHandler ( ) ;
}
}

import org.cef.callback.CefCallback ;
import org.cef.handler.CefLoadHandler ;
import org.cef.misc.IntRef ;
import org.cef.misc.StringRef ;
import org.cef.network.CefResponse ;

import java.io.InputStream ;
import java.net.URLConnection ;

public class WebviewOpenedConnection implements WebviewResourceState {
private URLConnection connection ;

private InputStream inputStream ;

public WebviewOpenedConnection ( URLConnection connection ) {
this .connection = connection ;
try {
this .inputStream = connection . getInputStream ( ) ;
} catch ( Exception exception ) {
System .out . println (exception ) ;
}
}

public void getResponseHeaders ( CefResponse cefResponse , IntRef responseLength , StringRef redirectUrl ) {
try {
String url = this .connection . getURL ( ) . toString ( ) ;
cefResponse . setMimeType ( this .connection . getContentType ( ) ) ;
try {
responseLength . set ( this .inputStream . available ( ) ) ;
cefResponse . setStatus ( 200 ) ;
} catch ( Exception e ) {
cefResponse . setError ( CefLoadHandler.ErrorCode . ERR_FILE_NOT_FOUND ) ;
cefResponse . setStatusText (e . getLocalizedMessage ( ) ) ;
cefResponse . setStatus ( 404 ) ;
}
} catch ( Exception e ) {
cefResponse . setError ( CefLoadHandler.ErrorCode . ERR_FILE_NOT_FOUND ) ;
cefResponse . setStatusText (e . getLocalizedMessage ( ) ) ;
cefResponse . setStatus ( 404 ) ;
}
}

public boolean readResponse ( byte [ ] dataOut , int designedBytesToRead , IntRef bytesRead , CefCallback callback ) {
try {
int availableSize = this .inputStream . available ( ) ;
if (availableSize > 0 ) {
int maxBytesToRead = Math . min (availableSize , designedBytesToRead ) ;
int realNumberOfReadBytes = this .inputStream . read (dataOut , 0 , maxBytesToRead ) ;
bytesRead . set (realNumberOfReadBytes ) ;
return true ;
}
} catch ( Exception exception ) {
//log output
} finally {
this . close ( ) ;
}
return false ;
}

public void close ( ) {
try {
if ( this .inputStream != null )
this .inputStream . close ( ) ;
} catch ( Exception exception ) {
//log output
}
}
}

4. 前端发送请求调用插件,Java 进程接收并处理。

//前端示例代码
调用浏览器代码 < /button >

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
疯了!曝易建联移民美国?这是中国男篮最后一个超巨……

疯了!曝易建联移民美国?这是中国男篮最后一个超巨……

篮球实战宝典
2024-04-28 16:29:59
狂轰66+15+12!把球给哈登啊,乔治赛后摊牌,泰伦卢也如梦初醒

狂轰66+15+12!把球给哈登啊,乔治赛后摊牌,泰伦卢也如梦初醒

巴叔GO聊体育
2024-04-29 10:27:32
突发!68岁向太洗澡摔倒卫生间躺一夜,绑担架送医手术5个半小时

突发!68岁向太洗澡摔倒卫生间躺一夜,绑担架送医手术5个半小时

娱乐白名单
2024-04-28 17:30:11
乌克兰总理宣布:乌克兰将彻底放弃后苏联社会政策模式

乌克兰总理宣布:乌克兰将彻底放弃后苏联社会政策模式

探索星空
2024-04-28 10:32:57
没人敢动的军中巨贪,背后有两大保护伞,刘源:丢乌纱帽也拿下他

没人敢动的军中巨贪,背后有两大保护伞,刘源:丢乌纱帽也拿下他

旧时楼台月
2024-04-22 14:42:41
沈阳、大连、长治市频发恶性案件!你了解民众戾气为何如此重吗?

沈阳、大连、长治市频发恶性案件!你了解民众戾气为何如此重吗?

小毅讲历史
2024-04-29 11:17:34
一旦开启武统,大陆和台湾谁先垮?柯文哲:大陆恐怕撑不过两周

一旦开启武统,大陆和台湾谁先垮?柯文哲:大陆恐怕撑不过两周

文辰国学
2024-04-28 11:25:54
内鬼开始下手了?当年颠覆苏联手法在中国重现,蹊跷事情接连发生

内鬼开始下手了?当年颠覆苏联手法在中国重现,蹊跷事情接连发生

昕梦倾城
2024-04-12 12:04:00
童年买的小鸡仔,为什么那么快就死了?终于知道答案了!

童年买的小鸡仔,为什么那么快就死了?终于知道答案了!

科普中国
2024-04-26 21:42:31
成都市纪委监委通报

成都市纪委监委通报

鲁中晨报
2024-04-29 18:01:02
西方集体退出新能源车市场,我们是不是该反思?

西方集体退出新能源车市场,我们是不是该反思?

怪口历史的K先生
2024-03-03 13:23:48
探店博主自称咖啡过敏,被曝找武汉多家咖啡店退款赔医药费

探店博主自称咖啡过敏,被曝找武汉多家咖啡店退款赔医药费

上游新闻
2024-04-29 16:59:33
离队,标价9000万欧!巴萨太子心寒,拒绝三倍涨薪,却被摆上货架

离队,标价9000万欧!巴萨太子心寒,拒绝三倍涨薪,却被摆上货架

阿泰希特
2024-04-29 11:54:56
时代的交替,杜兰特本轮系列赛多次成为爱德华兹背景板

时代的交替,杜兰特本轮系列赛多次成为爱德华兹背景板

懂球帝
2024-04-29 19:28:21
“26岁复旦研究生珠峰地区登山死亡”续:其父称保险公司和平台至今无任何回应

“26岁复旦研究生珠峰地区登山死亡”续:其父称保险公司和平台至今无任何回应

红星新闻
2024-04-29 15:56:18
高三女生扶起摔倒大妈却被反咬一口,拿出监控作证后,大妈破防了

高三女生扶起摔倒大妈却被反咬一口,拿出监控作证后,大妈破防了

妍妍教育日记
2024-04-27 21:28:34
闹大了!骆家辉直言不讳:中国自主生产尖端芯片,美国或有大动作

闹大了!骆家辉直言不讳:中国自主生产尖端芯片,美国或有大动作

搞笑的阿票
2024-04-28 17:30:03
李泽楷与她传绯闻,林丹为她不顾孕妻,她究竟有什么魅力

李泽楷与她传绯闻,林丹为她不顾孕妻,她究竟有什么魅力

通文知史
2024-04-28 18:55:05
网传胡锡进被邀请开讲座,主办方称规模是万人级别,现场看上座率不足10%!

网传胡锡进被邀请开讲座,主办方称规模是万人级别,现场看上座率不足10%!

可达鸭面面观
2024-04-29 09:56:21
“网红教授”罗翔,又添新身份!

“网红教授”罗翔,又添新身份!

新民周刊
2024-04-29 18:32:01
2024-04-29 20:30:44
开源中国
开源中国
每天为开发者推送最新技术资讯
6245文章数 34193关注度
往期回顾 全部

科技要闻

马斯克收获大礼,李彦宏梅开二度?

头条要闻

专家警告:美若将南海台海联动 或同时"出牌"牵制中国

头条要闻

专家警告:美若将南海台海联动 或同时"出牌"牵制中国

体育要闻

足球童话!执教16年,从业余联赛到德甲

娱乐要闻

田馥甄遭抵制,蔡依林却能稳稳捞金?

财经要闻

牛市,无需多言

汽车要闻

配置更丰富 静态体验2024款欧拉好猫

态度原创

旅游
教育
本地
家居
军事航空

旅游要闻

入境游热度持续攀升 “畅游中国”更便捷

教育要闻

未达标不予录取!以下院校有单科成绩、身体要求!

本地新闻

食味印象 | 潍坊:碳水脑袋的人间乐园

家居要闻

光影之间 空间暖意打造生活律动

军事要闻

以军轰炸加沙地带南部城市拉法 至少15人死亡

无障碍浏览 进入关怀版