北汽與Amber Dual合資逾2億人民幣 建馬來西亞電動車工廠

據馬來西亞媒體5月19日報導,北汽集團近日在其北京工廠與Amber Dual有限公司簽訂了合資協議,雙方將投資2-3億元人民幣,在馬來西亞吉打州建立一座新廠,並將耗資約5,000萬元人民幣用於研發,預計於明年7月份開始生產。   此馬來西亞廠將作為北汽在東南亞地區的電動車生產中心。北汽為該工廠設定的電動車年產量目標為2,000到3,000輛,此後將逐漸遞增。

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

【Spring】內嵌Tomcat&去Xml&調試Mvc

菜瓜:今天聽到個名詞“父子容器”,百度了一下,感覺概念有點空洞,這是什麼核武器?

水稻:你說的是SpringMvc和Spring吧,其實只是一個概念而已,用來將兩個容器做隔離,起到解耦的作用,其中子容器可以拿到父容器的bean,父容器拿不到子容器的。但是SpringBoot出來之後這個概念基本就被淡化掉,沒有太大意義,SpringBoot中只有一個容器了。

菜瓜:能不能給個demo?

水稻:可以。由於現在SpringBoot已經大行其道,Mvc你可能接觸的少,甚至沒接觸過。

  • 早些年啟動一個Mvc項目費老鼻子勁了,要配置各種Xml文件(Web.xml,spring.xml,spring-dispather.xml),然後開發完的項目要打成War包發到Tomcat容器中
  • 現在可以直接引入Tomcat包,用main方法直接調起。為了調試方便,我就演示一個Pom引入Tomcat的例子
  • ①啟動類
  • package com.vip.qc.mvc;
    
    import org.apache.catalina.Context;
    import org.apache.catalina.LifecycleException;
    import org.apache.catalina.LifecycleListener;
    import org.apache.catalina.startup.Tomcat;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    
    /**
     * 參考: * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
     * <p>
     * 嵌入tomcat,由Tomcat發起對Spring容器的初始化調用過程
     * <p>
     * - 啟動過程
     * * - Servlet規範,Servlet容器在啟動之後會SPI加載META-INF/services目錄下的實現類並調用其onStartup方法
     * * - Spring遵循規範實現了ServletContainerInitializer接口。該接口在執行時會收集WebApplicationInitializer接口實現類並循環調用其onStartup方法
     * * - 其中AbstractDispatcherServletInitializer
     * * * - 將spring上下文放入ContextLoaderListener監聽器,該監聽會發起對refresh方法的調用
     * * * - 註冊dispatcherServlet,後續會由tomcat調用HttpServletBean的init方法,完成子容器的refresh調用
     * *
     *
     * @author QuCheng on 2020/6/28.
     */
    public class SpringWebStart {
    
        public static void main(String[] args) {
            Tomcat tomcat = new Tomcat();
            try {
                // 此處需要取一個目錄
                Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
                context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());
                tomcat.setPort(8081);
                tomcat.start();
                tomcat.getServer().await();
            } catch (LifecycleException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        }
    
    
        static class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
            private final static String PACKAGE_PATH = "com.vip.qc.mvc";
    
            @Override
            protected String[] getServletMappings() {
                return new String[]{"/"};
            }
    
            @Override
            protected Class<?>[] getRootConfigClasses() {
                // spring 父容器
                return new Class[]{AppConfig.class};
            }
    
            @Override
            protected Class<?>[] getServletConfigClasses() {
                // servlet 子容器
                return new Class[]{ServletConfig.class};
            }
    
            @ComponentScan(value = PACKAGE_PATH,
                    excludeFilters = {
                            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
                            // 避免掃描到加了註解(@Configuration)的子容器
                            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ServletConfig.class)})
            static class AppConfig {
            }
    
            @ComponentScan(value = PACKAGE_PATH)
            static class ServletConfig {
            }
        }
    }
  • ②Controller&Service
  • package com.vip.qc.mvc.controller;
    
    import com.vip.qc.mvc.service.ServiceChild;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.annotation.Resource;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Controller
    public class ControllerT implements ApplicationContextAware {
    
        @Resource
        private ServiceChild child;
    
        @RequestMapping("/hello")
        @ResponseBody
        public String containter() {
            child.getParent();
            System.out.println("parentContainer");
            return "containter";
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("子容器" + applicationContext);
            System.out.println("子容器中獲取父容器bean" + applicationContext.getBean(ServiceChild.class));
        }
    }
    
    
    package com.vip.qc.mvc.service;
    
    import com.vip.qc.mvc.controller.ControllerT;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Service;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Service
    public class ServiceChild implements ApplicationContextAware {
    
        //    @Resource
        private ControllerT controllerT;
    
        public void getParent() {
    
            System.out.println(controllerT);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("父容器" + applicationContext);
            try {
                System.out.println("父容器中獲取子容器bean" + applicationContext.getBean(ControllerT.class));
            } catch (NoSuchBeanDefinitionException e) {
                System.out.println("找不到子容器的bean");
            }
        }
    }

    // 調用
    SpringWebStart的main方法啟動-會有如下打印
    父容器Root WebApplicationContext, started on Sun Jun 28 22:03:52 CST 2020
    找不到子容器的bean
    子容器WebApplicationContext for namespace 'dispatcher-servlet', started on Sun Jun 28 22:03:58 CST 2020, parent: Root WebApplicationContext
    子容器中獲取父容器beancom.vip.qc.mvc.service.ServiceChild@4acfc43a
    
    
  • Demo比較簡單,不過也能反映父子容器的關係

菜瓜:嗯,效果看到了,能不能講一下啟動過程

水稻:稍等,我去下載源碼。上面代碼演示中已經提前說明了,父子容器的加載是Tomcat依據Servlet規範發起調用完成的

  • spring-web源碼包的/META-INF中能找到SPI的實際加載類SpringServletContainerInitializer#onStartup()方法會搜集實現WebApplicationInitializer接口的類,並調用其onStartup方法
  • 上面MyWebApplicationInitializer啟動類是WebApplicationInitializer的子類,未實現onStartup,實際調用的是其抽象父類AbstractDispatcherServletInitializer的方法。跟進去 
  • @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
       //① 創建Spring父容器上下文-對象放入ContextLoadListener,後續調起完成初始化,
       super.onStartup(servletContext);
       //② 創建DispatcherServlet對象,後續會由tomcat調用其init方法,完成子容器的初始化工作
       registerDispatcherServlet(servletContext);
    }
    
    // ①進來
    protected void registerContextLoaderListener(ServletContext servletContext) {
        // 此處會回調我們啟動類的getRootConfigClasses()方法 - 父容器配置
        WebApplicationContext rootAppContext = createRootApplicationContext();
        if (rootAppContext != null) {
            ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
            istener.setContextInitializers(getRootApplicationContextInitializers());
            servletContext.addListener(listener);
        }
        else {
            logger.debug("No ContextLoaderListener registered, as " +
                  "createRootApplicationContext() did not return an application context");
        }
    }
    
    // ②進來
    protected void registerDispatcherServlet(ServletContext servletContext) {
            。。。
        // 此處會回調我們啟動類的getServletConfigClasses()方法 - 子容器配置
        WebApplicationContext servletAppContext = createServletApplicationContext();
            。。。
        // 初始化的dispatcherServlet,會加入Tomcat容器中-後續調用
        // FrameworkServlet#initServletBean()會完成上下文初始化工作
        FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
            。。。
    }

菜瓜:這樣容器就可以用了嗎?

水稻:是的,這樣就可以直接在瀏覽器上面訪問http://localhost:8081/hello,不過這是一個最簡陋的web項目

菜瓜:懂了,最簡陋是什麼意思

