Android 面試2018最新最全

劉國棟3364575發表於2018-05-17

為啥分享的文章不會同步更新??? 觀看效果不好,請檢視原文連結 blog.csdn.net/liu3364575/…

相信大家都有面試的經歷,相對比面試官的問的一些問題其實都是基礎的知識,但就是一些基礎的知識我們也不是很完美的回答出來,我們也知道現在的開發人員很多,一家公司一個崗位就會有很多的開發者投遞,在那麼多開發者中你如何讓面試官很深的認識你,給面試官一個很深的印象,能讓他在技術水平差不多的情況的下第一個想起的是你。 從這篇文章對整個面試中所問到的問題進行梳理,查缺補漏。

JAVA String,StringBuffer,StringBuilder區別? 一般回答是這樣的: String 字串常量 StringBuffer 字串變數(執行緒安全) StringBuilder 字串變數(非執行緒安全) 面試的時候千萬不要這麼說,這樣說會讓面試官你還只停留在會用的層面,不知道原理,你應該這麼說:

String,StringBuffer,StringBuilder最終底層儲存與操作的都是char陣列,但是String裡面的char陣列是final的,而StringBuffer,StringBuilder不是,也就是說,String是不可變的,想要新的字串只能重新生成String;而StringBuffer和StringBuilder只需要修改底層的char陣列就行,相對來說,開銷要小很多,String的大多數方法都是重新new一個新String物件返回,頻繁重新生成容易生成很多垃圾。

為什麼StringBuffer 是執行緒安全的? StringBuffer是執行緒安全的,StringBuilder是執行緒不安全的,因為StringBuffer的方法是加了synchronized鎖起來了的,而StringBuilder沒有

增刪比較多時用StringBuffer或StringBuilder(注意單執行緒與多執行緒)。

HashMap的實現機制? HashMap實際上是一個“連結串列雜湊”的資料結構,即陣列和連結串列的結合體。HashMap底層就是一個陣列結構,陣列中的每一項又是一個連結串列。當新建一個HashMap的時候,就會初始化一個陣列。

