Android面試題(三)

1、**Handler的使用場景?它有什麼做用? **java

在使用線程間通訊的時候會使用到Handler,它的做用就是實現線程間的通信。android

通常來講,若是在Activity中有一些耗時的操做,好比說訪問網絡、讀寫sd卡等,那麼此時咱們一般會彈出一個加載框,而後會啓動一個子線程,讓子線程去作耗時的操做。當子線程任務完成以後,那麼此時子線程須要通知主線程,讓主線程將加載框消失,這時候就須要使用到Handler了。算法

在咱們使用廣告輪播條的時候,咱們要讓廣告輪播條自動滑動起來,這時候,咱們也會使用到Handler,讓它發送一個延時消息。sql

因此總結來講,Handler的做用有兩點:一、發送消息 二、處理消息數據庫

還須要注意的是,Handler在哪一個線程中建立,那麼這個Handler維護的就是那個線程的消息隊列。apache

在回答Handler的使用的時候,咱們也能夠結合谷歌電子市場這個項目說說對Handler的使用。咱們會在Application的onCreate方法中把主線程的Handler給建立出來,而後經過封裝一些方法讓別人能夠很方便的往主線程的消息隊列發消息,就能夠避免出現主線程的Handler建立太多的實例對象,也能夠方便代碼的書寫。api

2、你能說說**Handler的機制嗎? **緩存

說到Handler的機制,有幾個很是重要的對象,分別是:Handler、Message、MessageQueue和Looper安全

任何一個線程,不管是子線程仍是主線程,都維護着一個消息隊列,這個消息隊列就是MessageQueue,有了這個消息隊列以後,是須要不斷的從這個消息隊列裏面取出消息進行處理的,這個就是Looper作的事情。那麼,誰往這個消息隊列裏面丟消息呢?那就是Handler了。服務器

Looper是用來管理所屬線程的消息隊列MessageQueue的。

每個線程都須要有一個looper,每個looper管理一個MessageQueue.

Handler.sendMessage的意思是將某一個message放到MessageQueue中去,looper是個死循環,不斷的讀MessageQueue中的新消息。

要讓looper的死循環運行起來,得調用Looper.loop()方法。

咱們一般都會在子線程中,發一個消息到主線程中的MessageQueue中去。Handler究竟是往主線程的MessageQueue發送消息呢仍是往子線程的         MessageQueue發送消息呢?這取決於Handler在哪裏建立。若是Handler在主線程中建立,那麼這個Handler就會把消息發到主線程的消息隊列,若是Handler是在子線程中建立,這個Handler就會把消息發到子線程的消息隊列。這裏須要注意的是,子線程的Looper須要咱們本身手動啓動,要調用Looper.prepare()和Looper.loop()方法,主線程的Looper系統已經幫咱們啓動了,所以咱們不須要爲主線程的Looper調用loop()方法

** **

3、如何從主線程發消息給子線程?主要注意什麼?** **

大多狀況下,咱們都是從子線程發送消息到主線程中,讓主線程進行一些ui操做,其實主線程也是能夠發送消息給子線程的。

若是須要實現主線程發送消息給子線程,那麼就須要往子線程的消息隊列MessageQueue裏面放消息。那麼誰能往子線程的MessageQueue放消息呢?答案就是子線程的Handler,因此,咱們要建立出子線程的Handler對象。

這時候須要注意的是,當咱們在子線程建立了Handler對象,子線程內部的Looper對象是不工做,因此不能造成消息循環。要讓Looper工做起來,得調用Looper.prepare()和Looper.loop()方法。具體的代碼以下:

public Handler mHandler;

class LooperThread extends Thread {

public void run() {

Looper.prepare();

mHandler = new Handler() {

public void handleMessage(Message msg) {

}

};

Looper.loop();

}

}

那麼如今有人就有疑問了,咱們絕大多數狀況下使用Handler並無調用Looper.prepare()和Looper.loop()方法。其實主線程的這兩個方法系統在應用程序一啓動的時候就幫咱們調用了。具體代碼以下:

在ActivityThread中的main方法:

public static final void main(String[] args) {

...

Looper.prepareMainLooper();

...

Looper.loop();

...

}

