android 面試知識點
阿新 • • 發佈:2019-01-11
自我介紹:
您好,我是xxx,從事android開發也有好幾年了,總共呆過兩家公司,第一家是xx,幹了1年,在這家公司做過3個專案,剛開始的時候主要寫一些需求文件,用xmind整理專案功能,做一些簡單的介面,大家都知道搞it的技術很重要,所以我在完成自己的工作之餘就經常看其他模組的實現程式碼,儘快提升自己的技術,也積極參與同事的討論,提出一些自己的想法,很快業務也熟悉了;我記得在第二個專案的時候就能獨立完成一些模組的功能,(做完第三個專案的時候/xxxx年xx月的時候/一年後)後來為了有更好的發展,就來到了第二家公司;這家公司對我的提升很多,專案很多,做了5、6個吧,包括電商類的,金融類的,教育類的,醫療類的都有,其中大部分是團隊開發,也有獨立開發的,比如哪個哪個,在團隊開發中,我主要擔任的也是核心開發人員,負責框架的搭建,需求的整理和分析,專案任務的安排和進度的把控,及專案規範性和效能的把控;這家公司成長很多,挺感謝公司的,不過現在做的專案和之前的業務都差不多,公司組織架構也比較穩定了,覺得發展到了一個瓶頸,加上現在移動互聯發展這麼快,新技術天天在更新,智慧家居,虛擬現實,vr,直播都是比較火的方向,我覺得也是以後的發展方向,it行業不跟著時代跟著技術進步,遲早會被淘汰,最近考慮了一下,還是決定離職,換個環境,周圍的同事朋友也有勸我不要這麼做的,我是一個不喜歡安逸的人,老這樣也沒意思,也為了以後的發展還是要大膽的嘗試探索,投了一下簡歷,也算是和咱公司有緣分,就到這來面試了,說了這麼多,口也渴了,停頓一秒。。。。面試官該說話了(當然前面隨時會打斷),應對問題,面試官一般問一些公司情況,個人情況,就會讓介紹專案,那就開始專案介紹節奏。。。,如果沒有讓介紹專案,談的不多,有空檔就主動介紹專案(不要冷場,挺尷尬的),
專案介紹:這個專案是6 月份開始做的,9月份第一版上線,
1.準備好錄音筆,模擬面試的時候就使用(真正去面試的時候,第一次都會忘了錄,或錄音失敗),面試錄音診斷是找到面試問題的唯一利器
2.學歷證(本科)和公司章提前買好,不要到了入職還沒有,影響得之不易的offer,加急製作,費用高
3.改簡歷,投簡歷,開始每天150份,智聯,51job,拉鉤,boss,獵聘,100offer等,開始面試電話可能不多,耐心等待,如果投完900家,還是沒有什麼電話,可以重新註冊一個賬號再投
4.招聘資訊共享,待會面試題,一個沒成,後續的補上,有備而去,很重要
5.到了面試地點,去早了直接進去不要等,不能按時到,提前和人事說一下(如果能聯絡上)
6. 見到了人事,寒暄幾句(天冷天熱,過來快慢,是否堵車,公司情況(位置好壞,好找不好找,辦公區環境(大,寬敞,乾淨))),恰當的誇誇她(根據情況,不要生硬),詢問技術面試官的職位和姓,見到時好打招呼,一定要起身,男的主動握手(女的就不要了,除非她先伸手)
7.如果兩個或以上,確定哪個是頭,主要和他交流,其他適當照顧到
8.自我介紹,介紹專案時,一定要自然,在回顧過去,眼睛可以平時或向上一點,不能左右顧盼
技術升級:演算法,底層,專案技術要精(專案也許不在多),框架洗講,直播,sdk開發,自定義view,RxJava,reactnative,外掛化
1.下拉重新整理時頭部導航條的透明度會隨著scrollview的滑動距離改變而改變,滑動到一定距離時,介面右下方會出現一個小火箭,點選它,介面會有一個置頂功能。首先我會自定義一個元件,繼承LinearLayout,新增一個header view,保證是第一個新增到linearlayout的最上端,設定topMargin的值為負的header View高度,即將其隱藏在最上方,之後需要新增footer view,由於是線性佈局可以直接新增,只要AdapterView的高度是MATCH_PARENT,那麼footer view就會被新增到最後,並隱藏。下來重新整理和上拉載入都要進行手指滑動距離的判斷,當header view的topMargin>=0 時,說明已經完全顯示出來了,修改header view 的提示狀態,footer 準備重新整理,手指移動過程,還沒有釋放移動footer view高度同樣和移動header view高度是一樣,都是通過修改header view的topmargin的值來達到。為了實現偏移控制,一般自定義View/ViewGroup都需要過載computeScroll()方法,我會判斷scrollview的滑動距離動態去給導航條的背景設定透明度,滑動漸變的效果,當滑到一定距離時,我會把小火箭顯示出來,點選是會呼叫scrollview的scrollTo()方法,實現置頂功能。
2.圖片的快取處理
在載入圖片的時候,如果圖片比較大時而介面上只需要顯示一小塊,那麼會造成記憶體浪費,繼續載入更多也可能會出現記憶體溢位,這時使用快取技術是必備的,這裡我用的是IamgeLoader的LruCache類,首先將ImageLoader類設成單例,並在建構函式中初始化了LruCache類,把它的最大快取容量設為最大可用記憶體的1/8。然後又提供了其它幾個方法可以操作LruCache,以及對圖片進行壓縮和讀取。首先自定義一個繼承自ScrollView的類,這樣就允許使用者可以通過滾動的方式來瀏覽更多的圖片。這裡提供了一個loadMoreImages()方法,是專門用於載入下一頁的圖片的,因此在onLayout()方法中我們要先呼叫一次這個方法,以初始化第一頁的圖片。然後在onTouch方法中每當監聽到手指離開螢幕的事件,就會通過一個handler來對當前ScrollView的滾動狀態進行判斷,如果發現已經滾動到了最底部,就會再次呼叫loadMoreImages()方法去載入圖片。在這個方法中,使用了一個迴圈來載入這一頁中的每一張圖片,每次都會開啟一個LoadImageTask,用於對圖片進行非同步載入。然後在LoadImageTask中,首先會先檢查一下這張圖片是不是已經存在於SD卡中了,如果還沒存在,就從網路上下載,然後把這張圖片存放在LruCache中。接著將這張圖按照一定的比例進行壓縮,並找出當前高度最小的一列,把壓縮後的圖片新增進去就可以了。另外,為了保證圖片都能夠合適地被回收,這裡還加入了一個可見性檢查的方法,即checkVisibility()方法。這個方法的核心思想就是檢查目前照片牆上的所有圖片,判斷出哪些是可見的,哪些是不可見。然後將那些不可見的圖片都替換成一張空圖,這樣就可以保證程式始終不會佔用過高的記憶體。當這些圖片又重新變為可見的時候,只需要再從LruCache中將這些圖片重新取出即可。如果某張圖片已經從LruCache中被移除了,就會開啟一個LoadImageTask,將這張圖片重新載入到記憶體中。
斷點續傳:
1:當用戶點選下載按鈕是就要把檔案路徑的資訊傳給後臺由service的onstartcom的方法接受穿過來的引數,由於service和activity都是主執行緒不能做耗時操作所以要開啟一個子執行緒去做耗時操作網路下載和把資料寫到本地檔案並且把進度資訊本地檔案儲存到資料庫並把進度傳給activity通過廣播發送網路檔案
網路下載的關鍵點:
1:獲取網路檔案的長度
2:在本地建立一個檔案,設定其長度
3:從資料庫中獲取上次下載的進度
4:從上次下載的位置下載資料,同時儲存進度到資料庫
5將下載進度回傳activity
6:下載完成後刪除下載資訊
3.流式佈局的實現
自定義ViewGroup,重點重寫下面兩個方法
1)onMeasure:測量子view的寬高,設定自己的寬和高
2)onLayout:設定子view的位置,onMeasure:根據子view的佈局檔案中屬性,來為子view設定測量模式和測量值
當這個流式佈局在被載入如記憶體並顯示在螢幕上這一過程中,首先會呼叫view.measure(w,h)這個方法,表示測量view的寬度與高度,其中引數w與h分別表示這個控制元件的父控制元件的寬高。在view.measure()方法的呼叫過程中又會呼叫view本身的一個回撥方法,onMeasure(),這個是view自身的一個回撥方法,用於讓開發者在自定義View的時候重新計算自身的大小。一般會在這個方法中迴圈遍歷,計算出這個控制元件的全部子孫控制元件的寬高。在View的寬高計算完成以後,考慮將這個控制元件顯示到螢幕的指定位置上,此時view的onLayout()方法會被呼叫。 一般同時會在這個方法中計算出全部子孫控制元件在這個控制元件中的位置。在onMeasure()中首先通過迴圈,遍歷這個控制元件的所有子控制元件,同時呼叫子控制元件的measure()方法,這時measure方法的兩個引數是控制元件能給這個子控制元件的最大寬高這裡getChildMeasureSpec()方法的作用是用來計算一個合適子檢視的尺寸大小(寬度或者高度),結合我們從子檢視的LayoutParams所給出的MeasureSpec資訊來獲取最合適的結果。比如,如果這個View知道自己的大小尺寸,並且子檢視的大小恰好跟父視窗一樣大,父視窗必須用給定的大小去layout子檢視,通過迴圈遍歷,控制每個item子控制元件的顯示位置,如果當前行還能放得下一個item,就放到當前行,如果放不下就放到下一行的最左邊。
4.支付功能:
微信支付:
1)從微信開放平臺上下載微信支付的SDK,使用其libammsdk.jar包。
2)在微信開放平臺註冊我們的應用(應用需要填寫包名,應用的簽名等)可以獲取到APPID和APPSecret,然後申請開通支付功能,提交企業以及銀行賬戶資料等待稽核,稽核通過了線上簽訂合同,之後就可以進行微信支付了,同時商戶會收到郵件獲得財付通商戶賬戶,支付祕鑰key,財付通許可權祕鑰,財付通商戶標示id等開發關鍵資料。
3)預付訂單:當用戶確定購買商品之後,首先請求微信伺服器獲取access_tocken,提交預付訂單package,添加簽名,提交預付訂單後會獲取微信伺服器返回的prepayid,接著把請求引數返回給手機端。
4)發起支付:呼叫api.sendReq(req);發起支付,發起支付的時候需要傳入支付相關資訊,相關資訊以KEY-VALUE鍵值對方式用&拼接組成。例如:訂單資訊(訂單號,交易付款時間,購買數量等),商品資訊(商品名稱,商品單價,商品描述等),支付資訊(支付金額等)和notify_url(這個是通知地址,也就是微信的伺服器把支付後成功與否等結果資訊同步給我們公司伺服器時候使用的地址)等。還需要拼接一個重要的簽名引數sign,sign的值是上述這些交易資訊MD5加密簽名後的結果。
5)支付資訊提交後會提交到微信的伺服器,微信伺服器完成整個支付功能後會給使用者(客戶端和伺服器分別提示)成功或者失敗的提示。
支付寶支付:
1)首先從支付寶平臺下載SDK,使用其中的alipay.jar包。
2)當用戶確定購買商品之後,首先生成訂單,我們訂單生成的規則就是用兩個2-9的隨機數+當前時間的毫秒值作為訂單號。
3)呼叫支付寶的介面:呼叫支付寶的支付方法(方法Alipay.pay(String info)),支付方法需要傳入支付相關資訊,相關資訊以KEY-VALUE鍵值對方式用&拼接組成。訂單資訊(訂單號,交易付款時間,購買數量等),商品資訊(商品名稱,商品單價,商品描述等),支付資訊(買方,賣方支付寶賬號資訊,支付金額等)和notify_url(這個是通知地址,也就是支付寶的伺服器把支付後成功與否等結果資訊同步給我們公司伺服器時候使用的地址)等。還需要拼接一個重要的簽名引數sign,sign的值是上述這些交易資訊RSA簽名後的結果。
4)支付資訊提交後會提交到支付寶的伺服器,支付寶的伺服器完成整個支付功能後會給使用者成功或者失敗的提示介面。這個過程都是支付寶的功能。支付寶支付完成後,也會把資訊同步給我們的伺服器(也就是通過前面的notify_url這個地址)。
5)我們的伺服器處理完畢之後,呼叫pay方法的時候其實也會接到返回值。返回值是json格式裡面有很多資訊,我們主要從中取結果狀態碼(resultStatus),根據狀態碼的數值判斷支付是否成功,除了支付寶本身的提示介面以外,我們也會給使用者一個我們自己的提示。
5.xmpp實現聊天功能:
我專案中即時通訊主要使用XMPP實現的,它有一個開源專案androidpn,使用的時候客戶端要用到asmack.jar包,這個jar包提供了通訊的功能,主要使用jar包中的XMPPConnection類。主要實現如下:
1)使用XMPPConnection類與伺服器openfire建立持久連線
2)在持久連線基礎上進行使用者註冊(使用者註冊之後伺服器就會記錄該使用者的資訊,下次就能夠識別)
3)在連線基礎上進行使用者登入認證(登入之後服務端會知道當前使用者的狀態)服務端只要記錄了該使用者的資訊,就可以往使用者手機上傳送推送的資訊了。
實現通訊功能主要是使用jar包中的 Chat 類完成資訊的傳送,傳送資訊的時候要注意的是使用者的格式是使用者名稱稱@域/名稱空間(例如:要給loupengfei聊天的話地址為:[email protected]127.0.0.1/AndroidpnClient),這樣服務端就能夠把資訊轉給響應名稱的人。
接收資訊:
1)在使用者建立連線的時候就定義了一個指定名稱空間的解析器provider(負責解析服務端發過來的xml格式的訊息)
2)同時也註冊一個包監聽器PacketListener,當有資訊傳送過來的時候我們定義的解析器會收到資訊,在解析中使用PULL解析方式把xml中資訊解析出來然後封裝成我們自定義的一個IQ的子類(就是訊息Bean),然後帶著訊息進入到包監聽類中,在包監聽類中結合handler把聊天內容等更新到UI上。
6.自定義圓形頭像:
1)首先通過setImageBitmap()方法設定圖片Bitmap
2)進入建構函式CircleImageView()獲取自定義引數,以及呼叫setup()函式
3)進入setup()函式(非常關鍵),進行圖片畫筆邊界畫筆(Paint)一些重繪引數初始化:構建渲染器BitmapShader用Bitmap來填充繪製區域,設定樣式和內外圓半徑計算等,以及呼叫updateShaderMatrix()函式和 invalidate()函式
4)進入updateShaderMatrix()函式,計算縮放比例和平移,設定BitmapShader的Matrix引數等
5)觸發ondraw()函式完成最終的繪製,使用配置好的Paint先畫出繪製內圓形來以後再畫邊界圓形。
7.瀑布流照片牆的實現
瀑布流的佈局方式雖然看起來好像排列的很隨意,其實它是有很科學的排列規則的。整個介面會根據螢幕的寬度劃分成等寬的若干列,由於手機的螢幕不是很大,這裡我就分成三列。每當需要新增一張圖片時,會將這張圖片的寬度壓縮成和列一樣寬,再按照同樣的壓縮比例對圖片的高度進行壓縮,然後在這三列中找出當前高度最小的一列,將圖片新增到這一列中。之後每當需要新增一張新圖片時,都去重複上面的操作,就會形成瀑布流格局的照片牆。
8.螢幕適配
1)比重適配,控制元件在其父佈局中的顯示權重,一般用於線性佈局中,如果控制元件過多,且顯示比例也不相同的時候,控制起來就比較麻煩了,畢竟反比不是那麼好確定的。
2)多個佈局檔案適配,就是對不同大小的螢幕提供不同的layout。比如橫豎屏的切換,可以在res目錄下建立layout-port和layout-land兩個目錄,裡面分別放置豎屏和橫屏兩種佈局檔案,以適應對橫屏豎屏自動切換。
3)多個drawable適配(圖片的適配),就是對不同密度的螢幕提供不同的圖片。在進行開發的時候,我們需要把合適大小的圖片放在合適的資料夾裡面。應儘量使用.9格式的圖片,如需對密度為low的螢幕提供合適的圖片,需新建資料夾drawable-ldpi/,並放入合適大小的圖片。相應的,medium對應drawable-mdpi/,high對應drawable-hdpi/,extra high對應drawable-xhdpi/。
4)多個values適配,為了優質的使用者體驗,需要去針對不同的dpi設定,編寫多套數值檔案,要把生成的所有values資料夾放到res目錄下,當設計師把UI高清設計圖給你之後,你就可以根據設計圖上的尺寸,以某一個解析度的機型為基礎,找到對應畫素數的單位,然後設定給控制元件。
9.記憶體優化
1)靜態變數引起記憶體洩露
靜態變數是類相關的變數,它的生命週期是從這個類被宣告,到這個類徹底被垃圾回收器回收才會被銷燬。所以,一般情況下,靜態變數從所在的類被使用開始就要一直佔用著記憶體空間,直到程式退出。如果不注意,靜態變數引用了佔用大量記憶體的資源,造成垃圾回收器無法對記憶體進行回收,就可能造成記憶體的浪費。
2)使用Application的Context
在Android中,Application Context的生命週期和應用的生命週期一樣長,而不是取決於某個Activity的生命週期。如果想保持一個長期生命的物件,並且這個物件需要一個 Context,就可以使用Application物件。可以通過呼叫Context.getApplicationContext()方法或者 Activity.getApplication()方法來獲得Application物件。
3)及時關閉資源
Cursor是Android查詢資料後得到的一個管理資料集合的類。正常情況下,如 果我們沒有關閉它,系統會在回收它時進行關閉,但是這樣的效率特別低。如果查詢得到的資料量較小時還好,如果Cursor的資料量非常大,特別是如果裡面 有Blob資訊時,就可能出現記憶體問題。所以一定要及時關閉Cursor。
4)避免使用浮點數
通常的經驗是,在Android裝置中,浮點數會比整型慢兩倍。
5)使用實體類比介面好
按照傳統的觀點Map會更好些,因為這樣你可以改變他的具體實現類,只要這個類繼承自Map介面。傳統的觀點對於傳統的程式是正確的,但是它並不適合嵌入式系統。呼叫一個介面的引用會比呼叫實體類的引用多花費一倍的時間。如果HashMap完全適合你的程式,那麼使用Map就沒有什麼價值。如果有些地方你不能確定,先避免使用Map,剩下的交給IDE提供的重構功能好了。(當然公共API是一個例外:一個好的API常常會犧牲一些效能)
6)避免使用列舉
列舉變數非常方便,但不幸的是它會犧牲執行的速度和並大幅增加檔案體積。
使用列舉變數可以讓你的API更出色,並能提供編譯時的檢查。所以在通常的時候你毫無疑問應該為公共API選擇列舉變數。但是當效能方面有所限制的時候,你就應該避免這種做法了。
7)使用Bitmap及時呼叫recycle()
Bitmap是記憶體消耗大戶,絕大多數的OOM崩潰都是在操作Bitmap時產生的,我們需要根據需求去載入圖片的大小,例如在列表中僅用於預覽時載入縮圖,只有當用戶點選具體條目想看詳細資訊的時候,這時另啟動一個fragment/activity/對話方塊等等,去顯示整個圖片。直接使用ImageView顯示bitmap會佔用較多資源,特別是圖片較大的時候,可能導致崩潰,使用BitmapFactory.Options設定inSampleSize, 這樣做可以減少對系統資源的要求。使用Bitmap過後,就需要及時的呼叫Bitmap.recycle()方法來釋放Bitmap佔用的記憶體空間,而不要等Android系統來進行釋放。
8)對Adapter進行優化
如果一個物件只具有軟引用,那麼如果記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體 空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體。軟引用可以和一個引用隊 列(ReferenceQueue)聯合使用,如果軟引用所引用的物件被垃圾回收,Java虛擬機器就會把這個軟引用加入到與之關聯的引用佇列中。
如果一個物件只具有弱引用,那麼在垃圾回收器執行緒掃描的過程中,一旦發現了只具有弱引 用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現那些只具有弱引用的物件。弱 引用也可以和一個引用佇列(ReferenceQueue)聯合使用,如果弱引用所引用的物件被垃圾回收,Java虛擬機器就會把這個弱引用加入到與之關聯 的引用佇列中。
弱引用與軟引用的根本區別在於:只具有弱引用的物件擁有更短暫的生命週期,可能隨時被回收。而只具有軟引用的物件只有當記憶體不夠的時候才被回收,在記憶體足夠的時候,通常不被回收。
listview如何優化?
1)convertView複用,對convetView進行判空,當convertView不為空時重複使用,為空則初始化,從而減少了很多不必要的View的建立
2)定義一個ViewHolder,封裝Listview Item條目中所有的元件,將convetView的tag設定為ViewHolder,不為空時通過ViewHolder的屬性獲取對應元件即可
3)當ListView載入資料量較大時可以採用分頁載入和圖片非同步載入
handler的原理?
Handler主要用於執行緒間的通訊。
一個Handler允許傳送和處理Message和Runable物件,UI主執行緒會自動分配一個Looper(訊息輪詢器),每個Looper中封裝著MessageQueue(訊息佇列),遵循先進先出原則。Looper負責不斷的從自己的訊息佇列裡取出隊頭的任務或訊息執行。一般是在子執行緒執行完耗時操作之後,通過Handler的sendMessage或post方法將Message和Runable物件傳遞給MessageQueue,而且在這些物件離開MessageQueue時,Handler負責執行他們(用到handleMessage方法,主要執行重新整理UI的程式碼)。
其中Message類就是定義了一個資訊,這個資訊中包含一個描述符和任意的資料物件,這個資訊被用來傳遞給Handler.Message物件提供額外的兩個int域和一個Object域。
Tcp和Udp的區別和聯絡?
TCP---傳輸控制協議,提供的是面向連線、可靠的位元組流服務,傳輸資料前經過“三次握手”建立連線,保證資料傳輸的可靠性,但效率比較低。一般用於對於資料傳輸安全性較高的場合。
UDP---使用者資料報協議,是一個簡單的面向資料報的運輸層協議,面向無連線。UDP不提供可靠性,資料傳輸可能發生錯序,丟包,但效率較高。一般用於對於實時性要求較高的場合。
方法的重寫和過載?
重寫是子類的方法覆蓋父類的方法,要求方法名和引數都相同 。
過載是在同一個類中的兩個或兩個以上的方法,擁有相同的方法名,但是引數卻不相同,方法體也不相同,最常見的過載的例子就是類的建構函式。
ArrayList和LinkedList區別?
存資料,ArrayList陣列儲存資料,索引值以下標來搜尋,查詢比較方,刪除增加比較麻煩,但是linkedList以連結串列式儲存資料,對於增刪比較方便。
Final ,finally,finalized,區別?
final用於宣告屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。
finally是異常處理語句結構的一部分,表示總是執行。
finalize是Object類的一個方法,在垃圾收集器執行的時候會呼叫被回收物件的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關閉檔案等。
載入大圖、多圖如何防止記憶體溢位?
大圖:解析每張圖片的時候都先檢查一下圖片的大小,確保圖片不會超出記憶體大小,如果只需要載入一張比較小的圖片展示出來,顯然載入大圖的原圖是不值得的,可以通過設定BitmapFactory.Options中inSampleSize的值實現圖片壓縮。
多圖:螢幕上顯示的圖片可以通過滑動螢幕等事件不斷地增加,最終導致OOM,可以使用LruCache類,主要演算法原理是把最近使用的物件用強引用儲存在 LinkedHashMap 中,並且把最近最少使用的物件在快取值達到預設定值之前從記憶體中移除。
如何人解決anr?forceclose(強制關閉)
(1) AsyncTask,其中doInBackground()和onPostExecute(Result)兩個方法非常重要
doInBackground() 這個方法執行在後臺執行緒中,主要負責執行那些很耗時的操作,如訪問網路。該方法必須重寫。
onPostExecute(Result) 這個方法運行於UI主執行緒,在doInBackground(Params…)方法執行後呼叫,該方法用於接收後臺任務執行後返回的結果,重新整理UI顯示。
(2.)子執行緒 + handler
在子執行緒中執行完耗時操作需要重新整理UI時,通過handler.sendMessage()發訊息給主執行緒, 然後在主執行緒Handler中的handleMessage()方法中執行重新整理UI操作
android中記憶體如何優化?
1)謹慎使用靜態變數
2)使用Application的Context
3)及時關閉資源(io流、cursor物件)
4)使用Bitmap及時呼叫recycle()
5)對Adapter進行優化
氣泡排序:
for (int i = 0; i < score.length-1; i++){ //最多做n-1趟排序
for(int j = 0 ;j < score.length - i - 1; j++){
if(score[j] < score[j + 1]){ //把小的值交換到後面
int temp = score[j];
score[j] = score[j + 1];
score[j + 1] = temp;
}
}
}
微信支付:
1)從微信開放平臺上下載微信支付的SDK,使用其libammsdk.jar包。
2)在微信開放平臺註冊我們的應用(應用需要填寫包名,應用的簽名等)可以獲取到APPID和APPSecret,然後申請開通支付功能,提交企業以及銀行賬戶資料等待稽核,稽核通過了線上簽訂合同,之後就可以進行微信支付了,同時商戶會收到郵件獲得財付通商戶賬戶,支付祕鑰key,財付通許可權祕鑰,財付通商戶標示id等開發關鍵資料。
3)預付訂單:當用戶確定購買商品之後,首先請求微信伺服器獲取access_tocken,提交預付訂單package,添加簽名,提交預付訂單後會獲取微信伺服器返回的prepayid,接著把請求引數返回給手機端。
4)發起支付:呼叫api.sendReq(req);發起支付,發起支付的時候需要傳入支付相關資訊,相關資訊以KEY-VALUE鍵值對方式用&拼接組成。例如:訂單資訊(訂單號,交易付款時間,購買數量等),商品資訊(商品名稱,商品單價,商品描述等),支付資訊(支付金額等)和notify_url(這個是通知地址,也就是微信的伺服器把支付後成功與否等結果資訊同步給我們公司伺服器時候使用的地址)等。還需要拼接一個重要的簽名引數sign,sign的值是上述這些交易資訊MD5加密簽名後的結果。
5)支付資訊提交後會提交到微信的伺服器,微信伺服器完成整個支付功能後會給使用者(客戶端和伺服器分別提示)成功或者失敗的提示。
支付寶支付:
1)首先從支付寶平臺下載SDK,使用其中的alipay.jar包。
2)當用戶確定購買商品之後,首先生成訂單,我們訂單生成的規則就是用兩個2-9的隨機數+當前時間的毫秒值作為訂單號。
3)呼叫支付寶的介面:呼叫支付寶的支付方法(方法Alipay.pay(String info)),支付方法需要傳入支付相關資訊,相關資訊以KEY-VALUE鍵值對方式用&拼接組成。訂單資訊(訂單號,交易付款時間,購買數量等),商品資訊(商品名稱,商品單價,商品描述等),支付資訊(買方,賣方支付寶賬號資訊,支付金額等)和notify_url(這個是通知地址,也就是支付寶的伺服器把支付後成功與否等結果資訊同步給我們公司伺服器時候使用的地址)等。還需要拼接一個重要的簽名引數sign,sign的值是上述這些交易資訊RSA簽名後的結果。(加密就是數字簽名)
4)支付資訊提交後會提交到支付寶的伺服器,支付寶的伺服器完成整個支付功能後會給使用者成功或者失敗的提示介面。這個過程都是支付寶的功能。支付寶支付完成後,也會把資訊同步給我們的伺服器(也就是通過前面的notify_url這個地址)。
5)我們的伺服器處理完畢之後,呼叫pay方法的時候其實也會接到返回值。返回值是json格式裡面有很多資訊,我們主要從中取結果狀態碼(resultStatus),根據狀態碼的數值判斷支付是否成功,除了支付寶本身的提示介面以外,我們也會給使用者一個我們自己的提示。
xmpp實現聊天功能:
我專案中即時通訊主要使用XMPP實現的,它有一個開源專案androidpn,使用的時候客戶端要用到asmack.jar包,這個jar包提供了通訊的功能,主要使用jar包中的XMPPConnection類。主要實現如下:
1)使用XMPPConnection類與伺服器建立持久連線
2)在持久連線基礎上進行使用者註冊(使用者註冊之後伺服器就會記錄該使用者的資訊,下次就能夠識別)
3)在連線基礎上進行使用者登入認證(登入之後服務端會知道當前使用者的狀態)服務端只要記錄了該使用者的資訊,就可以往使用者手機上傳送推送的資訊了。
實現通訊功能主要是使用jar包中的Chat類完成資訊的傳送,傳送資訊的時候要注意的是使用者的格式是使用者名稱稱@域/名稱空間(例如:要給loupengfei聊天的話地址為:[email protected]127.0.0.1/AndroidpnClient),這樣服務端就能夠把資訊轉給響應名稱的人。
接收資訊:
1)在使用者建立連線的時候就定義了一個指定名稱空間的解析器provider(負責解析服務端發過來的xml格式的訊息)
2)同時也註冊一個包監聽器PacketListener,當有資訊傳送過來的時候我們定義的解析器會收到資訊,在解析中使用PULL解析方式把xml中資訊解析出來然後封裝成我們自定義的一個IQ的子類(就是訊息Bean),然後帶著訊息進入到包監聽類中,在包監聽類中結合handler把聊天內容等更新到UI上。
四大元件詳細介紹:
Activity通常就是一個單獨的螢幕,它上面可以顯示一些控制元件也可以監聽並處理使用者的事件做出響應。
BroadcastReceive廣播接收器:
Service 是一段長生命週期的,沒有使用者介面的程式,可以用來開發如監控類程式。
android平臺提供了Content Provider使一個應用程式的指定資料集提供給其他應用程式。這些資料可以儲存在檔案系統中、在一個SQLite資料庫、或以任何其他合理的方式,
其他應用可以通過ContentResolver類(見ContentProviderAccessApp例子)從該內容提供者中獲取或存入資料.(相當於在應用外包了一層殼),
只有需要在多個應用程式間共享資料是才需要內容提供者。例如,通訊錄資料被多個應用程式使用,且必須儲存在一個內容提供者中
它的好處:統一資料訪問方式。
java中異常處理:
Activity的啟動模式:
standard:預設模式,可以不用寫配置。在這個模式下,都會預設建立一個新的例項。因此,在這種模式下,可以有多個相同的例項,也允許多個相同Activity疊加。
singleTop:可以有多個例項,但是不允許多個相同Activity疊加。即,如果Activity在棧頂的時候,啟動相同的Activity,不會建立新的例項,而會呼叫其onNewIntent方法。
singleTask:只有一個例項。在同一個應用程式中啟動他的時候,若Activity不存在,則會在當前task建立一個新的例項,若存在,則會把task中在其之上的其它Activity destory掉並呼叫它的onNewIntent方法。
如果是在別的應用程式中啟動它,則會新建一個task,並在該task中啟動這個Activity,singleTask允許別的Activity與其在一個task中共存,也就是說,如果我在這個singleTask的例項中再開啟新的Activity,這個新的Activity還是會在singleTask的例項的task中。
singleInstance:只有一個例項,並且這個例項獨立執行在一個task中,這個task只有這個例項,不允許有別的Activity存在。
執行緒:被稱為輕量級程序(Lightweight Process,LWP),是程式執行流的最小單元
webview的輔助類:
作用:
通過url地址載入網際網路或本地網頁
WebSettings
設定WebView的一些屬性、狀態等,例如允許使用javascript,允許使用快取,允許使用內建的縮放元件
WebViewClient
主要幫助WebView處理各種通知、請求事件(例如,點選連結時候如何顯示介面,頁面開始載入,載入完畢之後有何動作等)
WebChromeClient
輔助WebView處理Javascript的對話方塊、網站圖示、網站Title、載入進度等
java的反射機制原理以及應用場合:
JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有
屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態
獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。
OSI七層模型:物理層、資料鏈路層、網路層、傳輸層、會話層、表示層、應用層
常用的儲存方式有哪些?
SQLite:
SQLite是一個輕量級的資料庫,支援基本SQL語法,是常被採用的一種資料儲存方式。Android為此資料庫提供了一個名為SQLiteDatabase的類,封裝了一些操作資料庫的API。
SharedPreference:
除SQLite資料庫外,另一種常用的資料儲存方式,其本質就是一個xml檔案,常用於儲存較簡單的引數設定。
File:
即常說的檔案(I/O)儲存方法,常用語儲存大數量的資料,但是缺點是更新資料將是一件困難的事情。
ContentProvider:
Android 系統中能實現所有應用程式共享的一種資料儲存方式,由於資料通常在各應用間的是互相私密的,所以此儲存方式較少使用,但是其又是必不可少的一種儲存方式。 例如音訊,視訊,圖片和通訊錄,一般都可以採用此種方式進行儲存。每個ContentProvider都會對外提供一個公共的URI(包裝成Uri對 象),如果應用程式有資料需要共享時,就需要使用ContentProvider為這些資料定義一個URI,然後其他的應用程式就通過 Content Provider傳入這個URI來對資料進行操作。
網路儲存:
從網路讀取資料和寫入資料。 Android提供了通過網路來實現資料的儲存和獲取的方法。 我們可以呼叫WebService返回的資料或是解析HTTP協議實現網路資料互動。
package org.rsa.util;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.io.*;
import java.math.BigInteger;
/**
* RSA 工具類。提供加密,解密,生成金鑰對等方法。
* 需要到http://www.bouncycastle.org下載bcprov-jdk14-123.jar。
* RSA加密原理概述
* RSA的安全性依賴於大數的分解,公鑰和私鑰都是兩個大素數(大於100的十進位制位)的函式。
* 據猜測,從一個金鑰和密文推斷出明文的難度等同於分解兩個大素數的積
* ===================================================================
* (該演算法的安全性未得到理論的證明)
* ===================================================================
* 金鑰的產生:
* 1.選擇兩個大素數 p,q ,計算 n=p*q;
* 2.隨機選擇加密金鑰 e ,要求 e 和 (p-1)*(q-1)互質 (最大公因數為1的兩個數叫互質數)
* 3.利用 Euclid 演算法計算解密金鑰 d , 使其滿足 e*d = 1(mod(p-1)*(q-1)) (其中 n,d 也要互質)
* 4:至此得出公鑰為 (n,e) 私鑰為 (n,d)
* ===================================================================
* 加解密方法:
* 1.首先將要加密的資訊 m(二進位制表示) 分成等長的資料塊 m1,m2,...,mi 塊長 s(儘可能大) ,其中 2^s<n
* 2:對應的密文是: ci = mi^e(mod n)
* 3:解密時作如下計算: mi = ci^d(mod n)
* ===================================================================
* RSA速度
* 由於進行的都是大數計算,使得RSA最快的情況也比DES慢上100倍,無論 是軟體還是硬體實現。
*
DES全稱為Data Encryption Standard,即資料加密標準
* 速度一直是RSA的缺陷。一般來說只用於少量資料 加密。
*檔名:RSAUtil.java<br>
*@author 董利偉<br>
*版本:<br>
*描述:<br>
*建立時間:2008-9-23 下午09:58:16<br>
*檔案描述:<br>
*修改者:<br>
*修改日期:<br>
*修改描述:<br>
*/
public class RSAUtil {
//金鑰對
private KeyPair keyPair = null;
/**
* 初始化金鑰對
*/
public RSAUtil(){
try {
this.keyPair = this.generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成金鑰對
* @return KeyPair
* @throws Exception
*/
private KeyPair generateKeyPair() throws Exception {
try {
//KeyPairGenerator 類用於生成公鑰和私鑰對。金鑰對生成器是使用 getInstance 工廠方法
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
//這個值關係到塊加密的大小,可以更改,但是不要太大,否則效率會低
final int KEY_SIZE = 1024;
//每個提供者都必須提供(並記錄)預設的初始化,以防客戶端沒有顯式初始化
keyPairGen.initialize(KEY_SIZE, new SecureRandom());//隨機源
KeyPair keyPair = keyPairGen.genKeyPair();
return keyPair;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 生成公鑰
* @param modulus
* @param publicExponent
* @return RSAPublicKey
* @throws Exception
*/
private RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) throws Exception {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
throw new Exception(ex.getMessage());
}
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent));
try {
return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
} catch (InvalidKeySpecException ex) {
throw new Exception(ex.getMessage());
}
}
/**
* 生成私鑰
* @param modulus
* @param privateExponent
* @return RSAPrivateKey
* @throws Exception
*/
private RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) throws Exception {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
throw new Exception(ex.getMessage());
}
RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent));
try {
return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
} catch (InvalidKeySpecException ex) {
throw new Exception(ex.getMessage());
}
}
/**
* 加密
* @param key 加密的金鑰
* @param data 待加密的明文資料
* @return 加密後的資料
* @throws Exception
*
* 用於將Cipher初始化為解密模式的常量
public final static int DECRYPT_MODE
用於將Cipher初始化為加密模式的常量
public final static int ENCRYPT_MODE
*/
public byte[] encrypt(Key key, byte[] data) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
//完成用於加密或是解密操作的初始化
cipher.init(Cipher.ENCRYPT_MODE, key);
//獲得加密塊大小,如:加密前資料為128個byte,而key_size=1024 加密塊大小為127 byte,加密後為128個byte;
//因此共有2個加密塊,第一個127 byte第二個為1個byte
int blockSize = cipher.getBlockSize();//獲得加密塊加密後塊大小
int outputSize = cipher.getOutputSize(data.length);//獲得輸出緩衝區位元組長度:
int leavedSize = data.length % blockSize;
int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
byte[] raw = new byte[outputSize * blocksSize];
int i = 0;
while (data.length - i * blockSize > 0) {
if (data.length - i * blockSize > blockSize)
// 按單部分操作加密或解密資料,或者結束一個多部分操作。
// 呼叫doFinal方法將會重置Cipher物件到使用init進行初始化時的狀態,就是說,Cipher物件被重置,使得可以進行更多資料的加密或解密,
cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
else
cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
//這裡面doUpdate方法不可用,檢視原始碼後發現每次doUpdate後並沒有什麼實際動作除了把byte[]放到ByteArrayOutputStream中
//,而最後doFinal的時候才將所有的byte[]進行加密,可是到了此時加密塊大小很可能已經超出了OutputSize所以只好用dofinal方法。
i++;
}
return raw;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 解密
* @param key 解密的金鑰
* @param raw 已經加密的資料
* @return 解密後的明文
* @throws Exception
*/
public byte[] decrypt(Key key, byte[] raw) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(cipher.DECRYPT_MODE, key);
int blockSize = cipher.getBlockSize();
//位元組陣列流:
ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
int j = 0;
while (raw.length - j * blockSize > 0) {
bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
j++;
}
return bout.toByteArray();
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 返回公鑰
* @return
* @throws Exception
*/
public RSAPublicKey getRSAPublicKey() throws Exception{
//獲取公鑰
RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic();
//獲取公鑰係數(位元組陣列形式)
byte[] pubModBytes = pubKey.getModulus().toByteArray();
//返回公鑰公用指數(位元組陣列形式)
byte[] pubPubExpBytes = pubKey.getPublicExponent().toByteArray();
//生成公鑰
RSAPublicKey recoveryPubKey = this.generateRSAPublicKey(pubModBytes,pubPubExpBytes);
return recoveryPubKey;
}
/**
* 獲取私鑰
* @return
* @throws Exception
*/
public RSAPrivateKey getRSAPrivateKey() throws Exception{
//獲取私鑰
RSAPrivateKey priKey = (RSAPrivateKey) keyPair.getPrivate();
//返回私鑰係數(位元組陣列形式)
byte[] priModBytes = priKey.getModulus().toByteArray();
//返回私鑰專用指數(位元組陣列形式)
byte[] priPriExpBytes = priKey.getPrivateExponent().toByteArray();
//生成私鑰
RSAPrivateKey recoveryPriKey = this.generateRSAPrivateKey(priModBytes,priPriExpBytes);
return recoveryPriKey;
}
}
測試
package org.yanzi.websafe;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.md5.util.MD5Util;
import org.rsa.util.RSAUtil;
public class TestCode {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
/****************************RSA加密解密測試********************************/
try {
RSAUtil rsa = new RSAUtil();
String str = "yanzi1225627";
RSAPublicKey pubKey = rsa.getRSAPublicKey();//返回公鑰
RSAPrivateKey priKey = rsa.getRSAPrivateKey();//獲取私鑰
//加密的時候使用公鑰,和需要加密文字的byte[]
byte[] enRsaBytes = rsa.encrypt(pubKey,str.getBytes());
String enRsaStr = new String(enRsaBytes, "UTF-8");
System.out.println("加密後==" + enRsaStr);
System.out.println("解密後==" + new String(rsa.decrypt(priKey, rsa.encrypt(pubKey,str.getBytes()))));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/************************************MD5加密測試*****************************/
// String srcString = "yanzi1225627";
// System.out.println("MD5加密後 = " + MD5Util.getMD5String(srcString));
}
}
package org.md5.util;
import java.security.MessageDigest;
public class MD5Util {
public final static String getMD5String(String s) {
char hexDigits[] = { '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
try {
byte[] btInput = s.getBytes();
//獲得MD5摘要演算法的 MessageDigest 物件
MessageDigest mdInst = MessageDigest.getInstance("MD5");
//使用指定的位元組更新摘要
mdInst.update(btInput);
//獲得密文
byte[] md = mdInst.digest(); // 這個方法是加密後返回的byte陣列
//把密文轉換成十六進位制的字串形式
int j = md.length;// 記錄md的長度
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // hexDigits 陣列中對應的十六進位制數放入str中
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
所謂流媒體是指採用流式傳輸的方式在Internet播放的媒體格式。 流媒體又叫流式媒體,它是指商家用一個視訊傳送伺服器把節目當成資料包發出,傳送到網路上。使用者通過解壓裝置對這些資料進行解壓後,節目就會像傳送前那樣顯示出來。
流媒體(Streaming Media)的出現極大地方便了人們的工作和生活。在地球的另一端,某大學的課堂上,某個教授正在興致盎然地傳授一門你喜歡的課程,想聽?太遠!放棄?可惜!沒關係,網路時代能滿足你的願望。在網路上找到該線上課程,課程很長,但沒關係,只管點選播放,教授的身影很快出現在螢幕上,課程一邊播放一邊下載,雖然遠在天涯,卻如親臨現場!除了遠端教育,流媒體在視訊點播、網路電臺、網路視訊等方面也有著廣泛的應用。
流媒體,又叫流式媒體,是邊傳邊播的媒體,是多媒體的一種。邊傳邊播是指媒體提供商在網路上傳輸媒體的"同時",使用者一邊不斷地接收並觀看或收聽被傳輸的媒體。"流"媒體的"流"指的是這種媒體的傳輸方式(流的方式),而並不是指媒體本身。
基本資訊
中文名稱
流媒體
外文名稱
Streaming media
目錄
1媒體技術
2基本介紹
3技術應用
4技術問題
5常用格式
6傳輸協議
7播放方式
摺疊編輯本段媒體技術
摺疊流式傳輸的基礎
在網路上傳輸音/視訊等多媒體資訊,目前主要有下載和流式傳輸兩種方案。A/V檔案一般都較大,所以需要的儲存容量也較大;同時由於網路頻寬的限制,下載常常要花數分鐘甚至數小時,所以這種處理方法延遲也很大。流式傳輸時,聲音、影像或動畫等時基媒體由音視訊伺服器向用戶計算機的連續、實時傳送,使用者不必等到整個檔案全部下載完畢,而只需經過幾秒或十數秒的啟動延時即可進行觀看。當聲音等時基媒體在客戶機上播放時,檔案的剩餘部分將在後臺從伺服器內繼續下載。流式不僅使啟動延時成十倍、百倍地縮短,而且不需要太大的快取容量。流式傳輸避免了使用者必須等待整個檔案全部從Internet上下載才能觀看的缺點。
流媒體指在Internet/Intranet中使用流式傳輸技術的連續時基媒體,如:音訊、視訊或多媒體檔案。流式媒體在播放前並不下載整個檔案,只將開始部分內容存入記憶體,流式媒體的資料流隨時傳送隨時播放,只是在開始時有一些延遲。流媒體實現的關鍵技術就是流式傳輸。
流式傳輸定義很廣泛,現在主要指通過網路傳送媒體(如視訊、音訊)的技術總稱。其特定含義為通過Internet 將影視節目傳送到PC機。實現流式傳輸有兩種方法:實時流式傳輸(Realtime streaming)和順序流式傳輸(progressive streaming)。一般說來,如視訊為實時廣播,或使用流式傳輸媒體伺服器,或應用如RTSP的實時協議,即為實時流式傳輸。如使用HTTP伺服器,檔案即通過順序流傳送。採用哪種傳輸方法依賴你的需求。當然,流式檔案也支援在播放前完全下載到硬碟。
摺疊順序流式傳輸
順序流式傳輸是順序下載,在下載檔案的同時使用者可觀看線上媒體,在給定時刻,使用者只能觀看已下載的那部分,而不能跳到還未下載的前頭部分,順序流式傳輸不象實時流式傳輸在傳輸期間根據使用者連線的速度做調整。由於標準的HTTP伺服器可傳送這種形式的檔案,也不需要其他特殊協議,它經常被稱作HTTP流式傳輸。順序流式傳輸比較適合高質量的短片段,如片頭、片尾和廣告,由於該檔案在播放前觀看的部分是無損下載的,這種方法保證電影播放的最終質量。這意味著使用者在觀看前,必須經歷延遲,對較慢的連線尤其如此。對通過調變解調器釋出短片段,順序流式傳輸顯得很實用,它允許用比調變解調器更高的資料速率建立視訊片段。儘管有延遲,畢竟可讓你釋出較高質量的視訊片段。順序流式檔案是放在標準HTTP或