android面試題筆試題總結

一、Android基本常識

1、寫10個簡單的linux命令

mkdir 創建文件夾
rmdir 刪除文件夾
rm 刪除文件
mv 移動文件
cp 拷貝文件
cat 查看文件
tail 查看文件尾部
more 分頁查看文件
cd 切換當前目錄
ls 列出文件清單
reboot 重啓
date 顯示日期
cal  顯示日曆
ps 查看系統進程相當於windows的任務管理器
ifconfig 配置網絡

2、書寫出android工程的目錄結構

src 源文件
gen 生成的文件 R文件就在此
android. jar 依賴的android sdk
assets 資源文件
bin 生成的字節碼apk在此
libs 依賴jar和so
res 資源文件
drawable
drawable-hdpi
layout
menu
values
AndroidManifest.xml
project.properties

3、什麼是ANR 如何避免它?

       在Android上,如果你的應用程序有一段時間響應不夠靈敏,系統會向用戶顯示一個對話框,這個對話框稱作應用程序無響應(ANR:Application Not Responding)對話框。用戶可以選擇讓程序繼續運行,但是,他們在使用你的應用程序時,並不希望每次都要處理這個對話框。因此,在程序裏對響應性能的設計很重要,這樣,系統不會顯示ANR給用戶。不同的組件發生ANR的時間不一樣,主線程(Activity、Service)是5秒,BroadCastReceiver是10秒。
       解決方案:
       將所有耗時操作,比如訪問網絡,Socket通信,查詢大量SQL語句,複雜邏輯計算等都放在子線程中去,然後通過handler.sendMessage、runonUITread、AsyncTask等方式更新UI。無論如何都要確保用戶界面操作的流暢度。如果耗時操作需要讓用戶等待,那麼可以在界面上顯示進度條。

4、談談Android的優點和不足之處

優點:
1、開放性,開源,免費,可定製
2、掙脫運營商束縛 
3、豐富的硬件選擇
4、不受任何限制的開發商
5、無縫結合的Google應用
缺點:
1、安全問題、隱私問題 
2、同質化嚴重
3、運營商對Android手機仍然有影響
4、山寨化嚴重
5、過分依賴開發商,缺乏標準配置

5、一條最長的短信息約佔多少byte?

       在國內的三大運營商通常情況下中文70(包括標點),英文160個。對於國外的其他運行商具體多長需要看運營商類型了。
android內部是通過如下代碼進行判斷具體一個短信多少byte的。
ArrayList<String> android.telephony.SmsManager.divideMessage(String text)

6、sim卡的EF文件有何作用?

       基本文件EF(Elementary File)是SIM卡文件系統的一部分。

文件
文件標識符
文件縮寫
中文名稱
文件作用
MF
3F00
根目錄
備註:所有非ETSI GSM協議中規定的應用文件由各廠家自行定義在根目錄下(如:PIN1,PIN2…)
EFICCID
2FE2
ICCID
SIM卡唯一的識別號
包含運營商、卡商、髮卡時間、省市代碼等信息
DFGSM
7F20
GSM目錄
備註:根據ETSIGSM09.91的規定Phase2(或以上)的SIM卡中應該有7F21並指向7F20,用以兼容Phase1的手機
EFLP語言選擇
6F05
LP
語言選擇文件
包含一種或多種語言編碼
EFIMSI
6F07
IMSI
國際移動用戶識別符
包含SIM卡所對應的號段,比如46000代表135-139號段、46002代表1340-1348
EFKC語音加密密鑰
6F20
Kc
計算密鑰
用於SIM卡的加密、解密
EFPLMNsel網絡選擇表
6F30
PLMNsel
公共陸地網選擇
決定SIM卡選擇哪種網絡,在這裏應該選擇中國移動的網絡
EFHPLMN歸屬地網絡選擇表
6F31
HPLMN
兩次搜索PLMN的時間間隔
兩次搜索中國移動的網絡的時間間隔
EFACMmax最大計費額
6F37
ACMmax
包含累積呼叫表的最大值
全部的ACM數據存在SIM卡中,此處取最大值
EFSST SIM卡服務表
6F38
SST
SIM卡服務列表
指出SIM卡可以提供服務的種類,哪些業務被激活哪些業務沒有開通
EFACM累加計費計數器
6F39
ACM
累計呼叫列表
當前的呼叫和以前的呼叫的單位總和
EFGID1分組識別1
6F3E
GID1
1級分組識別文件
包含特定的SIM-ME組合的標識符,可以識別一組特定的SIM卡
EFGID2分組識別2
6F3F
GID2
2級分組識別文件
包含特定的SIM-ME組合的標識符,可以識別一組特定的SIM卡
EFPUCT單位價格/貨幣表
6F41
PUCT
呼叫單位的價格和貨幣表
PUCT是與計費通知有關的信息,ME用這個信息結合EFACM,以用戶選擇的貨幣來計算呼叫費用
EFCBMI小區廣播識別號
6F45
CBMI
小區廣播信息標識符
規定了用戶希望MS採納的小區廣播消息內容的類型
EFSPN服務提供商
6F46
SPN
服務提供商名稱
包含服務提供商的名稱和ME顯示的相應要求
EFCBMID
6F48
CBMID
數據下載的小區廣播消息識別符
移動臺將收到的CBMID傳送給SIM卡
EFSUME
6F54
SUME
建立菜單單元
建立SIM卡中的菜單
EFBCCH廣播信道
6F74
BCCH
廣播控制信道
由於BCCH的存儲,在選擇小區時,MS可以縮小對BCCH載波的搜索範圍
EFACC訪問控制級別
6F78
ACC
訪問控制級別
SIM卡有15個級別,10個普通級別,5個高級級別
EFFPLMN禁止網絡號
6F7B
FPLMN
禁用的PLMN
禁止選擇除中國移動以外的其他運營商,比如中國聯通、中國衛通等
EFLOCI位置信息
6F7E
LOCI
位置信息
存儲臨時移動用戶識別符、位置區信息等內容
EFAD管理數據
6FAD
AD
管理數據
包括關於不同類型SIM卡操作模式的信息。例如:常規模式(PLMN用戶用於GSM網絡操作),型號認證模式(允許ME在無線設備的認證期間的特殊應用);小區測試模式(在小區商用之前,進行小區測試),製造商特定模式(允許ME製造商在維護階段進行特定的性能自動測試)
EFPHASE階段
6FAE
PHASE
階段標識
標識SIM卡所處的階段信息,比如是普通SIM卡還是STK卡等
DFTELECOM
7F10
電信目錄

 
EFADN縮位撥號
6F3A
AND
電話簿
用於將電話記錄存放在SIM卡中
EFFDN固定撥號
6F3B
FDN
固定撥號
包括固定撥號(FDN)和/或補充業務控制字串(SSC),還包括相關網絡/承載能力的識別符和擴展記錄的識別符,以及有關的α識別符
EFSMS短消息
6F3C
SMS
短消息
用於將短消息記錄存放在SIM卡中
EFCCP能力配置參數
6F3D
CCP
能力配置參數
包括所需要的網絡和承載能力的參數,以及當採用一個縮位撥號號碼,固定撥號號碼,MSISDN、最後撥號號碼、服務撥號號碼或禁止撥號方式等,建立呼叫時相關的ME配置
EFMSISDN電話號碼
6F40
MSISDN
移動基站國際綜合業務網號
存放用戶的手機號
EFSMSP短信息參數
6F42
SMSP
短消息業務參數
包括短信中心號碼等信息
EFSMSS短信息狀態
6F43
SMSS
短消息狀態
這個標識是用來控制流量的
EFLND最後撥號
6F44
LND
最後撥叫號碼
存儲最後撥叫號碼
EFExt1擴展文件1
6F4A
EXT1
擴展文件1
包括AND,MSISDN或LND的擴展數據
EFExt2擴展文件2
6F4B
EXT2
擴展文件2
包含FDN的擴展數據

