Android面試題

Service 是什麼,啓動方式區別,生命週期,如何停用

  • Android 的 Service有兩個作用,後臺或跨進程(AIDL)
  • 兩個啓動方法,分別是Context.startService()和Context.bindService()。
  • 生命週期根據啓動的方式有兩條
    • onCreate(), onStart(),onDestroy()。onCreate()在重複啓動時不會執行。
    • onCreate(), onBind(),onUnBind(),onDestroy()。只能綁定一次。
      -銷燬方法是onDestroy()。
  • 執行耗時操作建議使用Intent Service,自帶多線程且自動停止。

BroadcastReceiver是什麼,種類,註冊方式,生命週期

  • BroadcastReceiver系統或應用活動通知接收
  • 廣播分類
    • 普通廣播:Context.sendBroadcast(Intent myIntent)
    • 有序廣播:Context.sendOrderedBroadcast(intent, receiverPermission)第二個參數爲[-1000,1000]的優先級,1000爲最高優先等級。
    • 異步廣播:Context.sendStickyBroadcast(Intent myIntent),需要權限 <uses-permission android:name="android.permission.BROADCAST_STICKY" />,需要手動去掉removeStickyBroadcast(intent)
    • 有序異步廣播:sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras)
  • 動態註冊,註冊後才存在,需手動解除註冊;靜態(系統)註冊,App未啓動也存在
  • 生命週期會調方法:void onReceive(Context curContext,Intent broadcastMsg),只有10秒時間,如果超過則報ANR錯誤。

Broadcast、EventBus和自己實現觀察者模式的不同

  • Observable耦合度三者最高
  • Broadcast重量級,消耗資源較多,跨進程傳遞消息。與SDK鏈接緊密,許多系統級事件用廣播通知:網絡變化、電量、短信發送和接受狀態
  • EventBus輕量且快速,靈活不依賴Context,使用簡單。採用觀察者可能會造成接口膨脹
    現在比較流行的開源控件RxJava也是用的觀察者模式,可以很方便的使用RxJava實現個RxBus取代EventBus。

ContentProvider

  • ContentProvider使一個應用程序的指定數據集提供給其他應用程序。這些數據可以存儲在文件系統中、在一個SQLite數據庫、或以任何其他合理的方式。其他應用可以通過ContentResolver類(見ContentProviderAccessApp例子)從該內容提供者中獲取或存入數據
  • 通過Uri進行訪問。Uri uri = Uri.parse("content://com.bing.provider.personprovider/person")
  • ContentUris類使用
    • Uri uri = Uri.parse("content://com.bing.provider.personprovider/person");
    • Uri resultUri = ContentUris.withAppendedId(uri, 10);
    • //生成後的Uri爲:content://com.bing.provider.personprovider/person/10
    • Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10")
    • long personid = ContentUris.parseId(uri);
    • //獲取的結果爲:10

Android系統中GC什麼情況下會出現內存泄露呢

導致內存泄露主要的原因是:沒有釋放申請的內存空間,垃圾回收器GC無法回收內存。如下情況能導致內存溢出:

  1. 數據鏈接沒有關閉。如數據庫contentprovider,io,sokect,cursor等
  2. (匿名)內部類對象會持有宿主類的強應用this。如果是new Thread這類操作,線程沒有執行結束時當前Activity不會被回收。(常見Handler操作)
  3. Context引用。TextView等等都會持有上下文引用,如果有static drawable,就會導致內存無法釋放
  4. 靜態集合類。,ashMap,Vector等。如果是靜態集合,要及時setnull,否則就會一直持有這些對象
  5. observer 我們在使用監聽器的時候,addxxxListener(),記得不用時removexxxListener,否則內存leak
  6. Bitmap對象不使用時,採用recycle()釋放內存

Android UI中的View如何刷新

