什么都略懂一点,生活更美好一些

0%

背景

昨天被问到了一道「阶梯数」的问题,题目还原如下:

给定一个整数数列,如果它满足取任意相邻两位上的值,只要满足差的绝对值都为 1,则称其为阶梯数。

Read more »

在 Dart 中有以下 4 种方式来初始化实例变量。

  1. 在实例变量声明时初始化
1
2
3
class Point {
var x = 0, y = 0;
}
  1. 使用构造函数初始化
1
2
3
4
class Point {
var x, y;
Point(this.x, this.y);
}
  1. 通过初始化列表(initializer list)
1
2
3
4
class Point {
var x, y;
Point(a, b) : x = a, y = b;
}
  1. 构造函数中初始化
1
2
3
4
5
6
7
class Point {
var x, y;
Point(a, b) {
x = a;
y = b;
}
}
Read more »

背景

Ant Desgin 用多了,想换种口味。所以最近改用 Material UI 开发页面。其中用到 Switch 组件时,在官方 demo 中发现一个箭头函数的写法:

1
2
3
const handleChange = name => event => {
setState({ ...state, [name]: event.target.checked });
};

作为一个「兼职」的前端开发,第一次看到双箭头函数的写法,自然要探个究竟。

定义

这种方式的写法究竟是什么呢?其实标题已经给出答案,官方定义其为柯里化函数:

在计算机科学中,柯里化(英語:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

Read more »

概述

移动端 APP 的热修复技术已经出来了很多年,Android 方面的解决方案也有很多(iOS 已经被苹果官方叫停),比如手淘的 Sophix,微信的 Tinker 等等,他们都能做到代码修复、资源修复以及 SO 文件修复,这里的「修复」也即意味着「替换」。在接入细节、即时生效或者性能损耗等等的一些方面上,两者存在着一些差异,我们这里不做探讨,本文主要关注的是类文件是如何被替换的并且给出一个简易的实现方案。

原理

目前实现代码修复主要有两种方式:native 底层替换和类加载替换。

前者的特点是修复粒度小,但有稳定性问题(Sophix 已解决),局限性在于不适用于类结构发生变化的修改和修复了的非静态方法被反射调用,它的优点是能即时生效。原理是通过对底层的 ArtMethod 结构体进行完整替换来实现。

后者的特性是修复粒度大,稳定性和兼容性较好,但改动需要冷启动才能生效。原理是在 APP 重新启动后让类加载器 Classloader 去加载新替换的类来实现改动。本文的热修复就是这种思路来实现。

那么问题来了,Android 中的类加载器是什么呢?

Read more »

概述

通过上述三篇文章的学习,我们初步掌握了 JNI 的编程方式。
下面重新复习一下 Java 代码调用本地函数的步骤,如下:

将需要本地实现的 Java 方法加上 native 声明;
使用 javac 命令编译 Java 类;
使用 javah 命令生成头文件;
在本地代码中实现头文件中声明的 native 方法;
编译本地代码,生成动态链接库
在 Java 类中加载动态链接库病调用 native 方法。

前面我们都通过 gcc 来生成动态链接库,可以看见生成步骤比较复杂,而在 Android 中我们就要借助 NDK 的帮助,Android NDK 是 Google 提供的开发工具集,通过它能让我们在 Android 中开发出具有 JNI 机制的应用。而新版的 NDK 使用了 GradleCMake 来帮助我们简化了一些步骤,比如手动生成头文件。

Read more »

概述

前面两篇文章的示例代码的主程序都是用 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 进程。

Read more »

概述

在上一篇的文章中,我们学会了在 Java 层如何通过 JNI 来调用 C 层的代码,这一篇文章我们将看一下如何在 C 层控制 Java 层的代码,主要包含以下内容:

  1. 创建 Java 对象
  2. 访问类静态成员域
  3. 调用类的静态方法
  4. 访问 Java 对象的成员变量
  5. 访问 Java 对象的方法

功能描述

我们下面要实现一个 Java 层与 C 层相互调用的混编程序,调用流程如下图:

Read more »

概述

Android 系统架构中包含了 Applications (应用程序层)、Application framework(应用程序框架层)、Libraries + Android runtime(系统运行库层)以及 Linux Kernel(Linux核心层)。从编程语言的角度看,每层的功能模块都是使用相应的语言编写的,在此过程中,C/C++ 与 Java相互通信时就需要一个媒介来联系起来,JNI(Java Native Interface) 就充当了这一角色,它允许 Java 代码和 基于 C/C++ 编写的应用程序、模块、库进行交互操作。

Read more »

前言

  • 本文假定读者已经安装并配置好了相应的开发环境: Gradle 、 Android SDK 、 Android NDK 及Android Studio等。
  • 通过源码编译我们可以进行定制化改造和修正bug:譬如华为7.0上的边缘滑动崩溃问题。
  • 阅读正文将花费您大约3分钟。

创建 Android 工程

虽然官方已经不再推荐 Android 命令行工具创建项目,我们还是瞄一眼它的命令格式:

1
2
android create project --target android-22 --name MyRNApp --path /home/doslin/blog/MyRNApp 
--activity MyActivity --package com.doslin.rnapp

运行后会提示:

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

Read more »

前言

  • 文中所有 RN 缩写指代React Native For Android
  • 分析的 RN 代码基于
1
2
3
4
{
"react": "15.4.1",
"react-native": "0.39.2"
}
  • 本文主要分析 Java 层实现,对 C++ 和 JS 笔墨较少。
  • 阅读正文将花费您大约20分钟。

背景

公司内几个 APP 已经接入并上线了多个 RN 模块,后续规划的定制化需求及性能优化需要我们对 RN 底层原理有更深入的理解。下面通过研读源代码来分析和总结下 Android 中的 RN 实现原理。

从示例入手

之前写过一篇弹射起步:Android原生项目集成React Native模块

示例代码如下:

1
2
3
4
5
6
public class MainActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "RN_Demo";
}
}

可以发现 RN 容器外层本质也是一个 Activity ,继承了 ReactActivity ,需要我们覆写 getMainComponentName() 方法,更改其返回值为组件名。

Read more »