4、你有沒有使用過**AsyncTask****,怎麼使用?**** **

使用過,可是用的很少。AsyncTask是用來處理異步任務的,和咱們本身建立子線程,而後經過handler進行通訊的效果差很少,只不過AsyncTask內部維護了一個線程池,用這個線程池來控制住線程的數量。

使用AsyncTask的話,通常咱們會new出這個類的對象,而後執行execute方法。

new AsyncTask<String, String, String>() {

// 2. 運行在主線程中 , 作一些準備操做 .

public void onPreExecute() {

}

// 3. 運行在子線程中 , 作一些耗時的任務 .

public String doInBackground(String... params) {

return null;

}

// 4. 運行主線程中 , result 就是 doInBackground 方法返回的值 . 作一些收尾操做 .

public void onPostExecute(String result) {

}

}.execute(String... params);    // 1. 開始執行異步任務 .

** **

5**、說說你對Application的理解?你在項目中使用到Application嗎?具體拿它作什麼?**** **

Application能夠理解爲是一個程序的入口,運行中的應用程序必然會建立Application的實例。它的特色是:生命週期長,onCreate能夠認爲是程序第一個執行的方法,而且運行在主線程中。在項目中,會有幾種場景使用到Application

a、在onCreate中能夠判斷軟件是否第一次運行

@Override

public void onCreate() {

super.onCreate();

SharedPreferences  sp = getSharedPreferences("setting",                    Context.MODE_WORLD_WRITEABLE);

boolean firstStart = sp.getBoolean("version_1", false);

if(!firstStart) {

// 這裏能夠作一些相似於初始化內置數據的工做

Editor editor = sp.edit();

editor.putBoolean("version_1", true);

editor.commit();

}

}

b、因爲生命週期長,能夠存儲一些數據,也能夠幫忙傳遞數據

c、捕獲全局異常,避免彈出Force Close框

public void onCreate() {

super.onCreate();

Thread.setDefaultUncaughtExceptionHandler(this);

}

@Override

public void uncaughtException(Thread thread, Throwable ex) {

Intent intent = new Intent(getApplicationContext(), MainActivity.class);

PendingIntent restartIntent = PendingIntent.getActivity(

getApplicationContext(), 0, intent,

Intent.FLAG_ACTIVITY_NEW_TASK);

AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000,

restartIntent);

}

d、獲取context對象,這樣的話,別的地方就能夠很是方便的建立View了。這裏須要注意一點的是,建立dialog是不能使用Application的context的,須要使用activity的context

e、建立主線程的handler,供別人使用

f、獲取主線程的線程id

@Override

public void onCreate() {

super.onCreate();

//context 常用到。 Toast 、 new View

context = getApplicationContext();

// 獲得主線程的 handler 對象,維護的是主線程的 MessageQueue

handler = new Handler();

// 哪一個方法調用了 myTid , myTid 返回的就是那個方法所在的線程 id

mainThreadId = android.os.Process.myTid();

}

6、**Android 中佈局的優化措施都有哪些? **

a、儘量減小布局的嵌套層級

儘可能多用RelativeLayout能夠頗有效的減小布局的嵌套層級。也可使用       hierarchyviewer這個工具來檢查佈局層次

b、不用設置沒必要要的背景,避免過分繪製        好比父控件設置了背景色,子控件徹底將父控件給覆蓋的狀況下,那麼父控件就沒     有必要設置背景。

c、使用標籤複用相同的佈局代碼 d、使用標籤減小視圖層次結構

e、經過實現 View 的延遲加載

八、如何使用數據庫 傳統的方式咱們會使用到SQLiteOpenHelper ,而後會本身寫一些sql語句,在真實的開發當中,咱們每每會藉助於一些第三方框架幫咱們處理數據相關的邏輯,這樣能夠幫助咱們幫主要精力花在其餘功能實現上。我比較經常使用的是LitePal,它能夠幫咱們很輕鬆的進行數據庫操做。