水稻:如果我們想加一些常見的Web功能,譬如說攔截器,過濾器啥的。可以通過@EnableWebMvc註解自定義一些功能

  • package com.vip.qc.mvc;
    
    import com.vip.qc.mvc.interceptor.MyInterceptor1;
    import com.vip.qc.mvc.interceptor.MyInterceptor2;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import javax.annotation.Resource;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    @EnableWebMvc
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Resource
        private MyInterceptor1 interceptor1;
        @Resource
        private MyInterceptor2 interceptor2;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(interceptor1).addPathPatterns("/interceptor/**");
            registry.addInterceptor(interceptor2).addPathPatterns("/interceptor/**");
        }
    }
    
    
    
    package com.vip.qc.mvc.interceptor;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    public class MyInterceptor1 implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("嘻嘻 我是攔截器1 pre");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("嘻嘻 我是攔截器1 post");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("嘻嘻 我是攔截器1 after");
        }
    }
    
    
    package com.vip.qc.mvc.interceptor;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    public class MyInterceptor2 implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("嘻嘻 我是攔截器2 pre");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("嘻嘻 我是攔截器2 post");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("嘻嘻 我是攔截器2 after");
        }
    
    } 

菜瓜:我知道,這裏還有個Mvc請求調用流程和這個攔截器有關。而且這個攔截器不是MethodInterceptor(切面)

水稻:沒錯,說到這裏順便複習一下Mvc的請求過程

  • 請求最開始都是通過Tomcat容器轉發過來的,調用鏈:HttpServlet#service() -> FrameworkServlet#processRequest() -> DispatcherServlet#doDispather()
  •  1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
     2     。。。
     3   processedRequest = checkMultipart(request);
     4   multipartRequestParsed = (processedRequest != request);
     5   // 1.返回一個持有methodHandler(按照URL匹配得出的被調用bean對象以及目標方法)調用鏈(攔截器鏈)對象
     6   mappedHandler = getHandler(processedRequest);
     7   。。。
     8   // 2.按照我們現在寫代碼的方式,只會用到HandlerMethod,其他三種基本不會用
     9   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    10   。。。
    11   // 3.前置過濾器 - 順序調用
    12   if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    13       return;
    14   }
    15   // 4.Actually invoke the handler.
    16   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    17   。。。
    18     applyDefaultViewName(processedRequest, mv);
    19   // 5.後置過濾器 - 逆序調用
    20   mappedHandler.applyPostHandle(processedRequest, response, mv);
    21   。。。
    22   // 6.處理試圖 - 內部render
    23   processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    24   }
    25   catch (Exception ex) {
    26      // 異常處理
    27      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    28   }
    29       // 異常處理
    30   catch (Throwable err) {
    31      triggerAfterCompletion(processedRequest, response, mappedHandler,
    32                     new NestedServletException("Handler processing failed", err));
    33   }
    34     。。。

     

 菜瓜:這個之前看過不少,百度一大堆,不過還是源碼親切

 

總結:

  • 目前基本互聯網項目都是SpringBoot起手了,再難遇到SpringMvc的項目,不過熟悉該流程有利於我們更加深刻的理解Ioc容器
  • Mvc攔截器鏈也是日常開發中會用到的功能,順便熟悉一下請求的執行過程

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

急轉彎!特斯拉取消提前復工,股價由漲轉跌

先前消息傳出,電動車大廠特斯拉(Tesla)要求員工於 29 日返回工作崗位,以便讓加州佛利蒙(Fremont)組裝廠恢復生產。但隨著該廠所在的舊金山灣區六郡宣佈延長居家禁令,特斯拉政策急轉彎,已通知員工無需提前復工。

CNBC 報導,根據特斯拉的內部信件,24 日和週末,特斯拉管理層要求數十名員工於 29 日重返工作崗位,讓佛利蒙廠產線重啟運作。不過,由於舊金山灣區六郡(舊金山、聖克拉拉、聖馬刁、馬林、康特拉哥斯,以及佛利蒙廠所在的阿拉米達郡)27 日宣佈,將「就地避難」(shelter-in-place)令的期限由 5 月 4 日延長至 5 月底,特斯拉隨後也取消本週的復工計畫。

特斯拉人力資源部週一發送內部訊息向員工表示:「按照執行領導團隊的指示,我們將不會在 4 月 29 日星期三恢復工作。請忽略關於本週復工的所有訊息和指示。」

報導指出,特斯拉佛利蒙廠生產 Model S 和 Model X 車型,以及較新的 Model 3 和 Model Y 車型,銷往北美和歐洲市場。位於中國上海的組裝廠,先前也因疫情短暫關閉約兩週,但在當地政府的協助下迅速恢復運作,目前每週營運 6 天。

在佛利蒙廠有望提前復工的利多消息帶動下,27 日特斯拉股價大漲 10.15% 收 798.75 美元,但取消復工的消息公佈後,盤後股價挫跌 2.10% 至 782.00 美元。今年以來,特斯拉股價累計飆漲 90.98%。

《華爾街日報》先前報導,根據特斯拉 4 月 2 日資料,2020 年第一季交車數達 88,400 輛,較去年同期成長 40%,雖然疫情導致整體車市市況不振,但特斯拉仍維持原先銷售目標。

今年初,特斯拉執行長馬斯克(Elon Musk)預估,特斯拉今年電動車銷量將維持強勁成長,比去年至少成長 36%,全球總交車數可望「輕鬆突破」(comfortably exceed)50 萬輛。

特斯拉預計 29 日發表 2020 年第一季財報。

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

延伸閱讀:

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

上周熱點回顧(6.22-6.28)

熱點隨筆:

· 程序員敲代碼時耳機里聽的到底是什麼? (風的姿態)
· CPU明明8個核,網卡為啥拚命折騰一號核? (軒轅之風)
· 手把手教你基於SqlSugar4編寫一個可視化代碼生成器(生成實體,以SqlServer為例,文末附源碼) (熊澤-學習中的苦與樂)
· 在運行時生成C# .NET類 (芝麻麻雀)
· 因為我的一個低級錯誤,生產數據庫崩潰了將近半個小時 (鄙人薛某)
· C# 人臉識別庫 (View12138)
· 基於領域驅動設計(DDD)超輕量級快速開發架構 (阿新)
· .Net Core 中GC的工作原理 (她微笑的臉)
· 關於技術文章“標題黨”一事我想說兩句 (精緻碼農)
· 【故障公告】阿里雲 RDS 實例 CPU 100% 故障引發全站無法正常訪問 (博客園團隊)
· 思考:如何保證服務穩定性? (老_張)
· 只看到了別人28歲從字節跳動退休,背後的期權知識你知道嗎? (四猿外)

熱點新聞:

· 瘋王,任正非!
· VSCode彩虹屁插件:釘宮理惠,英雄聯盟版現已生成,你Pick哪一個?
· 全國首創!廣東人坐火車就像坐地鐵一樣方便了:無需提前買票
· 95后快遞小哥獲評“高層次人才”:杭州買房享受百萬元補貼
· 二線手機廠商墜落簡史:鎚子、魅族、金立已成過客
· 外賣員確診背後:年近50 每天接老婆下班 工作14小時
· 歷時26年!中國終於有了自己的全球導航系統
· 看!北斗三號最後一顆組網衛星在太空張開“翅膀” 畫面燃了
· 攻克地獄級難度!川藏鐵路拉林段120座橋樑主體工程全部完工
· 作家王小山控訴攜程欠錢不還:願意下跪懇請梁建章退租
· 知乎熱議:替代Matlab的國產軟件出現 半年內實現Matlab功能的70%
· 這個比QQ空間還古老的網站 是多少女孩的精神家園?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

從0到1打造數據可信的數據產品:解析數據治理在過程可信變革中的運作流程

摘要:本文針對“數據牽引改進,工具固化規範”這一思路在業務團隊落地過程中的動作流程進行詳細闡述,並明確了支撐整個流程的關鍵角色定義和組織運作形式。

目的

為實現雲服務開發的過程可信,需要基於數據對各個服務產品部的可信變革動作進行數據採集、進展可視、目標牽引、能力評估,最終用數據反映目標達成。與傳統的“基於數據晾曬驅動業務團隊改進,6+1指標度量”的運作方式有本質的區別,我們是基於統一的作業工具上產生的客觀數據呈現,識別研發過程中基本的流程斷裂點和質量缺失動作,和業務團隊達成一致的目標后,把大部分改進動作固話到作業工具中自動化承載,我們稱這個思路為“數據牽引改進,工具固化規範”,也就是我們不僅告訴業務團隊哪裡有問題,同時也要基於我們的作業工具,輔助業務團隊一起改進完善。