7、如何判斷是否有SD卡?
      
       通過如下方法:Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED),如果返回true就是有sdcard,如果返回false則沒有。

8、dvm的進程和Linux的進程, 應用程序的進程是否爲同一個概念?
      
       dvm指dalvik的虛擬機。每一個Android應用程序都擁有一個獨立的Dalvik虛擬機實例,應用程序都在它自己的進程中運行。而每一個dvm都是在Linux 中的一個進程,所以說可以近似認爲是同一個概念。 

       什麼是android DVM:Dalvik是Google公司自己設計用於Android平臺的Java虛擬機,每一個Dalvik 應用作爲一個獨立的Linux 進程執行。獨立的進程可以防止在虛擬機崩潰的時候所有程序都被關閉。

Dalvik和Java虛擬機的區別
1、Dalvik主要是完成對象生命週期管理,堆棧管理,線程管理,安全和異常管理,以及垃圾回收等等重要功能。   
2、Dalvik負責進程隔離和線程管理,每一個Android應用在底層都會對應一個獨立的Dalvik虛擬機實例,其代碼在虛擬機的解釋下得以執行。   
3、不同於Java虛擬機運行java字節碼,Dalvik虛擬機運行的是其專有的文件格式Dex
4、dex文件格式可以減少整體文件尺寸,提高I/O操作的類查找速度。   
5、odex是爲了在運行過程中進一步提高性能,對dex文件的進一步優化。   
6、所有的Android應用的線程都對應一個Linux線程,虛擬機因而可以更多的依賴操作系統的線程調度和管理機制
7、有一個特殊的虛擬機進程Zygote,他是虛擬機實例的孵化器。它在系統啓動的時候就會產生,它會完成虛擬機的初始化,庫的加載,預製類庫和初始化的操作。如果系統需要一個新的虛擬機實例,它會迅速複製自身,以最快的數據提供給系統。對於一些只讀的系統庫,所有虛擬機實例都和Zygote共享一塊內存區域。

9、Android程序與Java程序的區別?
      
       Android程序用android sdk開發,java程序用javasdk開發。
       Android SDK引用了大部分的Java SDK,少數部分被AndroidSDK拋棄,比如說界面部分,java.awt swing  package除了java.awt.font被引用外,其他都被拋棄,在Android平臺開發中不能使用。android sdk 添加工具jar httpclient , pull  opengl

10、啓動應用後,改變系統語言,應用的語言會改變麼?

       這個一般是不會的,一般需要重啓應用才能改變應用語言。但是對應應用來說如果做了國際化處理則支持如果沒有處理那系統語言再更改也是無用的。

11、請介紹下adb、ddms、aapt的作用

       adb是Android Debug Bridge ,Android調試橋的意思,ddms是Dalvik Debug Monitor Service,dalvik調試監視服務。aapt即AndroidAsset Packaging Tool,在SDK的build-tools目錄下。該工具可以查看,創建,更新ZIP格式的文檔附件(zip, jar, apk)。也可將資源文件編譯成二進制文件,儘管我們沒有直接使用過該工具,但是開發工具會使用這個工具打包apk文件構成一個Android 應用程序。
Android 的主要調試工具是adb(Android debuging bridge),ddms是一個在adb基礎上的一個圖形化工具。adb,它是一個命令行工具。而ddms功能與adb相同,只是它有一個圖形化界面。對不喜歡命今操作方式的人來說是一個不錯的選擇。

12、ddms 和traceview的區別
      
       簡單的說ddms是一個程序執行查看器,在裏面可以看見線程和堆棧等信息,traceView是程序性能分析器。

13、補充知識:TraceView的使用

一、TraceView簡介

       Traceview是Android平臺特有的數據採集和分析工具,它主要用於分析Android中應用程序的hotspot(瓶頸)。Traceview本身只是一個數據分析工具,而數據的採集則需要使用Android SDK中的Debug類或者利用DDMS工具。二者的用法如下:
開發者在一些關鍵代碼段開始前調用Android SDK中Debug類的startMethodTracing函數,並在關鍵代碼段結束前調用stopMethodTracing函數。這兩個函數運行過程中將採集運行時間內該應用所有線程(注意,只能是Java線程)的函數執行情況,並將採集數據保存到/mnt/sdcard/下的一個文件中。開發者然後需要利用SDK中的Traceview工具來分析這些數據。
藉助Android SDK中的DDMS工具。DDMS可採集系統中某個正在運行的進程的函數調用信息。對開發者而言,此方法適用於沒有目標應用源代碼的情況。DDMS工具中Traceview的使用如下圖所示。

 

       點擊上圖中所示按鈕即可以採集目標進程的數據。當停止採集時,DDMS會自動觸發Traceview工具來瀏覽採集數據。

       下面,我們通過一個示例程序向讀者介紹Debug類以及Traceview的使用。
       實例程序如下圖所示:界面有4個按鈕,對應四個方法。

 

       點擊不同的方法會進行不同的耗時操作。

  1. public class MainActivity extends ActionBarActivity {
  2.         @Override
  3.         protected void onCreate(Bundle savedInstanceState) {
  4.                 super.onCreate(savedInstanceState);
  5.                 setContentView(R.layout.activity_main);
  6.         }

  7.         public void method1(View view) {
  8.                 int result = jisuan();
  9.                 System.out.println(result);
  10.         }
  11.         private int jisuan() {
  12.                 for (int i = 0; i < 10000; i++) {
  13.                         System.out.println(i);
  14.                 }
  15.                 return 1;
  16.         }

  17.         public void method2(View view) {
  18.                 SystemClock.sleep(2000);
  19.         }

  20.         public void method3(View view) {
  21.                 int sum = 0;
  22.                 for (int i = 0; i < 1000; i++) {
  23.                         sum += i;
  24.                 }
  25.                 System.out.println("sum=" + sum);
  26.         }

  27.         public void method4(View view) {
  28.                 Toast.makeText(this, "" + new Date(), 0).show();
  29.         }
  30. }
複製代碼

       我們分別點擊按鈕一次,要求找出最耗時的方法。點擊前通過DDMS 啓動 Start Method Profiling按鈕。

 


       然後依次點擊4個按鈕,都執行後再次點擊上圖中紅框中按鈕,停止收集數據。
       接下來我們開始對數據進行分析。
       當我們停止收集數據的時候會出現如下分析圖表。該圖表分爲2大部分,上面分不同的行,每一行代表一個線程的執行耗時情況。main線程對應行的的內容非常豐富,而其他線程在這段時間內幹得工作則要少得多。圖表的下半部分是具體的每個方法執行的時間情況。顯示方法執行情況的前提是先選中某個線程。

 

       我們主要是分析main線程。

       上面方法指標參數所代表的意思如下:

列名
描述
Name
該線程運行過程中所調用的函數名
Incl Cpu Time
某函數佔用的CPU時間,包含內部調用其它函數的CPU時間
Excl Cpu Time
某函數佔用的CPU時間,但不含內部調用其它函數所佔用的CPU時間
Incl Real Time
某函數運行的真實時間(以毫秒爲單位),內含調用其它函數所佔用的真實時間
Excl Real Time
某函數運行的真實時間(以毫秒爲單位),不含調用其它函數所佔用的真實時間
Call+Recur Calls/Total
某函數被調用次數以及遞歸調用佔總調用次數的百分比
Cpu Time/Call
某函數調用CPU時間與調用次數的比。相當於該函數平均執行時間
Real Time/Call
同CPU  Time/Call類似,只不過統計單位換成了真實時間

       我們爲了找到最耗時的操作,那麼可以通過點擊Incl Cpu Time,讓其按照時間的倒序排列。我點擊後效果如下圖:

 

       通過分析發現:method1最耗時,耗時2338毫秒。

 

       那麼有了上面的信息我們可以進入我們的method1方法查看分析我們的代碼了。