要分清View刷新的情景:多線程和雙緩衝。

  • 不使用多線程和雙緩衝。View發生改變重繪,使用View.invalidate()激活View.onDraw()方法。
  • 使用多線程和不使用雙緩衝。注意非UI線程不能對UI進行直接操作,需要用Handler進行線程通訊,在UI線程對View進行重繪。
  • 使用多線程和雙緩衝。SurfaceView直接實現了雙緩衝,實現Surfaceholder.Callback接口。Surfaceholder.lockCanvas()鎖定畫布,Surfaceholder.unlockCanvasandPost()解鎖畫布。

事件分發機制

Android開發: View - 事件分發

Handler機制,線程通訊

Android提供Handler來滿足線程間的通訊。

  • Handler:Message處理和發送。
    • 通過Looper獲取MessageQueue中的消息(FIFO)
    • 通過Looper將消息放入MessageQueue
  • Looper:輪詢器
    • 每個線程有且只有一個私人的Looper
    • 初始化Looper對象,此對象會擁有一個Message Queue對象
    • 主線程(UI線程)會自動初始化Looper。其他線程需要Looper.prepare()初始化,Looper.loop()開始輪詢
  • MessageQueue:消息隊列
    • 存放一個線程的Message所有
    • Message被髮送給Hander並處理後不會回收或刪除,會被標記爲閒置狀態
    • Handler發送消息是先檢索Message Queue中沒有沒閒置Message,有直接複用,否則新建Message。減少GC回收工作壓力,增加內存Leak危險

Handler使用時要特別注意內存Leak:

  1. 要使用 static 靜態類定義Handler派生類
  2. static 下訪問當前對象的字段屬性時需要弱引用(WeakReference)
  3. 在Activity銷燬時調用 Handler.removeMessages()將MessageQueue中的消息全部移除
  4. 自定義控件在定義Handler時,儘量使用new Handler(Looper.getMainLooper()), 不要使用new Handler()。因爲後者默認在主線程中執行,事實上有些View的創建在子線程中執行。

進程通訊(AIDL等)

  • 訪問其他App的Activity:需要App開放支持
  • 內容提供者Content Provider :使用Content Provider提供數據,使用Content Resolver讀取數據。
  • 廣播Broadcast:被動接受信息
  • AIDL服務:Android Interface Definition Language 安卓接口定義語言
    • AIDL服務提供程序:建立AIDL文件;在文件中創建接口申明方法;註冊Service實現聲明的接口方法
    • AIDL服務使用程序:copy自動生成的Java文件;建立ServiceConnection對象並綁定AIDL服務;獲取AIDL中的接口對象並調用放發獲取Value

AIDL步驟(一):提供AIDL服務
在package目錄下建立aidl.aidl文件,申明方法,類似接口

package  包名.aidl;  
interface  IMyService  {  
    //爲AIDL服務的接口方法,調用AIDL服務的程序需要調用該方法   
    String getValue();
} 

ODT會在gen目錄下產生一個IMyService.java文件,此文件ODT自動維護,開發者無需在意。

public  class  MyService extends  Service  {  
    //  IMyService.Stub類是根據IMyService.aidl文件生成的類
    //  該類中包含了接口方法(getValue)   
    public  class  MyServiceImpl extends  IMyService.Stub  {  
        @Override   
        public  String getValue() throws  RemoteException  {  
            return  "從AIDL服務獲得的值." ;  
        }  
    }  

    @Override   
    public  IBinder onBind(Intent intent)  {          
        //該方法必須返回MyServiceImpl類的對象實例   
        return  new  MyServiceImpl();  
    }  
}  

manifest配置信息

<!--  註冊服務 --> 
<service android:name=".MyService" >  
    <intent-filter>  
        <!--  指定調用AIDL服務的ID  -->  
        <action android:name="net.blogjava.mobile.aidlservice.IMyService"  />  
    </intent-filter>  
</service>  

AIDL步驟(二):使用AIDL服務

public class Main extends Activity implements OnClickListener  {  
private IMyService myService = null;  
    //  創建ServiceConnection對象  
    private ServiceConnection serviceConnection = new ServiceConnection(){  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service){  
            // 獲得AIDL服務對象  
            // IMyService.java是AIDL提供工程中自動生成的
            myService = IMyService.Stub.asInterface(service);  
            try  {  
                //  調用AIDL服務對象中的getValue方法
                //  並以對話框中顯示該方法的返回值  
                new AlertDialog
                        .Builder(Main.this)
                        .setMessage(myService.getValue())
                        .setPositiveButton("確定", null)  
                        .show();  
            }catch (Exception e)  {  
            }  
        }  