本文針對“數據牽引改進,工具固化規範”這一思路在業務團隊落地過程中的動作流程進行詳細闡述,並明確了支撐整個流程的關鍵角色定義和組織運作形式。

數據牽引改進,是指關注軟件交付過程中各種度量數據的收集、統計、分析和反饋,通過可視化的數據客觀反映整個研發過程的狀態,以全局視角分析系統約束點,並和業務團隊達成共識,提煉出客觀有效的改進目標;工具固化規範,針對識別出來的Gap點和重點問題進行分析,制定出可以在作業工具承載的模板規範,以及需要工程師行為做出改變的能力要求,並在作業工具上對這些規範要求的落地效果進行檢查,用數據度量改進效果。最後,對改進項目進行總結分享,打造學習型組織,不斷驅動持續改進和價值交付,牽引研發團隊模式和文化的轉變。

2020年的研發過程可信圍繞CleanCode、構建、開源、E2E追溯四個領域開展,這也是公司要求的可信變革中最基本、最重要、投入產出比最大的四個點。

整體流程說明

整個運作流程,圍繞數據,按照“定義軟件工程規範->定義數據分析模型->工具實現數據度量和分析->數據運營發現實際軟件工程活動和規範的偏差->工具輔助團隊改進->工具固化軟件工程規範”這個流程進行實施,並對最終效果進行階段性總結。隨着業務團隊能力的提升以及軟件工程規範性、開發模式的改變,對最初定義的軟件工程規範,會階段性的進行完善,循環往複、持續優化,最終讓業務團隊在遵守公司要求的研發過程可信規範的前提下,實現業務成功。

1) 定義軟件工程規範:圍繞公司可信變革的目標,BU對各個服務產品部的研發模式規範和能力要求,COE制定適合BU現狀的軟件工程規範;

2) 定義數據模型:COE針對制定的軟件工程規範,提煉出核心的、有針對性、可用工具度量的數據模型,並且和各個服務產品部達成一致;

3) 工具實現數據度量和分析:根據這幾個數據模型,數據分析工具自動從數據源進行採集、匯總、計算,並把結果呈現在數據看板上;業務團隊可以打開匯總數據,根據明細數據進行動作規範自檢和改進;

4) 數據運營發現實際軟件工程活動和規範的偏差:數據治理小組在實際運營過程中,分析度量指標的數據,識別業務團隊實際的軟件工程活動和要求規範不一致的Gap點和關鍵問題;

5) 工具輔助業務團隊改進:COE針對分析出來的Gap點和關鍵問題,制定相應的改進措施,作業工具承載流程規範模板化整改,並針對業務團隊的不規範行為,制定適合各個服務產品部的公約要求,促使業務團隊人員能力提升;

6) 工具固化軟件工程規範:針對業務團隊的公約要求,在作業工具上進行check,最終作業工具承載了整個軟件工程規範要求,以及融入到作業流程中的規範要求事前檢查。

三層數據分析模型

我們採用了三層數據分析模型,由作業工具自動採集用戶研發過程行為明細數據,數據分析工具進行准實時匯總計算呈現總體目標,三層數據系統性的輔助業務團隊系統性的識別研發過程中的不規範點和能力短板,讓業務團隊“知其然,知其所以然”。這三層數據模型是層層深入,迭代完善,下層支撐上層的關係。

第一層:目標、進展、結果數據分析;和公司可信變革目標牽引對齊,結合BU實際情況,形成BU的整體可信要求,並在數據分析看板上呈現各個服務產品部要達成的過程可信目標、每日的改進進展和最終完成情況;例如,對各個服務產品部要求達成CleanCode的目標。

第二層:詞法/語法分析數據;COE針對第一層的目標牽引,分解出來的具體實施環節的度量指標,只有這些分解的指標都完成,第一層的目標才達成。這一層數據的目的主要是圍繞幫助業務團隊分析自己的能力短板在哪裡,進行有針對性的改進指;通過打開匯總數據的層層下鑽,用明細數據來分析業務團隊在DevSecOps軟件工程規範流程中關鍵動作執行的缺失點,並針對性的制定改進規範要求,牽引作業工具或者業務團隊補齊該部分缺失動作;例如,CleanCode的過程可信目標達成,可以分解成:靜態檢查配置合規率、Committer合入保障率、代碼倉Clean三個目標,只有這三個目標達成,就可以認為CleanCode總體目標達成。

第三層:語義分析數據:COE打開第二層數據,不僅要看這些關鍵動過做沒做,還要看做的效果怎麼樣,最終效果體現在業務團隊的DevSecOps軟件工程規範提升;這一層的數據分析聚焦在防止為了指標而做第二層的數據,而是看業務團隊是否在真正參考BU制定的規範牽引的目標,提升業務交付過程中的效能、可信、質量能力,以及最終產生實際的業務效果。通過打開各個團隊的明細數據分析審視業務團隊執行的關鍵動作是否符合規範,是否在合適的階段點執行,執行效果是否有效;並階段性的總結和提煉經驗,形成知識資產固化到作業工具。例如,針對第二層的靜態檢查配置合規率,可以分解為:靜態檢查配置有效性和靜態檢查執行有效性。靜態檢查配置有效性,包括:檢查靜態檢查工具配置的數量、是否符合BU的配置規範,以及是否在代碼合入主幹master時進行了配置;靜態檢查執行有效性,主要看是否每一次MR提交時都執行靜態檢查、是否發現問題在研發活動的最早階段,攔截的問題的效果怎麼樣。只有第三層的動作度量都達成后,才可以說第二層的目標是達成的。

數據治理過程流程圖

為了實現“數據牽引改進,工具固化規範”這個目標,準確、一致、准實時的數據是核心關鍵,但因為數據採集不完整、業務團隊不規範、數據呈現維度不一致等原因,數據的準確性有一個不斷提升的過程,因此需要對各個層級展示的數據進行治理。整個數據治理過程中,由“業務團隊/作業工具/治理小組/數據分析工具(阿基米德)/COE”五個角色緊密配合,而且以年/半年為目標,不斷總結經驗,循環往複、持續優化的過程。

a) COE:和公司可信變革目標牽引對齊,結合BU能力現狀,形成BU的整體可信要求;

b) COE:針對BU的業務現狀,定義出適合BU現狀的軟件工程規範要求;業務團隊:和BU發布的各個領域的軟件工程規範牽引目標達成一致;

c) COE:針對規範分解出核心的度量指標,並制定度量數據模型;

d) 研發用戶:在使用作業工具進行研發活動;作業工具:承載了BU各個服務產品部在使用過程中沉澱的行為數據;

e) 數據分析(阿基米德):准實時接入作業工具的數據,展示各個服務產品部當前的研發能力現狀;

f) COE:和各個服務產品部達成一致,制定各個服務產品部的年度牽引目標;

g) 數據分析(阿基米德):用數據呈現各個服務產品部的牽引目標和能力現狀,統一數據口徑;呈現月/周/天的明細數據,以及支撐Gap分析和重點問題的數據視圖;

h) COE:根據牽引目標和能力現狀,分析Gap原因和關鍵問題;治理小組:在數據運營過程中,根據數據分析團隊當前的能力現狀是否和數據呈現一致;

i) 研發用戶:可以實時登錄數據工具(阿基米德)進行查看各個層級的明細數據;

j) 治理小組:根據准實時進展數據,分析當前團隊研發過程中的實際問題,並匯總給COE;

k) COE:結合細粒度的分析數據、以及治理小組匯總出來的各個服務產品部的實際問題,制定規範和改進措施,包括作業工具的規範和研發用戶的動作行為公約;

l) 作業工具:承載作業工具上落地的規範要求;治理小組:作為接口人,承接研發工程師的行為規範公約,結合各個服務產品部實際情況來負責落地;

