这一篇的技术成份稍微高一点点。要谈到的功能,从一开始开发浏览器就有想要做,但是一直找不到比较好的实作方式。在经过两三周忙於其他的功能开发後,终於在这周找到比较恰当的切入点和相关技术的参考,得以完成心目中大致上的效果。
讲了一堆废话,究竟是什麽功能呢?
不知道阅读模式的人,可以看一下下面的文章介绍。这功能几年前 Apple 在 Safari 浏览器中推出,让使用者可以更专心地阅读网页内容,不被广告和不必要的元件(标题栏,底部栏,侧边栏位等)干扰。
https://today.line.me/tw/v2/article/9PxxRK
之後,各家浏览器大厂也开始推出了类似的功能。 Brave 浏览器在推出他们自家的 SpeedReader 功能时,有顺便把市面上主要的实作都拿来做比较,有兴趣的人可以下载下面的 pdf 档来了解一下。主要比较了 Readability.js,Safari Reader View, Google Chrome DOM Distiller,BoilerPipe 和他们的 SpeedReader。
https://brave.com/wp-content/uploads/2020/08/speedreader-www19.pdf
对於这功能有了大致的了解後,要来决定一下怎麽开发这功能。第一个想法自然是找 Readability.js 来使用;一来它是 Open Source 的,二来,许多浏览器的阅读模式也是从它延伸而来的。用它的话,网路上可以找到的资源也会比较多一些。所以先上 Github 找了一个早期的版本来用。
https://github.com/Kerrick/readability-js
把 readability.js 放到 Android 的 assets 目录中,然後利用下面的方式载入档案,塞到目前的网页中。由於 readability.js 里头已经包含了初始化自己的程序码,而且会把处理过的内容,直接盖掉目前的网页内容,所以只要载入它就等着它把画面换成比较单纯的显示模式。
原始的网页 | 套用 Readability.js 的网页 |
---|---|
这方式实作上虽然很简单,但是产生出来的效果却不是很好。第一点是,很多应该不属於主要内容的部分,还是留在画面中;第二点是图片的部分,通常会过大。
字型忽大忽小尚可忽略不去计较,但是该小一点的图,大得吓人;和一堆不必要的元素依然存在,这效果很难让人有想阅读下去的念头。
仔细再研究了一下 readability.js 的内容,它在处理 html elements 时,除了会删除不必要的元素之外,也会把想要留下的元素加上特定的 class 或 id;然後它另外还有一个 readability.css 档案,应该就是用来规范这些新加的 class 是要怎麽呈现在画面中的。难怪只有执行 readability.js 的话,跑出来的画面有点惨不忍睹。
於是接下来的版本,我把 readability.css 也加进去。(也顺便把 WebView 从 Java 重构成 Kotlin 档案,不然改起来有点痛苦)
public void applyReaderMode() {
InputStream jsInput, cssInput;
try {
jsInput = getContext().getAssets().open("readability.js");
byte[] buffer = new byte[jsInput.available()];
jsInput.read(buffer);
jsInput.close();
cssInput = getContext().getAssets().open("readability.css");
byte[] cssBuffer = new byte[cssInput.available()];
cssInput.read(cssBuffer);
cssInput.close();
// String-ify the script byte-array using BASE64 encoding !!!
String encodedJs = Base64.encodeToString(buffer, Base64.NO_WRAP);
String encodedCss = Base64.encodeToString(cssBuffer, Base64.NO_WRAP);
loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
"script.innerHTML = window.atob('" + encodedJs + "');" +
"parent.appendChild(script)" +
"})()");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
套了之後,效果也没有因此而改善。所以我又试着去找其他的可能方案。
在开发的过程中,我主要拿来比较阅读模式效果的 reference app 分别是 Brave Browser 和 Firefox。Brave 在前面的 pdf 中有提到,它们的作法是在画面还没有真的绘制之前就可以先处理,速度会比其他的方案快;但缺点是,它的作法相对上也比较复杂,我不见得能够比照办理。
所以,我去找了 Firefox App的原始码来看(早该这麽做了)。原来 Mozilla 也有把它们的原始码放在 Github 上。而且针对 Readability 的改良版也特地独立成一个 repository 开发。
https://github.com/mozilla/readability
一开始我很开心地拿了这版本来套用。但又犯了一开始就犯的错误。javascript 只处理了资料的去留,但是真正呈现的部分还是需要对应的 css style file 来辅助才行。於是我找到了 Firefox Mobile App 的 repository,也找到了它 reader view 真正实作的地方。
https://github.com/mozilla-mobile/android-components/tree/master/components/feature/readerview
android-components/components/feature/readerview/src/main/assets/extensions/readerview/
目录下,除了有上述的 readability.js
外,它又包了一层 readerview.js
和辅助的 readerview.css
。这两者的实作才是真正发挥 Readability 威力的地方。有兴趣的人可以进去看一下。大概说就是:readability 把资料处理完变成 article object 後, readerver.js
会拿 article
中的每个资料栏位,一个个贴上特定的 class name,然後在 readerview.css
中,针对这些 class 加上 UI 的呈现方式。
因为 readerview.js
中有很多是跟 firefox App 互动的实作,我不行整个档案直接拿来套用,所以我是抽取里头我需要的程序码来用而已。抽出来的程序码都在这儿:
https://github.com/plateaukao/browser/blob/my_version/app/src/main/assets/MozReadability.js#L2261
於是,跟 Firefox App 效果几乎一样的阅读模式完成了!(还有预估阅读时间要多久,不过这数值感觉不是很准确)
原本的网页 | 套用後的阅读模式 |
---|---|
看到右边的画面时,有没有一种很感动的感觉。画面中的 header, footer,广告等会吸引眼球注意力的元件都被去除了,只留下中间的内容部分。在一个自制的小 App 中能实作这样子的功能真的很令人感动。
参考原始码版本: https://github.com/plateaukao/browser/releases/tag/v8.5.2
<<: 每个人都该学的30个Python技巧|技巧 11:回圈二部曲—while回圈(字幕、衬乐、练习)
>>: Day 11 : 用於生产的机械学习 - Data Labeling 资料标注
输入类型"周" 将”input type="week"“允许...
10 - or equals Ruby 的 ||=(or equals)运算符号使用频率是个使用频率...
在入场,且获利後,第一件事就是要保护获利,而不是不要赔钱。 通常有几个转折点需要注意,其中高档爆大量...
记录完後我们就要开始查询了以下是蛮简单的查询方式。 import psycopg2 import o...
终於 进入好想工作室第 131 天 终於 我也迎来了传说中的 it 铁人赛 思考了很久铁人赛的主题要...