二、Activity

1、什麼是Activity?

       四大組件之一,通常一個用戶交互界面對應一個activity。activity 是Context的子類,同時實現了window.callback和keyevent.callback, 可以處理與窗體用戶交互的事件。
       常見的Activity類型有FragmentActivitiy,ListActivity,TabAcitivty等。
       如果界面有共同的特點或者功能的時候,還會自己定義一個BaseActivity。

2、請描述一下Activity 生命週期

       Activity從創建到銷燬有多種狀態,從一種狀態到另一種狀態時會激發相應的回調方法,這些回調方法包括:onCreateonStart onResume onPause onStop onDestroy
       其實這些方法都是兩兩對應的,onCreate創建與onDestroy銷燬;
       onStart可見與onStop不可見;onResume可編輯(即焦點)與onPause;
       這6個方法是相對應的,那麼就只剩下一個onRestart方法了,這個方法在什麼時候調用呢?
       答案就是:在Activity被onStop後,但是沒有被onDestroy,在再次啓動此Activity時就調用onRestart(而不再調用onCreate)方法;如果被onDestroy了,則是調用onCreate方法。

3、如何保存Activity的狀態?

       Activity的狀態通常情況下系統會自動保存的,只有當我們需要保存額外的數據時才需要使用到這樣的功能。
       一般來說, 調用onPause()和onStop()方法後的activity實例仍然存在於內存中, activity的所有信息和狀態數據不會消失, 當activity重新回到前臺之後, 所有的改變都會得到保留。
       但是當系統內存不足時, 調用onPause()和onStop()方法後的activity可能會被系統摧毀, 此時內存中就不會存有該activity的實例對象了。如果之後這個activity重新回到前臺, 之前所作的改變就會消失。爲了避免此種情況的發生, 我們可以覆寫onSaveInstanceState()方法。onSaveInstanceState()方法接受一個Bundle類型的參數, 開發者可以將狀態數據存儲到這個Bundle對象中, 這樣即使activity被系統摧毀,當用戶重新啓動這個activity而調用它的onCreate()方法時, 上述的Bundle對象會作爲實參傳遞給onCreate()方法, 開發者可以從Bundle對象中取出保存的數據, 然後利用這些數據將activity恢復到被摧毀之前的狀態。
       需要注意的是, onSaveInstanceState()方法並不是一定會被調用的, 因爲有些場景是不需要保存狀態數據的. 比如用戶按下BACK鍵退出activity時, 用戶顯然想要關閉這個activity, 此時是沒有必要保存數據以供下次恢復的, 也就是onSaveInstanceState()方法不會被調用. 如果調用onSaveInstanceState()方法, 調用將發生在onPause()或onStop()方法之前。

  1. @Override
  2. protected void onSaveInstanceState(Bundle outState) {
  3.         super.onSaveInstanceState(outState);
  4. }
複製代碼

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

5、橫豎屏切換時Activity的生命週期

       此時的生命週期跟清單文件裏的配置有關係。
       1、不設置Activity的android:configChanges時,切屏會重新調用各個生命週期,默認首先銷燬當前activity,然後重新加載。
       2、設置Activity的android:configChanges="orientation|keyboardHidden|screenSize"時,切屏不會重新調用各個生命週期,只會執行onConfigurationChanged方法。
       通常在遊戲開發, 屏幕的朝向都是寫死的。

6、如何將一個Activity設置成窗口的樣式

      
       只需要給我們的Activity配置如下屬性即可。
       android:theme="@android:style/Theme.Dialog"

7、如何退出Activity?如何安全退出已調用多個Activity的Application?

       1、通常情況用戶退出一個Activity只需按返回鍵,我們寫代碼想退出activity直接調用finish()方法就行。
       2、記錄打開的Activity:每打開一個Activity,就記錄下來。在需要退出時,關閉每一個Activity即可。

  1. //僞代碼
  2. List<Activity> lists ;// 在application 全局的變量裏面 
  3. lists = new ArrayList<Activity>();
  4.                 lists.add(this);
  5.                 for(Activity activity: lists)
  6.                 {
  7.                         activity.finish();
  8.                 }
  9.                 lists.remove(this);

複製代碼

       3、發送特定廣播:
       在需要結束應用時,發送一個特定的廣播,每個Activity收到廣播後,關閉即可。
       //給某個activity 註冊接受接受廣播的意圖  
       registerReceiver(receiver, filter)
       //如果過接受到的是 關閉activity的廣播  就調用finish()方法把當前的activity finish()掉 

       4、遞歸退出
       在打開新的Activity時使用startActivityForResult,然後自己加標誌,在onActivityResult中處理,遞歸關閉。

       5、其實 也可以通過 intent的flag 來實現intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)激活一個新的activity。此時如果該任務棧中已經有該Activity,那麼系統會把這個Activity上面的所有Activity幹掉。其實相當於給Activity配置的啓動模式爲SingleTop。

8、請描述一下Activity的啓動模式都有哪些以及各自的特點

       啓動模式(launchMode)在多個Activity跳轉的過程中扮演着重要的角色,它可以決定是否生成新的Activity實例,是否重用已存在的Activity實例,是否和其他Activity實例公用一個task裏。這裏簡單介紹一下task的概念,task是一個具有棧結構的對象,一個task可以管理多個Activity,啓動一個應用,也就創建一個與之對應的task。
       Activity一共有以下四種launchMode:
       1.standard
       2.singleTop
       3.singleTask
       4.singleInstance
       我們可以在AndroidManifest.xml配置<activity>的android:launchMode屬性爲以上四種之一即可。

       下面我們結合實例一一介紹這四種lanchMode:

8.1 standard

      
       standard模式是默認的啓動模式,不用爲<activity>配置android:launchMode屬性即可,當然也可以指定值爲standard。
       我們將創建一個Activity,命名爲FirstActivity,來演示一下標準的啓動模式。FirstActivity代碼如下:

  1. public class FirstActivity extends Activity {
  2.     @Override
  3.     public void onCreate(Bundle savedInstanceState) {
  4.         super.onCreate(savedInstanceState);
  5.         setContentView(R.layout.first);
  6.         TextView textView = (TextView) findViewById(R.id.tv);
  7.         textView.setText(this.toString());
  8.         Button button = (Button) findViewById(R.id.bt);
  9.         button.setOnClickListener(new View.OnClickListener() {
  10.                         @Override
  11.                         public void onClick(View v) {
  12.                                 Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
  13.                             startActivity(intent);
  14.                         }
  15.                 });
  16.     }
  17. }
複製代碼

       FirstActivity界面中的TextView用於顯示當前Activity實例的序列號,Button用於跳轉到下一個FirstActivity界面。
       然後我們連續點擊幾次按鈕,將會出現下面的現象:

 

       我們注意到都是FirstActivity的實例,但序列號不同,並且我們需要連續按後退鍵兩次,才能回到第一個FirstActivity。standard模式的原理如下圖所示:

 

       如圖所示,每次跳轉系統都會在task中生成一個新的FirstActivity實例,並且放於棧結構的頂部,當我們按下後退鍵時,才能看到原來的FirstActivity實例。
       這就是standard啓動模式,不管有沒有已存在的實例,都生成新的實例。