m) 研發用戶:按照規範要求和針對數據的自檢進行研發過程行為規範化;

n) 研發工具:對研發用戶的行為規範是否滿足要求進行自動化檢查;最終目標是讓整個軟件工程規範都固化在工具中進行承載;

o) 數據分析(阿基米德):呈現按照規範改進后的明細數據和匯總目標;研發用戶:自助查看整改后的明細數據;

p) COE:根據數據改進的效果,以及過程中暴露的問題進行總結后形成經驗資產,並持續改進;

數據流圖

過程可信的數據在各個工具系統中採集、流轉、匯聚、分析、呈現,整個數據流圖如下:

其中,識別出6個重要的全量數據源:

a) 代碼庫數據:該數據由伏羲的服務信息樹上配置的代碼庫數據,加上阿基米德上人工配置的代碼庫,構成各個雲服務發布到生產倉的代碼全集;

b) Workitem信息流數據:當前識別vision上的需求、問題、task,加上Gitlab/Codeclub上的issue,構成可識別的Workitem數據全集;

c) SRE現網包數據:包括普羅部署、CCE、CPS、CDK各種類型部署的包數據,構成全量現網包數據;

d) 開源二進制包數據:開源中心倉數據(java、python、go、nodejs四種)語言,加上公司c/c++的數據構成全量開源二進制包數據;

e) 研發過程配置數據:阿基米德上配置的committer數據是全量的committer數據;阿基米德上識別出來的主分支是全量的主分支(邏輯“master”)數據;

f) 伏羲研發過程數據:伏羲三個庫,MongoDB的靜態檢查、門禁數據;MySQL中的測試、發布數據;MySQL中包和多個流水線的對應關係數據;一起構成了以“包”為維度的全量伏羲研發過程數據;

運作組織

數據治理運營團隊

按照過程可信在BU的落地策略,在CleanCode、構建、開源、E2E追溯四個領域設置數據治理運營團隊,由 “數據分析工具(阿基米德)—COE—各個服務產品部接口人組成的治理小組”三個角色組成,以“指標度量為牽引,數據的客觀呈現為落地方式,業務的價值反饋為最終目的”的原則來落地數據治理工作。

COE的職責:

1) 和公司可信變革目標牽引對齊,結合BU能力現狀,形成BU的整體可信要求;定義出適合BU現狀的軟件工程規範要求;針對規範分解出核心的度量指標,並制定度量數據模型;

2) 利用作業工具已經產生的數據,和治理小組一起分析識別數據質量的問題,按照三層數據分析模型,層層打開,識別業務團隊能力Gap點。

3) 分析典型問題,識別作業流的斷裂點進行補齊,和業務團隊的不規範動作,制定規範和公約要求,逐步改善數據質量。

4) 事後歸納總結,識別出流程缺失,組織缺失,責任缺失等機制行問題,並固化到作業工具中。

治理小組:

1) 結合各個服務產品部的實際情況,承接COE的數據治理規範在各個服務產品部的落地;

2) 識別數據治理動作在各個服務產品部落地過程中的實際問題,和COE一起分析,提出系統性的解決思路,最終固化到作業工具中。

3) 跟蹤過程可信在業務團隊落地的過程中的進展,為業務團隊最終達成可信變革目標負責,為改進過程產生實際的業務價值負責;

數據分析工具(阿基米德):

1) 確保接入的數據準確、實時、一致,用數據實時反映BU各個服務產品部的能力現狀,為COE和治理小組的數據運營提供數據支撐;

2) 系統性的落地COE的方案設計,實現整個BU統一標準的數據看板,能夠清晰的通過數據識別出來業務團隊的能力Gap,牽引業務團隊達成整體改進目標;

3) 按照三層數據模型進行數據展示,層層下鑽,讓業務團隊“知其然,知其所以然”,牽引業務團隊中的每一個人都能自己進行改進;

4) 通過數據分析,識別DevSecOps軟件工程規範在BU的業務團隊落地過程中的重點問題,以及該問題背後的流程、制度缺失,促使最終規範固化在作業工具中。

例會設置

“數據驅動DevSecOps能力提升例會”為研發領域數據治理相關問題的求助和裁決例會。

會議分為三個階段:

1) 第一階段,例行議題,形式類似於“體檢報告”,用數據反映業務團隊的現狀和問題;

2) 第二階段,申報議題,形式類似於“專家會診”,討論某一個具體數據治理過程中的問題和Top困難求助;

3) 第三階段,靈活安排議題,形式類似於“問題總結”,針對某一類的具體問題,進行集中討論和歸納總結定義,形成BU的規範流程和章程總結。

主數據承載系統

主數據是指具有高業務價值的、可以在企業內跨越多個業務部門被重複使用的數據,是單一、準確、權威的數據來源。和業務型數據、分析型數據相比,主數據主要有以下幾個特徵:

1) 特徵一致性:也就是能否保證主數據的關鍵特徵在不同應用、不同系統中的高度一致,直接關係了數據治理成敗;

2) 識別唯一性:在一個系統、一個平台,甚至一個企業範圍內,同一主數據實體要求具有唯一的數據標識,即數據編碼;

3) 長期有效性:貫穿該業務對象的整個生命周期甚至更長,當該主數據失去其效果時,系統採取的措施通常為軟刪除,而不是物理刪除;

4) 業務穩定性:在業務過程中其識別信息和關鍵特徵會被業務過程中產生的數據繼承、引用和複製。除非該主數據本身的特徵發生變化,否則該主數據不會隨着業務的過程中被其他系統修改。

主數據源識別原則:

a) 如果有多個數據源構成同類型數據的主數據,兩種處理策略:

1)選取一個源系統逐步收編其他源系統的數據,變成唯一主數據源

2)如果1)不能實現,由阿基米德系統進行封裝后屏蔽多個數據源系統,該類型數據的唯一數據源變成阿基米德,待後續1)實現后,阿基米德該類型主數據源失效。

3)當數據在多個作業系統中進行流轉時,判斷是否作為主數據源的標準是:數據在該系統有實際的業務動作產生,而不是只承載數據的流轉。

b) 如果確定為唯一數據源,其他消費該類型數據的系統不能和數據源產生衝突。

所有數據僅能在數據源產生,其它系統只能讀取不能修改。下游發現的數據源質量問題,應當在數據源頭進行修正。

c) 主數據使用方不得改變原始數據,但可以進行擴展。

數據消費方不得對獲取的數據進行增、刪、改,但可以在數據的基礎上進行屬性擴展。

d) 在滿足信息安全的前提下充分共享,不得拒絕合理的數據共享需求。

數據如果不流轉,不僅不會產生業務價值,還增加存儲成本;只有不斷流轉,對業務團隊產生實際價值時,還能得到使用效果的反饋,促進數據價值的進一步提升。

原則為:核心資產安全優先,非關鍵資產效率優先。

一類主數據源

二類主數據源

 

點擊關注,第一時間了解華為雲新鮮技術~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

陷垃圾危機 菲律賓計劃禁用一次性塑膠

摘錄自2019年12月5日中央通訊社綜合報導

菲律賓環境部長希瑪圖今天(5日)說,由於人們製造數量甚多的廢棄物,清理速度遠遠趕不及,菲律賓正處於垃圾危機中。環境部預計將在2週內規劃完成限用一次性塑膠的全國禁令。

ABS-CBN新聞網和「菲律賓每日詢問報」(Philippine Daily Inquirer)報導,希瑪圖(Roy Cimatu)說,在馬尼拉都會區,今年第一季製造的廢棄物達3萬4574.77立方公尺,第二季則為3萬2221.17立方公尺,已超過全年基線預估值5萬8112.31立方公尺。

他引述數據表示,菲律賓是全球第3大海洋塑膠污染來源國。為此,當局須加強固體廢棄物管理政策。菲律賓總統杜特蒂(Rodrigo Duterte)日前提出為因應氣候變遷問題,菲律賓應禁用塑膠。

希瑪圖說,除了一次性塑膠禁令,環境暨天然資源部(DENR)正擬定的命令也將涵括塑膠回收問題。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

