duangsuse::Echo
#Kotlin #backend #JVM #Java 但我肯定我的思路是没问题的,很清晰,但是我没有在可以接受的时间、可以接受的抄代码/记忆量内完成... 成功反驳了对脑力有限论调加以批判的年轻人 🐸 #Moha 好暴力
nextInt 是没有问题的,因为
Random.nextInt(100) 不会返回 100
duangsuse::Echo
Photo
val ACCEPTABLE_REGEX = ACCEPTABLE_EXTENSIONS.split(',').fold(StringBuilder()) { ac, x -> ac.append("|\\.").append(x) }.let { "($it)" }.let(::Regex)
private fun isImageFile(f: File) = ACCEPTABLE_REGEX.matches(f.name)
这两行代码很有趣它实现了 Trumeet 写了四行的功能(它利用扩展名判断一个 File 是不是可以接受的图像文件类型),不过可能会被说是 ineffective (低效)的
用更简洁的话说就是
ACCEPTABLE_EXTENSIONS.split(',').join("|\\.").let { "($it)" },不过我觉得用 fold 的话秀技一点,因为我就这点技术再不秀就没啥可秀的了,所以就是这种无聊的东西也得秀啊!(跑路Trumeet 的实现是:
for (acceptableName in ACCEPTABLE_FILES) {
if (name.toLowerCase().endsWith(acceptableName.toLowerCase())) {
return@listFiles true
}
}
logger.warn("Ignoring unacceptable file $name")
return@listFiles false
换句话说:对于所有可接受的扩展名:
若文件名的小写以扩展名的小写结尾:
让过滤器接受此文件
警告:
"Ignoring unacceptable file $name"让过滤器忽视此文件
其实根本不该说低效,稍微了解一点算法和数据结构的人都会知道,Regex,尤其是 JVM 这种大型平台上的正则表达式引擎,可以是高度优化的,举个例子,看起来使用正则低效
其实很多正则引擎基于复杂而快速的各种状态机匹配算法 — NFA、DFA,正则表达式也类似各种 parser compiler,但它的匹配是可以高度优化的,如果使用类似前缀树的方式进行剪枝,复杂度不至于变成 O(n**m) (指 lowercase 和比较子串次数,对每个文件名,都要把文件名和所有可接受文件后缀名比较一次),而正则就可以做到这种匹配优化,ACCEPTABLE_REGEX 的构造也就是这样的:
"png,jpg,jpeg,gif,webp,raw,bmp,img,svg".split(',').fold(StringBuilder()) { ac, x -> ac.append("|\\.").append(x) }.let { "($it)" }.let(::println)
(|\.png|\.jpg|\.jpeg|\.gif|\.webp|\.raw|\.bmp|\.img|\.svg)非常模式化
我还抽提出了一个定义
fun <A, B> Future<A>.catching(chain: Future<B>): Future<A> = this.setHandler { if (this.succeeded()) chain.complete() else chain.fail(this.cause()) }
它是这么用的,把当前的信号链接到另一个 Future 上val startServerPromise = Future.future<HttpServer> {
fut ->
server = vertx.createHttpServer()
val router = setupRouter()
server.requestHandler(router)
server.listen(env(ENV_PORT)?.toInt() ?: PORT_DEF, fut)
}.catching(startFuture)
顺带一提,如果要 equalsIgnoreCase 的话直接写上就好("ABC" as java.lang.String).equalsIgnoreCase("abc")
还有,上面那个『判断算法』的原 index 方法的逻辑其实相当不简洁,有点啰嗦:fun index(root: File) |>以一个更高的抽象层次换说,就是:
root.listFiles do |parent, name|
if theFile is not File(a Dir) |> proceed, return
if theFile is acceptable |> proceed
otherwise, logger warn file is not acceptable, filter the file
then stream |>
if theFile is plain file, add its absolute path to picture list
if not, then it must be Dir, logger info recursively scan file tree
列出目标文件夹里的所有文件,然后,对于每一文件:其实为啥不能是这样,反而更简洁高效一些:
若它是文件夹,则跳过以下步骤
若它是文件,并且是图像文件,则接受此文件
否则,logger 输出警告,忽略此文件
对于上一步结果中的每一文件:
若它是文件,则将它的绝对路径加入图片列表里
若它是目录,则对它应用上一个步骤
fun index(root: File) |>或者很啰嗦地说:
root.listFiles.forEach |>
when:
it.isFile and isAcceptable(it) -> pictures.add(it.absolutePath)
it.isDir -> index(it).also { logger info }
else -> logger warn, ignore file
列出目标文件夹里的所有文件,然后,对于每一文件:我是直接用了 Kotlin 的 FileTreeWalker
若它是目录,递归深度优先扫描
若它是文件并且是图像文件,则将它的绝对路径加入图像列表
否则(不可接受)logger 输出警告
啊,我感觉我老了,代码都不会写了... 😵
duangsuse::Echo
val ACCEPTABLE_REGEX = ACCEPTABLE_EXTENSIONS.split(',').fold(StringBuilder()) { ac, x -> ac.append("|\\.").append(x) }.let { "($it)" }.let(::Regex) private fun isImageFile(f: File) = ACCEPTABLE_REGEX.matches(f.name) 这两行代码很有趣 它实现了 Trumeet 写了四行的功能(它利用扩展名判断一个…
花了一点时间继续重构... 无意义的事情,但是也有点可取
https://github.com/duangsuse/RandomPicture/blob/master/src/main/kotlin/org/duangsuse/fushion/RandomPicture.kt
修复,Random nextInt 的参数应该是 lastIndex(这个其实 Trumeet 也没有注意到,因为 Vertx HttpServer send 不存在的 File 时只是作为 logging 严重警告)
修复,Regex 匹配的方式不正确,现在能生成正确的 Regex 并且使用 joinToString 而不是 fold
[DuangSUSE@duangsuse]~/Projects/RandomPicture% RANDOM_PICTURE=/home/DuangSUSE/WallpapperSlide java -jar build/libs/server-1.0-SNAPSHOT.jar
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Checking with regex /^.*(\.png|\.jpg|\.jpeg|\.gif|\.webp|\.raw|\.bmp|\.img|\.svg)$/
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Begin indexing images
警告: 16 files ignored, 91 files added
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Finished indexing images size 91 costs 36 ms
Server started at http://localhost:8080
这个东西主要还是默写和熟悉 Kotlin/JVM 的一种方式,连异步/并行编程都没有,也没有反射 AOP 什么的... 虽然我各种技能栈都很垃圾。
待会有好玩的,这些权当做练习。
https://github.com/duangsuse/RandomPicture/blob/master/src/main/kotlin/org/duangsuse/fushion/RandomPicture.kt
修复,Random nextInt 的参数应该是 lastIndex(这个其实 Trumeet 也没有注意到,因为 Vertx HttpServer send 不存在的 File 时只是作为 logging 严重警告)
修复,Regex 匹配的方式不正确,现在能生成正确的 Regex 并且使用 joinToString 而不是 fold
[DuangSUSE@duangsuse]~/Projects/RandomPicture% RANDOM_PICTURE=/home/DuangSUSE/WallpapperSlide java -jar build/libs/server-1.0-SNAPSHOT.jar
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Checking with regex /^.*(\.png|\.jpg|\.jpeg|\.gif|\.webp|\.raw|\.bmp|\.img|\.svg)$/
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Begin indexing images
警告: 16 files ignored, 91 files added
三月 31, 2019 8:39:34 下午 org.duangsuse.fushion.RandomPicture
信息: Finished indexing images size 91 costs 36 ms
Server started at http://localhost:8080
这个东西主要还是默写和熟悉 Kotlin/JVM 的一种方式,连异步/并行编程都没有,也没有反射 AOP 什么的... 虽然我各种技能栈都很垃圾。
待会有好玩的,这些权当做练习。
GitHub
duangsuse/RandomPicture
:framed_picture: A tiny vertx.HttpServer favorite list based random picture picker service: rewrite - duangsuse/RandomPicture
duangsuse::Echo
花了一点时间继续重构... 无意义的事情,但是也有点可取 https://github.com/duangsuse/RandomPicture/blob/master/src/main/kotlin/org/duangsuse/fushion/RandomPicture.kt 修复,Random nextInt 的参数应该是 lastIndex(这个其实 Trumeet 也没有注意到,因为 Vertx HttpServer send 不存在的 File 时只是作为 logging 严重警告) 修复,Regex…
因为摩天大楼不是从空想来的地基,这些『低级』『脑残』的事情总归还是要做的,必须学会了走路,学会好好开发才能走到更远的地方。
所以一方面必须继续学习计算机科学,另一方面,也得多做工程,很多人会去 Online Judge 写算法题,我没那个时间资本,只好做这些。
现在默写 Dockerfile:
所以一方面必须继续学习计算机科学,另一方面,也得多做工程,很多人会去 Online Judge 写算法题,我没那个时间资本,只好做这些。
现在默写 Dockerfile:
FROM openjdk:8u171-jdk-alpine3.6 AS builder现在默写 Gradle 的 Kotlin Options 和 shadowJar 配置:
ADD . /app
WORKDIR /app
###
RUN ./gradlew shadowJar && mv build/libs/*.jar /server.jar
###
FROM openjdk:8u171-jre-alpine3.6 AS env
COPY --from=builder /server.jar .
ENTRYPOINT java -jar server.jar
def expectedJvm = JavaVersion.JAVA_1_8
def manifestProp = ['Main-Verticle': 'org.duangsuse.fushion.RandomPicture']
compileKotlin {
KotlinOptions.languageVersion = '1.3'
KotlinOptions.jvmTarget = expectedJvm
// Kotlin reflect, metadata...
}
shadowJar {
basename 'server'
classifier null
version rootProject.version
manifest { attributes manifestProp }
}
duangsuse::Echo
因为摩天大楼不是从空想来的地基,这些『低级』『脑残』的事情总归还是要做的,必须学会了走路,学会好好开发才能走到更远的地方。 所以一方面必须继续学习计算机科学,另一方面,也得多做工程,很多人会去 Online Judge 写算法题,我没那个时间资本,只好做这些。 现在默写 Dockerfile: FROM openjdk:8u171-jdk-alpine3.6 AS builder ADD . /app WORKDIR /app ### RUN ./gradlew shadowJar && mv…
只要做到能默写的水平我就满足了。
现在能默写自动化构建系统脚本,以后就能默写更多诸如 JNDI、CI、OpenShift 的配置或脚本、Shellscripts,就能做到 DevOps 和更好的设计,理解跟宽阔的问题领域
这样写测试就不需要 Mock 对象了,思维风格在程序员和运维、打包者、用户之间可以灵活切换的话
就有可能熟悉诸如 AAPT2、Dexer 这种大型项目
希望全部都能默写下来,不能默写的话说明对模型不够熟悉,最好是用的时候也能联想到别人的抽象和实现方式,看到运行时的栈和堆、集合对象,能够看出分支控制流
甚至在需要的时候必须知道编译生成的字节码大概是怎么样的,需要像栈虚拟机一样执行程序
现在能默写自动化构建系统脚本,以后就能默写更多诸如 JNDI、CI、OpenShift 的配置或脚本、Shellscripts,就能做到 DevOps 和更好的设计,理解跟宽阔的问题领域
这样写测试就不需要 Mock 对象了,思维风格在程序员和运维、打包者、用户之间可以灵活切换的话
就有可能熟悉诸如 AAPT2、Dexer 这种大型项目
希望全部都能默写下来,不能默写的话说明对模型不够熟悉,最好是用的时候也能联想到别人的抽象和实现方式,看到运行时的栈和堆、集合对象,能够看出分支控制流
甚至在需要的时候必须知道编译生成的字节码大概是怎么样的,需要像栈虚拟机一样执行程序
duangsuse::Echo
Photo
duangsuse 是这么分的:
== 对象状态:
+ 无论什么 vertx 应用,都会用到
== HTTP 服务器部分:
+ 基于 async chain 的 vertx 应用(AbstractVerticle)都有一个基于 Promise (Future) 的 "
+ 需要到用户配置环境变量的部分递归找图像文件,那就抽象成
+ 递归寻找的时候需要计时间,不如泛化一下,允许传入一个函数来执行『计时』的操作
vertx 的包:
== 对象状态:
+ 无论什么 vertx 应用,都会用到
Logger,使用 LoggerFactory.getLogger(this.javaClass.name) 拿到一个 Logger
+ 服务程序需要实例化一个 HttpServer!,但不能直接在 <init> 方法里实例化,保存下来作为 lateinit var
+ 服务程序需要保存一个状态:待随机选择的图片文件列表,因为开始时可以不扫描文件,作为 MutableList<Path> 保存== HTTP 服务器部分:
+ 基于 async chain 的 vertx 应用(AbstractVerticle)都有一个基于 Promise (Future) 的 "
main(String... args)" 方法,覆盖它以添加启动逻辑override fun start(init: Future<Void>)+ start 里要先完成 indexImanges,再去 startServer,全都是异步进行的,用 Promise 表示它
Promise.all(indexImages, startServer)但是 startServer 包含整个应用程序真的『业务』代码路径,如果它失败应用程序也该失败才对,可以手动
setHandler,但是应该抽提出来,ES6 里也有这个方法:fun <A, B> Future<A>.catching(chain: Future<B>): Future<A> = this.setHandler { if (this.succeeded()) chain.complete() else chain.fail(this.cause()) }
+ 需要一个 server,而且这个 server 应该设置一个 requestHandler 才能用,那就抽象成private fun createHttpServer(router: Router): HttpServer+ 需要一个 router,这个是应用程序自己设置的,那就抽象成 drawRoutes
private fun drawRoutes(): Router+ 唯一的业务代码就是随机选择图片发送,发送文件的操作封装在 httpServer 里了,那就定义这个函数:
fun randomImage(): Path = pictures.sample()== 应用的辅助函数部分:
+ 需要到用户配置环境变量的部分递归找图像文件,那就抽象成
fun indexImage(path: Path)+ 递归找图像的时候需要一个目录作为起始点,并且必须首先检查它是目录,就抽象成
fun checkImagePath(parentPromise: Future<*>?): Path?+ 递归找图像需要判断一个文件是不是图像,使用正则表达式判断文件扩展名
object Helper { internal fun isImageFile(f: File) = ACCEPTABLE_REGEX.matches(f.name) }
== 其他:+ 递归寻找的时候需要计时间,不如泛化一下,允许传入一个函数来执行『计时』的操作
private fun withTimeLog(name: String, op: () -> String?)
private fun withTimeLog(name: String, desc: String) = withTimeLog(name) { desc }
== API:vertx 的包:
import io.vertx.core.*创建 HttpServer:
import io.vertx.ext.web.*
vertx.createHttpServer()
创建 URL 路由:Router.router(vertx)
新建 URL 路径:Router#route(HttpMethod, String)::handler(Function)
设置服务路由:HttpServerr#requestHandler(Router)
HttpServer 监听:HttpServer#listen(Port, ParentFuture)
duangsuse::Echo
index 的实现使用了 Kotlin 扩展的 FileTreeWalker
其实说简单一点,还可以这么定义:
fun scanImageFiles(fromDir: File) {
val (added, ignored) = fromDir.walk().partition(Helper::isImageFile)
picturePathes.addAll(added.map(File::getAbsolutePath))
logger.warn("${added.size} images added, ${ignored.size} plain files ignored")
}
duangsuse::Echo
其实说简单一点,还可以这么定义: fun scanImageFiles(fromDir: File) { val (added, ignored) = fromDir.walk().partition(Helper::isImageFile) picturePathes.addAll(added.map(File::getAbsolutePath)) logger.warn("${added.size} images added, ${ignored.size} plain files ignored")…
This media is not supported in your browser
VIEW IN TELEGRAM
啊真是傻逼死了,我作为一个编译原理爱好者居然在这种已经完成了的事情上多花了这么多时间!难道这些时间不应该拿来写解释器...
最近刚想到 Dynamic Scoping 和 Lexical Scoping 怎么写
最近刚想到 Dynamic Scoping 和 Lexical Scoping 怎么写
duangsuse::Echo
好吧,好吧,duangsuse 来恢复一下心情,顺便写今天最后一个代码:默写重写 @YuutaW 的 RandomPicture 完全默写,包括 Gradle 构建文件(除了 Vertx 的依赖关系 Coordinate 在外) 并且,不得使用 IDEA 等具有自动补齐功能的 IDE == 首先,我们来玩个简单的文件排序游戏,因为 duangsuse 不是学算法的(我以后会学)所以不考虑算法层的东西,就快速排序吧(因为它不需要我花时间去记,皮一下哦)(反正怎么说我,交换、插入、选择排序里,我也只会快排、冒泡、简单选择,虽然…
我待会把坑填完... #Android #blogPOst
https://blog.yuuta.moe/2017/07/29/begin-dir/
欸,Yuuta 的 Dir 在 17 年就开始了?
还是元旦?
原来 Yuuta 在我开发第一个应用之前就有作品? 🤔 WTF
我以为 Dir 是 18 年出的...
https://blog.yuuta.moe/2017/07/29/begin-dir/
欸,Yuuta 的 Dir 在 17 年就开始了?
还是元旦?
原来 Yuuta 在我开发第一个应用之前就有作品? 🤔 WTF
我以为 Dir 是 18 年出的...
blog.yuuta.moe
Dir 的诞生和开发历程 | Yuuta 的折腾 oo
标准不折腾就会死星人
duangsuse::Echo
我待会把坑填完... #Android #blogPOst https://blog.yuuta.moe/2017/07/29/begin-dir/ 欸,Yuuta 的 Dir 在 17 年就开始了? 还是元旦? 原来 Yuuta 在我开发第一个应用之前就有作品? 🤔 WTF 我以为 Dir 是 18 年出的...
既然提到了 Yuuta 鱼塔大佬,那我就给你们快速了解一下鱼塔的博客
https://blog.yuuta.moe
我所认为所有有干货(技术上的信息)的博文,如下:
== JavaEE
https://blog.yuuta.moe/2017/05/30/webservlet-jetty-appserver/
这篇博文《WebServlet+Jetty+Nginx轻松开发并部署应用服务器》是讲 Java Servlet 架构后端服务器 + Jetty 应用服务器 + Maven 开发 Http 服务端应用的
文章使用了 IntelliJ IDEA Community 作为演示 IDE、Maven 作为自动构建和依赖管理系统(J2EE 最令人头疼的事情来了:XML,一大堆名字... 还必须得填,如果忘了还得想,哪些属性还没设置呢...)
应用程序的 classpath 是由 maven Jetty 插件管理的,使用了 lifecycle:
servlet 的东西就不用说了,想必都能默写出来,后面会有精简手册。
Abstract:
https://blog.yuuta.moe/2017/05/05/use-app_process/
使用 app_process (Android DalvikVM 的前端,Zygote client)
举了个 Kill.java (用 Android Privileged Activities API 杀 CoolMarket)栗子
教了大家不使用 IDE 的 Android/Java 命令行工具开发
https://blog.yuuta.moe/2017/09/12/new-way-access-hidden-api/
Android SDK 有些不允许使用的内部 API(Hidden API)
这些 API 没有在官方 android.jar 接口包里定义,并且,现在还不允许直接使用 Java 反射使用这些接口(虚拟机上做了检查,black-grey List)
(谈到 Android.jar,也顺带了解一下 Gradle 的
就是说使用替换 Android.jar 的方式啦。
至于可不可能,因为运行时 Android 设备上是有这些类/方法的,不会找不到(但也有可能没有,所以如果这么 Hack,不能被 javac 的无报错迷惑,为了程序健壮性写好 catch finally 块)。
但是,因为我不知道到底是什么情况,到底具体捕捉那种 Exception 不清楚,自己查字节码(比如,
说到这里,继续科普一下 JVM 1.7 时候的一些 invoke* 指令:
(后面跟的都是用途之一而已)
invokestatic:调用静态方法
invokespecial:调用构造器
invokeinterface:寻找调用某接口抽象方法的实现
invokevirtual:调用虚方法(很多时候『非静态』方法都是虚的,Kotlin 里面很多方法都不是虚方法 — 只有
JVM 1.8 的时候加入了特别的 invokedynamic 允许用户代码协作 JVM 进行方法查找
这也实现了 Lambda(因为要使用 LambdaFactory 为指定接口创建实例对象),因为 DalvikVM 没有增添此方法,Android Dexer 使用 RuntimeDesugar 反射解决 Lambda 对象生成的问题。
顺便科普一下,Kotlin 和 Java 8 都支持『SingleAbstractMethod』单抽象方法 Lambda
也即:
https://blog.yuuta.moe/2017/07/10/zygote-appprocess-init/
你们不要被 Trumeet 放在上面的那张图迷惑了,其实这篇文章的内容很简单,也不包含流程分析。
简单介绍了一下 Zygote 和 /sbin/init 的关系,app_process 是什么
贴了 zygote RunCommand service 的代码
https://blog.yuuta.moe/2018/12/10/optimize-docker-image-by-using-multi-stage-building/
这个我之前说过,看这里
https://blog.yuuta.moe/2017/11/10/from-vibrator-to-system-service/
待会会讲,谈到了 Android Service 的架构,并且实现了一个『需要特权』的 Service
大概是这样的:
—
[outsider] OOServer
^
| AIDL IPC / IBinder
OOService [协调 C/S] IOOServeice (Stub.asInterface)
^
|
OO
此外还有 OOManager 和 SeviceResgistry 这种东西,待会画图
https://blog.yuuta.moe/2017/07/10/appprocess-uid/
简单的知识点科普,不到半面可以速看:
+ 使用 ADB,则 UID 是 Shell
+ 使用普通应用,则 UID 是应用的 UID
不过对 Java 应用来说,并没有一个所谓的『进程』来运行它,它的线程们独占整个虚拟机,而虚拟机就代表了『Java 程序』的操作系统视角
做系统调用的话,检查的也是 Calling VM process 的权限,很好理解的,和 Bash Shell 解释器 fork 子进程是一个道理
———
最后总结一下最值得一看的文章:
⭐️https://blog.yuuta.moe/2017/11/10/from-vibrator-to-system-service/
⭐️https://blog.yuuta.moe/2017/05/30/webservlet-jetty-appserver/
⭐️https://blog.yuuta.moe/2017/05/05/use-app_process/
是贴了不少代码的
https://blog.yuuta.moe
我所认为所有有干货(技术上的信息)的博文,如下:
== JavaEE
https://blog.yuuta.moe/2017/05/30/webservlet-jetty-appserver/
这篇博文《WebServlet+Jetty+Nginx轻松开发并部署应用服务器》是讲 Java Servlet 架构后端服务器 + Jetty 应用服务器 + Maven 开发 Http 服务端应用的
文章使用了 IntelliJ IDEA Community 作为演示 IDE、Maven 作为自动构建和依赖管理系统(J2EE 最令人头疼的事情来了:XML,一大堆名字... 还必须得填,如果忘了还得想,哪些属性还没设置呢...)
应用程序的 classpath 是由 maven Jetty 插件管理的,使用了 lifecycle:
jetty:run
应用程序的调试是 IDEA 远程调试,这个讲了一些servlet 的东西就不用说了,想必都能默写出来,后面会有精简手册。
Abstract:
我最近需要为 Dir 开发后端应用服务器,以前是用的某SaaS服务平台(也是WebServlet),觉得太贵,于是需要部署到自己的服务器。目前有比较成熟的 Python 、PHP 和 JavaScript等语言 可用,但是我需要尽快开发出来,不能花时间新学一门语言,于是就选择了一直在用的 WebServlet。== Android
https://blog.yuuta.moe/2017/05/05/use-app_process/
使用 app_process (Android DalvikVM 的前端,Zygote client)
举了个 Kill.java (用 Android Privileged Activities API 杀 CoolMarket)栗子
教了大家不使用 IDE 的 Android/Java 命令行工具开发
https://blog.yuuta.moe/2017/09/12/new-way-access-hidden-api/
Android SDK 有些不允许使用的内部 API(Hidden API)
这些 API 没有在官方 android.jar 接口包里定义,并且,现在还不允许直接使用 Java 反射使用这些接口(虚拟机上做了检查,black-grey List)
(谈到 Android.jar,也顺带了解一下 Gradle 的
implementation 和 compile 有啥区别吧)(你的应用依赖的库,他们不管依赖什么都不会影响到你的应用,反观 compile 则会让你的项目传递性依赖他们所依赖的东西)就是说使用替换 Android.jar 的方式啦。
至于可不可能,因为运行时 Android 设备上是有这些类/方法的,不会找不到(但也有可能没有,所以如果这么 Hack,不能被 javac 的无报错迷惑,为了程序健壮性写好 catch finally 块)。
但是,因为我不知道到底是什么情况,到底具体捕捉那种 Exception 不清楚,自己查字节码(比如,
invokevirtual)说到这里,继续科普一下 JVM 1.7 时候的一些 invoke* 指令:
(后面跟的都是用途之一而已)
invokestatic:调用静态方法
invokespecial:调用构造器
invokeinterface:寻找调用某接口抽象方法的实现
invokevirtual:调用虚方法(很多时候『非静态』方法都是虚的,Kotlin 里面很多方法都不是虚方法 — 只有
open 的方法可以被复写以支持子类型多态)JVM 1.8 的时候加入了特别的 invokedynamic 允许用户代码协作 JVM 进行方法查找
这也实现了 Lambda(因为要使用 LambdaFactory 为指定接口创建实例对象),因为 DalvikVM 没有增添此方法,Android Dexer 使用 RuntimeDesugar 反射解决 Lambda 对象生成的问题。
顺便科普一下,Kotlin 和 Java 8 都支持『SingleAbstractMethod』单抽象方法 Lambda
也即:
abstract class Invokable<T> { @FunctionalInterface T invoke(); }
static <T> T doInvoke(Invokable f) { return f.invoke(); }
//...Invokable fn = new Invokable<Void>(() -> System.out.println("Invokable invoked"));
doInvoke(fn); // Invokable invoked
但是不允许 Lambda 实现 SAM 接口,只能实现抽象类。https://blog.yuuta.moe/2017/07/10/zygote-appprocess-init/
你们不要被 Trumeet 放在上面的那张图迷惑了,其实这篇文章的内容很简单,也不包含流程分析。
简单介绍了一下 Zygote 和 /sbin/init 的关系,app_process 是什么
贴了 zygote RunCommand service 的代码
https://blog.yuuta.moe/2018/12/10/optimize-docker-image-by-using-multi-stage-building/
这个我之前说过,看这里
https://blog.yuuta.moe/2017/11/10/from-vibrator-to-system-service/
待会会讲,谈到了 Android Service 的架构,并且实现了一个『需要特权』的 Service
大概是这样的:
—
[outsider] OOServer
^
| AIDL IPC / IBinder
OOService [协调 C/S] IOOServeice (Stub.asInterface)
^
|
OO
此外还有 OOManager 和 SeviceResgistry 这种东西,待会画图
https://blog.yuuta.moe/2017/07/10/appprocess-uid/
简单的知识点科普,不到半面可以速看:
app-process 与 内部运行的Java 程序的UID是如何决定的 呢..
+ 使用 Root 执行 app_process 可行,进程 UID 是 Root+ 使用 ADB,则 UID 是 Shell
+ 使用普通应用,则 UID 是应用的 UID
u*_a*
也就是说 app_pocess 内的Java程序是和启动者的UID一致,而不是只允许Shell/Root调用app_process(
普通应用启动,UID也是普通应用的,想获取一些系统权限照样是不行的,保障了安全(
详情可以自己找资料了解 UNIX 进程权限系统,这个很好记。不过对 Java 应用来说,并没有一个所谓的『进程』来运行它,它的线程们独占整个虚拟机,而虚拟机就代表了『Java 程序』的操作系统视角
做系统调用的话,检查的也是 Calling VM process 的权限,很好理解的,和 Bash Shell 解释器 fork 子进程是一个道理
———
最后总结一下最值得一看的文章:
⭐️https://blog.yuuta.moe/2017/11/10/from-vibrator-to-system-service/
⭐️https://blog.yuuta.moe/2017/05/30/webservlet-jetty-appserver/
⭐️https://blog.yuuta.moe/2017/05/05/use-app_process/
是贴了不少代码的