8.2 singleTop

       我們在上面的基礎上爲<activity>指定屬性android:launchMode="singleTop",系統就會按照singleTop啓動模式處理跳轉行爲。我們重複上面幾個動作,將會出現下面的現象:

 
 

       我們看到這個結果跟standard有所不同,三個序列號是相同的,也就是說使用的都是同一個FirstActivity實例;如果按一下後退鍵,程序立即退出,說明當前棧結構中只有一個Activity實例。singleTop模式的原理如下圖所示:

 

       正如上圖所示,跳轉時系統會先在棧結構中尋找是否有一個FirstActivity實例正位於棧頂,如果有則不再生成新的,而是直接使用。也許朋友們會有疑問,我只看到棧內只有一個Activity,如果是多個Activity怎麼辦,如果不是在棧頂會如何?我們接下來再通過一個示例來證實一下大家的疑問。
       我們再新建一個Activity命名爲SecondActivity,如下:

  1. public class SecondActivity extends Activity {
  2.         @Override
  3.         protected void onCreate(Bundle savedInstanceState) {
  4.                 super.onCreate(savedInstanceState);
  5.                 setContentView(R.layout.second);
  6.                 TextView textView = (TextView) findViewById(R.id.tv);
  7.         textView.setText(this.toString());
  8.         Button button = (Button) findViewById(R.id.button);
  9.         button.setOnClickListener(new View.OnClickListener() {
  10.                         @Override
  11.                         public void onClick(View v) {
  12.                                 Intent intent = new Intent(SecondActivity.this, FirstActivity.class);
  13.                             startActivity(intent);                                
  14.                         }
  15.         });
  16.         }
  17. }
複製代碼

       然後將之前的FirstActivity跳轉代碼改爲:

  1. Intent intent = new Intent(FirstActivity.this, SecondActivity.class);  
  2. startActivity(intent);  
複製代碼

       這時候,FirstActivity會跳轉到SecondActivity,SecondActivity又會跳轉到FirstActivity。演示結果如下:

 

       我們看到,兩個FirstActivity的序列號是不同的,證明從SecondActivity跳轉到FirstActivity時生成了新的FirstActivity實例。原理圖如下:

 

       我們看到,當從SecondActivity跳轉到FirstActivity時,系統發現存在有FirstActivity實例,但不是位於棧頂,於是重新生成一個實例。
這就是singleTop啓動模式,如果發現有對應的Activity實例正位於棧頂,則重複利用,不再生成新的實例。

8.3 singleTask

       在上面的基礎上我們修改FirstActivity的屬性android:launchMode="singleTask"。演示的結果如下:

 


       我們注意到,在上面的過程中,FirstActivity的序列號是不變的,SecondActivity的序列號卻不是唯一的,說明從SecondActivity跳轉到FirstActivity時,沒有生成新的實例,但是從FirstActivity跳轉到SecondActivity時生成了新的實例。singleTask模式的原理圖如下圖所示:

 

       在圖中的下半部分是SecondActivity跳轉到FirstActivity後的棧結構變化的結果,我們注意到,SecondActivity消失了,沒錯,在這個跳轉過程中系統發現有存在的FirstActivity實例,於是不再生成新的實例,而是將FirstActivity之上的Activity實例統統出棧,將FirstActivity變爲棧頂對象,顯示到幕前。也許朋友們有疑問,如果將SecondActivity也設置爲singleTask模式,那麼SecondActivity實例是不是可以唯一呢?在我們這個示例中是不可能的,因爲每次從SecondActivity跳轉到FirstActivity時,SecondActivity實例都被迫出棧,下次等FirstActivity跳轉到SecondActivity時,找不到存在的SecondActivity實例,於是必須生成新的實例。但是如果我們有ThirdActivity,讓SecondActivity和ThirdActivity互相跳轉,那麼SecondActivity實例就可以保證唯一。
       這就是singleTask模式,如果發現有對應的Activity實例,則使此Activity實例之上的其他Activity實例統統出棧,使此Activity實例成爲棧頂對象,顯示到幕前。

8.4 singleInstance

       這種啓動模式比較特殊,因爲它會啓用一個新的棧結構,將Activity放置於這個新的棧結構中,並保證不再有其他Activity實例進入。
我們修改FirstActivity的launchMode="standard",SecondActivity的launchMode="singleInstance",由於涉及到了多個棧結構,我們需要在每個Activity中顯示當前棧結構的id,所以我們爲每個Activity添加如下代碼:

  1. TextView taskIdView = (TextView) findViewById(R.id.taskIdView);  
  2. taskIdView.setText("current task id: " + this.getTaskId()); 

複製代碼

       然後我們再演示一下這個流程:

 

       我們發現這兩個Activity實例分別被放置在不同的棧結構中,關於singleInstance的原理圖如下:

 

       我們看到從FirstActivity跳轉到SecondActivity時,重新啓用了一個新的棧結構,來放置SecondActivity實例,然後按下後退鍵,再次回到原始棧結構;圖中下半部分顯示的在SecondActivity中再次跳轉到FirstActivity,這個時候系統會在原始棧結構中生成一個FirstActivity實例,然後回退兩次,注意,並沒有退出,而是回到了SecondActivity,爲什麼呢?是因爲從SecondActivity跳轉到FirstActivity的時候,我們的起點變成了SecondActivity實例所在的棧結構,這樣一來,我們需要“迴歸”到這個棧結構。
       如果我們修改FirstActivity的launchMode值爲singleTop、singleTask、singleInstance中的任意一個,流程將會如圖所示:

 

       singleInstance啓動模式可能是最複雜的一種模式,爲了幫助大家理解,我舉一個例子,假如我們有一個share應用,其中的ShareActivity是入口Activity,也是可供其他應用調用的Activity,我們把這個Activity的啓動模式設置爲singleInstance,然後在其他應用中調用。我們編輯ShareActivity的配置:

  1.   <activity
  2.          android:name=".ShareActivity"
  3.          android:launchMode="singleInstance" >
  4.   <intent-filter>
  5.   <action android:name="android.intent.action.MAIN" />
  6.   <category android:name="android.intent.category.LAUNCHER" />
  7.             </intent-filter>
  8.    <intent-filter>
  9. <action android:name="android.intent.action.SINGLE_INSTANCE_SHARE" />
  10. <category android:name="android.intent.category.DEFAULT" />
  11.    </intent-filter>
  12. </activity>
複製代碼

       然後我們在其他應用中這樣啓動該Activity:

  1. Intent intent = new Intent("android.intent.action.SINGLE_INSTANCE_SHARE");  
  2. startActivity(intent);  
複製代碼

       當我們打開ShareActivity後再按後退鍵回到原來界面時,ShareActivity做爲一個獨立的個體存在,如果這時我們打開share應用,無需創建新的ShareActivity實例即可看到結果,因爲系統會自動查找,存在則直接利用。大家可以在ShareActivity中打印一下taskId,看看效果。關於這個過程,原理圖如下:

 


三、Service

1、Service是否在main thread中執行, service裏面是否能執行耗時的操作?

       默認情況,如果沒有顯示的指servic所運行的進程, Service和activity是運行在當前app所在進程的main thread(UI主線程)裏面。
       service裏面不能執行耗時的操作(網絡請求,拷貝數據庫,大文件 )。
       特殊情況 ,可以在清單文件配置 service 執行所在的進程,讓service在另外的進程中執行。

  1. <service
  2.     android:name="com.baidu.location.f"
  3.     android:enabled="true"
  4.     android:process=":remote" >
  5. </service>

複製代碼

2、Activity怎麼和Service綁定,怎麼在Activity中啓動自己對應的Service?

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