用python實現對元素的長截圖

一.目標

瀏覽網頁的時候,看見哪個元素,就能截取哪個元素當圖片,不管那個元素有多長

 

二.所用工具和第三方庫

python ,PIL,selenium

pycharm

三.代碼部分

長截圖整體思路:

1.獲取元素

2.移動,截圖,移動,截圖,直到抵達元素的底部

3.把截圖按照元素所在位置切割,在所有圖片中只保留該元素

4.拼接

 

如果driver在環境變量中,那麼不用指定路徑

b=webdriver.Chrome(executable_path=r"C:\Users\Desktop\chromedriver.exe")#指定一下driver
b.get("")
b.maximize_window()#最大化窗口

打開網站

 

 

 我們可以看見一個ID為maincontent的元素,寬度為850PX,長度為3828PX,這個長度必須使用才能長截圖才能完整截下來

 

el=b.find_element_by_id("maincontent")#找到元素

我們還需要一個重要的參數,就是你電腦一次能截取多高的像素

先用下圖代碼獲取一個圖片

#fp為存放圖片的地址
b.get_screenshot_as_file(fp)

 

也就是說用我電腦上截圖的默認高度為614像素

 

 所以我設置一個變量:

sc_hight=614

然後設置一下其他變量

    count = int(el.size["height"] / sc_hight)  # 元素的高度除以你每次截多少就是次數
    start_higth = el.location["y"]  # 元素的初始高度
    max_px = start_higth + (count - 1) * sc_hight  # for循環中最大的px
    last_px = el.size["height"] + start_higth - sc_hight  # 元素最底部的位置
    surplus_px = last_px - max_px  # 剩餘的邊的高度
    img_path = []  # 用來存放圖片地址

註釋:

1.count為元素的高度/每次截取的高度,比如這次實例中元素高度為3828PX,我每次截614px,需要6.2次,int之後變成6,也就是截6次,還剩一點,那一點後面再說

2.start_higth為初始高度,這個沒有什麼可說的

3.max_px為循環結束后,到達的高度

4.last_px為元素最底部的高度

5.surplus_px就是移動6次后,還沒有截取的高度

屏幕每次移動,移動sc_hight個像素,初始位置為(0,元素的Y值)

    for i in range(0, count):
        js = "scrollTo(0,%s)" % (start_higth + i * sc_hight)  # 用於移動滑輪,每次移動614px,初始值是元素的初始高度
        b.execute_script(js)  # 執行js
        time.sleep(0.5)
        fp = r"C:\Users\wdj\Desktop\%s.png" % i  # 圖片地址,運行的話,改一下
        b.get_screenshot_as_file(fp)  # 屏幕截圖,這裡是截取是完整的網頁圖片,你可以打斷點看一下圖片
        img = Image.open(fp=fp)
        img2 = img.crop((el.location["x"], 0, el.size["width"] + el.location["x"], sc_hight))  # 剪切圖片
        img2.save(fp)  # 保存圖片,覆蓋完整的網頁圖片
        img_path.append(fp)  # 添加圖片路徑
        time.sleep(0.5)
        print(js)
    else:
        js = "scrollTo(0,%s)" % last_px  # 滾動到最後一個位置
        b.execute_script(js)
        fp = r"C:\Users\wdj\Desktop\last.png"
        b.get_screenshot_as_file(fp)
        img = Image.open(fp=fp)
        print((el.location["x"], sc_hight - surplus_px, el.size["width"] + el.location["x"], sc_hight))
        img2 = img.crop((el.location["x"], sc_hight - surplus_px, el.size["width"] + el.location["x"], sc_hight))
        img2.save(fp)
        img_path.append(fp)
        print(js)

上面是把該元素的在頁面都截完,並且剪切,把圖片保存的路徑放入img_path

最後一步:把所有截圖都貼到新創建的圖片中

    new_img = Image.new("RGB", (el.size["width"], el.size["height"]))  # 創建一個新圖片,大小為元素的大小
    k = 0
    for i in img_path:
        tem_img = Image.open(i)
        new_img.paste(tem_img, (0, sc_hight * k))  # 把圖片貼上去,間隔一個截圖的距離
        k += 1
    else:
        new_img.save(r"C:\Users\wdj\Desktop\test.png")  # 保存

 

運行效果圖:


說明完整的截取下來了

 

 

 

補充優化:

如果是個小元素怎麼辦,不用長截圖就能截取的那種

因為很簡單我就直接貼代碼了

    start_higth = el.location["y"]
    js = "scrollTo(0,%s)" % (start_higth)
    b.execute_script(js)  # 執行js
    time.sleep(0.5)
    fp = r"C:\Users\wdj\Desktop\test.png" # 圖片地址,運行的話,改一下
    b.get_screenshot_as_file(fp)
    img = Image.open(fp=fp)
    img2 = img.crop((el.location["x"], 0, el.size["width"] + el.location["x"], el.size["height"]))  # 剪切圖片
    img2.save(fp)

效果如下:

 

 

完整代碼:

from selenium import webdriver
from PIL import Image
import time
def short_sc(el,b):
    start_higth = el.location["y"]
    js = "scrollTo(0,%s)" % (start_higth)
    b.execute_script(js)  # 執行js
    time.sleep(0.5)
    fp = r"C:\Users\wdj\Desktop\test.png" # 圖片地址,運行的話,改一下
    b.get_screenshot_as_file(fp)
    img = Image.open(fp=fp)
    img2 = img.crop((el.location["x"], 0, el.size["width"] + el.location["x"], el.size["height"]))  # 剪切圖片
    img2.save(fp)

def long_sc(el,b):
    count = int(el.size["height"] / sc_hight)  # 元素的高度除以你每次截多少就是次數
    start_higth = el.location["y"]  # 元素的初始高度
    max_px = start_higth + (count - 1) * sc_hight  # for循環中最大的px
    last_px = el.size["height"] + start_higth - sc_hight  # 元素最底部的位置
    surplus_px = last_px - max_px  # 剩餘的邊的高度
    img_path = []  # 用來存放圖片地址
    for i in range(0, count):
        js = "scrollTo(0,%s)" % (start_higth + i * sc_hight)  # 用於移動滑輪,每次移動614px,初始值是元素的初始高度
        b.execute_script(js)  # 執行js
        time.sleep(0.5)
        fp = r"C:\Users\wdj\Desktop\%s.png" % i  # 圖片地址,運行的話,改一下
        b.get_screenshot_as_file(fp)  # 屏幕截圖,這裡是截取是完整的網頁圖片,你可以打斷點看一下圖片
        img = Image.open(fp=fp)
        img2 = img.crop((el.location["x"], 0, el.size["width"] + el.location["x"], sc_hight))  # 剪切圖片
        img2.save(fp)  # 保存圖片,覆蓋完整的網頁圖片
        img_path.append(fp)  # 添加圖片路徑
        time.sleep(0.5)
        print(js)
    else:
        js = "scrollTo(0,%s)" % last_px  # 滾動到最後一個位置
        b.execute_script(js)
        fp = r"C:\Users\wdj\Desktop\last.png"
        b.get_screenshot_as_file(fp)
        img = Image.open(fp=fp)
        print((el.location["x"], sc_hight - surplus_px, el.size["width"] + el.location["x"], sc_hight))
        img2 = img.crop((el.location["x"], sc_hight - surplus_px, el.size["width"] + el.location["x"], sc_hight))
        img2.save(fp)
        img_path.append(fp)
        print(js)

    new_img = Image.new("RGB", (el.size["width"], el.size["height"]))  # 創建一個新圖片,大小為元素的大小
    k = 0
    for i in img_path:
        tem_img = Image.open(i)
        new_img.paste(tem_img, (0, sc_hight * k))  # 把圖片貼上去,間隔一個截圖的距離
        k += 1
    else:
        new_img.save(r"C:\Users\wdj\Desktop\test.png")  # 保存