九、圖片的優化 對於程序來講,圖片的優化主要分爲大圖片的優化和多圖片優化。 對於單張大圖片的優化來講,通常的思路是這樣的:咱們的手機是不須要顯示過高分辨率的圖片,由於手機的分辨率就那麼高,圖片的分辨率再高也是沒有什麼效果的。這種狀況下,咱們會選擇對圖片進行縮放。通常咱們會先獲得控件的大小,而後再獲得圖片的大小。根據他們的值,計算出一個比例進行縮放。具體的代碼以下: 僅請求圖片的大小,inJustDecodeBounds = true,僅請求圖片大小,而不會加載圖片到內存; public Bitmap decodeBitmap(){ BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 經過這個bitmap獲取圖片的寬和高 Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.jpg", options); float realWidth = options.outWidth; float realHeight = options.outHeight; // 計算縮放比 int scale = (int) ((realHeight > realWidth ? realHeight : realWidth) / 控件的高度或寬度); if (scale <= 0) { scale = 1; } options.inSampleSize = scale; options.inJustDecodeBounds = false; // 注意此次要把options.inJustDecodeBounds 設爲 false,此次圖片是要讀取出來的。 bitmap = BitmapFactory.decodeFile("/sdcard/MTXX/test.jpg", options); return bitmap; } 而圖片優化的另一方面則是關於多圖片的優化。爲了程序的流暢度,咱們每每會把以前建立好圖片緩存起來,以便下一次顯示這張圖片的話能夠更快的加載。一般是內存緩存--本地緩存。而咱們應用程序的內存大小是有限的,咱們不能無限制的將全部的圖片都緩存到內存中,這就須要控制住內存中圖片的緩存數量。一般咱們會使用LRUCache來對內存中的圖片數量進行一個控制。其實在xutils中的BitmapUtils它自身就帶來LRUCache功能。 使用緩存不是爲了解決OOM,偏偏正是由於使用了緩存,才形成了OOM

十、內存泄露和內存溢出的差異 內存泄露和內存溢出是徹底不一樣的兩個概念。內存泄露指的是因爲開發人員的疏忽,使得一些不可能被使用到的對象沒法被垃圾回收器回收,隨着時間的堆積,內存不斷增長,直到內存溢出。 因此內存泄露會形成內存溢出,而內存溢出,不必定是內存泄露形成的。 內存泄漏自己不會產生什麼危害,真正有危害的是內存泄漏的堆積。Android應用內存 泄漏的的緣由有如下幾個: a、register以後沒有unregister,add以後沒有remove b、查詢數據庫後沒有關閉遊標cursor file沒有close c、構造Adapter時,沒有使用 convertView 重用 d、Bitmap對象不在使用時調用recycle()釋放內存 e、對象被生命週期長的對象引用,如activity被靜態集合引用致使activity不能釋放

十一、怎麼解決OOM異常 分析方向分爲兩個,一個是分析圖片,絕大多數的OOM都是因爲圖片引發的。二是分析是否存在內存泄露的狀況。

12、如何進行內存分析** **

heap工具

Heap 視圖中部有一個Type叫作data object,即數據對象,也就是咱們的程序中大量存在的類類型的對象。在data object 一行中有一列是「Total Size」,其值就是當前進程中全部Java 數據對象的內存總量,通常狀況下,這個值的大小決定了是否會有內存泄漏。

若是這個值不斷的增長,那麼就判斷可能有內存泄露的狀況。若是這個值穩定在必定範圍以內,會變大,也會變小,那麼就說明此時的內存狀態挺好。 **

mat:mat是一個更爲強大的內存分析工具,能夠經過內存快照分析出內存的信息。

有的公司本身也會開發出專門分析內存的工具。

13、寫出幾種你認爲能夠提升**Android程序運行效率的方法? **

一、釋放主線程

二、優化界面顯示的佈局,包括層次、背景

三、儘可能減小對象的建立,好比咱們常常會在getView中new 出一個時間監聽者

四、避免內存泄露

能夠經過traceview工具來查看方法運行的時間。

14、**Android **中如何訪問網絡 Android 提供了 org.apache.http.HttpClientConnection 和 java.net.HttpURLConnection 兩個鏈接網絡對象。除此 之 外 一 般 我 比 較 喜 歡 使 用 xUtils 中的 HttpUtils 功能 , 該 模 塊 底 層 使 用 的 就 是org.apache.http.client.HttpClient,使用起來很是方便。還有一些其餘的網絡訪問框架:

OKhttp、Volley等

15、與服務器接口的訪問** **

與服務器訪問的方式有HTTP和SOCKET兩種。

Http:好比說新聞列表啥的,都是事先和服務器的開發人員肯定好具體的訪問地址,而後客戶端使用get或者post請求去訪問那個地址就能獲得具體的數據了。

Socket:,服務端開啓ServerSocket,客戶端開啓 socket,而後客戶端跟服務端創建長鏈接,這樣實現了客戶端跟服務端數據的即時通訊    保證數據安全。像推送、聊天通常就是使用這種訪問方式。

關於數據安全:咱們的數據有些是須要安全設置的有些不須要,咱們的新聞類數據不須要特殊的添加安全設置,而用戶註冊,用戶登陸以及用戶隱私數據保存是考慮安全性的。用戶的密碼等信息確定不能進行明文傳輸的,咱們將用戶的密碼在本地進行了MD5 算法的加密,而後再傳輸。同時保存在本地的時候也是加密後的數據。還有須要安全性更高的數據須要經過咱們自定義協議經過Socket 傳輸。

16、數據傳遞** **

a、intent:intent能夠傳遞一些基本的數據類型:String、int、float等,也能夠傳遞對象,這個對象必須實現Parcelable接口。實現Parcelable接口表明這個對象能夠被序列化到內存中,和Serializable相似,只不過Serializable是將對象寫到文件當中。

b、Application:Application的特色:一個應用程序運行的時候只有一個,它的生命週期是最長的,比Activity、Service都長,只要這個程序在運行,不管是否在前臺,它都會有一個Application對象。往Application存某一些對象,在頁面跳轉的時候會很是方便。

c、能夠經過view.setTag來進行數據的傳遞。

17、如何將一個** java 對象序列化到文件裏,Android如何經過intent傳遞對象? **

在 java 中可以被序列化的類必須先實現 Serializable 接口,該接口沒有任何抽象方法只是起到一個標記做用。

// 對象輸出流

ObjectOutputStream objectOutputStream = new ObjectOutputStream(new

FileOutputStream(new File("D://obj")));

objectOutputStream.writeObject(new User("zhangsan", 100));

objectOutputStream.close();

// 對象輸入流

ObjectInputStream objectInputStream = new ObjectInputStream(new

FileInputStream(new File("D://obj")));

User user = (User)objectInputStream.readObject();

System.out.println(user);

objectInputStream.close();

Activity若是但願經過Intent來傳遞一個對象的話,這個對象必須實現Parcelable接口。實現Parcelable接口表明這個對象能夠被序列化到內存中

19、在項目中,你是如何作版本適配** **

對於版本適配的話,咱們在應用程序中一般最低只會適配到2.2,不過因爲如今市場上絕大多數的手機系統版本已是4.0以上的了,如今也慢慢的愈來愈少的適配4.0如下的系統。

在適配的時候,主要有幾個jar咱們會常用到:v4包,v7包,nineoldandroid包。

v4:Fragment、ViewPager

v7:ActionBar的適配

nineoldandroid:屬性動畫的適配

有時候咱們也會在代碼中進行系統版本號的判斷。好比更改狀態欄顏色。咱們會判斷系統版本是否是大於等於4.4,若是是的話纔會調用相關的api,不然咱們就不修改狀態欄顏色。

** **

20、屏幕適配** **

佈局適配:多用相對佈局

圖片適配:在不一樣的文件夾下放下對應的圖片。drawable-xhdpi、drawable-xxhdpi

單位適配:在佈局文件中儘可能寫dp,在java代碼中儘可能將dp轉化成px

代碼適配:有時候須要根據屏幕的寬高動態計算控件的寬高

21、自定義控件,有沒有本身寫過自定義控件** **

這個能夠結合本身開發經從來說,好比下拉刷新ListView、滑動開關都是比較好說的。

說到自定義控件,還有幾個知識點可能會被問到。

a、自定義屬性

b、view的繪製流程:onMeasure、onLayout、onDraw

22、如何實現**SlidingMenu的效果 **

能夠藉助ViewDragHelper來幫助咱們完成控件拖動的效果。

相關文章
相關標籤/搜索