標籤: 收購蘋果電腦

  • 特斯拉汽車爆發?大摩:2040年已上路車數將狂增100倍

    特斯拉汽車爆發?大摩:2040年已上路車數將狂增100倍

    摩根士丹利(Morgan Stanley、通稱大摩)看好特斯拉(Tesla Inc.)汽車銷售大爆發,估計到了2019年,全球已經上路的特斯拉電動車,將是現在的三倍之多!

    CNBC、MarketWatch等外電報導,大摩分析師Adam Jonas 26日在研究報告中樂觀預期,未來幾年,人們每一天都會在路上看到Model 3,而已上路的特斯拉電動車數量,將在2040年暴增約100倍,該公司的客服與充電設施也將面臨挑戰。

    報告估計,今(2017)年底全球已上路特斯拉電動車數量將接近30萬台,明年還將成長近80%至53.1萬台,2030年的數量會是今年底的10倍,2040年全球使用中的特斯拉電動車,更會直逼3,200萬台。Jonas說,投資界已經有好久不曾看到成長這麼高的汽車廠商。

    特斯拉稍早已宣布,要在今年將全球超級充電站(Supercharger)的數量拉高一倍,而該公司最近還在芝加哥、波士頓開設了第一批「市中心」超級充電站。

    隨著超級充電站逐步擴張,特斯拉也能蒐集到電動車上路後的諸多路況數據。Jonas預測,到了2023年或2024年,特斯拉每天應可蒐集到1億英里的資料,充實自駕和行動服務技術,提升競爭力。

    根據特斯拉9月11日發布的新聞稿,該公司將在都會區數個方便的地點,例如超市、購物中心、市中心等地,打造超級充電站,讓特斯拉車主在購買雜貨、處理雜務之際,也能輕鬆充電。

    為了滿足都會區高效率、高車流量的需求,新一代充電站可為每輛汽車快速供應72千瓦(KW )的電力,車主只要花45~50分鐘就可為汽車充飽電。特斯拉表示,新款充電站,一開始會在波士頓、芝加哥推出。

    大摩Jonas今年3月23日就曾發表研究報告指出,特斯拉為每台車安裝超級電腦後,車子安全性提升至其他車輛兩倍已經不夠看,他相信Model 3的安全度會是其他車輛的十倍之多,這會讓死亡車禍的發生機會降低90%。

    MarketWatch、Business Insider等外電報導,Jonas 3月時認為,缺少特斯拉駕車輔助科技的二手車價值將因而猛掉,未來甚至會被禁止上路。特斯拉蒐集資料的能力超群,還能將先進的安全輔助技術應用到電動車,還未推出類似科技的傳統車廠,競爭力堪虞。

    假如Model 3大獲成功、數百萬輛擁有自駕功能的車輛上路,那麼對行車安全的統計資料應有影響,這會凸顯其科技的優異程度,並迫使主管機關下令要求所有車輛都必須配備類似的系統。

    (本文內容由授權使用。圖片出處:Steve Jurvetson via Flickr CC2.0)    

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 日本車商組「大聯盟」加快EV研發,鈴木傳結盟豐田陣營

    日本車商組「大聯盟」加快EV研發,鈴木傳結盟豐田陣營

    為了加快電動車(EV)研發腳步,豐田汽車(Toyota Motor)、馬自達(Mazda Motor)以及豐田最大零件製造商Denso 於9月28日宣布成立名為「EV Common Architecture Spirit」(簡稱:EV C.A. Spirit)的電動車開發合資企業。而根據日媒最新報導指出,上述豐田主導的EV聯盟有望獲得鈴木(Suzuki)等多家日系車廠加盟。

    日經新聞30日報導,鈴木計畫加盟豐田所籌組的EV聯盟,期望藉此加快EV的研發。據報導,鈴木計畫入股豐田主導的「EV C.A. Spirit」,預估持股比重將和馬自達、Denso一樣為5%,且除了玲木之外,豐田子公司日野(Hino)以及和豐田擁有資本關係、計畫在2021年開賣EV的SUBARU也考慮加盟,大發(Daihatsu)也有可能參一咖。

    EV C.A. Spirit將開發小型車、轎車、SUV和輕型卡車電動車技術,豐田將持有90%的股份,馬自達、Denso分別擁有5%股權

    豐田、馬自達目前都還沒販售純電動轎車。日經亞洲評論28日報導,馬自達、豐田分別計畫於2019年、2020年發表量產型電動車。

    (本文內容由授權使用。圖片出處:)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 豐田看好印尼製造,將投資 20 億美元在印尼開發電動車

    豐田看好印尼製造,將投資 20 億美元在印尼開發電動車

    印尼海洋事務統籌部(Coordinating Ministry for Maritime Affairs)27 日宣布,日本豐田汽車(Toyota)計劃在未來 4 年內投資 20 億美元,在印尼開發電動車款,首先以混合動力汽車(Hybrid vehicle)為起點,以加速應對全球汽車產業的電動化浪潮。

    路透社週四報導,豐田執行董事兼社長豐田章男在聲明中表示,2019 年至 2023 年期間,將逐步增加投資對印尼的投資,總額上看 28.3 兆印尼盾(約 20 億美元)。他指出,受惠於印尼政府的政策推動,豐田將印尼視為重要的電動車投資首選地,公司將分階段逐步投資,以遵循政府的電動車發展方針。

    印尼是東南亞地區的最大經濟體,擁有豐富的鎳礦資源,這是電動車鋰電池的主要材料之一,成為吸引外國汽車製造商的拉力。此外,印尼政府先前也宣布,將給予電池和電動車製造商減稅優惠,並與其他國家簽訂關稅減免協議,積極推動電動車產業發展。

    目前印尼已是東協第二大汽車生產國,僅次於泰國。韓媒《BusinessKorea》6 月 25 日報導,受中國車市步入寒冬和產能嚴重過剩影響,南韓現代汽車(Hyundai Motor)決定關閉位於北京郊區的北京 1 號工廠,並將生產業務從中國遷至印尼,加速打入廣大的東南亞市場。

    《新加坡商業評論》(Singapore Business Review)2 月 15 日報導,國際信評機構惠譽旗下的市場研究調查機構 Fitch Solutions 預測,印尼汽車年產量將在 2019 年達到 5.8% 的成長率,主要是源於全球電動車和 SUV 的需求不斷增加。

    除此之外,印尼汽車產量也受到菲律賓、沙烏地阿拉伯、泰國和越南等市場帶動,預估合計銷量將成長 7.8%。2017 年,上述國家佔印尼整體汽車出口比重分別為 25.7%、7.4%、13.8% 和 6.2%。

    (本文內容由 授權使用。首圖來源: CC BY 2.0)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • .NET高級特性-Emit(2)類的定義,.NET高級特性-Emit(1)

    .NET高級特性-Emit(2)類的定義,.NET高級特性-Emit(1)

      在上一篇博文發了一天左右的時間,就收到了博客園許多讀者的評論和推薦,非常感謝,我也會及時回復讀者的評論。之後我也將繼續撰寫博文,梳理相關.NET的知識,希望.NET的圈子能越來越大,開發者能了解/深入.NET的本質,將工作做的簡單又高效,拒絕重複勞動,拒絕CRUD。

      ok,咱們開始繼續Emit的探索。在這之前,我先放一下我往期關於Emit的文章,方便讀者閱讀。

      《》

    一、基礎知識

      既然C#作為一門面向對象的語言,所以首當其沖的我們需要讓Emit為我們動態構建類。

      廢話不多說,首先,我們先來回顧一下C#類的內部由什麼東西組成:

      (1) 字段-C#類中保存數據的地方,由訪問修飾符、類型和名稱組成;

      (2) 屬性-C#類中特有的東西,由訪問修飾符、類型、名稱和get/set訪問器組成,屬性的是用來控制類中字段數據的訪問,以實現類的封裝性;在Java當中寫作getXXX()和setXXX(val),C#當中將其變成了屬性這種語法糖;

      (3) 方法-C#類中對邏輯進行操作的基本單元,由訪問修飾符、方法名、泛型參數、入參、出參構成;

      (4) 構造器-C#類中一種特殊的方法,該方法是專門用來創建對象的方法,由訪問修飾符、與類名相同的方法名、入參構成。

      接着,我們再觀察C#類本身又具備哪些東西:

      (1) 訪問修飾符-實現對C#類的訪問控制

      (2) 繼承-C#類可以繼承一個父類,並需要實現父類當中所有抽象的方法以及選擇實現父類的虛方法,還有就是子類需要調用父類的構造器以實現對象的創建

      (3) 實現-C#類可以實現多個接口,並實現接口中的所有方法

      (4) 泛型-C#類可以包含泛型參數,此外,類還可以對泛型實現約束

      以上就是C#類所具備的一些元素,以下為樣例:

    public abstract class Bar
    {
        public abstract void PrintName();
    }
    public interface IFoo<T> { public T Name { get; set; } } //繼承Bar基類,實現IFoo接口,泛型參數T
    public class Foo<T> : Bar, IFoo<T>
      //泛型約束
      where T : struct {
    //構造器 public Foo(T name):base() { _name = name; } //字段 private T _name; //屬性 public T Name { get => _name; set => _name = value; } //方法 public override void PrintName() {
        Console.WriteLine(_name.ToString()); }
    }

      在探索完了C#類及其定義后,我們要來了解C#的項目結構組成。我們知道C#的一個csproj項目最終會對應生成一個dll文件或者exe文件,這一個文件我們稱之為程序集Assembly;而在一個程序集中,我們內部包含和定義了許多命名空間,這些命令空間在C#當中被稱為模塊Module,而模塊正是由一個一個的C#類Type組成。

     

     

     

       所以,當我們需要定義C#類時,就必須首先定義Assembly以及Module,如此才能進行下一步工作。

    二、IL概覽

       由於Emit實質是通過IL來生成C#代碼,故我們可以反向生成,先將寫好的目標代碼寫成cs文件,通過編譯器生成dll,再通過ildasm查看IL代碼,即可依葫蘆畫瓢的編寫出Emit代碼。所以我們來查看以下上節Foo所生成的IL代碼。

      

     

     

       從上圖我們可以很清晰的看到.NET的層級結構,位於樹頂層淺藍色圓點表示一個程序集Assembly,第二層藍色表示模塊Module,在模塊下的均為我們所定義的類,類中包含類的泛型參數、繼承類信息、實現接口信息,類的內部包含構造器、方法、字段、屬性以及它的get/set方法,由此,我們可以開始編寫Emit代碼了

    三、Emit編寫

      有了以上的對C#類的解讀和IL的解讀,我們知道了C#類本身所需要哪些元素,我們就開始根據這些元素來開始編寫Emit代碼了。這裏的代碼量會比較大,請讀者慢慢閱讀,也可以參照以上我寫的類生成il代碼進行比對。

      在Emit當中所有創建類型的幫助類均以Builder結尾,從下錶中我們可以看的非常清楚

    元素中文 元素名稱 對應Emit構建器名稱
    程序集  Assembly AssemblyBuilder
    模塊  Module ModuleBuilder
     Type TypeBuilder
    構造器  Constructor ConstructorBuilder
    屬性  Property PropertyBuilder
    字段  Field FieldBuilder
    方法  Method MethodBuilder

      由於創建類需要從Assembly開始創建,所以我們的入口是AssemblyBuilder

      (1) 首先,我們先引入命名空間,我們以上節Foo類為樣例進行編寫

    using System.Reflection.Emit;

      (2) 獲取基類和接口的類型

    var barType = typeof(Bar);
    var interfaceType = typeof(IFoo<>);

      (3) 定義Foo類型,我們可以看到在定義類之前我們需要創建Assembly和Module

    //定義類
    var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Edwin.Blog.Emit"), AssemblyBuilderAccess.Run);
    var moduleBuilder = assemblyBuilder.DefineDynamicModule("Edwin.Blog.Emit");
    var typeBuilder = moduleBuilder.DefineType("Foo", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit);

      (4) 定義泛型參數T,並添加約束

    //定義泛型參數
    var genericTypeBuilder = typeBuilder.DefineGenericParameters("T")[0];
    //設置泛型約束
    genericTypeBuilder.SetGenericParameterAttributes(GenericParameterAttributes.NotNullableValueTypeConstraint);

      (5) 繼承和實現接口,注意當實現類的泛型參數需傳遞給接口時,需要將泛型接口添加泛型參數后再調用AddInterfaceImplementation方法

    //繼承基類
    typeBuilder.SetParent(barType);
    //實現接口
    typeBuilder.AddInterfaceImplementation(interfaceType.MakeGenericType(genericTypeBuilder));

      (6) 定義字段,因為字段在構造器值需要使用,故先創建

    //定義字段
    var fieldBuilder = typeBuilder.DefineField("_name", genericTypeBuilder, FieldAttributes.Private);

      (7) 定義構造器,並編寫內部邏輯

    //定義構造器
    var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { genericTypeBuilder });
    var ctorIL = ctorBuilder.GetILGenerator();
    //Ldarg_0在實例方法中表示this,在靜態方法中表示第一個參數
    ctorIL.Emit(OpCodes.Ldarg_0);
    ctorIL.Emit(OpCodes.Ldarg_1);
    //為field賦值
    ctorIL.Emit(OpCodes.Stfld, fieldBuilder);
    ctorIL.Emit(OpCodes.Ret);

      (8) 定義Name屬性

    //定義屬性
    var propertyBuilder = typeBuilder.DefineProperty("Name", PropertyAttributes.None, genericTypeBuilder, Type.EmptyTypes);

      (9) 編寫Name屬性的get/set訪問器

    //定義get方法
    var getMethodBuilder = typeBuilder.DefineMethod("get_Name", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.SpecialName | MethodAttributes.Virtual, CallingConventions.Standard, genericTypeBuilder, Type.EmptyTypes);
    var getIL = getMethodBuilder.GetILGenerator();
    getIL.Emit(OpCodes.Ldarg_0);
    getIL.Emit(OpCodes.Ldfld, fieldBuilder);
    getIL.Emit(OpCodes.Ret);
    typeBuilder.DefineMethodOverride(getMethodBuilder, interfaceType.GetProperty("Name").GetGetMethod()); //實現對接口方法的重載
    propertyBuilder.SetGetMethod(getMethodBuilder); //設置為屬性的get方法
    //定義set方法
    var setMethodBuilder = typeBuilder.DefineMethod("set_Name", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.SpecialName | MethodAttributes.Virtual, CallingConventions.Standard, null, new Type[] { genericTypeBuilder });
    var setIL = setMethodBuilder.GetILGenerator();
    setIL.Emit(OpCodes.Ldarg_0);
    setIL.Emit(OpCodes.Ldarg_1);
    setIL.Emit(OpCodes.Stfld, fieldBuilder);
    setIL.Emit(OpCodes.Ret);
    typeBuilder.DefineMethodOverride(setMethodBuilder, interfaceType.GetProperty("Name").GetSetMethod()); //實現對接口方法的重載
    propertyBuilder.SetSetMethod(setMethodBuilder); //設置為屬性的set方法

       (10) 定義並實現PrintName方法

    //定義方法
    var printMethodBuilder = typeBuilder.DefineMethod("PrintName", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, CallingConventions.Standard, null, Type.EmptyTypes);
    var printIL = printMethodBuilder.GetILGenerator();
    printIL.Emit(OpCodes.Ldarg_0);
    printIL.Emit(OpCodes.Ldflda, fieldBuilder);
    printIL.Emit(OpCodes.Constrained, genericTypeBuilder);
    printIL.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString", Type.EmptyTypes));
    printIL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
    printIL.Emit(OpCodes.Ret);
    //實現對基類方法的重載
    typeBuilder.DefineMethodOverride(printMethodBuilder, barType.GetMethod("PrintName", Type.EmptyTypes));

      (11) 創建類

    var type = typeBuilder.CreateType(); //netstandard中請使用CreateTypeInfo().AsType()

      (12) 調用

    var obj = Activator.CreateInstance(type.MakeGenericType(typeof(DateTime)), DateTime.Now);
    (obj as Bar).PrintName();
    Console.WriteLine((obj as IFoo<DateTime>).Name);

    四、應用

      上面的樣例僅供學習只用,無法運用在實際項目當中,那麼,Emit構建類在實際項目中我們可以有什麼應用,提高我們的編碼效率

      (1) 動態DTO-當我們需要將實體映射到某個DTO時,可以用動態DTO來代替你手寫的DTO,選擇你需要的字段回傳給前端,或者前端把他想要的字段傳給後端

      (2) DynamicLinq-我的第一篇博文有個讀者提到了表達式樹,而linq使用的正是表達式樹,當表達式樹+Emit時,我們就可以用像SQL或者GraphQL那樣的查詢語句實現動態查詢

      (3) 對象合併-我們可以編寫實現一個像js當中Object.assign()一樣的方法,實現對兩個實體的合併

      (4) AOP動態代理-AOP的核心就是代理模式,但是與其對應的是需要手寫代理類,而Emit就可以幫你動態創建代理類,實現切面編程

      (5) …

    五、小結

      對於Emit,確實初學者會對其感到複雜和難以學習,但是只要搞懂其中的原理,其實最終就是C#和.NET語言的本質所在,在學習Emit的同時,也是在鍛煉你的基本功是否紮實,你是否對這門語言精通,是否有各種簡化代碼的應用。

      保持學習,勇於實踐;Write Less,Do More;作者之後還會繼續.NET高級特性系列,感謝閱讀!

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

  • Android DecorView 與 Activity 綁定原理分析

    Android DecorView 與 Activity 綁定原理分析

    一年多以前,曾經以為自己對 View 的添加显示邏輯已經有所了解了,事後發現也只是懂了些皮毛而已。經過一年多的實戰,Android 和 Java 基礎都有了提升,是時候該去看看 DecorView 的添加显示。

    概論

    Android 中 Activity 是作為應用程序的載體存在,代表着一個完整的用戶界面,提供了一個窗口來繪製各種視圖,當 Activity 啟動時,我們會通過 setContentView 方法來設置一個內容視圖,這個內容視圖就是用戶看到的界面。那麼 View 和 activity 是如何關聯在一起的呢 ?

     上圖是 View 和 Activity 之間的關係。先解釋圖中一些類的作用以及相關關係:

    • Activity : 對於每一個 activity 都會有擁有一個 PhoneWindow。

    • PhoneWindow :該類繼承於 Window 類,是 Window 類的具體實現,即我們可以通過該類具體去繪製窗口。並且,該類內部包含了一個 DecorView 對象,該 DectorView 對象是所有應用窗口的根 View。
    • DecorView 是一個應用窗口的根容器,它本質上是一個 FrameLayout。DecorView 有唯一一個子 View,它是一個垂直 LinearLayout,包含兩個子元素,一個是 TitleView( ActionBar 的容器),另一個是 ContentView(窗口內容的容器)。

    • ContentView :是一個 FrameLayout(android.R.id.content),我們平常用的 setContentView 就是設置它的子 View 。

    • WindowManager : 是一個接口,裏面常用的方法有:添加View,更新View和刪除View。主要是用來管理 Window 的。WindowManager 具體的實現類是WindowManagerImpl。最終,WindowManagerImpl 會將業務交給 WindowManagerGlobal 來處理。
    • WindowManagerService (WMS) : 負責管理各 app 窗口的創建,更新,刪除, 显示順序。運行在 system_server 進程。

    ViewRootImpl :擁有 DecorView 的實例,通過該實例來控制 DecorView 繪製。ViewRootImpl 的一個內部類 W,實現了 IWindow 接口,IWindow 接口是供 WMS 使用的,WSM 通過調用 IWindow 一些方法,通過 Binder 通信的方式,最後執行到了 W 中對應的方法中。同樣的,ViewRootImpl 通過 IWindowSession 來調用 WMS 的 Session 一些方法。Session 類繼承自 IWindowSession.Stub,每一個應用進程都有一個唯一的 Session 對象與 WMS 通信。

    DecorView 的創建 

    先從 Mainactivity 中的代碼看起,首先是調用了 setContentView;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    該方法是父類 AppCompatActivity 的方法,最終會調用 AppCompatDelegateImpl 的 setContentView 方法:

    // AppCompatDelegateImpl  
    public void setContentView(int resId) { this.ensureSubDecor(); ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290); contentParent.removeAllViews(); LayoutInflater.from(this.mContext).inflate(resId, contentParent); this.mOriginalWindowCallback.onContentChanged(); }

    ensureSubDecor 從字面理解就是創建 subDecorView,這個是根據主題來創建的,下文也會講到。創建完以後,從中獲取 contentParent,再將從 activity 傳入的 id xml 布局添加到裏面。不過大家注意的是,在添加之前先調用 removeAllViews() 方法,確保沒有其他子 View 的干擾。

        private void ensureSubDecor() {
            if (!this.mSubDecorInstalled) {
                this.mSubDecor = this.createSubDecor(); 
                ......
            }
            ......
        }        

     最終會調用 createSubDecor() ,來看看裏面的具體代碼邏輯:

     private ViewGroup createSubDecor() {
            // 1、獲取主題參數,進行一些設置,包括標題,actionbar 等 
            TypedArray a = this.mContext.obtainStyledAttributes(styleable.AppCompatTheme);
            if (!a.hasValue(styleable.AppCompatTheme_windowActionBar)) {
                a.recycle();
                throw new IllegalStateException("You need to use a Theme.AppCompat theme (or descendant) with this activity.");
            } else {
                if (a.getBoolean(styleable.AppCompatTheme_windowNoTitle, false)) {
                    this.requestWindowFeature(1);
                } else if (a.getBoolean(styleable.AppCompatTheme_windowActionBar, false)) {
                    this.requestWindowFeature(108);
                }
    
                if (a.getBoolean(styleable.AppCompatTheme_windowActionBarOverlay, false)) {
                    this.requestWindowFeature(109);
                }
    
                if (a.getBoolean(styleable.AppCompatTheme_windowActionModeOverlay, false)) {
                    this.requestWindowFeature(10);
                }
    
                this.mIsFloating = a.getBoolean(styleable.AppCompatTheme_android_windowIsFloating, false);
                a.recycle();
                // 2、確保優先初始化 DecorView
                this.mWindow.getDecorView();
                LayoutInflater inflater = LayoutInflater.from(this.mContext);
                ViewGroup subDecor = null;
                // 3、根據不同的設置來對 subDecor 進行初始化
                if (!this.mWindowNoTitle) {
                    if (this.mIsFloating) {
                        subDecor = (ViewGroup)inflater.inflate(layout.abc_dialog_title_material, (ViewGroup)null);
                        this.mHasActionBar = this.mOverlayActionBar = false;
                    } else if (this.mHasActionBar) {
                        TypedValue outValue = new TypedValue();
                        this.mContext.getTheme().resolveAttribute(attr.actionBarTheme, outValue, true);
                        Object themedContext;
                        if (outValue.resourceId != 0) {
                            themedContext = new ContextThemeWrapper(this.mContext, outValue.resourceId);
                        } else {
                            themedContext = this.mContext;
                        }
    
                        subDecor = (ViewGroup)LayoutInflater.from((Context)themedContext).inflate(layout.abc_screen_toolbar, (ViewGroup)null);
                        this.mDecorContentParent = (DecorContentParent)subDecor.findViewById(id.decor_content_parent);
                        this.mDecorContentParent.setWindowCallback(this.getWindowCallback());
                        if (this.mOverlayActionBar) {
                            this.mDecorContentParent.initFeature(109);
                        }
    
                        if (this.mFeatureProgress) {
                            this.mDecorContentParent.initFeature(2);
                        }
    
                        if (this.mFeatureIndeterminateProgress) {
                            this.mDecorContentParent.initFeature(5);
                        }
                    }
                } else {
                    if (this.mOverlayActionMode) {
                        subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple_overlay_action_mode, (ViewGroup)null);
                    } else {
                        subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple, (ViewGroup)null);
                    }
    
                    if (VERSION.SDK_INT >= 21) {
                        ViewCompat.setOnApplyWindowInsetsListener(subDecor, new OnApplyWindowInsetsListener() {
                            public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
                                int top = insets.getSystemWindowInsetTop();
                                int newTop = AppCompatDelegateImpl.this.updateStatusGuard(top);
                                if (top != newTop) {
                                    insets = insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), newTop, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
                                }
    
                                return ViewCompat.onApplyWindowInsets(v, insets);
                            }
                        });
                    } else {
                        ((FitWindowsViewGroup)subDecor).setOnFitSystemWindowsListener(new OnFitSystemWindowsListener() {
                            public void onFitSystemWindows(Rect insets) {
                                insets.top = AppCompatDelegateImpl.this.updateStatusGuard(insets.top);
                            }
                        });
                    }
                }
    
                if (subDecor == null) {
                    throw new IllegalArgumentException("AppCompat does not support the current theme features: { windowActionBar: " + this.mHasActionBar + ", windowActionBarOverlay: " + this.mOverlayActionBar + ", android:windowIsFloating: " + this.mIsFloating + ", windowActionModeOverlay: " + this.mOverlayActionMode + ", windowNoTitle: " + this.mWindowNoTitle + " }");
                } else {
                    if (this.mDecorContentParent == null) {
                        this.mTitleView = (TextView)subDecor.findViewById(id.title);
                    }
    
                    ViewUtils.makeOptionalFitsSystemWindows(subDecor);
                    ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);
                    ViewGroup windowContentView = (ViewGroup)this.mWindow.findViewById(16908290);
                    if (windowContentView != null) {
                        while(windowContentView.getChildCount() > 0) {
                            View child = windowContentView.getChildAt(0);
                            windowContentView.removeViewAt(0);
                            contentView.addView(child);
                        }
    
                        windowContentView.setId(-1);
                        contentView.setId(16908290);
                        if (windowContentView instanceof FrameLayout) {
                            ((FrameLayout)windowContentView).setForeground((Drawable)null);
                        }
                    }
                    // 將 subDecor 添加到 DecorView 中
                    this.mWindow.setContentView(subDecor);
                    contentView.setAttachListener(new OnAttachListener() {
                        public void onAttachedFromWindow() {
                        }
    
                        public void onDetachedFromWindow() {
                            AppCompatDelegateImpl.this.dismissPopups();
                        }
                    });
                    return subDecor;
                }
            }
        }
                        

    上面的代碼總結來說就是在做一件事,就是創建 subDecor。攤開來說具體如下:

    1、根據用戶選擇的主題來設置一些显示特性,包括標題,actionbar 等。

    2、根據不同特性來初始化 subDecor;對 subDecor 內部的子 View 進行初始化。

    3、最後添加到 DecorView中。

    添加的具體代碼如下:此處是通過調用 

     // AppCompatDelegateImpl   this.mWindow.getDecorView();
    
     // phoneWindow    public final View getDecorView() {
            if (mDecor == null || mForceDecorInstall) {
                installDecor();
            }
            return mDecor;
        }
     
    
    private void installDecor() {
            mForceDecorInstall = false;
            if (mDecor == null) {
     // 生成 DecorView             mDecor = generateDecor(-1);
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            } else {
     // 這樣 DecorView 就持有了window             mDecor.setWindow(this);
            }
          ......
    }
    
    
       protected DecorView generateDecor(int featureId) {
            // System process doesn't have application context and in that case we need to directly use // the context we have. Otherwise we want the application context, so we don't cling to the // activity.
            Context context;
            if (mUseDecorContext) {
                Context applicationContext = getContext().getApplicationContext();
                if (applicationContext == null) {
                    context = getContext();
                } else {
                    context = new DecorContext(applicationContext, getContext());
                    if (mTheme != -1) {
                        context.setTheme(mTheme);
                    }
                }
            } else {
                context = getContext();
            }
            return new DecorView(context, featureId, this, getAttributes());
       }

    到此,DecorView 的創建就講完了。可是我們似乎並沒有看到 DecorView 是被添加的,什麼時候對用戶可見的。

     WindowManager

    View 創建完以後,那 Decorview 是怎麼添加到屏幕中去的呢?當然是 WindowManager 呢,那麼是如何將 View 傳到 WindowManager 中呢。

    看 ActivityThread 中的 handleResumeActivity 方法:

    // ActivityThread
    public
    void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { ...... final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) { try { willBeVisible = ActivityManager.getService().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; ...... if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } else { // The activity will get a callback for this {@link LayoutParams} change // earlier. However, at that time the decor will not be set (this is set // in this method), so no action will be taken. This call ensures the // callback occurs with the decor set. a.onWindowAttributesChanged(l); } } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } // Get rid of anything left hanging around. cleanUpPendingRemoveWindows(r, false /* force */); // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { if (r.newConfig != null) { performConfigurationChangedForActivity(r, r.newConfig); if (DEBUG_CONFIGURATION) { Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig); } r.newConfig = null; } if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward); WindowManager.LayoutParams l = r.window.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != forwardBit) { l.softInputMode = (l.softInputMode & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) | forwardBit; if (r.activity.mVisibleFromClient) { ViewManager wm = a.getWindowManager(); View decor = r.window.getDecorView(); wm.updateViewLayout(decor, l); } } r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) {           // 這裏也會調用addview r.activity.makeVisible(); } } r.nextIdle = mNewActivities; mNewActivities = r; if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r); Looper.myQueue().addIdleHandler(new Idler()); }

    上面的代碼主要做了以下幾件事:

    1、獲取到 DecorView,設置不可見,然後通過 wm.addView(decor, l) 將 view 添加到 WindowManager;

    2、在某些情況下,比如此時點擊了輸入框調起了鍵盤,就會調用 wm.updateViewLayout(decor, l) 來更新 View 的布局。

    3、這些做完以後,會調用 activity 的  makeVisible ,讓視圖可見。如果此時 DecorView 沒有添加到 WindowManager,那麼會添加。 

    // Activity
    void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }

     接下來,看下 addview 的邏輯。 WindowManager 的實現類是 WindowManagerImpl,而它則是通過 WindowManagerGlobal 代理實現 addView 的,我們看下 addView 的方法:

    // WindowManagerGlobal  
     public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
               // ......
        
                root = new ViewRootImpl(view.getContext(), display);
                view.setLayoutParams(wparams);
    
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
               // do this last because it fires off messages to start doing things
                try {
                    root.setView(view, wparams, panelParentView);
                } catch (RuntimeException e) {
                    // BadTokenException or InvalidDisplayException, clean up.
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                    throw e;
                } 
    }

    在這裏,實例化了 ViewRootImpl 。同時調用 ViewRootImpl 的 setView 方法來持有了 DecorView。此外這裏還保存了 DecorView ,Params,以及 ViewRootImpl 的實例。

    現在我們終於知道為啥 View 是在 OnResume 的時候可見的呢。

     ViewRootImpl

    實際上,View 的繪製是由 ViewRootImpl 來負責的。每個應用程序窗口的 DecorView 都有一個與之關聯的 ViewRootImpl 對象,這種關聯關係是由 WindowManager 來維護的。

    先看 ViewRootImpl 的 setView 方法,該方法很長,我們將一些不重要的點註釋掉:

       /**
         * We have one child
         */
        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    mView = view;
                    ......
                   
                    mAdded = true;
                    int res; /* = WindowManagerImpl.ADD_OKAY; */
    
                    // Schedule the first layout -before- adding to the window
                    // manager, to make sure we do the relayout before receiving
                    // any other events from the system.
    
                    requestLayout();
                    ......
                }
            }
        }

    這裏先將 mView 保存了 DecorView 的實例,然後調用 requestLayout() 方法,以完成應用程序用戶界面的初次布局。

     public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                checkThread();
                mLayoutRequested = true;
                scheduleTraversals();
            }
        }

    因為是 UI 繪製,所以一定要確保是在主線程進行的,checkThread 主要是做一個校驗。接着調用 scheduleTraversals 開始計劃繪製了。

    void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }

    這裏主要關注兩點:

    mTraversalBarrier : Handler 的同步屏障。它的作用是可以攔截 Looper 對同步消息的獲取和分發,加入同步屏障之後,Looper 只會獲取和處理異步消息,如果沒有異步消息那麼就會進入阻塞狀態。也就是說,對 View 繪製渲染的處理操作可以優先處理(設置為異步消息)。

    mChoreographer: 編舞者。統一動畫、輸入和繪製時機。也是這章需要重點分析的內容。

    mTraversalRunnable :TraversalRunnable 的實例,是一個Runnable,最終肯定會調用其 run 方法:

    final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }

    doTraversal,如其名,開始繪製了,該方法內部最終會調用 performTraversals 進行繪製。

      void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
    
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }

    到此,DecorView 與 activity 之間的綁定關係就講完了,下一章,將會介紹 performTraversals 所做的事情,也就是 View 繪製流程。 

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 豐田衝刺燃料電池車,傳產能將擴增至 10 倍以上

    豐田衝刺燃料電池車,傳產能將擴增至 10 倍以上

    日刊工業新聞 3 日,豐田汽車(Toyota)計劃於 2020 年將燃料電池車(FCV)月產能提高至 3,000 台,將達現行的 10 倍以上水準。豐田計劃在 2020 年下半推出 FCV 車「MIRAI」的次代車款。

    據報導,豐田目前利用元町工廠的專用產線生產「MIRAI」,年產能約 3,000 台,依此換算月產能相當於 250 台左右。

    2018 年 MIRAI 全球銷售量約 2,400 台,而豐田目標在 2020 年以後將 FCV 年銷售量提高至 3 萬台以上水準。

    截至台灣時間 3 日上午 10 點 21 分為止,豐田下跌 0.84%。

    豐田於 2018 年 5 月 24 日宣布,為了因應計劃在 2020 年以後將 FCV 全球年銷售量提高至 3 萬台以上水準的目標,決議將增產 FCV 關鍵零件,計劃在愛知縣豐田市的本社工廠廠區內興建新廠房、增產燃料電池堆(Fuel Cell stack),且也計劃在愛知縣三好市的下山工廠內增設用來儲存氫燃料的高壓氫氣槽專用產線。上述新廠預計於 2020 年左右啟用。

    (本文內容由 授權使用。首圖來源: CC BY 2.0)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 區塊鏈成重要突破口,與幣圈無關卻與5G有關

    區塊鏈成重要突破口,與幣圈無關卻與5G有關

      幾乎是一夜之間,區塊鏈就成了更熱門的話題,此前,如果我們還在說“物聯網、大數據、雲計算、人工智能和 5G”,那麼,以後這一長串的背後可能就要加上“區塊鏈”。

      誠然,國家將區塊鏈當成戰略來發展,並“要把區塊鏈作為核心技術自主創新的重要突破口,明確主攻方向,加大投入力度,着力攻克一批關鍵核心技術,加快推動區塊鏈技術和產業創新發展”,這對整個區塊鏈產業都是巨大的利好,但是,這種利好卻實實在在與現在網絡上那些頂着區塊鏈帽子實際卻是在搞傳銷的“幣”們,毫無關係。如果有關係,那也一定是負面。

      在國內,區塊鏈技術佔據優勢的公司依然是 BAT,特別是阿里巴巴和螞蟻金服。在 2018 年的世界互聯網大會上,螞蟻金服“自主可控金融級商用區塊鏈平台”與“阿里雲 supET 工業互聯網平台”同時入選 15 項世界互聯網領先科技成果之列。前者的區塊鏈因素顯而易見,後者實際上也在由區塊鏈提供製造生產質量追溯和供應鏈管理服務。

      據全球知名智能信息服務機構科睿唯安公布信息,截至 2019 年 4 月 30 日,阿里巴巴以 290 件區塊鏈專利方案數量穩居全球第一。結合權威知識產權產業媒體 IPRdaily 過去兩年發布的“全球區塊鏈專利企業排行榜”显示,阿里巴巴申請的區塊鏈專利數量已經連續三年全球第一。2019 上半年全球區塊鏈企業發明專利排行榜(TOP100) 中,阿里巴巴以 322 件專利位列第一,中國平安以 274 件專利排名第二,Nchain 以 241 件專利排名第三。此外,百度( 7 名)、騰訊( 13 名)、京東( 14 名)、華為( 28 名)、獵豹( 40 名)、中國移動( 44 名)等均榜上有名。

      從上面的數據可以看出來,真正在區塊鏈的研究上佔據優勢且不斷應用的,沒有一個是“發幣”的,都是實實在在的在落地。

      當然了,也許與幣有關。此前有消息傳言,央行可能將在未來幾個月內正式推出國家支持的数字貨幣“DCEP”,初期將向中國工商銀行、中國建設銀行、中國銀行、中國農業銀行、阿里巴巴、騰訊以及銀聯七家機構發行。如果成行,那將是世界上是首個法定数字貨幣,意義深遠。

      在官方發布的新聞中,將區塊鏈發展方向定位為四個方面:

      1、要探索“區塊鏈+”在民生領域的運用,积極推動區塊鏈技術在教育、就業、養老、精準脫貧、醫療健康、商品防偽、食品安全、公益、社會救助等領域的應用,為人民群眾提供更加智能、更加便捷、更加優質的公共服務。

      2016 年,螞蟻金服聯合中國紅十字基金會等公益機構上線區塊鏈試驗項目,使捐款人可以追蹤善款的完整流轉情況。2017 年 3 月,支付寶愛心捐贈平台全面引入區塊鏈技術,所有捐贈數據上鏈。實現了實時賬目公示,有助於解決公益財務透明的“痛點”。螞蟻金服公益運用區塊鏈技術追蹤籌款,建立起第三方公示體系區塊鏈資金流公示,為公益機構進行數據統計、項目執行跟蹤提供便利。區塊鏈具有不可篡改的特性,任何寫入區塊鏈的記錄均不能更改,可以供公眾監督及審計。而“區塊鏈+公益”正是利用這一特性,發揮公眾賬本的價值,不論用戶是捐十塊、二十塊還是幾百塊,讓用戶所獻出的每一筆都記錄在區塊鏈上,有跡可循,持續追溯。

      2016 年 10 月,阿里與微軟、小蟻、法大大等合作開發“法鏈”,推出基於阿里雲平台的郵箱存證產品,通過法鏈上備份的电子郵件和雲服務,阿里將使中國法院能大規模採用数字證據郵件。

      2017 年 3 月,阿里巴巴與普華永道合作,打造可追溯的跨境食品供應鏈,用於跟蹤產品從生產者到消費者之間的整個流程。

      2017 年 8 月,阿里健康與江蘇常州市合作推出我國首個基於醫療場景的區塊鏈應用――“醫聯體+區塊鏈”試點項目。

      2017 年 10 月 11 日,螞蟻金服 CTO 程立在螞蟻金服金融科技開放峰會上首度披露未來的技術布局――“BASIC”戰略,其中的B對應的就是區塊鏈(Blockchain),同時,技術實驗室宣布開放區塊鏈技術,支持進口食品安全溯源、商品正品溯源等,第一個落地場景將是海外奶粉品牌的追蹤,先是產自澳洲、新西蘭的 26 個品牌的奶粉。2017 年 11 月 24 日,天貓國際宣布升級全球原產地溯源計劃,未來將覆蓋全球 63 個國家和地區,3700 個品類,14500 個海外品牌,也將向全行業開放,賦能整個行業。2018 年 2 月,菜鳥與天貓國際官方消息,已啟用區塊鏈技術跟蹤、上傳、查證跨境進口商品的物流全鏈路信息,涵蓋生產、運輸、通關、報檢、第三方檢驗等商品進口全流程,為每個跨境進口商品打上獨一無二的“身份證”,供消費者查詢驗證。

      騰訊旗下的微眾銀行於 2016 年 6 月開發出面向金融業的聯盟鏈雲服務 BaaS,並在 2017 年 1 月落地了第一個商業場景,即微黃金項目。微黃金是騰訊的一項在線黃金交易服務,用戶可以在微信低門檻靈活購買黃金,其背後正是由騰訊開發底層基礎架構的聯盟鏈。騰訊、工商銀行等多個節點共同參与記帳。2018 年 4 月底,騰訊發布第一款區塊鏈遊戲《一起來捉妖》,試圖通過區塊鏈技術保障了遊戲稀有內容投放的公開公平性,同時用戶可以將稀有妖怪上鏈永久保存。

      2017 年 12 月,沃爾瑪、京東、IBM、清華大學电子商務交易技術國家工程實驗室共同宣布成立安全食品區塊鏈溯源聯盟,運用區塊鏈技術搭建“京東區塊鏈防偽追溯開放平台”。該平台推出之後,用戶只需打開京東 APP 找到購物訂單,通過“一鍵溯源”或直接掃描產品溯源碼,即可獲取溯源信息。

      2018 年 10 月,百度正式發布自主研發的區塊鏈網絡系統——超級鏈。同時,宣布與海南省政府合作,區塊鏈實驗室及度鏈公司落戶海南,並推出“區塊鏈+大閘蟹溯源”應用。

      2、要推動區塊鏈底層技術服務和新型智慧城市建設相結合,探索在信息基礎設施、智慧交通、能源電力等領域的推廣應用,提升城市管理的智能化、精準化水平。

      5 月 22 日,上海、浙江、江蘇、安徽 4 地法院成立全國首個區域司法鏈,實現從起訴到執行全程上鏈,極大提高了訴訟效率和法院公信力,降低訴訟成本。最早引入區塊鏈技術的杭州互聯網法院,上鏈半年,其知識產權糾紛案件的調撤率上升至 95.3%。

      2017 年 5 月,阿里投資 Symbiont,該公司致力於利用區塊鏈技術打造一個發行和交易智能證券的平台。此外,深圳財富文化集團已經打造了一個文化產權交易的區塊鏈平台,並與螞蟻雙鏈打通,解決文化藝術品流通、交易和一系列金融服務。

      2017 年 11 月 8 日,阿里巴巴集團、螞蟻金服集團與雄安新區簽署了戰略合作協議,阿里巴巴與螞蟻金服將承建数字雄安區塊鏈實施平台。

      另外,國網電商公司已建成國家電網公司系統內首個司法級可信區塊鏈公共服務平台,作為唯一央企與北京互聯網法院“天平鏈”互信互通,掛牌工信部區塊鏈重點實驗室電力應用實驗基地,參与首個區塊鏈國家標準制定,實現了區塊鏈技術在電力積分通兌、光伏簽約、票據繳費、电子發票等多場景落地應用。

      3、要利用區塊鏈技術促進城市間在信息、資金、人才、徵信等方面更大規模的互聯互通,保障生產要素在區域內有序高效流動。

      在 9 月末舉行的雲棲大會·螞蟻區塊鏈生態峰會上,螞蟻金服集團副總裁蔣國飛透露,螞蟻區塊鏈已落地 40 多個應用。含括跨境支付、供應鏈金融、通用溯源、电子票據等多個行業領域。並於同期宣布了與全球最大的種子和農藥公司拜耳合作,應用方向進一步擴展。

      螞蟻金服 8 月份宣布,基於區塊鏈技術的供應鏈協作網絡——螞蟻區塊鏈“雙鏈通”全面升級開放。這一服務運用區塊鏈技術可解決供應鏈金融中的信任難題,同時讓小微商家也能享受高效便捷的金融服務。目前,這一模式已在成都率先應用。通過與成都商業銀行、成都中小企業融資擔保公司的合作,“雙鏈通”完成了供應鏈金融的全鏈路覆蓋。上鏈后,整個融資流轉過程清晰留痕、不可篡改,所有參与方都要通過“雙鏈通”進行身份核實和意願確認,数字簽名實時上鏈,杜絕了資金挪用等風險。

      4、要探索利用區塊鏈數據共享模式,實現政務數據跨部門、跨區域共同維護和利用,促進業務協同辦理,深化“最多跑一次”改革,為人民群眾帶來更好的政務服務體驗。

      3 月,杭州地鐵聯合支付寶、螞蟻區塊鏈推出了區塊鏈电子發票。全程手機操作,耗時不到 10 秒,上班族再也不用前往窗口排隊取票了。支付寶聯合雲南省相關部門,在醫療、教育等多個民生服務場景落地區塊鏈电子票據。從地鐵电子發票到法院訴訟案件,再到电子票據平台,螞蟻區塊鏈在過去近 3 年時間里,相繼落地了 40 多個場景。其中長三角佔比超過一半。“上鏈”后長三角人辦事效率大大提升,不少事項可享受“秒級”服務。

      我們看到,在 5G 時代,無差別計算能力的流通成本會大幅下降,無差別計算能力是 AI 最重要的勞動要素,而區塊鏈就是 AI 最重要的勞動要素。阿里巴巴達摩院在《2019 十大科技趨勢》中提到,5G 和區塊鏈是未來十年的發展方向。《福布斯》發布的《2020 十大科技趨勢》也提到,5G 和區塊鏈都將於 2020 年崛起,未來與人們日常生活相關的每一個行業都會被變革。還是那句話,“誰能把當前的技術和資源用到最充分,誰就是這個時代的最強者”,也許 5G 的流量充沛,正是區塊鏈的黃金歲月。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

  • JVM 問題排查和性能優化常用的 JDK 工具

    JVM 問題排查和性能優化常用的 JDK 工具

    JDK 提供了一系列用於監控、診斷 Java 進程的工具,它們在 JDK 安裝目錄的 bin 目錄下,有 jps、jcmd、jstack、jinfo、jmap 等。其中jmc、jconsole、jvisualvm 是 GUI 工具,其他大部分都是命令行工具。

    cd $JAVA_HOME/bin
    ls

    本篇只是個入門介紹,不涉及深入分析。每一個工具都有它專門的作用,掌握使用方法只是很簡單的入門階段,更重要的是根據工具得到的信息去分析系統存在的問題以及性能瓶頸,每一個工具的使用和分析都可以單獨成文。

    jps

    如果你用過 Linux,那肯定熟悉 ps 命令,用來查看進程列表的。jps 就好比是 ps 命令的子集,它查詢的是當前用戶下已經啟動的 Java 進程。這是進行線上問題排查的大門鑰匙,有了它才能下手後面的動作。

    下面是 jps 的幫助文檔

    usage: jps [-help]
           jps [-q] [-mlvV] [<hostid>]
    
    Definitions:
        <hostid>:      <hostname>[:<port>]

    一般的用法是 jps -l,前面一列显示 pid,後面一列显示進程名稱。

    還可以用下列參數查看更具體的 Java 進程信息,用法為 jps -lv

    jstack

    查看 Java 進程內當前時刻的線程快照,也就是每條線程正在執行的方法棧情況,用於定位線程停頓、死鎖等長時間等待的問題。

    以下是 jstack 的幫助文檔。

    Usage:
        jstack [-l] <pid>
            (to connect to running process)
        jstack -F [-m] [-l] <pid>
            (to connect to a hung process)
        jstack [-m] [-l] <executable> <core>
            (to connect to a core file)
        jstack [-m] [-l] [server_id@]<remote server IP or hostname>
            (to connect to a remote debug server)
    
    Options:
        -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
        -m  to print both java and native frames (mixed mode)
        -l  long listing. Prints additional information about locks
        -h or -help to print this help message
    

    最常用的就是 jstack -pid 或者 jstack -l pid,打印線程狀態、棧使用情況。

    如果是線上查看不方便的話,可以用命令 jstack -l pid > stack.log,輸出到文件中下載到本地查看。

    jstack -m pid,打印 Java 和 Native 棧信息

    如果 -l 和 -m 都不起作用的時候,可以使用 java -F pid 強制 dump。

    jinfo

    它的主要作用是查看 JVM 配置參數,還可以動態設置部分參數值。jinfo 使用時需要 attach 到目標 JVM 上。關於 attach jvm 可以點擊查看

    使用 jinfo -h查看幫助文檔

    Usage:
        jinfo [option] <pid>
            (to connect to running process)
        jinfo [option] <executable <core>
            (to connect to a core file)
        jinfo [option] [server_id@]<remote server IP or hostname>
            (to connect to remote debug server)
    
    where <option> is one of:
        -flag <name>         to print the value of the named VM flag
        -flag [+|-]<name>    to enable or disable the named VM flag
        -flag <name>=<value> to set the named VM flag to the given value
        -flags               to print VM flags
        -sysprops            to print Java system properties
        <no option>          to print both of the above
        -h | -help           to print this help message

    jinfo -flags pid

    查看 JVM 參數,其中 Non-default VM flags 是虛擬機默認設置的參數,Command line 是用戶指定的參數,比如命令行啟動 jar 包的時候加上的參數。

    jinfo -flag 參數名 pid

    可以查看指定參數的值,比如查看堆的最大值(-XX:MaxHeapSize 也就是 -Xmx ):

    jinfo -flag MaxHeapSize 92041
    
    -XX:MaxHeapSize=20971520

    jinfo -sysprops pid

    查看系統參數

    jinfo pid

    查看 jvm 參數和系統參數

    以上信息,如果我們用過 visualVM 等監控工具,一定非常熟悉。另外,我之前做過一個 ,也實現了這個功能。

    另外,還可以修改部分參數值。

    jinfo -flag [+|-] pid

    jinfo -flag = pid

    可以修改部分 JVM 參數。

    前者可以修改布爾值參數,比如開啟簡單 GC 日誌

    jinfo -flag +PrintGC 92041

    後者是設置非布爾值參數的,比如設置 HeapDumpPath

    jinfo -flag HeapDumpPath=/users/fengzheng/jvmlog

    哪些參數是允許動態修改的呢,用下面這個命令可以查看

    #Linux 和 Mac 
    java -XX:+PrintFlagsInitial | grep manageable
    
    #windows
    java -XX:+PrintFlagsInitial | findstr manageable

    jmap

    jmap 查看給定進程、核心文件、遠程調試服務器的共享對象內存映射和堆內存細節的工具,可查看堆使用情況、堆內對象直方圖、加載類、生成堆快照等。

    Usage:
        jmap [option] <pid>
            (to connect to running process)
        jmap [option] <executable <core>
            (to connect to a core file)
        jmap [option] [server_id@]<remote server IP or hostname>
            (to connect to remote debug server)
    
    where <option> is one of:
        <none>               to print same info as Solaris pmap
        -heap                to print java heap summary
        -histo[:live]        to print histogram of java object heap; if the "live"
                             suboption is specified, only count live objects
        -clstats             to print class loader statistics
        -finalizerinfo       to print information on objects awaiting finalization
        -dump:<dump-options> to dump java heap in hprof binary format
                             dump-options:
                               live         dump only live objects; if not specified,
                                            all objects in the heap are dumped.
                               format=b     binary format
                               file=<file>  dump heap to <file>
                             Example: jmap -dump:live,format=b,file=heap.bin <pid>
        -F                   force. Use with -dump:<dump-options> <pid> or -histo
                             to force a heap dump or histogram when <pid> does not
                             respond. The "live" suboption is not supported
                             in this mode.
        -h | -help           to print this help message
        -J<flag>             to pass <flag> directly to the runtime system

    jmap -heap pid

    打印 JVM 堆概要信息,包括堆配置、新生代、老生代信息

    jmap -histo pid

    打印類的直方圖,也就是各個類實例的個數和空間佔用情況。

    如果加 :live,jamp -histo:live pid 則只打印活動類的信息。這個命令會出發 GC 動作,會導致 JVM 停頓,所以在線上環境要慎用。

    jmap -dump

    dump 當前 JVM 堆,一般用法如下:

    #dump 所有對象在堆中的分佈情況
    jmap -dump:format=b,file=/Users/fengzheng/jvmlog/jamp_dump.hprof 95463
    
    #加:live 參數 dump 存活對象在隊中的分佈情況
    jmap -dump:live,format=b,file=/Users/fengzheng/jvmlog/jamp_dump.hprof 95463

    之後再用堆分析工具,比如 visualVM、JProfile、MAT 等進行分析。和我們設置

    -XX:+HeapDumpOnOutOfMemoryError 參數后,在發生 OOM 的時候 dump 的堆信息是一樣的。

    注意,dump 的過程會比較慢,在這個過程中會發生 JVM 停頓,而且在使用 :live 參數后,會觸發 GC 操作。

    jmap -clstats pid

    Java 類加載器(ClassLoader)信息,包括加載器名稱、已加載類個數、佔用空間、父加載器、是否存活、類型信息。

    jmap -finalizerinfo pid

    查看等待被回收的對象。

    jstat

    jstat 主要用來通過垃圾回收相關信息來判斷 JVM 性能問題,也可以查看類加載、編譯的情況,主要的用法是通過持續的固定時間間隔的輸出來觀察。比如每 3 秒打印一次 GC 回收次數,連續打印 10 次,通過動態的變化來觀察 GC 是否過於密集。

    下面是 jstat 的幫助手冊。

    Usage: jstat -help|-options
           jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
    
    Definitions:
      <option>      An option reported by the -options option
      <vmid>        Virtual Machine Identifier. A vmid takes the following form:
                         <lvmid>[@<hostname>[:<port>]]
                    Where <lvmid> is the local vm identifier for the target
                    Java virtual machine, typically a process id; <hostname> is
                    the name of the host running the target Java virtual machine;
                    and <port> is the port number for the rmiregistry on the
                    target host. See the jvmstat documentation for a more complete
                    description of the Virtual Machine Identifier.
      <lines>       Number of samples between header lines.
      <interval>    Sampling interval. The following forms are allowed:
                        <n>["ms"|"s"]
                    Where <n> is an integer and the suffix specifies the units as 
                    milliseconds("ms") or seconds("s"). The default units are "ms".
      <count>       Number of samples to take before terminating.
      -J<flag>      Pass <flag> directly to the runtime system.

    通過 jstat -options 可以看到 jstat 支持查看哪些信息。

    $ jstat -options
    -class  #類加載情況 加載個數和空間使用
    -compiler #即時編譯器信息
    -gc  # GC情況 包括 young gc、full gc 次數、時間等
    -gccapacity #年輕代、老年代的使用情況
    -gccause #GC 統計信息和回收原因
    -gcmetacapacity #显示有關metaspace大小的統計信息
    -gcnew #新生代 GC 統計
    -gcnewcapacity #新生代內存統計
    -gcold #老年代 GC 統計
    -gcoldcapacity #老年代內存使用情況
    -gcutil #GC 匯總信息
    -printcompilation #編譯方法統計

    上述這些大多數可以對應到 visualVM 的這一部分显示

    示例用法,如下是打印 5301 進程下的垃圾回收情況,-h 3 表示每 3 行輸出一次標題信息,3s 5 表示每 3s 輸出一次,一共輸出 5 次

    jstat -gcutil -h 3 5301 3s 5

    最後輸出的內容如下:

    jstat -gcutil -h 3 5301 3s 5
      S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
     99.92   0.00  11.90  35.29  94.96  94.08     34   12.675     3    1.946   14.621
     99.92   0.00  11.90  35.29  94.96  94.08     34   12.675     3    1.946   14.621
     99.92   0.00  11.90  35.29  94.96  94.08     34   12.675     3    1.946   14.621
      S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
     99.92   0.00  11.94  35.29  94.96  94.08     34   12.675     3    1.946   14.621
     99.92   0.00  11.94  35.29  94.96  94.08     34   12.675     3    1.946   14.621

    jcmd

    jcmd 會將命令發送給 JVM。這些命令包括用於控制 Java Flight Recording(飛行記錄)、診斷命令等。 必須運行在 JVM 本地,不能遠程使用,並且必須用 JVM 啟動用戶執行。

    通過 jps 命令找到一個 JVM 進程,然後使用下面的代碼可以看到 jcmd 支持的命令

    #進程 5173 
    jcmd 5173 help 
    
    5173:
    The following commands are available:
    JFR.stop
    JFR.start
    JFR.dump
    JFR.check
    VM.native_memory
    VM.check_commercial_features
    VM.unlock_commercial_features
    ManagementAgent.stop
    ManagementAgent.start_local
    ManagementAgent.start
    GC.rotate_log
    Thread.print
    GC.class_stats
    GC.class_histogram
    GC.heap_dump
    GC.run_finalization
    GC.run
    VM.uptime
    VM.flags
    VM.system_properties
    VM.command_line
    VM.version
    help

    基本包含了問題排查的常用命令,並且和上面介紹的幾個工具有部分重合。

    通過命令 jcmd 5173 help GC.heap_dump 可以查詢到 GC.heap_dump 命令的使用方法,其他命令都可以通過這個方法找到使用說明

    jcmd 5173 help GC.heap_dump
    5173:
    GC.heap_dump
    Generate a HPROF format dump of the Java heap.
    
    Impact: High: Depends on Java heap size and content. Request a full GC unless the '-all' option is specified.
    
    Permission: java.lang.management.ManagementPermission(monitor)
    
    Syntax : GC.heap_dump [options] <filename>
    
    Arguments:
        filename :  Name of the dump file (STRING, no default value)
    
    Options: (options must be specified using the <key> or <key>=<value> syntax)
        -all : [optional] Dump all objects, including unreachable objects (BOOLEAN, false)

    然後通過如下代碼就可以 dump 堆信息下來了,和 jmap -dump 的作用一樣

    jcmd 5173 GC.heap_dump /Users/fengzheng/jvmlog/jcmd_heap_dump.hprof

    拋磚引玉就到此了,之後會對 jinfo、jmap、jstack、jstat、jcmd 做詳細說明,記得關注啊。

    相關閱讀:

    歡迎關注,不定期更新本系列和其他文章
    古時的風箏 ,進入公眾號可以加入交流群

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • Python 命令行之旅:深入 click 之子命令篇

    Python 命令行之旅:深入 click 之子命令篇

    作者:HelloGitHub-Prodesire

    HelloGitHub 的《講解開源項目》系列,項目地址:https://github.com/HelloGitHub-Team/Article

    一、前言

    在上兩篇文章中,我們介紹了 click 中的”參數“和“選項”,本文將繼續深入了解 click,着重講解它的“命令”和”組“。

    本系列文章默認使用 Python 3 作為解釋器進行講解。
    若你仍在使用 Python 2,請注意兩者之間語法和庫的使用差異哦~

    二、命令和組

    Click 中非常重要的特性就是任意嵌套命令行工具的概念,通過 和 (實際上是 )來實現。

    所謂命令組就是若干個命令(或叫子命令)的集合,也成為多命令。

    2.1 回調調用

    對於一個普通的命令來說,回調發生在命令被執行的時候。如果這個程序的實現中只有命令,那麼回調總是會被觸發,就像我們在上一篇文章中舉出的所有示例一樣。不過像 --help 這類選項則會阻止進入回調。

    對於組和多個子命令來說,情況略有不同。回調通常發生在子命令被執行的時候:

    @click.group()
    @click.option('--debug/--no-debug', default=False)
    def cli(debug):
        click.echo('Debug mode is %s' % ('on' if debug else 'off'))
    
    @cli.command()  # @cli, not @click!
    def sync():
        click.echo('Syncing')

    執行效果如下:

    Usage: tool.py [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --debug / --no-debug
      --help                Show this message and exit.
    
    Commands:
      sync
    
    $ tool.py --debug sync
    Debug mode is on
    Syncing

    在上面的示例中,我們將函數 cli 定義為一個組,把函數 sync 定義為這個組內的子命令。當我們調用 tool.py --debug sync 命令時,會依次觸發 clisync 的處理邏輯(也就是命令的回調)。

    2.2 嵌套處理和上下文

    從上面的例子可以看到,命令組 cli 接收的參數和子命令 sync 彼此獨立。但是有時我們希望在子命令中能獲取到命令組的參數,這就可以用 來實現。

    每當命令被調用時,click 會創建新的上下文,並鏈接到父上下文。通常,我們是看不到上下文信息的。但我們可以通過 裝飾器來顯式讓 click 傳遞上下文,此變量會作為第一個參數進行傳遞。

    @click.group()
    @click.option('--debug/--no-debug', default=False)
    @click.pass_context
    def cli(ctx, debug):
        # 確保 ctx.obj 存在並且是個 dict。 (以防 `cli()` 指定 obj 為其他類型
        ctx.ensure_object(dict)
    
        ctx.obj['DEBUG'] = debug
    
    @cli.command()
    @click.pass_context
    def sync(ctx):
        click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off'))
    
    if __name__ == '__main__':
        cli(obj={})

    在上面的示例中:

    • 通過為命令組 cli 和子命令 sync 指定裝飾器 click.pass_context,兩個函數的第一個參數都是 ctx 上下文
    • 在命令組 cli 中,給上下文的 obj 變量(字典)賦值
    • 在子命令 sync 中通過 ctx.obj['DEBUG'] 獲得上一步的參數
    • 通過這種方式完成了從命令組到子命令的參數傳遞

    2.3 不使用命令來調用命令組

    默認情況下,調用子命令的時候才會調用命令組。而有時你可能想直接調用命令組,通過指定 click.groupinvoke_without_command=True 來實現:

    @click.group(invoke_without_command=True)
    @click.pass_context
    def cli(ctx):
        if ctx.invoked_subcommand is None:
            click.echo('I was invoked without subcommand')
        else:
            click.echo('I am about to invoke %s' % ctx.invoked_subcommand)
    
    @cli.command()
    def sync():
        click.echo('The subcommand')

    調用命令有:

    $ tool
    I was invoked without subcommand
    $ tool sync
    I am about to invoke sync
    The subcommand

    在上面的示例中,通過 ctx.invoked_subcommand 來判斷是否由子命令觸發,針對兩種情況打印日誌。

    2.4 自定義命令組/多命令

    除了使用 來定義命令組外,你還可以自定義命令組(也就是多命令),這樣你就可以延遲加載子命令,這會很有用。

    自定義多命令需要實現 list_commandsget_command 方法:

    import click
    import os
    
    plugin_folder = os.path.join(os.path.dirname(__file__), 'commands')
    
    class MyCLI(click.MultiCommand):
    
        def list_commands(self, ctx):
            rv = []  # 命令名稱列表
            for filename in os.listdir(plugin_folder):
                if filename.endswith('.py'):
                    rv.append(filename[:-3])
            rv.sort()
            return rv
    
        def get_command(self, ctx, name):
            ns = {}
            fn = os.path.join(plugin_folder, name + '.py')  # 命令對應的 Python 文件
            with open(fn) as f:
                code = compile(f.read(), fn, 'exec')
                eval(code, ns, ns)
            return ns['cli']
    
    cli = MyCLI(help='This tool\'s subcommands are loaded from a '
                'plugin folder dynamically.')
    
    # 等價方式是通過 click.command 裝飾器,指定 cls=MyCLI
    # @click.command(cls=MyCLI)
    # def cli():
    #     pass
    
    if __name__ == '__main__':
        cli()

    2.5 合併命令組/多命令

    當有多個命令組,每個命令組中有一些命令,你想把所有的命令合併在一個集合中時,click.CommandCollection 就派上了用場:

    
    @click.group()
    def cli1():
        pass
    
    @cli1.command()
    def cmd1():
        """Command on cli1"""
    
    @click.group()
    def cli2():
        pass
    
    @cli2.command()
    def cmd2():
        """Command on cli2"""
    
    cli = click.CommandCollection(sources=[cli1, cli2])
    
    if __name__ == '__main__':
        cli()

    調用命令有:

    $ cli --help
    Usage: cli [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --help  Show this message and exit.
    
    Commands:
      cmd1  Command on cli1
      cmd2  Command on cli2

    從上面的示例可以看出,cmd1cmd2 分別屬於 cli1cli2,通過 click.CommandCollection 可以將這些子命令合併在一起,將其能力提供個同一個命令程序。

    Tips:如果多個命令組中定義了同樣的子命令,那麼取第一個命令組中的子命令。

    2.6 鏈式命令組/多命令

    有時單級子命令可能滿足不了你的需求,你甚至希望能有多級子命令。典型地,setuptools 包中就支持多級/鏈式子命令: setup.py sdist bdist_wheel upload。在 click 3.0 之後,實現鏈式命令組變得非常簡單,只需在 click.group 中指定 chain=True

    @click.group(chain=True)
    def cli():
        pass
    
    
    @cli.command('sdist')
    def sdist():
        click.echo('sdist called')
    
    
    @cli.command('bdist_wheel')
    def bdist_wheel():
        click.echo('bdist_wheel called')

    調用命令則有:

    $ setup.py sdist bdist_wheel
    sdist called
    bdist_wheel called

    2.7 命令組/多命令管道

    鏈式命令組中一個常見的場景就是實現管道,這樣在上一個命令處理好后,可將結果傳給下一個命令處理。

    實現命令組管道的要點是讓每個命令返回一個處理函數,然後編寫一個總的管道調度函數(並由 MultiCommand.resultcallback() 裝飾):

    @click.group(chain=True, invoke_without_command=True)
    @click.option('-i', '--input', type=click.File('r'))
    def cli(input):
        pass
    
    @cli.resultcallback()
    def process_pipeline(processors, input):
        iterator = (x.rstrip('\r\n') for x in input)
        for processor in processors:
            iterator = processor(iterator)
        for item in iterator:
            click.echo(item)
    
    @cli.command('uppercase')
    def make_uppercase():
        def processor(iterator):
            for line in iterator:
                yield line.upper()
        return processor
    
    @cli.command('lowercase')
    def make_lowercase():
        def processor(iterator):
            for line in iterator:
                yield line.lower()
        return processor
    
    @cli.command('strip')
    def make_strip():
        def processor(iterator):
            for line in iterator:
                yield line.strip()
        return processor

    在上面的示例中:

    • cli 定義為了鏈式命令組,並且指定 invoke_without_command=True,也就意味着可以不傳子命令來觸發命令組
    • 定義了三個命令處理函數,分別對應 uppercaselowercasestrip 命令
    • 在管道調度函數 process_pipeline 中,將輸入 input 變成生成器,然後調用處理函數(實際輸入幾個命令,就有幾個處理函數)進行處理

    2.8 覆蓋默認值

    默認情況下,參數的默認值是從通過裝飾器參數 default 定義。我們還可以通過 Context.default_map 上下文字典來覆蓋默認值:

    @click.group()
    def cli():
        pass
    
    @cli.command()
    @click.option('--port', default=8000)
    def runserver(port):
        click.echo('Serving on http://127.0.0.1:%d/' % port)
    
    if __name__ == '__main__':
        cli(default_map={
            'runserver': {
                'port': 5000
            }
        })

    在上面的示例中,通過在 cli 中指定 default_map 變可覆蓋命令(一級鍵)的選項(二級鍵)默認值(二級鍵的值)。

    我們還可以在 click.group 中指定 context_settings 來達到同樣的目的:

    
    CONTEXT_SETTINGS = dict(
        default_map={'runserver': {'port': 5000}}
    )
    
    @click.group(context_settings=CONTEXT_SETTINGS)
    def cli():
        pass
    
    @cli.command()
    @click.option('--port', default=8000)
    def runserver(port):
        click.echo('Serving on http://127.0.0.1:%d/' % port)
    
    if __name__ == '__main__':
        cli()

    調用命令則有:

    $ cli runserver
    Serving on http://127.0.0.1:5000/

    三、總結

    本文首先介紹了命令的回調調用、上下文,再進一步介紹命令組的自定義、合併、鏈接、管道等功能,了解到了 click 的強大。而命令組中更加高階的能力()則可看官方文檔進一步了解。

    我們通過介紹 click 的參數、選項和命令已經能夠完全實現命令行程序的所有功能。而 click 還為我們提供了許多錦上添花的功能,比如實用工具、參數自動補全等,我們將在下節詳細介紹。

    『講解開源項目系列』——讓對開源項目感興趣的人不再畏懼、讓開源項目的發起者不再孤單。跟着我們的文章,你會發現編程的樂趣、使用和發現參与開源項目如此簡單。歡迎留言聯繫我們、加入我們,讓更多人愛上開源、貢獻開源~

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • Facebook改進換臉術:無需“投喂”圖片,從視頻里直接變臉

    Facebook改進換臉術:無需“投喂”圖片,從視頻里直接變臉

      曉查 發自 凹非寺 
      量子位 報道 公眾號 QbitAI

      近兩年來,Deepfakes 讓許多歐美明星吃盡了苦頭,面對自己的頭像被替換到各種視頻中,卻無能為力。

      比如黑寡婦就對自己的臉被替換到小電影中感到很無奈,呼籲大家停止用 AI 作惡。

      而最近,Facebook 人工智能研究院讓換臉技術再次進化。

      過去 Deepfakes 這項技術需要很多準備材料:一是被替換人臉的原視頻,二是來自換臉人面部各個角度的照片。有這兩樣東西才能造出完美無暇的換臉視頻。

      而來自 Facebook 的技術不需要照片,可以從原視頻直接生成換臉視頻,甚至能對實時視頻進行換臉。

      它讓“大表姐”變得不再熟悉。 

      這項技術的換臉實際上是毫無違和感地修改五官特徵,好讓 AI 無法識別出,因此也就不需要照片了。

      而且 Facebook 的研究人員還表示,這項技術修改后的明星臉仍然可以被人識別出來,但是 AI 卻不行。 

      Facebook 研發這項技術可不是為了換臉好玩,最近因使用人臉識別技術飽受爭議,這家公司希望通過這項新技術來保護用戶的隱私。

      人臉識別和換臉技術對普通民眾的隱私也造成了很大的威脅。比如前一陣大熱的換臉應用 ZAO,讓每個人都享受到換臉帶來的樂趣,但同時也會收集用戶圖片。

      研究人員在論文摘要中說:“人臉識別可能會導致隱私丟失,而換臉技術可能會被用於製作誤導性視頻。”Facebook 用後者來去除視頻中的隱私信息。

      Facebook 聲稱,該技術屬於業內首創,足以抵禦複雜的人臉識別系統。

      Facebook 將在下周韓國首爾舉行的國際計算機視覺國際會議(ICCV)上介紹該工作。

      本周,Facebook 還聯合微軟和亞馬遜,提供 Deepfakes 換臉挑戰數據集,希望能夠提高識別換臉視頻算法的魯棒性,以控制假視頻的傳播。

      此舉頗有些以彼之矛攻彼之盾的意味。

      原文鏈接:

      https://venturebeat.com/2019/10/25/facebook-alters-video-to-make-people-invisible-to-facial-recognition/

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!