b=webdriver.Chrome(executable_path=r"C:\Users\wdj\Desktop\chromedriver.exe")#指定一下driver
b.get("https://www.w3school.com.cn/html/html_links.asp")
b.maximize_window()#最大化窗口
# b.get_screenshot_as_file(fp)
sc_hight=614#你屏幕截圖默認的大小,可以去截一張,去畫圖裡面看看是多少像素,我這裡是614像素

# b.switch_to.frame(b.find_element_by_xpath('//*[@id="intro"]/iframe'))
el=b.find_element_by_id("maincontent")#找到元素
if el.size["height"]>sc_hight:
    long_sc(el,b)
else:
    short_sc(el,b)

完整代碼

 

PS:

有些特殊情況,比如截取的元素在iframe中,直接用driver.switch_to.frame(iframe元素)即可

或者不是iframe,但是元素有overflow屬性,直接用JS把他的overflow去掉就行

 

 

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

Hibernate一對多、多對一的關係表達

一、關係表達:

1、一對多、多對一表的關係:

學生表:

 

 

  班級表:

 

 

 在學生表中,學生的學號是主鍵。在班級表中,班級號是主鍵,因此,學生表的外鍵是classno。因此,班級對應學生是一對多,學生對應班級是多對一。因為,一個班級可以有多個學生,但是一個學生只能在一個班級。

2、對象的一對多、多對一關係:

(1)在Class類中,定義Set集合,表達一對多的關係:

 

 

 

package pers.zhb.domain;
import java.util.HashSet;
import java.util.Set;
public class Class {
    private String classno;
    private String department;
    private String monitor;
    private String classname;
    private Set<Student> classes=new HashSet<Student>();//使用set集合表達一對多關係
    public Class(){
    }
    public Set<Student> getClasses() {
        return classes;
    }
    public void setClasses(Set<Student> classes) {
        this.classes = classes;
    }
    public String getMonitor() {
        return monitor;
    }

    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }



    public String getClassname() {
        return classname;
    }

    public void setClassname(String classname) {
        this.classname = classname;
    }
    public String getClassno() {
        return classno;
    }

    public void setClassno(String classno) {
        this.classno = classno;
    }
    @Override
    public String toString() {
        return "Class{" +
                "classno='" + classno + '\'' +
                ", department='" + department + '\'' +
                ", monitor='" + monitor + '\'' +
                ", classname='" + classname + '\'' +
                ", classes=" + classes +
                '}';
    }
}
package pers.zhb.domain;
public class Student {
    private Integer studentno;
    private String sname;
    private String sex;
    private String birthday;
    private String classno;
    private Float point;
    private String phone;
    private String email;
    private Clas aClas;
    public Student(){//無參的構造方法
    }
    public Clas getaClas() {
        return aClas;
    }

    public void setaClas(Clas aClas) {
        this.aClas = aClas;
    }
    @Override
    public String toString() {
        return "Student{" +
                "studentno='" + studentno + '\'' +
                ", sname='" + sname + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday='" + birthday + '\'' +
                ", classno='" + classno + '\'' +
                ", point=" + point +
                ", phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                '}';
    }

    public int getStudentno() {
        return studentno;
    }