3、請描述一下Service的生命週期

       Service有綁定模式和非綁定模式,以及這兩種模式的混合使用方式。不同的使用方法生命週期方法也不同。
       非綁定模式:當第一次調用startService的時候執行的方法依次爲onCreate()、onStartCommand(),當Service關閉的時候調用onDestory方法。
       綁定模式:第一次bindService()的時候,執行的方法爲onCreate()、onBind()解除綁定的時候會執行onUnbind()、onDestory()。
       上面的兩種生命週期是在相對單純的模式下的情形。我們在開發的過程中還必須注意Service實例只會有一個,也就是說如果當前要啓動的Service已經存在了那麼就不會再次創建該Service當然也不會調用onCreate()方法。
       一個Service可以被多個客戶進行綁定,只有所有的綁定對象都執行了onBind()方法後該Service纔會銷燬,不過如果有一個客戶執行了onStart()方法,那麼這個時候如果所有的bind客戶都執行了unBind()該Service也不會銷燬。
       Service的生命週期圖如下所示,幫助大家記憶。

 

4、什麼是IntentService?有何優點?
      
       我們通常只會使用Service,可能IntentService對大部分同學來說都是第一次聽說。那麼看了下面的介紹相信你就不再陌生了。如果你還是不瞭解那麼在面試的時候你就坦誠說沒用過或者不瞭解等。並不是所有的問題都需要回答上來的。
      
       一、IntentService簡介
       IntentService是Service的子類,比普通的Service增加了額外的功能。先看Service本身存在兩個問題:
       Service不會專門啓動一條單獨的進程,Service與它所在應用位於同一個進程中;
       Service也不是專門一條新線程,因此不應該在Service中直接處理耗時的任務;

       二、IntentService特徵
       會創建獨立的worker線程來處理所有的Intent請求;
       會創建獨立的worker線程來處理onHandleIntent()方法實現的代碼,無需處理多線程問題;
       所有請求處理完成後,IntentService會自動停止,無需調用stopSelf()方法停止Service;
       爲Service的onBind()提供默認實現,返回null;
       爲Service的onStartCommand提供默認實現,將請求Intent添加到隊列中;

       三、使用IntentService
       本人寫了一個IntentService的使用例子供參考。該例子中一個MainActivity一個MyIntentService,這兩個類都是四大組件當然需要在清單文件中註冊。這裏只給出核心代碼:

       MainActivity.java
  1. public void click(View view){
  2.         Intent intent = new Intent(this, MyIntentService.class);
  3.         intent.putExtra("start", "MyIntentService");
  4.         startService(intent);
  5. }

複製代碼

       MyIntentService.java
  1. public class MyIntentService extends IntentService {

  2.         private String ex = "";
  3.         private Handler mHandler = new Handler() {
  4.                 public void handleMessage(android.os.Message msg) {
  5.                         Toast.makeText(MyIntentService.this, "-e " + ex, Toast.LENGTH_LONG).show();
  6.                 }
  7.         };

  8.         public MyIntentService(){
  9.                 super("MyIntentService");
  10.         }

  11.         @Override
  12.         public int onStartCommand(Intent intent, int flags, int startId) {
  13.                 ex = intent.getStringExtra("start");
  14.                 return super.onStartCommand(intent, flags, startId);
  15.         }

  16.         @Override
  17.         protected void onHandleIntent(Intent intent) {
  18.                 /**
  19.                  * 模擬執行耗時任務 
  20.                  * 該方法是在子線程中執行的,因此需要用到handler跟主線程進行通信
  21.                  */
  22.                 try {
  23.                         Thread.sleep(1000);
  24.                 } catch (InterruptedException e) {
  25.                         e.printStackTrace();
  26.                 }
  27.                 mHandler.sendEmptyMessage(0);
  28.                 try {
  29.                         Thread.sleep(1000);
  30.                 } catch (InterruptedException e) {
  31.                         e.printStackTrace();
  32.                 }
  33.         }
  34. }
複製代碼

       運行後效果如下:

 

5、說說Activity、Intent、Service是什麼關係
      
       他們都是Android開發中使用頻率最高的類。其中Activity和Service都是Android四大組件之一。他倆都是Context類的子類ContextWrapper的子類,因此他倆可以算是兄弟關係吧。不過兄弟倆各有各自的本領,Activity負責用戶界面的顯示和交互,Service負責後臺任務的處理。Activity和Service之間可以通過Intent傳遞數據,因此可以把Intent看作是通信使者。

6、Service和Activity在同一個線程嗎

       對於同一app來說默認情況下是在同一個線程中的,mainThread (UI Thread)。

7、Service裏面可以彈吐司麼

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

四、BroadCastReceiver

1、請描述一下BroadcastReceiver

       BroadCastReceiver是Android四大組件之一,主要用於接收系統或者app發送的廣播事件。
       廣播分兩種:有序廣播和無序廣播。
       內部通信實現機制:通過Android系統的Binder機制實現通信。
       無序廣播:完全異步,邏輯上可以被任何廣播接收者接收到。優點是效率較高。缺點是一個接收者不能將處理結果傳遞給下一個接收者,並無法終止廣播intent的傳播。
       有序廣播:按照被接收者的優先級順序,在被接收者中依次傳播。比如有三個廣播接收者A,B,C,優先級是A > B> C。那這個消息先傳給A,再傳給B,最後傳給C。每個接收者有權終止廣播,比如B終止廣播,C就無法接收到。此外A接收到廣播後可以對結果對象進行操作,當廣播傳給B時,B可以從結果對象中取得A存入的數據。
       在通過Context.sendOrderedBroadcast(intent, receiverPermission,resultReceiver, scheduler, initialCode, initialData, initialExtras)時我們可以指定resultReceiver廣播接收者,這個接收者我們可以認爲是最終接收者,通常情況下如果比他優先級更高的接收者如果沒有終止廣播,那麼他的onReceive會被執行兩次,第一次是正常的按照優先級順序執行,第二次是作爲最終接收者接收。如果比他優先級高的接收者終止了廣播,那麼他依然能接收到廣播。
       在我們的項目中經常使用廣播接收者接收系統通知,比如開機啓動、sd掛載、低電量、外播電話、鎖屏等。
       如果我們做的是播放器,那麼監聽到用戶鎖屏後我們應該將我們的播放之暫停等。

2、在manifest和代碼中如何註冊和使用BroadcastReceiver

       在清單文件中註冊廣播接收者稱爲靜態註冊,在代碼中註冊稱爲動態註冊。靜態註冊的廣播接收者只要app在系統中運行則一直可以接收到廣播消息,動態註冊的廣播接收者當註冊的Activity或者Service銷燬了那麼就接收不到廣播了。
      
       靜態註冊:在清單文件中進行如下配置
  1.   <receiver android:name=".BroadcastReceiver1" >
  2.             <intent-filter>
  3.                 <action android:name="android.intent.action.CALL" >
  4.                 </action>
  5.             </intent-filter>
  6. </receiver>
複製代碼

       動態註冊:在代碼中進行如下注冊
  1. receiver = new BroadcastReceiver();  
  2.                IntentFilter intentFilter = new IntentFilter();  
  3.                intentFilter.addAction(CALL_ACTION);  
  4.                context.registerReceiver(receiver, intentFilter); 

複製代碼

3、BroadCastReceiver的生命週期
      
       a. 廣播接收者的生命週期非常短暫的,在接收到廣播的時候創建,onReceive()方法結束之後銷燬;
       b. 廣播接收者中不要做一些耗時的工作,否則會彈出Application No Response錯誤對話框;
       c. 最好也不要在廣播接收者中創建子線程做耗時的工作,因爲廣播接收者被銷燬後進程就成爲了空進程,很容易被系統殺掉;
       d. 耗時的較長的工作最好放在服務中完成;

五、ContentProvider

1、請介紹下ContentProvider是如何實現數據共享的
      
       在Android中如果想將自己應用的數據(一般多爲數據庫中的數據)提供給第三發應用,那麼我們只能通過ContentProvider來實現了。
       ContentProvider是應用程序之間共享數據的接口。使用的時候首先自定義一個類繼承ContentProvider,然後覆寫query、insert、update、delete等方法。因爲其是四大組件之一因此必須在AndroidManifest文件中進行註冊。

  1. <provider     
  2. android:exported="true"     android:name="com.itheima.contenProvider.provider.PersonContentProvider"android:authorities="com.itheima.person" />

