在 Dart 中有以下 4 种方式来初始化实例变量。
- 在实例变量声明时初始化
1 | class Point { |
- 使用构造函数初始化
1 | class Point { |
- 通过初始化列表(initializer list)
1 | class Point { |
- 构造函数中初始化
1 | class Point { |
在 Dart 中有以下 4 种方式来初始化实例变量。
1 | class Point { |
1 | class Point { |
1 | class Point { |
1 | class Point { |
Ant Desgin 用多了,想换种口味。所以最近改用 Material UI 开发页面。其中用到 Switch 组件时,在官方 demo 中发现一个箭头函数的写法:
1 | const handleChange = name => event => { |
作为一个「兼职」的前端开发,第一次看到双箭头函数的写法,自然要探个究竟。
这种方式的写法究竟是什么呢?其实标题已经给出答案,官方定义其为柯里化函数:
在计算机科学中,柯里化(英語:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
移动端 APP 的热修复技术已经出来了很多年,Android 方面的解决方案也有很多(iOS 已经被苹果官方叫停),比如手淘的 Sophix,微信的 Tinker 等等,他们都能做到代码修复、资源修复以及 SO 文件修复,这里的「修复」也即意味着「替换」。在接入细节、即时生效或者性能损耗等等的一些方面上,两者存在着一些差异,我们这里不做探讨,本文主要关注的是类文件是如何被替换的并且给出一个简易的实现方案。
目前实现代码修复主要有两种方式:native 底层替换和类加载替换。
前者的特点是修复粒度小,但有稳定性问题(Sophix 已解决),局限性在于不适用于类结构发生变化的修改和修复了的非静态方法被反射调用,它的优点是能即时生效。原理是通过对底层的 ArtMethod 结构体进行完整替换来实现。
后者的特性是修复粒度大,稳定性和兼容性较好,但改动需要冷启动才能生效。原理是在 APP 重新启动后让类加载器 Classloader 去加载新替换的类来实现改动。本文的热修复就是这种思路来实现。
那么问题来了,Android 中的类加载器是什么呢?
通过上述三篇文章的学习,我们初步掌握了 JNI 的编程方式。
下面重新复习一下 Java 代码调用本地函数的步骤,如下:
将需要本地实现的 Java 方法加上 native 声明;
使用 javac 命令编译 Java 类;
使用 javah 命令生成头文件;
在本地代码中实现头文件中声明的 native 方法;
编译本地代码,生成动态链接库
在 Java 类中加载动态链接库病调用 native 方法。
前面我们都通过 gcc 来生成动态链接库,可以看见生成步骤比较复杂,而在 Android 中我们就要借助 NDK 的帮助,Android NDK 是 Google 提供的开发工具集,通过它能让我们在 Android 中开发出具有 JNI 机制的应用。而新版的 NDK 使用了 Gradle
与 CMake
来帮助我们简化了一些步骤,比如手动生成头文件。
前面两篇文章的示例代码的主程序都是用 Java 代码编写的,我们看到了如何在 Java 代码中使用 JNI 的方式调用 C 函数。下面我们将学习如何在由 C/C++ 编写的主程序中来运行 Java 类。
我们知道由 Java 类编译生成的字节码需要运行在 Java 虚拟机上,那么在 C/C++ 中运行 Java 代码也需要虚拟机环境吗?答案是肯定的。JNI 为这种情况提供了一套 Invocation API ,它允许本地代码在自身内存区域内加载 Java 虚拟机。编写并运行 Java 代码的 java
命令是由 C 语言编写的,它也是通过 Invocation API
来接收命令参数。
除此之外, Android 系统的 dalvikvm
虚拟机的主入口( dalvikvm-dalvik/dalvikvm/main.c
)也是通过 Invocation API 进行工作的。在 Android 启动时, app_process
调用 JNI invocation API
在自身程序域内加载 dalvikvm
虚拟机,而后调用 ZygoteInit
类的 main()
方法,从而运行 Zygote
进程。
虽然官方已经不再推荐 Android 命令行工具创建项目,我们还是瞄一眼它的命令格式:
1 | android create project --target android-22 --name MyRNApp --path /home/doslin/blog/MyRNApp |
运行后会提示:
The android command is no longer available.
For manual SDK and AVD management, please use Android Studio.
For command-line tools, use tools/bin/sdkmanager and tools/bin/avdmanager
1 | { |
公司内几个 APP 已经接入并上线了多个 RN 模块,后续规划的定制化需求及性能优化需要我们对 RN 底层原理有更深入的理解。下面通过研读源代码来分析和总结下 Android 中的 RN 实现原理。
之前写过一篇弹射起步:Android原生项目集成React Native模块。
示例代码如下:
1 | public class MainActivity extends ReactActivity { |
可以发现 RN 容器外层本质也是一个 Activity ,继承了 ReactActivity ,需要我们覆写 getMainComponentName()
方法,更改其返回值为组件名。
作为开发者,使用 Git 作为项目的代码控制系统已经是必备技能了(当然,还有本文的MarkDown),而日常工作中的你是否只停留在无尽的 pull
与 push
中呢?
下面就来介绍 Git 中的几种实用命令,相信会对你今后可能遇到的实际工作场景带来实质性的效率提升。
介绍命令之前,首先我们先需要了解下 Git 中操作区域的概念。
Git 的操作区域和 vi 中的缓冲区的概念很类似,不同的 Git 命令会在不同的操作区域中工作。
如图所示,Git的 add
操作会将文件的修改记录保存在暂存区,之后进行的 commit
操作会将暂存区的修改内容全部提交到历史区,最后进行的 push
操作将会把本地的操作记录推送到远程仓库中去。
之所以此处远程库的背景色使用了暗色调,是因为推送到远程库的代码是需要你“负责”的,一旦发布即是历史的诞生,请对 push
的操作保持敬畏和责任感。同时这里也牵涉到 Git 中提交哲学的概念。