    public void setStudentno(int studentno) {
        this.studentno = studentno;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getClassno() {
        return classno;
    }

    public void setClassno(String classno) {
        this.classno = classno;
    }

    public float getPoint() {
        return point;
    }

    public void setPoint(float point) {
        this.point = point;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

  

  

(2)定義學生和班級的關係:

 

 

 

package pers.zhb.domain;
import java.util.HashSet;
import java.util.Set;
public class Clas {
    private String classno;
    private String department;
    private String monitor;
    private String classname;
    private Set<Student> students=new HashSet<Student>();//使用set集合表達一對多關係
    public Clas(){
    }
    public Set<Student> getStudents() {
        return students;
    }
    public void setClasses(Set<Student> students) {
        this.students = students;
    }
    public String getMonitor() {
        return monitor;
    }

    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }



    public String getClassname() {
        return classname;
    }

    public void setClassname(String classname) {
        this.classname = classname;
    }
    public String getClassno() {
        return classno;
    }

    public void setClassno(String classno) {
        this.classno = classno;
    }
    @Override
    public String toString() {
        return "Class{" +
                "classno='" + classno + '\'' +
                ", department='" + department + '\'' +
                ", monitor='" + monitor + '\'' +
                ", classname='" + classname + '\'' +
                ",students=" + students +
                '}';
    }
}

  

3、配置映射文件:

  Class.hbm.xml:

(1)實現一對多的關係映射,即:一個班級對應多個學生:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="pers.zhb.domain">
    <class name="Clas" table="class">
        <id name="classno" column="classno">
            <generator class="native"></generator>
        </id><!--主鍵-->
        <property name="department" column="department"></property>
        <property name="monitor" column="monitor"></property>
        <property name="classname" column="classname"></property>
        <set name="students" table="student"><!--一對多關係配置-->
        <key column="classno" update="false"></key><!--指定了集合表的外鍵-->
            <one-to-many class="Student"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

 

<set name="students">

指定映射的存儲學生的集合的名字。

<key column="classesno"></key>

映射的class表的外鍵。

<one-to-many class="Student"></one-to-many>

指定學生的類型。

(2)實現多對一的關係映射,即:多個學生對應一個班級。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="pers.zhb.domain">
    <class name="Student" table="student">
        <id name="studentno" column="studentno" >
            <generator class="native"></generator>
        </id>
        <property name="birthday" column="birthday"></property>
        <property name="classno" column="classno" insert="false" update="false"></property>
        <property name="email" column="email"></property>
        <property name="phone" column="phone"></property>
        <property name="sex" column="sex"></property>
        <property name="sname" column="sname"></property>
        <property name="point" column="point"></property>
        <many-to-one name="aClas" column="classno" class="Clas"></many-to-one>
    </class>
</hibernate-mapping>

 

name屬性:映射的班級。

column屬性:映射的班級對象對應的外鍵。

class屬性:指定班級的類型。

4、主配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!--配置數據庫信息-必須的-->
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/stu_mangement</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <!--配置hibernate信息-可選的-->
        <property name="hibernate.show_sql">true</property><!--輸出底層sql語句-->
        <property name="hibernate.format_sql">true</property><!--格式化輸出sql語句-->
        <property name="hibernate.hbm2ddl.auto">update</property><!--hibernate幫助創建表,如果已經有表更新表,如果沒有則創建新表-->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <property name="hibernate.connection.isolation">4</property>
        <!--指定session與當前線程綁定-->
        <property name="hibernate.current_session_context_class">thread</property>
        <!--配置數據庫的方言,讓hibernate識別框架自己的特有語句-->
        <!--把映射文件放到核心配置文件-->
        <mapping resource="pers/zhb/domain/Student.hbm.xml"/><!--都在src目錄下-->
        <mapping resource="pers/zhb/domain/Class.hbm.xml"/><!--都在src目錄下-->
    </session-factory>
</hibernate-configuration>

 二、具體運用:

1、增加:

(1)創建一個新班級併為新班級添加兩名學生:

public class Test {
    public static void testSel() {
            Session session = HibernateUtils.openSession();//獲得session
            Transaction transaction = session.beginTransaction();//開啟事務
            Clas clas=new Clas();
            clas.setClassname("計科171");
            clas.setClassno(4600);
            clas.setDepartment("一號樓");
            clas.setMonitor("zhai");

            Student student=new Student();
            student.setSname("");
            student.setStudentno(2017151411);
            student.setPoint(123f);
            student.setSex("");
            student.setBirthday("2019-11-11");
            student.setPhone("18739496522");
            student.setClassno("221221");
            student.setEmail("34288334@qq.com");

            Student student1=new Student();
            student1.setSname("翟hb");
            student1.setStudentno(2017151419);
            student1.setPoint(666f);
            student1.setSex("");
            student1.setBirthday("2019-11-11");
            student1.setPhone("18739496522");
            student1.setClassno("221221");
            student1.setEmail("34288334@qq.com");

            clas.getStudents().add(student);//一對多,一個班級下有多個學生
            clas.getStudents().add(student1);//獲取Set集合對象並向其中添加元素

            student.setaClas(clas);//多對一,學生屬於哪一個班級
            student1.setaClas(clas);

            session.save(clas);
            session.save(student);
            session.save(student1);

            transaction.commit();//提交事務
            session.close();//關閉資源
        }

 

 (2)為一個已經存在的班級添加學生:

 public static void testAdd(){
            Session session = HibernateUtils.openSession();//獲得session
            Transaction transaction = session.beginTransaction();//開啟事務
            Clas clas=session.get(Clas.class,80501);//獲得一個已經存在的班級
            Student student=new Student();//創建一個學生對象
            student.setSname("翟zz");
            student.setStudentno(20190000);
            student.setPoint(133f);
            student.setSex("男");
            student.setBirthday("2019-11-16");
            student.setPhone("18739496522");
            student.setEmail("34288334@qq.com");

            Student student1=new Student();//再創建一個學生對象
            student1.setSname("翟zz");
            student1.setStudentno(20190000);
            student1.setPoint(133f);
            student1.setSex("男");
            student1.setBirthday("2019-11-16");
            student1.setPhone("18739496522");
            student1.setEmail("34288334@qq.com");

            clas.getStudents().add(student);//學生添加到班級
            student.setaClas(clas);//班級與學生對應
            clas.getStudents().add(student1);
            student1.setaClas(clas);

            session.save(student);
            session.save(student1);


            transaction.commit();//提交事務
            session.close();//關閉資源

        }

  

 

 2、刪除:

刪除80501班的一名學生信息:

 public static void testDel() {
           Session session = HibernateUtils.openSession();//獲得session
           Transaction transaction = session.beginTransaction();//開啟事務
           Clas clas=session.get(Clas.class,80501);//獲得要刪除的學生屬於那一個班級
           Student student=session.get(Student.class,937221532);//獲得要刪除的學生
           clas.getStudents().remove(student);
           student.setaClas(null);
           transaction.commit();//提交事務
           session.close();//關閉資源
       }

 

 

 

 

 

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

2016第二屆亞太新能源汽車國際峰會:聚焦汽車智能化,網聯化,輕量化和電氣化技術融合

由中國汽車工程研究院、英納威-俄羅斯聖彼德堡國立技術大學中俄功能材料新能源技術研究院支援,上海領研商務諮詢有限公司主辦「2016 第二屆 亞太新能源汽車國際峰會」於6月2-3日在北京萬豪酒店隆重召開,來自相關政府部門、整車企業、行業機構、新能源汽車關鍵零部件供應商,智慧網聯汽車關鍵零部件企業,先進輕量化材料及解決方案企業,諮詢公司,工程開發公司,檢測認證機構,高校科研院所及投資公司等150余名行業代表蒞臨現場,分享交流新能源汽車產業政策及未來發展趨勢、新能源汽車動力總成及輕量化技術、整車集成及動力電池技術、先進智慧網聯及無線充電技術等。

2016ANEVS會議現場

第一天上午來自權威行業機構,知名諮詢公司及整車廠的發言嘉賓和大家分享了新能源汽車產業政策及未來發展趨勢:

中國內燃機工業協會副秘書長魏安力以應對第四階段乘用車油耗法規的節能技術路線分析,在分析國家能源安全及大氣污染控制的背景下,闡述了現階段混合動力技術是當前國內汽車節能減排的最佳技術路徑選擇。

貝恩公司的全球合夥人戴加輝先生,就全球新能源汽車市場發展趨勢和挑戰發表了精彩的演講,重點介紹了國內外汽車電氣化發展中存在的挑戰,純電動和插電式混合動力及動力電池發展現狀及趨勢等。

來自北汽集團的新能源汽車管理部部長詹文章博士以北汽集團新能源汽車發展戰略和實踐發表主題演講,分享了北汽集團未來新能源汽車發展技術和戰略規劃以及電氣化和智慧網聯及輕量化技術融合發展趨勢。

吉利汽車研究院總工程師熊飛博士,就新能源汽車輕量化技術開發與落地發表演講,重點介紹了應用于吉利新能源汽車的先進輕量化技術及未來發展規劃。

特斯拉汽車中國區公共政策與充電基礎設施總監的高翔先生,就高性能智慧互聯電動汽車-清潔交通的革新發表演講,著重闡述了特斯拉汽車在電氣化和智慧互聯技術融合方面的發展經驗。

來自奇瑞汽車的副總裁兼動力總成技術中心執行總監韓爾樑先生,就最有競爭力的奇瑞動力系統研發發表主題演講,和大家分享了奇瑞未來新能源汽車動力系統發展規劃,特別重點宣導發展燃料電池汽車的研發和技術儲備。

德國國際合作機構的Markus Wagner先生,就電動交通和可再生能源在德國的能源經濟型效應及發展現狀發表演講,介紹了德國在可再生能源和電動交通協同發展的經驗。

來自羅蘭貝格的高級項目經理鄭贇先生就全球智慧網聯汽車市場發展趨勢和挑戰發表演講,系統介紹了國內外智慧網聯汽車的發展現狀和存在的挑戰,還特別介紹了羅蘭貝格關於汽車電氣化和輕量化技術融合發展的最新研究成果。

第二天,針對汽車電氣化和智慧化,網聯化及智慧化技術融合,會議的部分演講嘉賓介紹了最新的技術解決方案:

來自吉凱恩傳動系統的中國區工程技術支援總監克裡澌汀·高熙先生,就應用於新能源汽車的先進變速器技術發表演講,重點介紹和展示了應用于寶馬、三菱、富豪及保時捷等新能源汽車的變速器和傳動系統解決方案。

來自能啟能電子的技術總監徐向陽先生,就新能源汽車火情預警及控制策略發表演講,重點介紹和展示了他們目前所做的對新能源汽車電池熱失控的早期預警、智慧判斷、提前干預和主動滅火等研究。

來自諾貝麗斯的中國區董事總經理劉清先生,就全鋁車身在新能源汽車中輕量化應用的最新發展發表演講,重點介紹了全鋁車身對於新能源汽車的重要性及全鋁車身汽車的閉合循環系統。

來自東軟睿馳的副總經理曹斌先生,就新能源汽車動力電池安全方面的思考及研發發表演講,重點介紹了PACK電池包、BMS電池管理系統及ICS等在新能源汽車的應用及安全的解決方案。

來自地平線機器人科技的業務拓展總監李星宇先生,就深度學習為自動駕駛帶來的全新突破發表演講,重點介紹了自動駕駛的演算法、晶片、系統集成三方面。

 

更多關於發言嘉賓詳細演講資料,請關注微信公眾號:領研GIIAutomotive

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

Android音頻開發(1):基礎知識

Android音頻開發(1):基礎知識

導讀

人的說話頻率基本上為300Hz~3400Hz,但是人耳朵聽覺頻率基本上為20Hz~20000Hz。

對於人類的語音信號而言,實際處理一般經過以下步驟:

人嘴說話——>聲電轉換——>抽樣(模數轉換)——>量化(將数字信號用適當的數值表示)——>編碼(數據壓縮)——>

傳輸(網絡或者其他方式)

——> 解碼(數據還原)——>反抽樣(數模轉換)——>電聲轉換——>人耳聽聲。

  • 抽樣率

實際中,人發出的聲音信號為模擬信號,想要在實際中處理必須為数字信號,即採用抽樣、量化、編碼的處理方案。

處理的第一步為抽樣,即模數轉換。

簡單地說就是通過波形採樣的方法記錄1秒鐘長度的聲音,需要多少個數據。

根據奈魁斯特(NYQUIST)採樣定理,用兩倍於一個正弦波的頻繁率進行採樣就能完全真實地還原該波形。

所以,對於聲音信號而言,要想對離散信號進行還原,必須將抽樣頻率定為40KHz以上。實際中,一般定為44.1KHz。

44.1KHz採樣率的聲音就是要花費44000個數據來描述1秒鐘的聲音波形。

原則上採樣率越高,聲音的質量越好,採樣頻率一般共分為22.05KHz、44.1KHz、48KHz三個等級。

22.05 KHz只能達到FM廣播的聲音品質,44.1KHz則是理論上的CD音質界限,48KHz則已達到DVD音質了。

  • 碼率

對於音頻信號而言,實際上必須進行編碼。在這裏,編碼指信源編碼,即數據壓縮。如果,未經過數據壓縮,直接量化進行傳輸則被稱為PCM(脈衝編碼調製)。
要算一個PCM音頻流的碼率是一件很輕鬆的事情,採樣率值×採樣大小值×聲道數 bps。
一個採樣率為44.1KHz,採樣大小為16bit,雙聲道的PCM編碼的WAV文件,它的數據速率則為 44.1K×16×2 =1411.2 Kbps。
我們常說128K的MP3,對應的WAV的參數,就是這個1411.2 Kbps,這個參數也被稱為數據帶寬,它和ADSL中的帶寬是一個概念。將碼率除以8,就可以得到這個WAV的數據速率,即176.4KB/s。

這表示存儲一秒鐘採樣率為44.1KHz,採樣大小為16bit,雙聲道的PCM編碼的音頻信號,需要176.4KB的空間,1分鐘則約為10.34M,這對大部分用戶是不可接受的,尤其是喜歡在電腦上聽音樂的朋友,要降低磁盤佔用

只有2種方法,降低採樣指標或者壓縮。降低指標是不可取的,因此專家們研發了各種壓縮方案。最原始的有DPCM、ADPCM,其中最出名的為MP3。

所以,採用了數據壓縮以後的碼率遠小於原始碼率。

一、發的主要應用有哪些?

音頻播放器,錄音機,語音電話,音視頻監控應用,音視頻直播應用,音頻編輯/處理軟件,藍牙耳機/音箱,等等。

二、頻開發的具體內容有哪些?

(1)音頻採集/播放

(2)音頻算法處理(去噪、靜音檢測、回聲消除、音效處理、功放/增強、混音/分離,等等)

(3)音頻的編解碼和格式轉換

(4)音頻傳輸協議的開發(SIP,A2DP、AVRCP,等等)

三、 音頻應用的難點在哪?

延時敏感、卡頓敏感、噪聲抑制(Denoise)、回聲消除(AEC)、靜音檢測(VAD)、混音算法,等等。

四、 音頻開發基礎概念有哪些?

在音頻開發中,下面的這幾個概念經常會遇到。

1. 採樣率(samplerate)

採樣就是把模擬信號数字化的過程,不僅僅是音頻需要採樣,所有的模擬信號都需要通過採樣轉換為可以用0101來表示的数字信號,示意圖如下所示:

藍色代表模擬音頻信號,紅色的點代表採樣得到的量化數值。

採樣頻率越高,紅色的間隔就越密集,記錄這一段音頻信號所用的數據量就越大,同時音頻質量也就越高。

根據奈奎斯特理論,採樣頻率只要不低於音頻信號最高頻率的兩倍,就可以無損失地還原原始的聲音。

通常人耳能聽到頻率範圍大約在20Hz~20kHz之間的聲音,為了保證聲音不失真,採樣頻率應在40kHz以上。常用的音頻採樣頻率有:8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz、96kHz、192kHz等。

對採樣率為44.1kHz的AAC音頻進行解碼時,一幀的解碼時間須控制在23.22毫秒內。

通常是按1024個採樣點一幀

分析:

1. AAC

一個AAC原始幀包含某段時間內1024個採樣點相關數據。

用1024主要是因為AAC是用的1024點的mdct。

音頻幀的播放時間=一個AAC幀對應的採樣樣本的個數/採樣頻率(單位為s)

採樣率(samplerate)為 44100Hz,表示每秒 44100個採樣點,

所以,根據公式,

音頻幀的播放時長 = 一個AAC幀對應的採樣點個數 / 採樣頻率

則,當前一幀的播放時間 = 1024 * 1000/44100= 23.22 ms(單位為ms)

48kHz採樣率,

則,當前一幀的播放時間 = 1024 * 1000/48000= 21.333ms(單位為ms)

22.05kHz採樣率,

則,當前一幀的播放時間 = 1024 * 1000/22050= 46.439ms(單位為ms)

2. MP3

mp3 每幀均為1152個字節,

則:

每幀播放時長 = 1152 * 1000 / sample_rate

例如:sample_rate = 44100HZ時,

計算出的時長為26.122ms,

這就是經常聽到的mp3每幀播放時間固定為26ms的由來。

2. 量化精度(位寬)

上圖中,每一個紅色的採樣點,都需要用一個數值來表示大小,這個數值的數據類型大小可以是:4bit、8bit、16bit、32bit等等,位數越多,表示得就越精細,聲音質量自然就越好,當然,數據量也會成倍增大。

常見的位寬是:8bit 或者 16bit

3. 聲道數(channels)

由於音頻的採集和播放是可以疊加的,因此,可以同時從多個音頻源採集聲音,並分別輸出到不同的揚聲器,故聲道數一般表示聲音錄製時的音源數量或回放時相應的揚聲器數量。

單聲道(Mono)和雙聲道(Stereo)比較常見,顧名思義,前者的聲道數為1,後者為2

4. 音頻幀(frame)

是用於測量显示幀數的量度。所謂的測量單位為每秒显示幀數(Frames per Second,簡稱:FPS)或“赫茲”(Hz)。

音頻跟視頻很不一樣,視頻每一幀就是一張圖像,而從上面的正玄波可以看出,音頻數據是流式的,本身沒有明確的一幀幀的概念,在實際的應用中,為了音頻算法處理/傳輸的方便,一般約定俗成取2.5ms~60ms為單位的數據量為一幀音頻。

這個時間被稱之為“採樣時間”,其長度沒有特別的標準,它是根據編×××和具體應用的需求來決定的,我們可以計算一下一幀音頻幀的大小:

假設某通道的音頻信號是採樣率為8kHz,位寬為16bit,20ms一幀,雙通道,則一幀音頻數據的大小為:

int size = 8000 x 16bit x 0.02s x 2 = 5120 bit = 640 byte

五、常見的音頻編碼方式有哪些?

上面提到過,模擬的音頻信號轉換為数字信號需要經過採樣和量化,量化的過程被稱之為編碼,根據不同的量化策略,產生了許多不同的編碼方式,常見的編碼方式有:PCM 和 ADPCM,這些數據代表着無損的原始数字音頻信號,添加一些文件頭信息,就可以存儲為WAV文件了,它是一種由微軟和IBM聯合開發的用於音頻数字存儲的標準,可以很容易地被解析和播放。

我們在音頻開發過程中,會經常涉及到WAV文件的讀寫,以驗證採集、傳輸、接收的音頻數據的正確性。

六、常見的音頻壓縮格式有哪些?

首先簡單介紹一下音頻數據壓縮的最基本的原理:因為有冗餘信息,所以可以壓縮。

(1) 頻譜掩蔽效應: 人耳所能察覺的聲音信號的頻率範圍為20Hz~20KHz,在這個頻率範圍以外的音頻信號屬於冗餘信號。

(2) 時域掩蔽效應: 當強音信號和弱音信號同時出現時,弱信號會聽不到,因此,弱音信號也屬於冗餘信號。

下面簡單列出常見的音頻壓縮格式:

MP3,AAC,OGG,WMA,Opus,FLAC,APE,m4a,AMR,等等

七、Adndroid VoIP相關的開源應用有哪些 ?

imsdroid,sipdroid,csipsimple,linphone,WebRTC 等等

八、音頻算法處理的開源庫有哪些 ?

speex、ffmpeg,webrtc audio module(NS、VAD、AECM、AGC),等等

九、Android提供了哪些音頻開發相關的API?

音頻採集: MediaRecoder,AudioRecord

音頻播放: SoundPool,MediaPlayer,AudioTrack

音頻編解碼: MediaCodec

NDK API: OpenSL ES

十、音頻開發的延時標準是什麼?

ITU-TG.114規定,對於高質量語音可接受的時延是300ms。一般來說,如果時延在300~400ms,通話的交互性比較差,但還可以接受。時延大於400ms時,則交互通信非常困難。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享