整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Webview.apk-Google 官方的私有插件

          Webview.apk-Google 官方的私有插件化方案

          文由 Gemini Wen 創作


          在 Android 跨入 5.0 版本之后,我們在使用 Android 手機的過程中,可能會發現一個奇特的現象,就是手機里的 WebView 是可以在應用商店升級,而不需要跟隨系統的。

          這一點在 iOS 中尚未實現,(iOS OTA 的歷史也不是特別的悠久)。但是 webview.apk 不是一個普普通通的 apk,首先它沒有圖標,不算是點擊啟動的“App”。同時,更新這個 APK,會讓所有使用 webview 的應用都得到更新,哪怕是 webview 中的 UI ,比如前進后退也一樣,得到更新。

          這一點是如何做到的呢?今天我們來分析下 webview 這個奇特的 APK。


          Android 資源和資源ID

          如果開發過 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 的插件化的呢?如果我們揭開了這一層神秘的面紗,我們是不是也可以用這個插件化的特性了呢?

          答案當然是肯定的。


          WebView APK 和 android 系統資源

          我作為一個 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 源碼,告訴你一切

          下載源碼過程和編譯過程就不講了,為了調試方便,建議大家編譯出一個沒有優化的 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,沒關系,我們后面的篇章解開這個謎底。


          DynamicRefTable

          我們根據剛剛上面的源碼往下看,繼續看 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;

          這段代碼告訴我們幾件事:

          1. 剛剛的 webview 的 packageId 是經過 remapp 后的
          2. 它的類型變成了 TYPE_DYNAMIC_REFERENCE

          看英文翻譯是“動態引用”的意思。我們使用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;
          }

          得到幾個結論:

          1. 如果 packageId 是 0x7f 的話,不轉換,原來的 ID 還是原來的 ID
          2. 如果 packageId 是 0 或者 packageId 是 7f 且 mAppAsLib 是真的話,把 packgeId 換成 mAssignedPackageId
          3. 否則從 mLookupTable 這個表中做一個映射,換成 translatedId 返回。

          條件一很明確,二的話應該是 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 以下的應用的話,還需要經過一些修改才能實現:

          1. 依然需要手動管理 packageId。
          2. 把 aapt 中關于 dynamic reference 的地方改成 reference。

          期待各大廠商在努力更新 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

          1. 新增OSS數據源
          2. 進入DataWorks數據集成控制臺,新增OSS類型數據源。

          1. 具體參數如下所示,測試數據源連通性通過即可點擊完成。Endpoint地址請參見OSS各區域的外網、內網地址,本例中為http://oss-cn-shanghai.aliyuncs.comhttp://oss-cn-shanghai-internal.aliyuncs.com(由于本文中OSS和DataWorks項目處于同一個region中,本文選用后者,通過內網連接)。

          1. 新建數據同步任務
          2. 在DataWorks上新建數據同步類型節點。

          1. 新建的同時,在DataWorks新建一個建表任務,用于存放JSON數據,本例中新建表名為mqdata。

          1. 表參數可以通過圖形化界面完成。本例中mqdata表僅有一列,類型為string,列名為MQ data。

          1. 完成上述新建后,您可以在圖形化界面配置數據同步任務參數,如下圖所示。選擇目標數據源名稱為odps_first,選擇目標表為剛建立的mqdata。數據來源類型為OSS,Object前綴可填寫文件路徑及名稱。列分隔符使用TXT文件中不存在的字符即可,本文中使用 ^(對于OSS中的TXT格式數據源,Dataworks支持多字符分隔符,所以您可以使用例如 %&%#^$$^%這樣很難出現的字符作為列分隔符,保證分割為一列)。

          1. 映射方式選擇默認的同行映射即可。

          1. 點擊左上方的切換腳本按鈕,切換為腳本模式。修改fileFormat參數為: "fileFormat":"binary"。該步驟可以保證OSS中的JSON文件同步到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
           }
           }
          }
          
          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值。如下圖所示,可以看到已成功獲取數據。

          作者:付帥


          主站蜘蛛池模板: 熟女大屁股白浆一区二区| 日韩人妻无码一区二区三区久久99 | 国产大秀视频一区二区三区| 无码少妇精品一区二区免费动态| 日本免费一区尤物| 亚洲国产系列一区二区三区| 国产无套精品一区二区| 一区二区三区四区无限乱码 | 国产伦精品一区二区三区视频金莲| 精品午夜福利无人区乱码一区| 亚洲一区二区高清| 亚洲中文字幕丝袜制服一区| 人妻无码一区二区视频| 一区二区三区美女视频| 波多野结衣在线观看一区二区三区| 性色AV一区二区三区无码| 麻豆国产在线不卡一区二区 | 国偷自产视频一区二区久 | 亚欧免费视频一区二区三区| 亚洲狠狠狠一区二区三区| 久久国产精品无码一区二区三区| 国产在线一区二区综合免费视频| 国产在线aaa片一区二区99| 日本一区二区免费看| 人妻无码一区二区三区AV| 精品国产一区AV天美传媒| 亚洲一区二区三区免费在线观看| 精品无码一区二区三区电影| 中文字幕av一区| 国产成人一区二区三区精品久久| 色视频综合无码一区二区三区| 狠狠做深爱婷婷综合一区 | 亚洲一区二区三区国产精品无码| 97人妻无码一区二区精品免费| 国产精品视频一区| 一区二区三区在线| 精品性影院一区二区三区内射| 亚洲.国产.欧美一区二区三区| 国产伦精品一区二区三区四区| 久久久国产一区二区三区| ...91久久精品一区二区三区|