複製代碼

       第三方可以通過ContentResolver來訪問該Provider。

2、請介紹下Android的數據存儲方式

       File存儲
       SharedPreference存儲
       ContentProvider存儲
       SQLiteDataBase存儲
       網絡存儲

3、爲什麼要用ContentProvider?它和sql的實現上有什麼差別?

       ContentProvider屏蔽了數據存儲的細節,內部實現對用戶完全透明,用戶只需要關心操作數據的uri就可以了,ContentProvider可以實現不同app之間共享。
       Sql也有增刪改查的方法,但是sql只能查詢本應用下的數據庫。而ContentProvider 還可以去增刪改查本地文件. xml文件的讀取等。

4、說說ContentProvider、ContentResolver、ContentObserver之間的關係

       ContentProvider 內容提供者,用於對外提供數據
       ContentResolver.notifyChange(uri)發出消息
       ContentResolver 內容解析者,用於獲取內容提供者提供的數據
       ContentObserver 內容監聽器,可以監聽數據的改變狀態
       ContentResolver.registerContentObserver()監聽消息。

六、Android中的佈局

1、Android中常用的佈局都有哪些

       FrameLayout 
       RelativeLayout 
       LinearLayout 
       AbsoluteLayout 
       TableLayout

2、談談UI中, Padding和Margin有什麼區別?

       android:padding和android:layout_margin的區別,其實概念很簡單,padding是站在父view的角度描述問題,它規定它裏面的內容必須與這個父view邊界的距離。margin則是站在自己的角度描述問題,規定自己和其他(上下左右)的view之間的距離,如果同一級只有一個view,那麼它的效果基本上就和padding一樣了。

七、ListView

1、ListView如何提高其效率?

       ① 複用ConvertView
       ② 自定義靜態類ViewHolder
       ③ 使用分頁加載
       ④ 使用WeakRefrence引用ImageView對象

2、當ListView數據集改變後,如何更新ListView

       使用該ListView的adapter的notifyDataSetChanged()方法。該方法會使ListView重新繪製。

3、ListView如何實現分頁加載

       ① 設置ListView的滾動監聽器:setOnScrollListener(new OnScrollListener{….})。在監聽器中有兩個方法: 滾動狀態發生變化的方法(onScrollStateChanged)和listView被滾動時調用的方法(onScroll)
       ② 在滾動狀態發生改變的方法中,有三種狀態:
       手指按下移動的狀態:                     SCROLL_STATE_TOUCH_SCROLL: // 觸摸滑動
       慣性滾動(滑翔(flgin)狀態):    SCROLL_STATE_FLING: // 滑翔
       靜止狀態:                                      SCROLL_STATE_IDLE: // 靜止
       對不同的狀態進行處理:
       分批加載數據,只關心靜止狀態:關心最後一個可見的條目,如果最後一個可見條目就是數據適配器(集合)裏的最後一個,此時可加載更多的數據。在每次加載的時候,計算出滾動的數量,當滾動的數量大於等於總數量的時候,可以提示用戶無更多數據了。

4、ListView可以顯示多種類型的條目嗎

       這個當然可以的,ListView顯示的每個條目都是通過baseAdapter的getView(int position, View convertView, ViewGroup parent)來展示的,理論上我們完全可以讓每個條目都是不同類型的view,除此之外adapter還提供了getViewTypeCount()和getItemViewType(int position)兩個方法。在getView方法中我們可以根據不同的viewtype加載不同的佈局文件。

5、ListView如何定位到指定位置

       可以通過ListView提供的lv.setSelection(48);方法。

6、當在ScrollView中如何嵌入ListView
      
       通常情況下我們不會在ScrollView中嵌套ListView,但是如果面試官非讓我嵌套的話也是可以的。

       在ScrollView添加一個ListView會導致listview控件顯示不全,通常只會顯示一條,這是因爲兩個控件的滾動事件衝突導致。所以需要通過listview中的item數量去計算listview的顯示高度,從而使其完整展示,如下提供一個方法供大家參考。

  1. lv = (ListView) findViewById(R.id.lv);
  2.         adapter = new MyAdapter();
  3.         lv.setAdapter(adapter);
  4.         setListViewHeightBasedOnChildren(lv);

  5. public void setListViewHeightBasedOnChildren(ListView listView) {
  6.         ListAdapter listAdapter = listView.getAdapter();
  7.         if (listAdapter == null) {
  8.                 return;
  9.         }
  10.         int totalHeight = 0;
  11.         for (int i = 0; i < listAdapter.getCount(); i++) {
  12.                 View listItem = listAdapter.getView(i, null, listView);
  13.                 listItem.measure(0, 0);
  14.                 totalHeight += listItem.getMeasuredHeight();
  15.         }
  16.         ViewGroup.LayoutParams params = listView.getLayoutParams();
  17.         params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
  18.         params.height += 5;// if without this statement,the listview will be a little short 
  19.         listView.setLayoutParams(params);
  20. }
複製代碼

7、ListView中如何優化圖片

       圖片的優化策略比較多。
       1、處理圖片的方式:
       如果ListView中自定義的Item中有涉及到大量圖片的,一定要對圖片進行細心的處理,因爲圖片佔的內存是ListView項中最頭疼的,處理圖片的方法大致有以下幾種:
       ①不要直接拿路徑就去循環BitmapFactory.decodeFile;使用Options保存圖片大小、不要加載圖片到內存去。
       對圖片一定要經過邊界壓縮尤其是比較大的圖片,如果你的圖片是後臺服務器處理好的那就不需要了
       ③在ListView中取圖片時也不要直接拿個路徑去取圖片,而是以WeakReference(使用WeakReference代替強引用。比如可以使用WeakReference mContextRef)、SoftReference、WeakHashMap等的來存儲圖片信息。
       ④在getView中做圖片轉換時,產生的中間變量一定及時釋放

       2、異步加載圖片基本思想:
       1)先從內存緩存中獲取圖片顯示(內存緩衝)
       2)獲取不到的話從SD卡里獲取(SD卡緩衝)
       3)都獲取不到的話從網絡下載圖片並保存到SD卡同時加入內存並顯示(視情況看是否要顯示)
       原理:
       優化一:先從內存中加載,沒有則開啓線程從SD卡或網絡中獲取,這裏注意從SD卡獲取圖片是放在子線程裏執行的,否則快速滑屏的話會不夠流暢。
       優化二:於此同時,在adapter裏有個busy變量,表示listview是否處於滑動狀態,如果是滑動狀態則僅從內存中獲取圖片,沒有的話無需再開啓線程去外存或網絡獲取圖片。
       優化三:ImageLoader裏的線程使用了線程池,從而避免了過多線程頻繁創建和銷燬,如果每次總是new一個線程去執行這是非常不可取的,好一點的用的AsyncTask類,其實內部也是用到了線程池。在從網絡獲取圖片時,先是將其保存到sd卡,然後再加載到內存,這麼做的好處是在加載到內存時可以做個壓縮處理,以減少圖片所佔內存。

8、ListView中圖片錯位的問題是如何產生的
      
       圖片錯位問題的本質源於我們的listview使用了緩存convertView,假設一種場景,一個listview一屏顯示九個item,那麼在拉出第十個item的時候,事實上該item是重複使用了第一個item,也就是說在第一個item從網絡中下載圖片並最終要顯示的時候,其實該item已經不在當前顯示區域內了,此時顯示的後果將可能在第十個item上輸出圖像,這就導致了圖片錯位的問題。所以解決之道在於可見則顯示,不可見則不顯示。