/**

  • The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry[] table;

static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; final int hash; …… } 1 2 3 4 5 6 7 8 9 10 11 12 可以看出,Entry就是陣列中的元素,每個 Map.Entry 其實就是一個key-value對,它持有一個指向下一個元素的引用,這就構成了連結串列。

HashMap如果Hash衝突了怎麼解決? 可以看下這篇文章 https://blog.csdn.net/qq_27093465/article/details/52269862

如何執行緒安全的使用HashMap? 無非就三種實現方式: 1)Hashtable 2)ConcurrentHashMap 3)Synchronized Map

//Hashtable Map<String, String> hashtable = new Hashtable<>();

//synchronizedMap Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());

//ConcurrentHashMap Map<String, String> concurrentHashMap = new ConcurrentHashMap<>(); 1 2 3 4 5 6 7 8 HashTable原始碼中是使用synchronized來保證執行緒安全的

public synchronized V get(Object key) { // 省略實現 } public synchronized V put(K key, V value) { // 省略實現 } 1 2 3 4 5 6 所以當一個執行緒訪問HashTable的同步方法時,其他執行緒如果也要訪問同步方法,會被阻塞住。舉個例子,當一個執行緒使用put方法時,另一個執行緒不但不可以使用put方法,連get方法都不可以,好霸道啊!!!so~~,效率很低,現在基本不會選擇它了。

ConcurrentHashMap(以下簡稱CHM)是JUC包中的一個類,Spring的原始碼中有很多使用CHM的地方。CHM的一些重要特性和什麼情況下應該使用CHM。需要注意的是,上面部落格是基於Java 7的,和8有區別,在8中CHM摒棄了Segment(鎖段)的概念,而是啟用了一種全新的方式實現,利用CAS演算法。

// synchronizedMap方法 public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { return new SynchronizedMap<>(m); } // SynchronizedMap類 private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable { private static final long serialVersionUID = 1978198479659022715L;

   private final Map<K,V> m;     // Backing Map
   final Object      mutex;        // Object on which to synchronize

   SynchronizedMap(Map<K,V> m) {
       this.m = Objects.requireNonNull(m);
       mutex = this;
   }

   SynchronizedMap(Map<K,V> m, Object mutex) {
       this.m = m;
       this.mutex = mutex;
   }

   public int size() {
       synchronized (mutex) {return m.size();}
   }
   public boolean isEmpty() {
       synchronized (mutex) {return m.isEmpty();}
   }
   public boolean containsKey(Object key) {
       synchronized (mutex) {return m.containsKey(key);}
   }
   public boolean containsValue(Object value) {
       synchronized (mutex) {return m.containsValue(value);}
   }
   public V get(Object key) {
       synchronized (mutex) {return m.get(key);}
   }

   public V put(K key, V value) {
       synchronized (mutex) {return m.put(key, value);}
   }
   public V remove(Object key) {
       synchronized (mutex) {return m.remove(key);}
   }
   // 省略其他方法
複製程式碼

} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 synchronizedMap從原始碼中可以看出呼叫synchronizedMap()方法後會返回一個SynchronizedMap類的物件,而在SynchronizedMap類中使用了synchronized同步關鍵字來保證對Map的操作是執行緒安全的。

HasMap為什麼會執行緒不安全? 個人覺得HashMap在併發時可能出現的問題主要是兩方面, 首先如果多個執行緒同時使用put方法新增元素,而且假設正好存在兩個put的key發生了碰撞(hash值一樣),那麼根據HashMap的實現,這兩個key會新增到陣列的同一個位置,這樣最終就會發生其中一個執行緒的put的資料被覆蓋。

第二就是如果多個執行緒同時檢測到元素個數超過陣列大小*loadFactor,這樣就會發生多個執行緒同時對Node陣列進行擴容,都在重新計算元素位置以及複製資料,但是最終只有一個執行緒擴容後的陣列會賦給table,也就是說其他執行緒的都會丟失,並且各自執行緒put的資料也丟失。

HashMap出現死迴圈的原因? 因為多執行緒會導致HashMap的Entry節點形成環鏈,這樣當遍歷集合時Entry的next節點用於不為空,從而形成死迴圈

Activity: 當面試官問你什麼Activity,你是不是會覺得一陣懵* ?

在日常應用中,Android是使用者互動的介面,他提供了一個介面,讓使用者進行點選,各種滑動操作等。

Activity的四種狀態: running / paused / stopped / killed running:點選螢幕,螢幕做出響應,處於activity棧頂的狀態 paused:失去焦點不能進行操作,或者是一個透明的在棧頂的Activity,注意並不是被銷燬了啊,它的成員資訊和變數都還在,當然還有另外一種狀態,當記憶體緊張的時候會北迴收掉 stopped:當一個Activity被另外一個Activity完全覆蓋的時候,被覆蓋的那個處於stopped狀態,不可見,成員資訊和變數都還在,如果記憶體緊張也是會被回收的 killed:已經被系統回收

生命週期: 啟動:onCreate >> onStart >> onResume 點選HOME鍵回到主介面:onPause >> onStop 再次回到遠Activity:onResrart>>onStart >> onResume 退出當前Activity:onPause >> onStop >> onDestroy

程式優先順序: 前臺 / 可見 /服務 /後臺 / 空 前臺:正在進行互動的Activity或者與前臺activity繫結的services 可見:一個activity可見但並處於前臺,不能點選 服務:在後臺開啟的服務 後臺:當一個Activity被點選home鍵,退居後臺,沒有=被回收 空 :不屬於前面四種程式的任意一種,處於快取的目的而保留

啟動模式: standard / singletop / singletask / singleinstance

如何配置Activity的啟動模式? 直接在AndroidManifest.xml配置的android:launchMode屬性為以上四種之一即可

standrd:標準模式,每次啟動都會重新建立一個activity例項加入到任務棧中,不會考慮是不是有此例項存在,不會複用,消耗記憶體資源

singletop :棧頂複用模式,只檢測任務棧棧頂,只有在棧頂的Activity不會被建立,就算是在第二位也是會被建立

singletask:棧內複用模式,也是一個單例模式,檢測整個任務棧,如果有並把在之上的例項全部移除掉,回掉onNewIntent方法

singleinstance:一個activity如果在整個系統中只存在一個例項,而且這個activity獨享整個任務棧

應用場景: singleTop:適合接收通知啟動的內容顯示頁面。例如,某個新聞客戶端的新聞內容頁面,如果收到10個新聞推送,每次都開啟一個新聞內容頁面是很煩人,另外,singleTop啟動模式適合於一些不常用的Activity頁面,比如“找回密碼”、“設定介面”等。

singleTask:最典型的樣例就是應用中展示的主頁(Home頁),假設使用者在主頁跳轉到其他頁面,執行多次操作後想返回到主頁,假設不使用SingleTask模式,在點選返回的過程中會多次看到主頁,這明顯就是設計不合理了。

singleInstance:比如說,使用微信調起自己的客戶端某個頁面,不做任何處理的情況下,按下回退或者當前Activity.finish(),頁面不會停留在自己的客戶端而是返回到微信的客戶端頁面。但是如果這個頁面的啟動模式設定為singleTask,當按下返回鍵或者Activity。finish(),頁面都會停留在自己的客戶端(因為自己的Application回退棧不為空),這明顯不符合邏輯的。產品的要求是,回退必須回到微信客戶端,而且要保證不殺死自己的Application.因此,顯然其他的其他的啟動模式都不具備這個功能。

使用方法: 方法一:

android:launchMode="standard|singleInstance|single Task|singleTop 1 使用android:launchMode=”standard|singleInstance|single Task|singleTop”來控制Acivity任務棧。

方法二:

Intent Flags: Intent intent=new Intent(); intent.setClass(MainActivity.this, MainActivity2.class); intent.addFlags(Intent. FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); 1 2 3 4 5 Flags有很多,比如: Intent.FLAG_ACTIVITY_NEW_TASK 相當於singleTask Intent. FLAG_ACTIVITY_CLEAR_TOP 相當於singleTop

scheme(sigeimo)跳轉協議: android中的scheme是一種頁內跳轉協議,是一種非常好的實現機制,通過定義自己的scheme協議,可以非常方便的在app內部跳轉到各個介面;通過scheme協議,伺服器可以定製化告訴app跳轉到哪個頁面,可以通過通知欄訊息欄定製化跳轉頁面,也可以通過H5頁面中定義連線跳轉指定的activity頁面等

應用場景: 1 服務端下發一個url路徑,客戶端根據下方的路徑跳轉到指定介面 2 從H5跳轉到App內 3 App根據url挑戰到另外的一個App

兩個 Activity 之間跳轉時必然會執行的是哪幾個方法? 一般情況下比如說有兩個 activity,分別叫 A,B,當在 A 裡面啟用 B 元件的時候, A 會呼叫 onPause()方法,然後 B 呼叫 onCreate() ,onStart(), onResume()。 這個時候 B 覆蓋了窗體, A 會呼叫 onStop()方法. 如果 B 是個透明的,或者 是對話方塊的樣式, 就不會呼叫 A 的 onStop()方法。

橫豎屏切換時 Activity 的生命週期? 此時的生命週期跟清單檔案裡的配置有關係。 1.不設定 Activity 的 android:configChanges 時,切屏會重新呼叫各個生命週期預設首先銷燬當前 activity,然後重新載入。 2.設定 Activity android:configChanges=”orientation|keyboardHidden|screenSize”時,切 屏不會重新呼叫各個生命週期,只會執行 onConfigurationChanged 方法。 通常在遊戲開發, 螢幕的朝向都是寫死的。

如何將一個 Activity 設定成視窗的樣式? 只需要給我們的 Activity 配置如下屬性即可。 android:theme=”@android:style/Theme.Dialog”

Android 中的 Context, Activity,Appliction 有什麼區別? 相同:Activity 和 Application 都是 Context 的子類。 Context 從字面上理解就是上下文的意思,在實際應用中它也確實是起到了管理 上下文環境中各個引數和變數的總用,方便我們可以簡單的訪問到各種資源。

不同:維護的生命週期不同。Context 維護的是當前的 Activity 的生命週期, Application 維護的是整個專案的生命週期。使用 context 的時候,小心記憶體洩露,防止記憶體洩露,注意一下幾個方面: 1)不要讓生命週期長的物件引用 activity context,即保證引用 activity 的對 象要與 activity 本身生命週期是一樣的。 2 )對於生命週期長的物件,可以使用 application,context。 3 )避免非靜態的內部類,儘量使用靜態類,避免生命週期問題,注意內部類 對外部物件引用導致的生命週期變化。

如何獲取當前螢幕Activity的物件? 使用ActivityLifecycleCallbacks 應用場景:可以利用ActivityLifecycleCallbacks 做一些資料埋點,統計之類的應用,對其統一做處理。這樣對減少Activity的程式碼入侵。儘量簡化和模組化的注入生命週期方法。

ActivityLifecycleCallbacks 是什麼? 見名知意,Activity生命週期回撥,Application通過此介面提供了一套回撥方法,用於讓開發者對Activity的生命週期事件進行集中處理。 但是這個要求API 14+ (Android 4.0+)以上使用,幸好我們這個最低支援,滿足需求。

ActivityLifecycleCallbacks 怎麼使用? 重寫Application的onCreate()方法,或在Application的無參構造方法內,呼叫Application.registerActivityLifecycleCallbacks()方法,並實現ActivityLifecycleCallbacks介面

知道onNewIntent嗎? 如果IntentActivity處於任務棧的頂端,也就是說之前開啟過的Activity,現在處於onPause、onStop 狀態的話,其他應用再傳送Intent的話,執行順序為: onNewIntent,onRestart,onStart,onResume。

除了用Intent 去啟動一個Activity,還有其他方法嗎? 使用adb shell am 命令

1)ams啟動一個activity adb shell am start com.example.fuchenxuan/.MainActivity 2)am傳送一個廣播,使用action adb shell am broadcast -a magcomm.action.TOUCH_LETTER

Android Service與Activity之間通訊的幾種方式? 通過Binder物件 1)當Activity通過呼叫bindService(Intent service, ServiceConnection conn,int flags),得到一個Service的一個物件,通過這個物件我們可以直接訪問Service中的方法。 2)通過Broadcast Receiver(廣播)的形式 3)EventBus 是一個Android開源事件匯流排框架 後面我們會有專門的講解。

TaskAffinity 是什麼? 標識Activity任務棧名稱的屬性:TaskAffinity,預設為應用包名

如果新Activity是透明主題時,舊Activity會不會走onStop? 不會!

android完全退出應用程式的三種方式?

第一種方法:首先獲取當前程式的id,然後殺死該程式。 建議使用這種方式

android.os.Process.killProcess(android.os.Process.myPid()) 1 第二種方法:終止當前正在執行的Java虛擬機器,導致程式終止

System.exit(0); 1 2 第三種方法:強制關閉與該包有關聯的一切執行

ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
manager.restartPackage(getPackageName()); 1 2 使用這種方式關閉應用程式需要加上許可權

1 介紹下Android應用程式啟動過程 1)Launcher通過Binder程式間通訊機制通知ActivityManagerService,它要啟動一個Activity;

2)ActivityManagerService通過Binder程式間通訊機制通知Launcher進入Paused狀態;

3)Launcher通過Binder程式間通訊機制通知ActivityManagerService,它已經準備就緒進入Paused狀態,於是ActivityManagerService就建立一個新的程式,用來啟動一個ActivityThread例項,即將要啟動的Activity就是在這個ActivityThread例項中執行;

4)ActivityThread通過Binder程式間通訊機制將一個ApplicationThread型別的Binder物件傳遞給ActivityManagerService,以便以後ActivityManagerService能夠通過這個Binder物件和它進行通訊;

5)ActivityManagerService通過Binder程式間通訊機制通知ActivityThread,現在一切準備就緒,它可以真正執行Activity的啟動操作了。 如果想深入研究的可以檢視羅昇陽大神的部落格https://blog.csdn.net/luoshengyang/article/details/6689748

Fragment: 三個小問題看你能完美解答幾個 1 Fragment為什麼被稱為第五大元件? 2 Fragment的生命週期 ? 3 Fragment之間的通訊 ?

1 Fragment為什麼被稱為第五大元件? 首先Fragment的使用頻率並不低於其他四大元件,他有自己的生命週期,同時可以動態靈活的載入到Activity中,所以說Fragment可以被稱為第五大元件

載入到Activity的兩種方式: 1)新增Fragment到Activity的佈局檔案當中,也叫靜態載入,name屬性哦 2)動態的在activity中新增fragment ,也叫動態載入 步驟: FragmentManage用來管理所有要啟動的fragment,並用FragmentTransaction新增和替換相對應的fragment,並用容器資源來作為標誌位來設定fragment所要顯示到activity當中的位置,最後提交commit方法 (1)建立待新增的碎片例項 (2)獲取FragmentManager,在活動中可以直接通過呼叫 getSupportFragmentManager()方法得到。 (3)開啟一個事務,通過呼叫beginTransaction()方法開啟。 (4)向容器內新增或替換碎片,一般使用repalce()方法實現,需要傳入容器的id和待新增的碎片例項。 (5)提交事務,呼叫commit()方法來完成。

FragmentPageAdapter與FragmentStatePageAdapter的區別? 1 FragmentStatePageAdapter適合介面多,FragmentPageAdapter適合介面少 2 FragmentStatePageAdapter比FragmentPageAdapter更節省記憶體 原始碼分析: FragmentPageAdapter適用於頁面較少的情況下,因為只對ui分離並沒有回收記憶體, 因為原始碼裡面destoryItem是detach方法()只是對fragment和activity的ui脫離開來,並不回收記憶體 FragmentStatePageAdapter用於介面較多的情況下,介面多也就意味著更加的消耗記憶體,FragmentStatePageAdapter在每次切換fragment的時候,他是回收記憶體的,因為原始碼裡面destoryItem的remove方法真正的釋放的記憶體

Fragement的生命週期: onAttach >> onCreate >> onCreateView >> onActivityCreated >>onStart >> onResume >> onPause >> onStop >> onDestoryView >> onDestory >>onDetach 很顯然你要是像背書似的將上面的生命週期說一遍,你認為你跟其他的競爭者有什麼優勢?

你應該將Activity與Fragment的生命週期結合起來說: 首先,呼叫Activity的onCreate方法 當Fragment建立的時候會執行onAttach,是在Activity與fragment關聯之後呼叫的 在之後執行onCreate方法,也就是在fragment建立的時候呼叫,注意,此時的activity還沒有建立完畢 在呼叫fragment的onCreateView方法,系統首次繪製使用者介面的時候呼叫 呼叫fragment的onActivityCreated方法,也就是activity被渲染繪製成功後呼叫, 現在是要呼叫activity的onStart的方法,當activity可見之後呼叫, fragment的onStart方法,表示fragment也可見了 接著呼叫activity的onResume的方法,表示當前activity可以與使用者互動了,當activity可見之後呼叫 fragment的onResume方法,表示當前fragment也可以與使用者進行互動操作了,以上步驟完成了從建立到互動的所有步驟

在之後,Fragment的onPause Activity的onPause Fragment的onStop Activity的onStop 現在來到了Frament的onDestoryView方法,表示當前fragment不在被使用,介面銷燬,緊接著來到onDestory方法,表示fragment已經銷燬了,最後呼叫onDetach來解除與activity的聯絡 最後呼叫Activity的onDestory方法

Fragment通訊: 1)在Fragment中呼叫Activity的方法 geyActivity 2)在Activity中呼叫Fragment的方法 介面回撥(在Fragment建立介面,在Activity實現) 3)在Fragment呼叫Fragment的方法 getActivity.findFragmentById 或者廣播

Fragment的replace、add、remove?

add:將一個fragment例項新增到Activity的最上層,一般在使用add的時候會配合 hide,show一起使用,為了避免Fragment的重複建立節省資源。 remove:將fragment從fragment佇列中刪除 replace:替換fragment的例項,replace是FragmentManager的方法

commitAllowingStateLoss與commit的區別? Activity被系統回收(介面已經不存在了),為了能在下次開啟的時候恢復原來的樣子,系統為我們儲存介面的所有狀態,這個時候再去修改介面理論上肯定是不被允許的,為了避免這種異常可以使用:

transaction.commitAllowingStateLoss(); 1 來提交新增Fragment到Activity的事務,與commit()不同的是使用這種方法允許丟失介面的狀態和資訊。

ViewPager與Fragment結合使用時的懶載入問題? 所謂的 “懶載入” 就是資料只有在Fragment對於使用者可見的時才進行載入,我們需要判定Fragment在什麼時候是處於可見的狀態。一般我們通常是通過Fragment中的生命週期方法onResume來判斷Fragment是否可見,但是由於ViewPager預載入的特性,Fragment即便不可見也會執行onResume方法,可以通過setUserVisibleHint()方法來進行判斷:

什麼時候被呼叫?

當fragment被建立的時,setUserVisibleHint(boolean isVisibleToUser)方法會被呼叫,且傳入引數值為false。

當fragment可見時,setUserVisibleHint(boolean isVisibleToUser)方法會被呼叫,且傳入引數值為true。

當fragment由 可見 -> 不可見 時,setUserVisibleHint(boolean isVisibleToUser)方法會被呼叫,且傳入引數值為false。 1 2 3 4 5 所以我們只需要當setUserVisibleHint(boolean isVisibleToUser)方法中的 isVisibleToUser 引數的值為true的時候我們才開始進行資料的載入就可以了。

但是有一點需要需要注意的是 setUserVisibleHint(boolean isVisibleToUser)方法在Fragment的生命週期方法onCreate 之前呼叫的,也就是說他並不在Fragment的生命週期中。既然是在 onCreate 方法之前被呼叫,這樣就存在許多不確定因素,如果Fragmnet的View還沒有完成初始化之前,就在setUserVisibleHint()方法中進行UI的操作,這樣顯然會導致空指標的出現。因此我們需要對Fragment建立的View進行快取,確保快取的View不為空的情況下我們才可以在setUserVisibleHint方法中進行UI操作。

Service Service是什麼? 四大元件之一,可以在後臺處理一些耗時的邏輯,也可以執行某些長時間執行的任務,而且看不到介面,包括在程式退出的時候依然能在繼續執行

Service與Broadcastrecevier有一個共同點,都是執行在主執行緒當中,都不能進行長耗時操作

Service與Thread的區別? Thread程式最小的執行單元,Thread可以進行非同步操作,相對獨立;而Service是Android的一種機制,如果是本地的Service,依賴與它所在的主執行緒之上,相比Thread沒有那麼獨立

為什麼要用Service而不是Thread呢? Thread的執行是獨立於Activity的,也就是當一個Activity被finish之後,如果沒有主動停止Thread或者Thread中的run沒有執行完畢時那麼這個執行緒會一直執行下去。因此這裡會出現一個問題:當 Activity 被 finish 之後,你不再持有該 Thread 的引用。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制。

Service 是否在 main thread 中執行, service 裡面是否 能執行耗時的操作? 預設情況,如果沒有顯示的指 servic 所執行的程式, Service 和 activity 是運 行在當前 app 所在程式的 main thread(UI 主執行緒)裡面。 service 裡面不能執行耗時的操作(網路請求,拷貝資料庫,大檔案 ) 特殊情況 ,可以在清單檔案配置 service 執行所在的程式 ,讓 service 在另 外的程式中執行

1 2 3 Service 裡面可以彈吐司麼? 可以的。彈吐司有個條件就是得有一個 Context 上下文,而 Service 本身就是 Context 的子類,因此在 Service 裡面彈吐司是完全可以的。比如我們在 Service 中完成下載任務後可以彈一個吐司通知使用者

Service 的 onStartCommand 方法有幾種返回值?各代表什麼意思?

有四種返回值,不同值代表的意思如下: START_STICKY:如果 service 程式被 kill 掉,保留 service 的狀態為開始狀態,但不保留遞送的 intent 物件。隨 後系統會嘗試重新建立 service,由於服務狀態為開始狀態,所以建立服務後一定會呼叫 onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到 service,那麼引數 Intent 將為 null。

START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完 onStartCommand 後,服務被異常 kill 掉,系統不會自動重啟該服務。

START_REDELIVER_INTENT:重傳 Intent。使用這個返回值時,如果在執行完 onStartCommand 後,服務被異 常 kill 掉,系統會自動重啟該服務,並將 Intent 的值傳入。

START_STICKY_COMPATIBILITY:START_STICKY 的相容版本,但不保證服務被 kill 後一定能重啟。

Service 的 onRebind(Intent)方法在什麼情況下會執行? 如果在 onUnbind()方法返回 true 的情況下會執行,否則不執行。

Activity 呼叫 Service 中的方法都有哪些方式? Binder: 通過 Binder 介面的形式實現,當 Activity 繫結 Service 成功的時候 Activity 會在 ServiceConnection 的類 的 onServiceConnected()回撥方法中獲取到 Service 的 onBind()方法 return 過來的 Binder 的子類,然後通過物件呼叫方法。 Aidl: aidl 比較適合當客戶端和服務端不在同一個應用下的場景。

Messenger: 它引用了一個Handler物件,以便others能夠向它傳送訊息(使用mMessenger.send(Message msg)方法)。該類允許跨程式間基於Message的通訊(即兩個程式間可以通過Message進行通訊),在服務端使用Handler建立一個Messenger,客戶端持有這個Messenger就可以與服務端通訊了。一個Messeger不能同時雙向傳送,兩個就就能雙向傳送了

如何提高service的優先順序? 可以用 setForeground(true) 來設定 Service 的優先順序。

service 如何定時執行? 使用AlarmManager,根據AlarmManager的工作原理,alarmmanager會定時的發出一條廣播,然後在自己的專案裡面註冊這個廣播,重寫onReceive方法,在這個方法裡面啟動一個service,然後在service裡面進行網路的訪問操作,當獲取到新訊息的時候進行推送,同時再設定一個alarmmanager進行下一次的輪詢,當本次輪詢結束的時候可以stopself結束改service。這樣即使這一次的輪詢失敗了,也不會影響到下一次的輪詢。這樣就能保證推送任務不會中斷

在 service 的生命週期方法 onstartConmand()可不可以執行網路操作?如何在 service 中執行網路操作? 可以直接在 Service 中執行網路操作,在 onStartCommand()方法中可以執行網路操作

Service 和 Activity 在同一個執行緒嗎? 對於同一 app 來說預設情況下是在同一個執行緒中的,main Thread (UI Thread)。

什麼是 IntentService?有何優點? 會建立獨立的工作執行緒來處理所有的 Intent 請求; 會建立獨立的工作執行緒來處理 onHandleIntent()方法實現的程式碼,無需 處理多執行緒問題; 所有請求處理完成後,IntentService 會自動停止,無需呼叫 stopSelf()方法 停止 Service; 為 Service 的 onBind()提供預設實現,返回 null; 為 Service 的 onStartCommand 提供預設實現,將請求 Intent 新增到佇列 中;

Activity 怎麼和 Service 繫結,怎麼在 Activity 中啟動自 己對應的 Service? Activity 通過 bindService(Intent service, ServiceConnection conn, int flags)跟 Service 進行繫結,當繫結成功的時候 Service 會將代理物件通過回撥 的形式傳給 conn,這樣我們就拿到了 Service 提供的服務代理物件。 在 Activity 中可以通過 startService 和 bindService 方法啟動 Service。一 般情況下如果想獲取 Service 的服務物件那麼肯定需要通過 bindService()方 法,比如音樂播放器,第三方支付等。如果僅僅只是為了開啟一個後臺任務那麼 可以使用 startService()方法。

IntentService 適用場景 IntentService 內建的是 HandlerThread 作為非同步執行緒,每一個交給 IntentService 的任務都將以佇列的方式逐個被執行到,一旦佇列中有某個任務執行時間過長,那麼就會導致後續的任務都會被延遲處理 正在執行的 IntentService 的程式相比起純粹的後臺程式更不容易被系統殺死,該程式的優先順序是介於前臺程式與純後臺程式之間的

Broadcast Receiver 廣播的定義: 在Android中,Broadcast是一種廣泛運用在程式之間的傳輸資訊的機制,Android中我們要傳送的廣播內容中是一個Intent,這個Intent可以攜帶我們要傳輸的資料

廣播的使用場景: 1)在同一個App具有多個程式的不同元件之間的訊息傳遞 2)不同app之間的訊息通訊

廣播的種類: 1)普通廣播 2)系統廣播 (sendOrderedBroadcast) 3 ) 本地廣播(只在app內部傳播)

不同註冊方式廣播接收器回撥onReceive(context, intent)中context型別不一致? manifest靜態註冊的ContextReceiver,回撥onReceive(context, intent)中的context是ReceiverRestrictedContext;

程式碼動態註冊的ContextReceiver,回撥onReceive(context, intent)中的context是Activity Context;

內部實現機制? 1)自定義廣播接受者Broadcast Receiver,並複寫onRecvice方法 2)通過Binder機制像AMS進行註冊 3)廣播傳送者通過Binder機制像AMS傳送廣播 4)AMS查詢符合相應條件(IntentFilter/Permission等)的Broadcast Receiver,將廣播傳送到Broadcast Receiver相應的訊息佇列迴圈當中去(一般是Activity中) 5)訊息迴圈拿到此廣播,回撥Broadcast Receiver中的onReceive方法、

如何讓自己的廣播只讓指定的 app 接收? 通過自定義廣播許可權來保護自己發出的廣播。 在清單檔案裡receiver必須有這個許可權才能收到廣播。 首先,需要定義許可權: 然後,宣告許可權: 這時接收者就能收到傳送的廣播。

廣播的優先順序對無序廣播生效嗎? 生效的**

動態註冊的廣播優先順序誰高? 誰先註冊誰優先順序高。

如何判斷當前 BroadcastReceiver 接收到的是有序廣播還是無序廣播? 在 BroadcastReceiver 類中 onReceive()方法中,可以呼叫 boolean b = isOrderedBroadcast();判斷接收到的廣播是否為有序廣播。

粘性廣播有什麼作用?怎麼使用?

粘性廣播主要為了解決,在傳送完廣播之後,動態註冊的接收者,也能夠收到廣播。舉個例子首先傳送一廣播,我的接收者是通過程式中的某個按鈕動態註冊的。如果不是粘性廣播,我註冊完接收者肯定無法收到廣播了。這是通過傳送粘性廣播就能夠在我動態註冊接收者後也能收到廣播。

網路 HttpConnection 與HttpURLConnection的區別? 在Android 2.2版本之前,HttpClient擁有較少的bug,因此使用它是最好的選擇 2.3之後使用HttpURLConnection,它的API簡單,體積較小,壓縮和快取機制可以有效地減少網路訪問的流量,在提升速度和省電方面也起到了較大的作用,利於維護與優化

TCP與UDP的區別? 1)基於連線與無連線 2)對系統資源的要求(TCP較多,UDP較少) 3)UDP程式結構較簡單一些 4)流模式與資料包模式 5)TCP保證資料的順序性以及正確性,UDP不能保證,可能存在丟包

Socket: soket是套接字,我們可以先在服務端初始化ServerSocket,然後對指定的埠進行繫結與監聽,通過呼叫accept方法與getInputstream方法進行等待客戶端的連線與資料的接收。現在客戶端進行建立socket物件傳入ip和埠號,通過getOutputStream進行資料的輸入,並且制定結束字元,否則服務端會一直處於阻塞狀態。

socket.close() 與socket.shutdownOutput()的區別? 1)在客戶端或者服務端通過socket.shutdownOutput()都是單向關閉的,即關閉客戶端的輸出流並不會關閉服務端的輸出流,所以是一種單方向的關閉流; 2)通過socket.shutdownOutput()關閉輸出流,但socket仍然是連線狀態,連線並未關閉 3)如果直接關閉輸入或者輸出流,即:in.close()或者out.close(),會直接關閉socket

socket長連線? 在不關閉流的情況下,開啟迴圈執行緒進行定時傳送與服務端約定的心跳包資料

Handler 什麼是Handler? handler通過傳送和處理Message和Runnable物件來關聯相對應執行緒的MessageQueue 作用: 1)可以讓對應的Message和Runnable在未來的某個時間點進行相應處理 2)讓自己想要處理的耗時操作放在子執行緒,讓更新ui的操作放在主執行緒

Handler傳送訊息的幾種方式? 1)post(Runnable)延遲訊息處理 2)sendMessage(message)

PS:post(Runnable)的Runnable中可以更新UI嗎? 可以的 ,因為經常會post一個Runnable,處理的程式碼直接寫在Runnable的run方法中,其實就是將這個Runnable傳送到Handler所線上程(一般是主執行緒)的訊息佇列中。sendMessage方法主執行緒處理方法一般則是寫在handleMessage中

Handler處理訊息有哪幾種方式?

直接看dispatchMessage()原始碼

//1. post()方法的最終處理方法 private static void handleCallback(Message message) { message.callback.run(); } //2. sendMessage()方法的最終處理方法 public void handleMessage(Message msg) { } 1 2 3 4 5 6 7 8 Handler、Message、Looper、MessageQueue

Message(訊息) 定義:Handler接收和處理的訊息物件(Bean物件) 作用:通訊時相關資訊的存放和傳遞

ThreadLocal 定義:ThreadLocal是執行緒內部的儲存類,通過它可以實現在每個執行緒中儲存自己的私有資料。即資料儲存以後,只能在指定的執行緒中獲取這個儲存的物件,而其它執行緒則不能獲取到當前執行緒儲存的這個物件。 作用:負責儲存和獲取本執行緒的Looper

MessageQueue(訊息佇列) 定義:採用單連結串列的資料結構來儲存訊息列表 作用:用來存放通過Handler發過來的Message,按照先進先出執行

Handler(處理者) 定義:Message的主要處理者 作用:負責傳送Message到訊息佇列&處理Looper分派過來的Message

Looper(迴圈器) 定義:扮演Message Queue和Handler之間橋樑的角色 作用: 訊息迴圈:迴圈取出Message Queue的Message 訊息派發:將取出的Message交付給相應的Handler

首先知道定義人,然後結合關係說清楚一些在加上HandlerThread基本上Handler這塊是沒什麼大問題了

Message、Handler、MessageQueue、Looper的之間的關係? 首先,是這個MessagQueue,MessageQueue是一個訊息佇列,它可以儲存Handler傳送過來的訊息,其內部提供了進隊和出隊的方法來管理這個訊息佇列,其出隊和進隊的原理是採用單連結串列的資料結構進行插入和刪除的,即enqueueMessage()方法和next()方法。這裡提到的Message,其實就是一個Bean物件,裡面的屬性用來記錄Message的各種資訊。 然後,是這個Looper,Looper是一個迴圈器,它可以迴圈的取出MessageQueue中的Message,其內部提供了Looper的初始化和迴圈出去Message的方法,即prepare()方法和loop()方法。在prepare()方法中,Looper會關聯一個MessageQueue,而且將Looper存進一個ThreadLocal中,在loop()方法中,通過ThreadLocal取出Looper,使用MessageQueue的next()方法取出Message後,判斷Message是否為空,如果是則Looper阻塞,如果不是,則通過dispatchMessage()方法分發該Message到Handler中,而Handler執行handlerMessage()方法,由於handlerMessage()方法是個空方法,這也是為什麼需要在Handler中重寫handlerMessage()方法的原因。這裡要注意的是Looper只能在一個執行緒中只能存在一個。這裡提到的ThreadLocal,其實就是一個物件,用來在不同執行緒中存放對應執行緒的Looper。 最後,是這個Handler,Handler是Looper和MessageQueue的橋樑,Handler內部提供了傳送Message的一系列方法,最終會通過MessageQueue的enqueueMessage()方法將Message存進MessageQueue中。我們平時可以直接在主執行緒中使用Handler,那是因為在應用程式啟動時,在入口的main方法中已經預設為我們建立好了Looper。

Handler引起的記憶體洩漏以及解決辦法?

HandlerThread作用 當系統有多個耗時任務需要執行時,每個任務都會開啟一個新執行緒去執行耗時任務,這樣會導致系統多次建立和銷燬執行緒,從而影響效能。為了解決這一問題,Google提供了HandlerThread,HandlerThread是線上程中建立一個Looper迴圈器,讓Looper輪詢訊息佇列,當有耗時任務進入佇列時,則不需要開啟新執行緒,在原有的執行緒中執行耗時任務即可,否則執行緒阻塞。

HandlerThread是什麼?有哪些特點

1)HandlerThread本質上是一個執行緒類,它繼承了Thread 2)HandlerThread有自己內部Looper物件,可以進行Looper迴圈 3)通過獲取HandlerThread的looper物件傳遞給Handler物件,可以在handlerMessage方法中執行非同步任務 4)優點是不會阻塞,減少對效能的消耗,缺點是不能同時進行多工的處理,需要等待進行處理 5)與執行緒池注重併發不同,HandlerThread是一個序列佇列,HandlerThread背後只有一個執行緒

子執行緒為什麼不能開啟handler? handler在呼叫sendMessage或者post(Runnable)的時候都需要一個MessageQueue 訊息佇列來儲存傳送的訊息,而預設子執行緒中是沒有開啟Looper輪循器的,而訊息佇列又是由Looper來進行管理的,所以是沒有辦法開啟的, 如果子執行緒想要開啟,需要初始化looper,並且呼叫loop.loopers開啟一個迴圈

如果簡歷中寫了AsyncTask,就看一下,沒寫的直接跳過上Rxjava

AsyncTask是什麼 AsyncTask是一種輕量級的非同步任務類,它可以線上程池中執行後臺任務,然後把執行的進度和最終結果傳遞給主執行緒並主執行緒中更新UI,通過AsyncTask可以更加方便執行後臺任務以及在主執行緒中訪問UI,但是AsyncTask並不適合進行特別耗時的後臺任務,對於特別耗時的任務來說,建議使用執行緒池。

AsyncTask使用方法 三個引數 Params:表示後臺任務執行時的引數型別,該引數會傳給AysncTask的doInBackground()方法 Progress:表示後臺任務的執行進度的引數型別,該引數會作為onProgressUpdate()方法的引數 Result:表示後臺任務的返回結果的引數型別,該引數會作為onPostExecute()方法的引數 五個方法 onPreExecute():非同步任務開啟之前回撥,在主執行緒中執行 doInBackground():執行非同步任務,線上程池中執行 onProgressUpdate():當doInBackground中呼叫publishProgress時回撥,在主執行緒中執行 onPostExecute():在非同步任務執行之後回撥,在主執行緒中執行 onCancelled():在非同步任務被取消時回撥

AsyncTask引起的記憶體洩漏 原因:非靜態內部類持有外部類的匿名引用,導致Activity無法釋放 解決: AsyncTask內部持有外部Activity的弱引用 AsyncTask改為靜態內部類 Activity的onDestory()中呼叫AsyncTask.cancel()

結果丟失 螢幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新建立,之前執行的AsyncTask會持有一個之前Activity的引用,這個引用已經無效,這時呼叫onPostExecute()再去更新介面將不再生效。

AsyncTask並行or序列 AsyncTask在Android 2.3之前預設採用並行執行任務,AsyncTask在Android 2.3之後預設採用序列執行任務 如果需要在Android 2.3之後採用並行執行任務,可以呼叫AsyncTask的executeOnExecutor();

AsyncTask內部的執行緒池 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; sDefaultExecutor是SerialExecutor的一個例項,而且它是個靜態變數。也就是說,一個程式裡面所有AsyncTask物件都共享同一個SerialExecutor物件。

ListView+RecyclerView 既然RecyclerView在很多方面能取代ListView,Google為什麼沒把ListView劃上一條過時的橫線? ListView採用的是RecyclerBin的回收機制在一些輕量級的List顯示時效率更高

ListView怎麼和ScrollView相容? 方法一:重寫ListView, 覆蓋onMeasure()方法 方法二:動態設定listview的高度,不需要重寫ListView 方法三:在xml檔案中,直接將Listview的高度寫死

listview怎麼優化? 1)、convertView複用,對convetView進行判空,當convertView不為空時重複使用,為空則初始化,從而減少了很多不必要的View的建立 2)定義一個ViewHolder,封裝Listview Item條目中所有的元件,將convetView的tag設定為ViewHolder,不為空時通過ViewHolder的屬性獲取對應元件即可 3)、當ListView載入資料量較大時可以採用分頁載入和圖片非同步載入

上拉載入和下拉重新整理怎麼實現? 實現OnScrollListener 介面重寫onScrollStateChanged 和onScroll方法, 使用onscroll方法實現”滑動“後處理檢查是否還有新的記錄,如果有,呼叫 addFooterView,新增記錄到adapter, adapter調notifyDataSetChanged 更新資料;如果沒有記錄了,把自定義的mFooterView去掉。使用onScrollStateChanged可以檢測是否滾到最後一行且停止滾動然後執行載入

listview失去焦點怎麼處理? 在listview子佈局裡面寫,可以解決焦點失去的問題 android:descendantFocusability=”blocksDescendants”

ListView圖片非同步載入實現思路? 1.先從記憶體快取中獲取圖片顯示(記憶體緩衝) 2.獲取不到的話從SD卡里獲取(SD卡緩衝,,從SD卡獲取圖片是放在子執行緒裡執行的,否則快速滑屏的話會不夠流暢) 3.都獲取不到的話從網路下載圖片並儲存到SD卡同時加入記憶體並顯示(視情況看是否要顯示)

你知道Listview裡有Button就點不動了你知道嗎? 原因是button強制獲取了item的焦點,只要設定button的focusable為false即可。

listview分頁載入的步驟? 通常實現分頁載入有兩種方式,一種是在ListView底部設定一個按鈕,使用者點選即載入。另一種是當使用者滑動到底部時自動載入。 當使用者滑動到底部時自動載入實現思路: 實現OnScrollListener 介面重寫onScrollStateChanged 和onScroll方法,使用onscroll方法實現”滑動“後處理檢查是否還有新的記錄,如果有,新增記錄到adapter, adapter呼叫 notifyDataSetChanged 更新資料;如果沒有記錄了,則不再載入資料。使用onScrollStateChanged可以檢測是否滾到最後一行且停止滾動然後執行載入.

ViewHolder內部類非得要宣告成static的呢? 因為非靜態成員類的例項會包含一個額外的指向外圍物件的引用,儲存這份引用要消耗時間和空間,並且導致外圍類例項符合垃圾回收時仍然被保留。如果沒有外圍例項的情況下,也需要分配例項,就不能使用非靜態成員類,因為非靜態成員類的例項必須要有一個外圍例項。

ScrollView巢狀ListView和GridView衝突的方法 重寫ListView的onMeasure方法,來自定義高度:

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } 1 2 3 4 5 BaseAdapter四個關鍵方法的含義? 1) getCount() 返回資料來源中資料的個數,如果該方法的返回值為0,那麼介面卡就不用生成佈局物件了,提高了程式效能 2)getItem(int position) 根據位置返回資料項 3)getItemId(int position) 返回資料項的位置 4)getView(int position, View convertView, ViewGroup parent)

RecyclerView滑動刪除原理實現? 兩種方法: 一種就是通過重寫RecyclerView的onTouchEvent()方法來檢測手勢的變化實現的,大致的流程如下: 1、根據手指觸控的座標點找到對應Item的ViewHolder,進而得到相應的Item佈局View。 2、手指繼續移動,在條件滿足的情況下,通過scrollBy()使Item佈局View內容跟隨手指一起移動,當然要注意邊界檢測。 3、手指抬起時,根據Item佈局View內容移動的距離以及手指的滑動速度,判斷是否顯示刪除按鈕,進而通過startScroll()使Item佈局View自動滑動到目標位置。 4、點選刪除按鈕則刪除對應Item,點選其它區域則隱藏刪除按鈕。

另外一種: 實現原理 主要是藉助 ItemTouchHelper.Callback 類來實現,我們要關注的方法為

  • getMovementFlags( )
  • onMove()
  • onSwiped()
  • onSelectedChanged()
  • clearView()
  • isLongPressDragEnabled()

首先自定義一個MyCallback類繼承 ItemTouchHelper.Callback ,定義兩個int變數dragFlags 與 swipeFlags並實現下方法。這個方法主要是為了獲取我們當前的事件是拖動還是滑動

其他: Android View重新整理機制? 在Android的佈局體系中,父View負責重新整理、佈局顯示子View;而當子View需要重新整理時,則是通知父View來完成

RelativeLayout和LinearLayout效能比較? 1.RelativeLayout會讓子View呼叫2次onMeasure,LinearLayout 在有weight時,也會呼叫子View2次onMeasure 2.RelativeLayout的子View如果高度和RelativeLayout不同,則會引發效率問題,當子View很複雜時,這個問題會更加嚴重。如果可以,儘量使用padding代替margin。 3.在不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。

View和ViewGroup什麼區別? Android的UI介面都是由View和ViewGroup及其派生類組合而成的。其中,View是所有UI元件的基類,而ViewGroup是容納這些元件的容器,其本身也是從View派生出來的

自定義View優化策略 為了加速你的view,對於頻繁呼叫的方法,需要儘量減少不必要的程式碼。先從onDraw開始,需要特別注意不應該在這裡做記憶體分配的事情,因為它會導致GC,從而導致卡頓。在初始化或者動畫間隙期間做分配記憶體的動作。不要在動畫正在執行的時候做記憶體分配的事情。 你還需要儘可能的減少onDraw被呼叫的次數,大多數時候導致onDraw都是因為呼叫了invalidate().因此請儘量減少呼叫invaildate()的次數。如果可能的話,儘量呼叫含有4個引數的invalidate()方法而不是沒有引數的invalidate()。沒有引數的invalidate會強制重繪整個view。 另外一個非常耗時的操作是請求layout。任何時候執行requestLayout(),會使得Android UI系統去遍歷整個View的層級來計算出每一個view的大小。如果找到有衝突的值,它會需要重新計算好幾次。另外需要儘量保持View的層級是扁平化的,這樣對提高效率很有幫助。 如果你有一個複雜的UI,你應該考慮寫一個自定義的ViewGroup來執行他的layout操作。與內建的view不同,自定義的view可以使得程式僅僅測量這一部分,這避免了遍歷整個view的層級結構來計算大小。這個PieChart 例子展示瞭如何繼承ViewGroup作為自定義view的一部分。PieChart 有子views,但是它從來不測量它們。而是根據他自身的layout法則,直接設定它們的大小

音視訊 SurfaceView是什麼 ? 它繼承自類View,因此它本質上是一個View。但與普通View不同的是,它有自己的Surface。有自己的Surface,在WMS中有對應的WindowState,在SurfaceFlinger中有Layer。我們知道,一般的Activity包含的多個View會組成View hierachy的樹形結構,只有最頂層的DecorView,也就是根結點檢視,才是對WMS可見的。這個DecorView在WMS中有一個對應的WindowState。相應地,在SF中對應的Layer。而SurfaceView自帶一個Surface,這個Surface在WMS中有自己對應的WindowState,在SF中也會有自己的Layer。雖然在App端它仍在View hierachy中,但在Server端(WMS和SF)中,它與宿主視窗是分離的。這樣的好處是對這個Surface的渲染可以放到單獨執行緒去做,渲染時可以有自己的GL context。這對於一些遊戲、視訊等效能相關的應用非常有益,因為它不會影響主執行緒對事件的響應。但它也有缺點,因為這個Surface不在View hierachy中,它的顯示也不受View的屬性控制,所以不能進行平移,縮放等變換,也不能放在其它ViewGroup中,一些View中的特性也無法使用。

SurfaceView優點及缺點? 優點:可以在一個獨立的執行緒中進行繪製,不會影響主執行緒。使用雙緩衝機制,播放視訊時畫面更流暢

缺點:Surface不在View hierachy中,它的顯示也不受View的屬性控制,所以不能進行平移,縮放等變換,也不能放在其它ViewGroup中。SurfaceView 不能巢狀使用

SurfaceView中雙緩衝? 雙緩衝:在運用時可以理解為:SurfaceView在更新檢視時用到了兩張Canvas,一張frontCanvas和一張backCanvas,每次實際顯示的是frontCanvas,backCanvas儲存的是上一次更改前的檢視,當使用lockCanvas()獲取畫布時,得到的實際上是backCanvas而不是正在顯示的frontCanvas,之後你在獲取到的backCanvas上繪製新檢視,再unlockCanvasAndPost(canvas)此檢視,那麼上傳的這張canvas將替換原來的frontCanvas作為新的frontCanvas,原來的frontCanvas將切換到後臺作為backCanvas。例如,如果你已經先後兩次繪製了檢視A和B,那麼你再呼叫lockCanvas()獲取檢視,獲得的將是A而不是正在顯示的B,之後你講重繪的C檢視上傳,那麼C將取代B作為新的frontCanvas顯示在SurfaceView上,原來的B則轉換為backCanvas。

TextureView是什麼? 在4.0(API level 14)中引入,與SurfaceView一樣繼承View, 它可以將內容流直接投影到View中,它可以將內容流直接投影到View中,可以用於實現Live preview等功能。和SurfaceView不同,它不會在WMS中單獨建立視窗,而是作為View hierachy中的一個普通View,因此可以和其它普通View一樣進行移動,旋轉,縮放,動畫等變化。值得注意的是TextureView必須在硬體加速的視窗中。它顯示的內容流資料可以來自App程式或是遠端程式。從類圖中可以看到,TextureView繼承自View,它與其它的View一樣在View hierachy中管理與繪製。TextureView過載了draw()方法,其中主要SurfaceTexture中收到的影象資料作為紋理更新到對應的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用於通知TextureView內容流有新影象到來。SurfaceTextureListener介面用於讓TextureView的使用者知道SurfaceTexture已準備好,這樣就可以把SurfaceTexture交給相應的內容源。Surface為BufferQueue的Producer介面實現類,使生產者可以通過它的軟體或硬體渲染介面為SurfaceTexture內部的BufferQueue提供graphic buffer。

TextureView優點及缺點?

優點:支援移動、旋轉、縮放等動畫,支援截圖

缺點:必須在硬體加速的視窗中使用,佔用記憶體比SurfaceView高,在5.0以前在主執行緒渲染,5.0以後有單獨的渲染執行緒。

誰的效能更優? 這裡寫圖片描述

播放器應該選擇誰?

從效能和安全性角度出發,使用播放器優先選SurfaceView。 1、在android 7.0上系統surfaceview的效能比TextureView更有優勢,支援物件的內容位置和包含的應用內容同步更新,平移、縮放不會產生黑邊。 在7.0以下系統如果使用場景有動畫效果,可以選擇性使用TextureView

2、由於失效(invalidation)和緩衝的特性,TextureView增加了額外1~3幀的延遲顯示畫面更新

3、TextureView總是使用GL合成,而SurfaceView可以使用硬體overlay後端,可以佔用更少的記憶體頻寬,消耗更少的能量

4、TextureView的內部緩衝佇列導致比SurfaceView使用更多的記憶體

5、SurfaceView: 內部自己持有surface,surface 建立、銷燬、大小改變時系統來處理的,通過surfaceHolder 的callback回撥通知。當畫布建立好時,可以將surface繫結到MediaPlayer中。SurfaceView如果為使用者可見的時候,建立SurfaceView的SurfaceHolder用於顯示視訊流解析的幀圖片,如果發現SurfaceView變為使用者不可見的時候,則立即銷燬SurfaceView的SurfaceHolder,以達到節約系統資源的目的

視訊編碼標準兩大系統是什麼?

視訊編碼標準有兩大系統:MPEG和ITU-T,如下 視訊編碼標準 MPEG標準由MPEG制定 MPEG-1 | MPEG-2 | (MPEG-3) | MPEG-4 | MPEG-7 | MPEG-21 ITU-T標準由VCEG制定 H.261 | (H.262) | H.263 | H.263v2 | H.264

什麼是音視訊編碼格式?什麼是音視訊封裝格式?

常見的AVI、RMVB、MKV、ASF、WMV、MP4、3GP、FLV等檔案其實只能算是一種封裝標準。

一個完整的視訊檔案是由音訊和視訊2部分組成的。H264、Xvid等就是視訊編碼格式,MP3、AAC等就是音訊編碼格式。

例如:將一個Xvid視訊編碼檔案和一個MP3視訊編碼檔案按AVI封裝標準封裝以後,就得到一個AVI字尾的視訊檔案,這個就是我們常見的AVI視訊檔案了。

由於很多種視訊編碼檔案、音訊編碼檔案都符合AVI封裝要求,則意味著即使是AVI字尾,也可能裡面的具體編碼格式不同。因此出現在一些裝置上,同是AVI字尾檔案,一些能正常播放,還有一些就無法播放。

同樣的情況也存在於其他容器格式。即使RMVB、WMV等也不例外,事實上,很多封裝容器對音訊編碼和視訊編碼的組合方式放的很開,如AVI還可以使用H.264+AAC組合,可以在具體使用中自己體會。尤其是MKV封裝容器,基本無論什麼樣的組合都可以!但一般MKV用的最多的就是H.264+AAC組合,此組合檔案體積最小,清晰度最高。因此網上很多MKV視訊都是高清晰度的。

因此,視訊轉換需要設定的本質就是:A設定需要的視訊編碼、B設定需要的音訊編碼、C選擇需要的容器封裝。一個完整的視訊轉換設定都至少包括了上面3個步驟。

平時說的軟解和硬解,具體是什麼?

硬解就是硬體解碼,指利用GPU來部分代替CPU進行解碼,軟解就是軟體解碼,指利用軟體讓CPU來進行解碼。兩者的具體區別如下所示:

硬解碼:是將原來全部交由CPU來處理的視訊資料的一部分交由GPU來做,而GPU的並行運算能力要遠遠高於CPU,這樣可以大大的降低對CPU的負載,CPU的佔用率較低了之後就可以同時執行一些其他的程式了,當然,對於較好的處理器來說,比如i5 2320,或者AMD 任何一款四核心處理器來說,硬解和軟體的區別只是個人偏好問題了吧。  

軟解碼:即通過軟體讓CPU來對視訊進行解碼處理;而硬解碼:指不借助於CPU,而通過專用的子卡裝置來獨立完成視訊解碼任務。曾經的VCD/DVD解壓卡、視訊壓縮卡等都隸屬於硬解碼這個範疇。而現如今,要完成高清解碼已經不再需要額外的子卡,因為硬解碼的模組已經被整合到顯示卡GPU的內部,所以目前的主流顯示卡(集顯)都能夠支援硬解碼技術。

何為直播?何為點播?

直播:是一個三方互動(主播、伺服器、觀眾),這個互動式實時的!儘管會根據選擇的協議不同而有一些延遲,但我們仍認為它直播是實時的!—>主播在本地傳送音視訊給伺服器(推流),觀眾從伺服器實時解碼(拉流)收看收聽主播傳送給伺服器的音視訊(直播內容)。直播是不能快進的 點播:首先一定要明確的一點,點播不存在推流這一過程,你本身你的流已經早就推給伺服器了,或者這麼說也不對,應該是你的音視訊早就上傳到了伺服器,觀眾只需要線上收看即可,由於你的音視訊上傳到了伺服器,觀眾則可以通過快進,快退,調整進度條等方式進行收看!

簡述推流、拉流的工作流程? 推流:在直播中,一方向伺服器傳送請求,向伺服器推送自己正在實時直播的資料,而這些內容在推送到伺服器的這一過程中是以 “流” 的形式傳遞的,這就是“推流”,把音視訊資料以流的方式推送(或上傳)到伺服器的過程就是“推流”!推流方的音視訊往往會很大,在推流的過程中首先按照 aac音訊-編碼 和 h264視訊-編碼的標準把推過來的音視訊壓縮 ,然後合併成 MP4或者 FLV格式,然後根據直播的封裝協議,最後傳給伺服器完成推流過程。

拉流:與推流正好相反,拉流是使用者從伺服器獲取推流方給伺服器的音視訊的過程,這就是“拉流”!拉流首先aac音訊-解碼 和 h.264視訊-解碼的內部把推過來的音視訊解壓縮,然後合成 MP4或者 FLV 格式,再解封裝,最後到我們的客戶端與觀眾進行互動。

常見的直播協議有哪些?之間有什麼區別?

常見的直播協議有三種 RTMP、HLS、FLV…

1)RTMP:real time messaging protocol~實時傳輸協議,RTMP協議比較全能,既可以用來推送又可以用來直播,其核心理念是將大塊的視訊幀和音訊幀“剁碎”,然後以小資料包的形式在網際網路上進行傳輸,而且支援加密,因此隱私性相對比較理想,但拆包組包的過程比較複雜,所以在海量併發時也容易出現一些不可預期的穩定性問題。

2)FLV:FLV協議由Adobe公司主推,格式極其簡單,只是在大塊的視訊幀和音視訊頭部加入一些標記頭資訊,由於這種極致的簡潔,在延遲表現和大規模併發方面都很成熟。唯一的不足就是在手機瀏覽器上的支援非常有限,但是用作手機端APP直播協議卻異常合適。

3)HLS:蘋果原生:HTTP Live Streaming,遵循的是 HTTP 超文字傳輸協議,埠號8080,將視訊分成5-10秒的視訊小分片,然後用m3u8索引表進行管理,由於客戶端下載到的視訊都是5-10秒的完整資料,故視訊的流暢性很好,但也同樣引入了很大的延遲(HLS的一般延遲在10-30s左右)。

點播中常見的資料傳輸協議主要有哪些?

常見的點播協議:HLS,HTTP

何為Nginx?有什麼特點? Nginx 是一個遵循 HTTP 協議的伺服器!記憶體佔用少,併發能力強! 還有等等的優點,可自行google

何為homebrew?你用它安裝過什麼?常用命令有哪些? homebrew是一個 Mac系統下所獨有的套件管理器,我要做直播,需要 rtmp 和 nginx ,單獨安裝很複雜,只要在終端裡輸入簡單的安裝相應的套件命令即可完成安裝,複雜的過程都靠 homebrew 規避掉了! 我用它安裝過很多東西,比如nginx 搭建流媒體伺服器等。 常用命令:brew install 、brew uninstall、brew search、brew list、brew update、brew help 等~

FFmpeg是什麼?

FFmpeg是一套用來記錄和轉換數字音視訊,並能將其轉化為流的開源計算機程式。拉流和推流離不開 FFmpeg 的幫助!

RTMP、HLS協議各自的預設埠號是?

RTMP埠號:1935 HLS埠號 :8080

m3u8構成是?直播中m3u8、ts如何實時更新?

是一個索引地址/播放列表,通過FFmpeg將本地的xxx.mp4進行切片處理,生成m3u8播放列表(索引檔案)和N多個 .ts檔案,並將其(m3u8、N個ts)放置在本地搭建好的webServer伺服器的指定目錄下,我就可以得到一個可以實時播放的網址,我們把這個m3u8地址複製到 VLC 上就可以實時觀看! 在 HLS 流下,本地視訊被分割成一個一個的小切片,一般10秒一個,這些個小切片被 m3u8管理,並且隨著終端的FFmpeg 向本地拉流的命令而實時更新,影片進度隨著拉流的進度而更新,播放過的片段不在本地儲存,自動刪除,直到該檔案播放完畢或停止,ts 切片會相應的被刪除,流停止,影片不會立即停止,影片播放會滯後於拉流一段時間

PS: FFmpeg推流至Nginx:可以推兩種流:RTMP流,推流至rtmplive;HLS流,推流至hls;其中,HLS流表現較明顯,在nginx的臨時目錄下,直觀的可看到m3u8索引檔案和N多個.ts檔案。m3u8列表會實時更新,且會動態更改當前播放索引切片(.ts)。這種實時更新的機制,不會使得.ts檔案長時間存在於Nginx伺服器上,且當推流結束之後,該目錄下的內容會被全部清除,這樣無形中減緩了nginx伺服器的壓力。另外,也闡釋了HLS這種流媒體播放相較RTMP延時較高的原因。

說說你平時在播放過程中做的優化工作 預載入,弱網優化,播放出錯重試機制,運營商劫持引起的起播慢,mediaserver的cpu佔有率很高,引起播放卡頓。起播時,只保留播放程式,kill 其他程式 。

未完待續

相關文章