文由 Gemini Wen 创作
在 Android 跨入 5.0 版本之后,我们在使用 Android 手机的过程中,可能会发现一个奇特的现象,就是手机里的 WebView 是可以在应用商店升级,而不需要跟随系统的。
这一点在 iOS 中尚未实现,(iOS OTA 的历史也不是特别的悠久)。但是 webview.apk 不是一个普普通通的 apk,首先它没有图标,不算是点击启动的“App”。同时,更新这个 APK,会让所有使用 webview 的应用都得到更新,哪怕是 webview 中的 UI ,比如前进后退也一样,得到更新。
这一点是如何做到的呢?今天我们来分析下 webview 这个奇特的 APK。
如果开发过 Android 的小伙伴,对 R 这个类是熟悉得不能再熟悉了,一个 R 类,里面所有的“字符串”我们都看得懂,但是一堆十六进制的数字,我们可能并不是非常的熟悉,比如看见一个 R 长这样:
public class R {
public static class layout {
public static final int activity_main=0x7f020000
}
}
后面那串十六进制的数字,我们一般称之为资源 ID (resId),如果你对 R 更熟悉一点,更可以知道资源 id 其实是有规律的,它的规律大概是
0xPPTTEEEE
其中 PP 是 packageId,TT 是 typeId,EEEE 是按规律出来的实体ID(EntryId),今天我们要关注的是前四位。如果你曾经关注的话,你大概会知道,我们写出来的 App,一般 PP 值是 7F。
我们知道 android 针对不同机型以及不同场景,定义了许许多多 config,最经典的多语言场景:values/values-en/values-zh-CN 我们使用一个字符串资源可能使用的是相同的 ID,但是拿到的具体值是不同的。这个模型就是一个表模型 —— id 作为主键,查询到一行数据,再根据实际情况选择某一列,一行一列确定一个最终值:
这种模型对我们在不同场景下需要使用“同一含义”的资源提供了非常大的便捷。Android 中有一个类叫 AssetManager 就是负责读取 R 中的 id 值,最终到一个叫 resources.arsc 的表中找到具体资源的路径或者值返回给 App 的。
我们经常听见 Android 插件化方案里,有一个概念叫 固定ID,这是什么意思呢?我们假设一开始一个 App 访问的资源 id 是 0x7f0103,它是一张图片,这时候我们下发了新的插件包,在构建的过程中,新增了一个字符串,恰好这张图片在编译中进行了某种排序,排序的结果使得 oxPPTT 中的 string 的 TT 变成了 01,于是这个字符串的 id 又恰好变成了 0x7f0103。那么老代码再去访问这个资源的时候,访问 0x7f0103,这时候拿到的不再是图片,而是一个字符串,那么 App 的 Crash 就是灾难性的了。
因此,我们期望资源 id 一旦生成,就不要再动来动去了。但是这里又有一个非常显眼的问题:如果 packageId 永远是 7f,那么显然是不够用的,我们知道有一定的方案可以更改 packgeId,只要在不同业务包中使用不同的 packageId,这样能极大避免 id 碰撞的问题,为插件化使用外部资源提供了条件。
等等!我们在开头说到了 webview.apk 的更新 —— 代码,资源都可以更新。这听上去不就是插件化的一种吗?Google 应用开发者无感知的情况下,到底是怎么实现 webview 的插件化的呢?如果我们揭开了这一层神秘的面纱,我们是不是也可以用这个插件化的特性了呢?
答案当然是肯定的。
我作为一个 Android 工具链开发,在开始好奇 webview 的时候,把 webview.apk 下载过来的第一时间,就是把它拖进 Android Studio,看一看这个 APK 到底有哪里不同。
仔细看,它资源的 packgeId 是 00!直觉告诉我,0 这个值很特殊。
我们再看下大名鼎鼎的 android sdk 中的 android.jar 提供的资源。
这里说个题外话,我们使用 android 系统资源,比如 @android:color/red 这样的方式,其实就是使用到了 android.jar 中提供的资源。我们可以把这个 android.jar 重命名成 android.apk,拖进 Android Studio 中进行查看。
我们看到,android.jar 中资源的 packageId 是 01。直觉告诉我,1 这个值也很特殊,(2 看上去就不那么特殊了)这个 01 的实现,其实靠猜也知道是怎么做的 —— 把 packageId 01 作为保留 id,android 系统中资源的 id 永久固定,那么所有 app 拿到的 0x01 开头的资源永远是确定的,比如,我们去查看 color/black 这个资源,查看上面那张表里的结果是 0x0106000c,那么我至少确定我这个版本所有 android 手机的 @android:color/black 这个资源的 id 全都是 0x0106000c。我们可以做一个 demo 为证,我编译一个xml文件:
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
</ImageView>
然后查看编译出来的结果
我们看见 android:background 的值变成了 @ref/0x0106000c。这个 apk 在 Android 手机上运行的时候,会在 AssetsManager 里面加载两个资源包,一个是自己的 App 资源包,一个是 android framework 资源包,这时候去找 0x0106000c 的时候,就会找到系统的资源里面去。
有一个 android.jar 是个特殊的 01 没问题,那如果系统中存在许多的 apk,他们的值分别是 2,3,4,5,…… 想想都觉得要天下大乱了,如果这是真的,他们怎么管理这些资源 packageId 呢?
带着这些好奇,我下载了 aapt 的源码,准备在真相世界里一探究竟。
下载源码过程和编译过程就不讲了,为了调试方便,建议大家编译出一个没有优化的 aapt debug 版,内涵是使用-O0关闭优化,并使用 debug 模式编译即可,我使用的版本是 android 28.0.3 版本
我们首先可以先瞅一眼,R 下面值的定义为什么是 0xPPTTEEEE,这个定义在 ResourceType.h,同时我们发现了以下几行代码
#define Res_GETPACKAGE(id) ((id>>24)-1)
#define Res_GETTYPE(id) (((id>>16)&0xFF)-1)
#define Res_GETENTRY(id) (id&0xFFFF)
#define APP_PACKAGE_ID 0x7f
#define SYS_PACKAGE_ID 0x01
前三行是 id 的定义,后两行是特殊 packageId 实锤。好了,01 被认定是系统包资源,7f 被认定为 App 包资源。
我们知道,在 xml 中引用其他资源包的方式,是使用@开头的,所以,假设你需要使用 webview 中的资源的时候,你需要指定包名,其实我们在使用 android 提供的资源的时候也是这么做的,还记得 @android:color/black 吗? 其实 @android 中的 android 就是 android.jar 里面资源的包名,我们再看一眼 android.jar 的包格式,注意图中的 packageName:
知道这点以后,我们使用 webview 中的资源的方式就变成如下例子:
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@com.google.android.webview:drawable/icon_webview">
</ImageView>
我们执行下编译,发现报错了:
res/layout/layout_activity.xml:2: error: Error: Resource is not public. (at 'src'
with value '@com.google.android.webview:drawable/icon_webview').
如果你之前使用过 public.xml 这个文件的话(你可能在这见过它:https://developer.android.com/studio/projects/android-library.html#PrivateResources),那么这里我需要说明下 —— 不仅仅是 library 有 private 资源的概念,跨 apk 使用资源同样有 public 的概念。但是,这个 public 标记像 aar 一样,其实并不是严格限制的。
在使用 aar 私有资源的时候,我们只要能拼出全部名称,是可以强行使用的。同时,apk,其实也有办法强行引用到这个资源,这一点我也是通过查看源码的方式得到结论的,具体在 ResourceTypes.cpp 中,有相关的代码:
bool createIfNotFound=false;
const char16_t* resourceRefName;
int resourceNameLen;
if (len > 2 && s[1]=='+') {
createIfNotFound=true;
resourceRefName=s + 2;
resourceNameLen=len - 2;
} else if (len > 2 && s[1]=='*') {
enforcePrivate=false;
resourceRefName=s + 2;
resourceNameLen=len - 2;
} else {
createIfNotFound=false;
resourceRefName=s + 1;
resourceNameLen=len - 1;
}
String16 package, type, name;
if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name,
defType, defPackage, &errorMsg)) {
if (accessor !=NULL) {
accessor->reportError(accessorCookie, errorMsg);
}
return false;
}
uint32_t specFlags=0;
uint32_t rid=identifierForName(name.string(), name.size(), type.string(),
type.size(), package.string(), package.size(), &specFlags);
if (rid !=0) {
if (enforcePrivate) {
if (accessor==NULL || accessor->getAssetsPackage() !=package) {
if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC)==0) {
if (accessor !=NULL) {
accessor->reportError(accessorCookie, "Resource is not public.");
}
return false;
}
}
}
// ...
}
我们查看上面相关的代码,知道只要关闭 enforcePrivate 这个开关即可,查看这一段逻辑,可以很轻松得到结论,只要这样写就行了:
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@*com.google.android.webview:drawable/icon_webview">
</ImageView>
注意 @ 和包名之间多了一个 *,这个星号,就是无视私有资源直接引用的意思,再一次使用 aapt 编译,资源编译成功。查看编译出来的文件
看我们的引用变成了 @dref/0x02060061 咦,packageId 怎么变成了 02,没关系,我们后面的篇章解开这个谜底。
我们根据刚刚上面的源码往下看,继续看 stringToValue 这个函数,会看见这么一段代码
if (accessor) {
rid=Res_MAKEID(
accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
Res_GETTYPE(rid), Res_GETENTRY(rid));
if (kDebugTableNoisy) {
ALOGI("Incl %s:%s/%s: 0x%08x\n",
String8(package).string(), String8(type).string(),
String8(name).string(), rid);
}
}
uint32_t packageId=Res_GETPACKAGE(rid) + 1;
if (packageId !=APP_PACKAGE_ID && packageId !=SYS_PACKAGE_ID) {
outValue->dataType=Res_value::TYPE_DYNAMIC_REFERENCE;
}
outValue->data=rid;
这段代码告诉我们几件事:
看英文翻译是“动态引用”的意思。我们使用aapt d --values resources out.apk命令把资源信息打印出来,可以发现
Package Groups (1)
Package Group 0 id=0x7f packageCount=1 name=test
DynamicRefTable entryCount=1:
0x02 -> com.google.android.webview
Package 0 id=0x7f name=test
type 1 configCount=1 entryCount=1
spec resource 0x7f020000 test:layout/layout_activity: flags=0x00000000
config (default):
resource 0x7f020000 test:layout/layout_activity: t=0x03 d=0x00000000 (s=0x0008 r=0x00)
(string16) "res/layout/layout_activity.xml"
这里有关的是一个 DynamicRefTable,看它里面的值,好像是 packageId 和 packageName 映射。也就是说,0x02 的 packageId 所在的资源,应该是在叫 com.google.android.webview 的包里的。
我们查询 TYPE_DYNAMIC_REFERENCE 和 DynamicRefTable 有关的代码,找到了这么一个函数,我们看下定义:
status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res=*resId;
size_t packageId=Res_GETPACKAGE(res) + 1;
if (packageId==APP_PACKAGE_ID && !mAppAsLib) {
// No lookup needs to be done, app package IDs are absolute.
return NO_ERROR;
}
if (packageId==0 || (packageId==APP_PACKAGE_ID && mAppAsLib)) {
// The package ID is 0x00. That means that a shared library is accessing
// its own local resource.
// Or if app resource is loaded as shared library, the resource which has
// app package Id is local resources.
// so we fix up those resources with the calling package ID.
*resId=(0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);
return NO_ERROR;
}
// Do a proper lookup.
uint8_t translatedId=mLookupTable[packageId];
if (translatedId==0) {
ALOGW("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
(uint8_t)mAssignedPackageId, (uint8_t)packageId);
for (size_t i=0; i < 256; i++) {
if (mLookupTable[i] !=0) {
ALOGW("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
}
}
return UNKNOWN_ERROR;
}
*resId=(res & 0x00ffffff) | (((uint32_t) translatedId) << 24);
return NO_ERROR;
}
得到几个结论:
条件一很明确,二的话应该是 webview.apk 访问自己的资源情况,暂时不管。条件三就是我们现在想要知道的场景了。
我对 mLookupTable 这个变量非常好奇,于是跟踪调用,查看定义,最终找到一些关键信息,在 AssetManager2 中找到相关代码,我们给它添加额外的注释说明
void AssetManager2::BuildDynamicRefTable() {
package_groups_.clear();
package_ids_.fill(0xff);
// 0x01 is reserved for the android package.
int next_package_id=0x02;
const size_t apk_assets_count=apk_assets_.size();
for (size_t i=0; i < apk_assets_count; i++) {
const ApkAssets* apk_asset=apk_assets_[i];
for (const std::unique_ptr<const LoadedPackage>& package :
apk_asset->GetLoadedArsc()->GetPackages()) {
// Get the package ID or assign one if a shared library.
int package_id;
if (package->IsDynamic()) {
//在 LoadedArsc 中,发现如果 packageId==0,就被定义为 DynamicPackage
package_id=next_package_id++;
} else {
//否则使用自己定义的 packageId (非0)
package_id=package->GetPackageId();
}
// Add the mapping for package ID to index if not present.
uint8_t idx=package_ids_[package_id];
if (idx==0xff) {
// 把这个 packageId 记录下来,并赋值进内存中和 package 绑定起来
package_ids_[package_id]=idx=static_cast<uint8_t>(package_groups_.size());
package_groups_.push_back({});
package_groups_.back().dynamic_ref_table.mAssignedPackageId=package_id;
}
PackageGroup* package_group=&package_groups_[idx];
// Add the package and to the set of packages with the same ID.
package_group->packages_.push_back(package.get());
package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
// 同时更改 DynamicRefTable 中 包名 和 packageId 的对应关系
// Add the package name -> build time ID mappings.
for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
String16 package_name(entry.package_name.c_str(), entry.package_name.size());
package_group->dynamic_ref_table.mEntries.replaceValueFor(
package_name, static_cast<uint8_t>(entry.package_id));
}
}
}
// 使用 O(n^2) 的方式,把已经缓存的所有 DynamicRefTable 中的 包名 -> id 的关系全部重映射一遍
// Now assign the runtime IDs so that we have a build-time to runtime ID map.
const auto package_groups_end=package_groups_.end();
for (auto iter=package_groups_.begin(); iter !=package_groups_end; ++iter) {
const std::string& package_name=iter->packages_[0]->GetPackageName();
for (auto iter2=package_groups_.begin(); iter2 !=package_groups_end; ++iter2) {
iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
iter->dynamic_ref_table.mAssignedPackageId);
}
}
}
上面的中文注释是我加的,这一段逻辑其实很简单,我们经过这样的处理,完成了 buildId -> runtimeId 的映射。也就是说,WebView 的 packageId 是在运行时动态计算生成的!
这样的的确确解决了 packageId 维护的问题,因为 pacakgeId 可以重置,我们只要维护 packageName 就行了。
经过以上的调研,我们目前知道了Google 官方的“插件化资源”是如何实现的。但是这个方案也有一个弊端,就是在 5.0 以下的手机上会 crash,原因是 5.0 以下的系统并不认识 TYPE_DYNAMIC_REFERENCE 这个类型。因此如果你的 App 还需要支持 5.0 以下的应用的话,还需要经过一些修改才能实现:
期待各大厂商在努力更新 Android 版本上能迈出更大的步伐,一旦 5.0 以下的手机绝迹,我相信我们的 Android App 生态也会变得更加美好。
在这里我也分享一份几位大佬一起收录整理的Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
如果你有需要的话,可以私信我【资料】我发给你,欢迎大家来白嫖~
喜欢本文的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗~
理 | 张红月
出品 | CSDN(ID:CSDNnews)
本周热门项目
TabNine:支持23种语言及5种主流编辑器AI补代码工具问世(下附链接)
https://github.com/zxqfl/TabNine
一位来自加拿大的大四学霸,开发了一款”Deep TabNine“(Github )代码补全工具,TabNine是基于GPT-2构建的代码补全工具,这是一种Transformer架构,原产自OpenAI,是个“逆天”语言模型。
TabNine支持23种编程语言(Python、Java、C/C++、Haskell……)、5种编辑器(VS Code、Sublime Text、Atom、Emacs、Vim)。值得称道的是,Deep TabNine不同于其它各种代码补全插件,它是根据程序员过去的习惯自动补全,并在后面给出几种选项的概率。如果有类似代码出现在之前的项目里,TabNine还会在补全候选框中直接给出地址,方便用户点击进去查阅。
阿波罗11号指令模块和登月模块源码荣登Github趋势日榜TOP 1(下附链接)
https://github.com/chrislgarry/Apollo-11
1969年,阿波罗 11号登月任务是历史上最具标志性的事件之一,象征着人类首次踏上月球,开启宇宙探索的新篇章。今年恰好是登月50周年,集聚了30多万名技术人员和14.5万行计算机代码Apollo-11号源码在Github上开源,荣登Github趋势日榜TOP 1。
该项目是阿波罗11号制导计算机(AGC)中的指令模块(Comanche055)和登月模块(Luminary099)的原始代码。项目的电子化过程是由Virtual AGC和MIT Museum共同完成。
上世纪60年代,MIT一起实验室的程序员们需要给登月计划开发飞行控制软件,但是当时并没有现在如此成熟的技术,他们必须自己打造一套系统。
于是,他们提出了一种存储计算机程序的新方法——线存储器,并创造了一种特殊版本的汇编语言。现在许多程序员听到“汇编语言”都有可能瑟瑟发抖,而MIT的程序员为阿波罗制导计算机(AGC)编写了许许多多这种晦涩难懂的代码。
百度网盘克星诞生
近期GitHub上有两款百度网盘不限速下载器的项目火了,有了这两个下载器,百度网盘的会员都不用买了。堪称是百度网盘最大的敌人,同学们要抓紧时间下载,可能过几天这个项目就要被删库跑路了。
这两个项目分别是BND(https://github.com/b3log/baidu-netdisk-downloaderx)和pan-light(https://github.com/peterq/pan-light)。BND是一款图形界面的百度网盘不限速下载器,支持Windows、Linux和Mac,分为BND1和BND2两个系列。而pan-light项目是一款不限速的百度网盘客户端,基于 Golang + Qt5 开发。本项意义在于探究 Golang 在图形界面客户端、Web 服务端、事件调度、WebSocket、P2P 长连接等方面的应用和实践。
学生党学编程,这份文档就够了(下附链接)
https://github.com/dipakkr/A-to-Z-Resources-for-Students
这份文档除了学生党,也非常适用于职场开发者。目前已超5000 Star,该资料主要包含以下内容:编程学习资源、黑客马拉松与其它活动、学生福利计划、开源项目、创业项目与孵化器、实习生资源、开发者线下聚会、技术大会、值得关注的技术人、值得关注的网站、附加链接、编码训练营、其它资源。编程资源有Python、机器学习、深度学习、Android、后端、前端Web开发、全站Web开发、数据结构、C/C++语言、Git、R、MongoDB等。对于想学编程的人来说,这真是一个宝藏!
2019年最新BAT大厂面试题总结(下附链接)
https://github.com/0voice/interview_internal_reference
这个项目的作者收集了2019年最新总结,阿里,腾讯,百度,美团,头条等技术面试题目,以及答案,专家出题人分析汇总。内容分为阿里篇、华为篇、百度篇、腾讯篇、美团篇、头条篇、滴滴篇、京东篇、MySQL篇、Redis篇、MongDB篇、ZooKeeper篇、Nginx篇、算法篇、内存篇、CPU篇、磁盘篇、网络通信篇、安全篇、并发篇。
本周热门内容
“10x工程师”引热议,真有这样的工程师存在吗?(戳标题查看完整内容)
推特上“10x工程师”话题异常火爆,引发的热议经久不散。这个话题由一位印度初创公司投资人 Shekhar Kirani 的一条推特引发,他写道;“如果你恰巧遇见了这种稀缺的工程师种类,千万要抓住。招揽一位 “10x 工程师”作为工程师团队的一员,你的创业公司的成功率将大大提高。”对此,有人赞同,甚至有人提出,当年乔布斯跟比尔·盖茨也说过,优秀的程序员至少是平庸程序员50-100倍效率。但也有人表达了反对意见,Mozilla的soapdog痛斥:“这完全是一坨狗屎。工程师所做的是运用他们辛苦所学,为问题提供有意义,可重复,可预测和可理解的解决方案,而你所说的全是性格缺陷。”
那么,你认为真有这样的“10倍工程师存在吗?”笔者在CSDN APP上做了一次PK,结果87%以上的用户认为这样的工程师大有人在,而且还不少。
图灵登上英国50英镑新钞!人工智能之父荣耀比肩英国女王(下附链接)
https://www.bankofengland.co.uk/banknotes/50-pound-note-nominations
今年是人工智能之父艾伦·图灵诞辰107年,英国央行英格兰银行宣布,图灵将成为英国50英镑新钞人物!以表彰其对今天人们生活方式产生的巨大影响。图灵奠定了计算机科学的基础,在二战时帮助破解了德国的加密系统,他还对逻辑和哲学作出了重大贡献,提出了人工智能概念。英格兰银行行长 Mark Carney 称,图灵的工作对我们今天的生活具有巨大的影响,作为计算机科学和人工智能之父,以及二战英雄,他是一个巨人,他的肩膀扛起了今天的许多人。纸币使用了 1951 年拍摄的图灵照片。
Google 已经取消中国搜索引擎项目
Paypal 联合创始人 Peter Thiel 在全美保守主义会议上发表演说,呼吁 FBI 和 CIA 应该调查 Google 是否叛国。他对 Google 提出的三个问题分别是:1.有多少外国情报机关渗透了Google 人工智能的曼哈顿计划?2. Google 的管理阶层是否认为他们自己被中国的情报机构全面渗透?3. Google 是否认为自己被全面渗透,所以才会做出类似于叛国的决定:与中国军方而非美国军方合作?因为他们所做的理性决定实在太糟、太短视,好似这项科技若没有从正规管道出台,还是会从后门被偷走。目前 Google 对此的回应是,它并没有与中国军方合作。特朗普随后宣布,将对 Google 与中国政府的关系展开调查。
而在美国参议院司法委员会的听证会上,Google 公共政策副总裁 Karan Bhatia 称该公司已经取消了审查版搜索引擎项目 Project Dragonfly。Project Dragonfly 是在去年 8 月被 The Intercept 曝光的,之后就遭到广泛的批评,Google 雇员也联合施压要求公司终止该项目。Google 早在 2010 年就退出了中国搜索市场,但通过 Project Dragonfly Google 想要重返中国市场重新推出搜索产品,并将会根据要求审查内容。Google CEO Sundar Pichai 去年底在国会听证会上作证称,该公司目前没有计划在中国发布搜索产品。
现实版“黑客帝国” 马斯克发布脑机接口系统
本周刷屏朋友圈的莫过于马斯克的最新发布了!这次没有发布火箭、卫星、超级高铁,而是为成立两年的初创公司 Neuralink 发布了首款脑机接口产品。马斯克表示,Neuralink团队已经成功地让一只猴子通过大脑控制电脑。他还透露,明年还有可能进行人类患者临床试验。整个方法,核心一共有三部分。
一是“线”(threads),直径4-6微米,比人的头发丝(约75微米)还要细很多。与其他脑机接口中使用的材料相比,不仅对大脑损害性更小,而且还能传输更多数据。分布在96个线程上的每个阵列中,能够拥有多达3072个电极。二是“缝线的机器”。这是一个神经外科机器人,每分钟能够植入六根线。整个过程,特别像缝纫机。第三,Neuralink还开发了一种定制芯片,来更好地读取,清理和放大来自大脑的信号。
中文repo“霸榜”GitHub Trending,国外开发者不开心了(戳标题查看完整内容)
近日,一位叫Balazs Saros 的国外开发者在Medium上发表了一篇名为"Chinese repos are ruining the Github trending page"的博文,翻译一下他的意思就是“中文 repo 正在破坏 GitHub Trending 的页面”。
Github 的 Trending 页面是发现有趣的新 repo 的好功能,也给了新项目获得更多注意力的绝佳机会。但现在,Balazs 表示自己越来越不愿意去看这个页面了,因为满屏充斥着非英语 repo,尤其是中文 repo,前 10 个里有 9 个都是中文 repo,为此他截了一张 GitHub Trending 页面的图作证。
CSDN社区精选
CSS3新特性总结及CSS组件、特效汇总(下附链接)
https://blog.csdn.net/chuangxin/article/details/96380283
所谓CSS组件就是按照约定的DOM结构+组件class,即可实现组件展示效果,与js无关。现在有很多前端UI,比如:Bootstrap,妹子UI,layui就有很多CSS组件,甚至目前流行的前端3剑客:angular, react, vue都有很多对应的css组件。比如:栅格布局;导航、面包线、选项卡;表单、按钮;徽章、引用块等。除此之外,实际项目中我们也会积累一些自主的css组件和特效。该系列博文共包含6篇,分别是CSS3选择器、边框、背景使用细节及案例演示、CSS3 字体@font-face详解、如何创建和修改woff字体文件及text-shadow等文本效果、CSS3 2D转换和3D转换 transform 变形使用详解、CSS3过度transition和动画animation @keyframes规则详解、多列columns column-count和flex布局、多列columns column-count和flex布局。
为什么Windows/iOS操作很流畅而Linux/Android却很卡顿呢? (下附链接)
https://blog.csdn.net/dog250/article/details/96362789
先说是不是,再问为什么。我就知道有人会这么说,然而那样就成了一篇议论文了,而我只是想写一篇随笔。所以,不管事实是不是那样,反正我就是觉得Windows、MacOS、iOS都很流畅,而Linux、Android却很卡。当然了,这里说的是GUI,如果考量点换成是Web服务的吞吐和时延,那估计结论要反过来了,不过那是客户端程序感觉到的事,作为人,who care!
我写这篇文章还有一个意思,那就是想牵引一个话题,如果我们想把Linux、Android(当然,Android内核也是Linux)优化到GUI不再卡顿,我们应该怎么做。
十分钟学会 Web 开发利器 Tornado(下附链接)
https://blog.csdn.net/xufive/article/details/96281879
Python 旗下,群英荟萃,豪杰并起。单是用于 Web 开发的,就有 Webpy、Web2py、Bottle、Pyramid|、Zope2、Flask、Tornado、Django 等等,不一而足。最近几年较为流行的,大概也就是Flask、Tornado 和 Django 了。
对于 Tornado,我有很深的情感。如果把 Web 开发框架比作程序员手中的冷兵器,我觉得 Flask 好比是花枪, 轻灵飘逸,舞之令人眼花缭乱;Django 像大戟,合矛戈为一体,可直刺,可横击,威力无比;Tornado 秀外而惠中,更像是剑。剑在中国传统武术中有着很高的地位,为兵器之神,被认为有君子之风。
史上最全的Android面试题集锦(下附链接)
https://blog.csdn.net/xiangzhihong8/article/details/96280254
在Android开发中,不管是插件化还是组件化,都是基于Android系统的类加载器ClassLoader来设计的。只不过Android平台上虚拟机运行的是Dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把所有Class文件进行合并、优化,然后再生成一个最终的class.dex,目的是把不同class文件重复的东西只需保留一份,在早期的Android应用开发中,如果不对Android应用进行分dex处理,那么最后一个应用的apk只会有一个dex文件。
技术栈中的爱马仕?Facebook发布全新JavaScript引擎:Hermes(戳标题查看完整内容)
最近,一个崭新的JavaScript引擎面世:Hermes,它是Facebook在Chain React 2019 大会上发布 & 用于在React Native应用提高性能的,本文将进行全面介绍。
Java 代码界 3% 的王者?看我是如何解错这 5 道题的(下附链接)
https://blog.csdn.net/qing_gee/article/details/96151818
前些日子,阿里妹发表了一篇文章《悬赏征集!5 道题征集代码界前 3% 的超级王者》——看到这个标题,我内心非常非常激动,因为终于可以证明自己技术很牛逼了。但遗憾的是,凭借 8 年的 Java 开发经验,我发现这五道题自己全解错了!惨痛的教训再次证明,我是那被秒杀的 97% 的工程师之一。
不过,好歹我这人脸皮特别厚,虽然全都做错了,但还是敢于坦然地面对自己。本文分享这 5 道题,并进行全面解析,想挑战的,可以过来围观!
计算机组成原理(下附链接)
https://blog.csdn.net/Jmilk
对于刚入门的开发者来说,这个系列的文章则是必备读品。目前作者已经完成3篇,分别是计算机组成原理——中央处理器、计算机组成原理——指令系统、计算机组成原理——存储系统。
忆贵州三年的教书编程岁月:不弛于空想,不骛于虚声(下附链接)
https://blog.csdn.net/Eastmount/article/details/95803921
他曾说过“回到贵州,只求一辈子都用心对待每一个学生,教他们些东西,写点代码;摸着良心,对得起每一个学生,足矣!哪怕被世界所抛弃,至少还有娜女神和好友们的支持,足矣!
一行 Python 代码能实现什么丧心病狂的功能?(下附链接)
https://blog.csdn.net/xufive/article/details/96475103
Python 高手们只用一行代码都能干些什么?当然,限定条件是不能引用自定义的模块,可以使用内置模块或通用的第三方模块。上网一搜,发现这个问题好像是 python 的专属问题,其他语言很难用一行代码做点什么。本文作者用一行Python代码打印迷宫,打印乘法口诀、表白爱情、打印各种小动物,心动了吧,快去围观吧!
CSDN课程精选
Flutter从入门到精通(下附链接)
https://edu.csdn.net/combo/detail/1221?utm_source=edm0
想要学习Flutter,案例+就业,这一个套餐就够了!
《21天通关Python》买课包邮送书!(下附链接)
https://edu.csdn.net/bundled/detail/49?utm_source=edm0
CSDN活动精选
7.27华为云开发者沙龙杭州站(下附链接)
https://huiyi.csdn.net/activity/product/goods_list?project_id=4276
7.21 巨杉TechDay 第4期:云时代的数据库架构设计与演进(下附链接)
https://huiyi.csdn.net/activity/product/goods_list?project_id=4268
免费参加英特尔在线培训,参与调研更有好礼相赠!(下附链接)
https://click.hm.baidu.com/clk?f962d2b8779b04d926cda4ee1c6c8313
【End】
文为您介绍如何利用DataWorks数据集成将JSON数据从OSS迁移到MaxCompute,并使用MaxCompute内置字符串函数GET_JSON_OBJECT提取JSON信息。
数据上传OSS
将您的JSON文件重命名后缀为TXT文件,并上传到OSS。本文中使用的JSON文件示例如下。
{ "store": { "book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 }, { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 } ], "bicycle": { "color": "red", "price": 19.95 } }, "expensive": 10 }
将applog.txt文件上传到OSS,本文中OSS Bucket位于华东2区。
使用DataWorks导入数据到MaxCompute
{ "type": "job", "steps": [ { "stepType": "oss", "parameter": { "fieldDelimiterOrigin": "^", "nullFormat": "", "compress": "", "datasource": "OSS_userlog", "column": [ { "name": 0, "type": "string", "index": 0 } ], "skipHeader": "false", "encoding": "UTF-8", "fieldDelimiter": "^", "fileFormat": "binary", "object": [ "applog.txt" ] }, "name": "Reader", "category": "reader" }, { "stepType": "odps", "parameter": { "partition": "", "isCompress": false, "truncate": true, "datasource": "odps_first", "column": [ "mqdata" ], "emptyAsNull": false, "table": "mqdata" }, "name": "Writer", "category": "writer" } ], "version": "2.0", "order": { "hops": [ { "from": "Reader", "to": "Writer" } ] }, "setting": { "errorLimit": { "record": "" }, "speed": { "concurrent": 2, "throttle": false, "dmu": 1 } } }
获取JSON字段信息
在您的业务流程中新建一个ODPS SQL节点。
您可以首先输入 SELECT*from mqdata;语句,查看当前mqdata表中数据。当然这一步及后续步骤,您也可以直接在MaxCompute客户端中输入命令运行。
确认导入表中的数据结果无误后,您可以使用MaxCompute内建字符串函数GET_JSON_OBJECT获取您想要的JSON数据。本例中使用 SELECT GET_JSON_OBJECT(mqdata.MQdata,'$.expensive') FROM mqdata;获取JSON文件中的 expensive值。如下图所示,可以看到已成功获取数据。
作者:付帅
*请认真填写需求信息,我们会在24小时内与您取得联系。