9、Java中引用類型都有哪些

       Java中對象的引用分爲四種級別,這四種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。

       強引用(StrongReference)

       這個就不多說,我們寫代碼天天在用的就是強引用。如果一個對象被被人擁有強引用,那麼垃圾回收器絕不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足問題。

       Java的對象是位於heap中的,heap中對象有強可及對象、軟可及對象、弱可及對象、虛可及對象和不可到達對象。應用的強弱順序是強、軟、弱、和虛。對於對象是屬於哪種可及的對象,由他的最強的引用決定。如下代碼:

  1. String abc=new String("abc");  //1       
  2. SoftReference<String> softRef=new SoftReference<String>(abc);  //2       
  3. WeakReference<String> weakRef = new WeakReference<String>(abc); //3       
  4. abc=null; //4       
  5. softRef.clear();//5 

複製代碼

       第一行在heap堆中創建內容爲“abc”的對象,並建立abc到該對象的強引用,該對象是強可及的。
       第二行和第三行分別建立對heap中對象的軟引用和弱引用,此時heap中的abc對象已經有3個引用,顯然此時abc對象仍是強可及的。
       第四行之後heap中對象不再是強可及的,變成軟可及的。
       第五行執行之後變成弱可及的。

       軟引用(SoftReference)

       如果一個對象只具有軟引用,那麼如果內存空間足夠,垃圾回收器就不會回收它,如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。

       軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
軟引用是主要用於內存敏感的高速緩存。在jvm報告內存不足之前會清除所有的軟引用,這樣以來gc就有可能收集軟可及的對象,可能解決內存吃緊問題,避免內存溢出。什麼時候會被收集
      
       取決於gc的算法和gc運行時可用內存的大小。當gc決定要收集軟引用時執行以下過程,以上面的softRef爲例:
       1、首先將softRef的referent(abc)設置爲null,不再引用heap中的new String("abc")對象。
       2、將heap中的newString("abc")對象設置爲可結束的(finalizable)。
       3、當heap中的new String("abc")對象的finalize()方法被運行而且該對象佔用的內存被釋放, softRef被添加到它的ReferenceQueue(如果有的話)中。
      
       注意:對ReferenceQueue軟引用和弱引用可以有可無,但是虛引用必須有。
       被 SoftReference 指到的對象,即使沒有任何 Direct Reference,也不會被清除。一直要到 JVM 內存不足且沒有Direct Reference 時纔會清除,SoftReference 是用來設計 object-cache 之用的。如此一來 SoftReference 不但可以把對象 cache 起來,也不會造成內存不足的錯誤(OutOfMemoryError)。
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg 
      
       弱引用(WeakReference)

       如果一個對象只具有弱引用,那該類就是可有可無的對象,因爲只要該對象被gc掃描到了隨時都會把它幹掉。弱引用與軟引用的區別在於:只具有弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。
弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。

       虛引用(PhantomReference)

       "虛引用"顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命週期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。虛引用主要用來跟蹤對象被垃圾回收的活動。
      
       虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序如果發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。

       建立虛引用之後通過get方法返回結果始終爲null,通過源代碼你會發現,虛引用通向會把引用的對象寫進referent,只是get方法返回結果爲null。先看一下和gc交互的過程再說一下他的作用。
       1、不把referent設置爲null, 直接把heap中的newString("abc")對象設置爲可結束的(finalizable)。
       2、與軟引用和弱引用不同, 先把PhantomRefrence對象添加到它的ReferenceQueue中.然後在釋放虛可及的對象。

八、JNI&NDK

1、在Android中如何調用C語言

       當我們的Java需要調用C語言的時候可以通過JNI的方式,Java Native Interface。Android提供了對JNI的支持,因此我們在Android中可以使用JNI調用C語言。在Android開發目錄的libs目錄下添加xxx.so文件,不過xxx.so文件需要放在對應的CPU架構名目錄下,比如armeabi,x86等。
       在Java代碼需要通過System.loadLibrary(libName);加載so文件。同時C語言中的方法在java中必須以native關鍵字來聲明。普通Java方法調用這個native方法接口,虛擬機內部自動調用so文件中對應的方法。

2、請介紹一下NDK

       1.NDK 是一系列工具的集合
       NDK 提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so 和java 應用一起打包成apk。NDK 集成了交叉編譯器,並提供了相應的mk 文件隔離CPU、平臺、ABI 等差異,開發人員只需要簡單修改mk 文件(指出“哪些文件需要編譯”、“編譯特性要求”等),就可以創建出so。
      
       2.NDK 提供了一份穩定、功能有限的API 頭文件聲明
       Google 明確聲明該API 是穩定的,在後續所有版本中都穩定支持當前發佈的API。從該版本的NDK 中看出,這些API支持的功能非常有限,包含有:C 標準庫(libc)、標準數學庫(libm)、壓縮庫(libz)、Log 庫(liblog)。

3、JNI調用常用的兩個參數
       JNIEnv*env, jobject obj
       第一個是指向虛擬機對象的指針,是一個二級指針。裏面封裝了很多方法和中間變量供我們使用。
       第二個代表着調用該方法的Java對象的C語言表示。

九、Android中的網絡訪問

       1、Android中如何訪問網絡

       Android提供了org.apache.http.HttpClientConnection和java.net.HttpURLConnection兩個連接網絡對象。使用哪個都行,具體要看企業領導的要求了。
       除此之外一般我比較喜歡使用xUtils中的HttpUtils功能,該模塊底層使用的就是org.apache.http.client.HttpClient,使用起來非常方便。

       2、如何解析服務器傳來的JSON文件

       在Android中內置了JSON的解析API,在org.json包中包含了如下幾個類:JSONArray、JSONObject、JSONStringer、JSONTokener和一個異常類JSONException。
      
       1、JSON解析步驟
       1)讀取網絡文件數據並轉爲一個json字符串
       InputStreamin = conn.getInputStream();
       StringjsonStr = DataUtil.Stream2String(in);//將流轉換成字符串的工具類
       2)將字符串傳入相應的JSON構造函數中
       ①通過構造函數將json字符串轉換成json對象
       JSONObject  jsonObject = new JSONObject(jsonStr);
       ②通過構造函數將json字符串轉換成json數組:
       JSONArray array = new JSONArray(jsonStr);
       3)解析出JSON中的數據信息:
       ①從json對象中獲取你所需要的鍵所對應的值
       JSONObject json=jsonObject.getJSONObject("weatherinfo");
       Stringcity = json.getString("city");
       Stringtemp = json.getString("temp")
       ②遍歷JSON數組,獲取數組中每一個json對象,同時可以獲取json對象中鍵對應的值
       for(int i = 0; i < array.length(); i++) {
              JSONObject obj = array.getJSONObject(i);
              Stringtitle=obj.getString("title");
              Stringdescription=obj.getString("description");
       }

       2、生成JSON對象和數組
       1)生成JSON:
       方法1、創建一個map,通過構造方法將map轉換成json對象
       Map<String,Object> map = new HashMap<String, Object>();
       map.put("name","zhangsan");
       map.put("age",24);
       JSONObjectjson = new JSONObject(map);
       方法2、創建一個json對象,通過put方法添加數據
       JSONObjectjson=new JSONObject();
       json.put("name","zhangsan");
       json.put("age",24);
       2)生成JSON數組:
       創建一個list,通過構造方法將list轉換成json對象
       Map<String,Object> map1 = new HashMap<String, Object>();
       map1.put("name","zhangsan");
       map1.put("age",24);
       Map<String,Object> map2 = new HashMap<String, Object>();
       map2.put("name","lisi");
       map2.put("age",25);
       List<Map<String,Object>> list=new ArrayList<Map<String,Object>>();
       list.add(map1);
       list.add(map2);
       JSONArrayarray=new JSONArray(list);
       System.out.println(array.toString());

