文由 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一起實驗室的程序員們需要給登月計劃開發飛行控制軟件,但是當時并沒有現在如此成熟的技術,他們必須自己打造一套系統。
于是,他們提出了一種存儲計算機程序的新方法——線存儲器,并創造了一種特殊版本的匯編語言?,F在許多程序員聽到“匯編語言”都有可能瑟瑟發抖,而MIT的程序員為阿波羅制導計算機(AGC)編寫了許許多多這種晦澀難懂的代碼。
百度網盤克星誕生
近期GitHub上有兩款百度網盤不限速下載器的項目火了,有了這兩個下載器,百度網盤的會員都不用買了??胺Q是百度網盤最大的敵人,同學們要抓緊時間下載,可能過幾天這個項目就要被刪庫跑路了。
這兩個項目分別是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無關?,F在有很多前端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小時內與您取得聯系。