        @Override  
        public void onServiceDisconnected(ComponentName name)  
        {  
        }  
    };  

    @Override  
    public void onClick(View view){  
        //綁定AIDL服務  
        bindService(new Intent("提供程序包名.aidl.IMyService"),  
                serviceConnection, Context.BIND_AUTO_CREATE);  
    }  
}  

如何對 Android 應用進行性能分析

android 性能主要之響應速度 和UI刷新速度。
可以參考博客:Android系統性能調優工具介紹
首先從函數的耗時來說,有一個工具TraceView 這是androidsdk自帶的工作,用於測量函數耗時的。
UI佈局的分析,可以有2塊,一塊就是Hierarchy Viewer 可以看到View的佈局層次,以及每個View刷新加載的時間。
這樣可以很快定位到那塊layout & View 耗時最長。
還有就是通過自定義View來減少view的層次。

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

  1. 使用Shared Preferences存儲數據,用來存儲key-value,pairs格式的數據,它是一個輕量級的鍵值存儲機制,只可以存儲基本數據類型。
  2. 使用文件存儲數據,通過FileInputStream和FileOutputStream對文件進行操作。在Android中,文件是一個應用程序私有的,一個應用程序無法讀寫其他應用程序的文件。
  3. 使用SQLite數據庫存儲數據,Android提供的一個標準數據庫,支持SQL語句。
  4. 使用Content Provider存儲數據,是所有應用程序之間數據存儲和檢索的一個橋樑,它的作用就是使得各個應用程序之間實現數據共享。它是一個特殊的存儲數據的類型,它提供了一套標準的接口用來獲取數據,操作數據。系統也提供了音頻、視頻、圖像和個人信息等幾個常用的Content Provider。如果你想公開自己的私有數據,可以創建自己的Content Provider類,或者當你對這些數據擁有控制寫入的權限時,將這些數據添加到Content Provider中實現共享。外部訪問通過Content Resolver去訪問並操作這些被暴露的數據。
  5. 使用網絡存儲數據

描述一下Intent 和 Intent Filter

  • Intent在Android中被翻譯爲”意圖”,他是三種應用程序基本組件-Activity,Service和broadcast receiver之間相互激活的手段。在調用Intent名稱時使用ComponentName也就是類的全名時爲顯示調用。這種方式一般用於應用程序的內部調用,因爲你不一定會知道別人寫的類的全名。
  • Intent Filter是指意圖過濾,不出現在代碼中,而是出現在android Manifest文件中,以<intent-filter>的形式。(有一個例外是broadcast receiver的intent
    filter是使用Context.registerReceiver()來動態設定的,其中intent filter也是在代碼中創建的)而一個intent有action,data,category等字段。一個隱式intent爲了能夠被某個intent filter接收,必須通過3個測試,一個intent爲了被某個組件接收,則必須通過它所有的intent filter中的一個。

談談對Android NDK的理解。

android NDK是一套工具,允許Android應用開發者嵌入從C、C++源代碼編譯來的本地機器代碼到各自的應用軟件包中。
1、 NDK是一系列工具的集合。
NDK提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應用一起打包成apk。這些工具對開發者幫助時巨大的。
NDK集成了交叉編輯器,並提供了相應的mk文件隔離CPU、平臺、API等差異,開發人員只需要簡單修改mk文件(指出“那些文件需要編譯”、“編譯特性要求”等),就可以創建出so。NDK可以自動將so和Java應用一起打包,極大的減輕了開發人員的打包工作。
2、NDK提供了一份穩定、功能有限的API頭文件聲明。
這些API支持的功能非常有限,包含有:C標準庫(libc)、標準數學庫(libm)、壓縮庫(libz)、log庫(liblog)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章