3、如何解析服務器傳來的XML格式數據
      
       Android爲我們提供了原生的XML解析和生成支持。
       1、XML解析
              獲取解析器: Xml.newPullParser()
              設置輸入流: setInput()
              獲取當前事件類型: getEventType()
              解析下一個事件, 獲取類型: next()
              獲取標籤名: getName()
              獲取屬性值: getAttributeValue()
              獲取下一個文本: nextText() 
              獲取當前文本: getText() 
              5種事件類型: START_DOCUMENT, END_DOCUMENT, START_TAG,END_TAG, TEXT

       示例代碼:
  1. public List<Person>getPersons(InuptStream in){  
  2.        XmlPullParserparser=Xml.newPullParser();//獲取解析器
  3.        parser.setInput(in,"utf-8");
  4.        for(inttype=){   //循環解析
  5.        }      
  6. }
複製代碼

       2、XML生成
              獲取生成工具: Xml.newSerializer()
              設置輸出流: setOutput()
              開始文檔: startDocument()
              結束文檔: endDocument()
              開始標籤: startTag()
              結束標籤: endTag()
              屬性: attribute()
              文本: text()    

       示例代碼:
  1. XmlSerializer serial=Xml.newSerializer();//獲取xml序列化工具
  2. serial.setOuput(put,"utf-8");
  3. serial.startDocument("utf-8",true);
  4. serial.startTag(null,"persons");
  5. for(Person p:persons){
  6.        serial.startTag(null,"persons");      
  7.        serial.attribute(null,"id",p.getId().toString());
  8.        serial.startTag(null,"name");   
  9.        serial.attribute(null,"name",p.getName().toString());
  10.        serial.endTag(null,"name");
  11.        serial.startTag(null,"age");      
  12.        serial.attribute(null,"age",p.getAge().toString());
  13.        serial.endTag(null,"age");
  14.        serial.endTag(null,"persons");      
  15. }
複製代碼

4、如何從網絡上加載一個圖片顯示到界面

       可以通過BitmapFactory.decodeStream(inputStream);方法將圖片轉換爲bitmap,然後通過imageView.setImageBitmap(bitmap);將該圖片設置到ImageView中。這是原生的方法,還可以使用第三方開源的工具來實現,比如使用SmartImageView作爲ImageView控件,然後直接設置一個url地址即可。也可以使用xUtils中的BitmapUtils工具。

5、如何播放網絡視頻

       除了使用Android提供的MediaPlayer和VideoView外通常還可以使用第三方開源萬能播放器,VitamioPlayer。該播放器兼容性好,支持幾乎所有主流視頻格式。

十、Intent

1、Intent傳遞數據時,可以傳遞哪些類型數據?
       Intent可以傳遞的數據類型非常的豐富,java的基本數據類型和String以及他們的數組形式都可以,除此之外還可以傳遞實現了Serializable和Parcelable接口的對象。

2、Serializable和Parcelable的區別

       1.在使用內存的時候,Parcelable 類比Serializable性能高,所以推薦使用Parcelable類。
       2.Serializable在序列化的時候會產生大量的臨時變量,從而引起頻繁的GC。
       3.Parcelable不能使用在要將數據存儲在磁盤上的情況。儘管Serializable效率低點,但在這種情況下,還是建議你用Serializable 。
       實現:
       1、Serializable 的實現,只需要繼承Serializable 即可。這只是給對象打了一個標記,系統會自動將其序列化。
       2、Parcelabel 的實現,需要在類中添加一個靜態成員變量 CREATOR,這個變量需要繼承 Parcelable.Creator 接口。

  1. public class MyParcelable implements Parcelable {
  2.     private int mData;

  3.     public int describeContents() {
  4.         return 0;
  5.     }

  6.     public void writeToParcel(Parcel out, int flags) {
  7.         out.writeInt(mData);
  8.     }

  9.     public static final Parcelable.Creator<MyParcelable> CREATOR
  10.             = new Parcelable.Creator<MyParcelable>() {
  11.         public MyParcelable createFromParcel(Parcel in) {
  12.             return new MyParcelable(in);
  13.         }

  14.         public MyParcelable[] newArray(int size) {
  15.             return new MyParcelable[size];
  16.         }
  17.     };
  18.     
  19.     private MyParcelable(Parcel in) {
  20.         mData = in.readInt();
  21.     }
  22. }
複製代碼

3、請描述一下Intent 和 IntentFilter

       Android 中通過 Intent 對象來表示一條消息,一個 Intent對象不僅包含有這個消息的目的地,還可以包含消息的內容,這好比一封 Email,其中不僅應該包含收件地址,還可以包含具體的內容。對於一個 Intent 對象,消息“目的地”是必須的,而內容則是可選項。
      
       通過Intent 可以實現各種系統組件的調用與激活。
       IntentFilter: 可以理解爲郵局或者是一個信箋的分揀系統…
       這個分揀系統通過3個參數來識別
       Action: 動作    view 
       Data: 數據uri   uri 
       Category : 而外的附加信息 
      
       Action 匹配

       Action 是一個用戶定義的字符串,用於描述一個 Android 應用程序組件,一個 IntentFilter 可以包含多個 Action。在 AndroidManifest.xml 的 Activity 定義時可以在其 <intent-filter >節點指定一個 Action 列表用於標示 Activity 所能接受的“動作”,例如:
       <intent-filter > 
       <action android:name="android.intent.action.MAIN"/> 
       <actionandroid:name="cn.itheima.action" /> 
       ……
       </intent-filter> 

       如果我們在啓動一個 Activity 時使用這樣的Intent 對象:
       Intentintent =new Intent(); 
       intent.setAction("cn.itheima.action");

       那麼所有的 Action 列表中包含了“cn.itheima”的 Activity 都將會匹配成功。
       Android 預定義了一系列的 Action 分別表示特定的系統動作。這些Action 通過常量的方式定義在 android.content. Intent中,以“ACTION_”開頭。我們可以在 Android 提供的文檔中找到它們的詳細說明。

       URI 數據匹配

       一個 Intent 可以通過 URI 攜帶外部數據給目標組件。在 <intent-filter >節點中,通過 <data/>節點匹配外部數據。
       mimeType 屬性指定攜帶外部數據的數據類型,scheme 指定協議,host、port、path 指定數據的位置、端口、和路徑。如下:
<data android:mimeType="mimeType"android:scheme="scheme" 
android:host="host"android:port="port" android:path="path"/> 
       電話的uri   tel:12345 

       自己定義的uri itcast://cn.itcast/person/10
       如果在 Intent Filter 中指定了這些屬性,那麼只有所有的屬性都匹配成功時 URI 數據匹配纔會成功。

       Category 類別匹配

       <intent-filter >節點中可以爲組件定義一個 Category 類別列表,當 Intent 中包含這個列表的所有項目時 Category 類別匹配纔會成功。

十一、Fragment

1、Fragment跟Activity之間是如何傳值的
      
       當Fragment跟Activity綁定之後,在Fragment中可以直接通過getActivity()方法獲取到其綁定的Activity對象,這樣就可以調用Activity的方法了。在Activity中可以通過如下方法獲取到Fragment實例:

  1. FragmentManager fragmentManager = getFragmentManager();
  2.                 Fragment fragment = fragmentManager.findFragmentByTag(tag);
  3.                 Fragment fragment = fragmentManager.findFragmentById(id);

複製代碼

       獲取到Fragment之後就可以調用Fragment的方法。也就實現了通信功能。

2、描述一下Fragment的生命週期

 

3 0
發佈了26 篇原創文章 · 獲贊 53 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章