2020 Android 面試總結 - 01

  1. 字符串反轉

寫出一個程序,接受一個字符串,然後輸出該字符串反轉後的字符串。例如:輸入 abcd,輸出:dcba

    public String reverseString(String s) {
        char[] chars = s.toCharArray();
        char temp;
        int length = chars.length;
        for (int i = 0 ,j = length -1; i < length / 2 ; i++,j--) {
            temp = chars[i];
            chars[i] = chars[j];
            chars[j] = temp;
        }
        return new String(chars);
    }

我們只需要遍歷char數組中一半的元素就可以了,數組長度爲奇數時中間的一個元素不需要管,而int類型除以2正好沒有帶上中間的元素。
參考文章https://blog.csdn.net/zhwyj1019/article/details/81876505

  1. 字符串匹配問題

對於字符串str,其中絕對不含有字符’.’和‘’。再給定字符串exp,其中可以含有’.’或’‘’,’’字符不能是exp的首字符,並且任意兩個’‘字符不相鄰。exp中的’.’代表任何一個字符,exp中的’’表示’‘的前一個字符可以有0個或者多個。請寫一個函數,判斷str是否能被exp匹配(注意:輸入的數據不保證合法,但只含小寫字母和‘.’和‘*’)。

遞歸版本:

class Solution {
    public boolean isMatch(String s, String p) {
        if(s == null || p == null)
            return false;
        char[] str = s.toCharArray();
        char[] pat = p.toCharArray();
        return isValid(str, pat) ? process(str, pat, 0, 0):false;
    }
    
    public boolean isValid(char[] s, char[] p){//判斷輸入是否有效
        for(int i = 0; i < s.length; i++){
            if(s[i] == '*' || s[i] == '.')
                return false;
        }
        for(int i = 0; i < p.length; i++){
            if(p[i] == '*' &&(i == 0 || p[i-1] == '*'))
                return false;
        }
        return true;
    }
    
    public boolean process(char[] s, char[] p, int si, int pi){//判斷是否匹配
        if(pi == p.length)
            return si == s.length;
        if(pi +1  == p.length || p[pi+1] != '*'){
            return si != s.length && (p[pi] == s[si] || p[pi] == '.') && process(s,p,si+1,pi+1);
        }
        while(si != s.length && (p[pi] == s[si] || p[pi] == '.')){
            if(process(s,p,si,pi+2))
                return true;
            si++;
        }
        return process(s,p,si,pi+2);
    }
}

首先,isValid函數判斷輸入是否有效,即字符串s不能包含’‘和’.’,字符串p中’‘字符前不能爲空或者字符’*’

判斷函數process用遞歸判斷,pi,si分別標識字符串p和s的所在位置,即判斷字符串p的pi位置之後的字符串是否能模式識別字符串s的si位置之後的字符串。

如果p的pi+1位置不是’*'的話,只需要判斷現在所處的字符是否匹配,並且再繼續往下判斷si+1和pi+1位置後的字符串。

如果p的pi+1位置是’‘的話,兩種情況:一是’‘代表0次,我們判斷p的pi+2位置之後和s的si位置之後即可;二是’*'代表非0次,我們保持pi不動,si向後移直到跳出循環即可。

原文鏈接:https://blog.csdn.net/wannuoge4766/article/details/89211379

  1. 選擇題
    Service中如何實現更改Activity界面元素(B)
    A . 通過把當前activity對象傳遞給service對象
    B . 通過向Activity發送廣播
    C . 通過Context對象更改Activity界面元素
    D . 可以在Service中,調用Activity的方法實現更改界面元素

  2. 選擇題
    下列哪些語句關於內存回收的說明是正確的? (B)
    A.程序員必須創建一個線程來釋放內存
    B.內存回收程序負責釋放無用內存
    C.內存回收程序允許程序員直接釋放內存
    D.內存回收程序可以在指定的時間釋放內存對象

  3. 選擇題
    下面關於Android dvm的進程和Linux的進程,應用程序的進程說法正確的是(D)
    A. DVM指dalvik的虛擬機.每一個Android應用程序都在它自己的進程中運行,不一定擁有一個獨立 的Dalvik虛擬機實例.而每一個DVM都是在Linux中的一個進程,所以說可以認爲是同一個概念.
    B. DVM指dalvik的虛擬機.每一個Android應用程序都在它自己的進程中運行,不一定擁有一個獨立的Dalvik虛擬機實例.而每一個DVM不一定都是在Linux 中的一個進程,所以說不是一個概念.
    C. DVM指dalvik的虛擬機.每一個Android應用程序都在它自己的進程中運行,都擁有一個獨立的Dalvik虛擬機實例.而每一個DVM不一定都是在Linux 中的一個進程,所以說不是一個概念
    D. DVM指dalvik的虛擬機.每一個Android應用程序都在它自己的進程中運行,都擁有一個獨立的 Dalvik虛擬機實例.而每一個DVM都是在Linux 中的一個進程,所以說可以認爲是同一個概念.

  4. 有序廣播和無序廣播的區別

1.無序廣播
通過Context.sendBroadcast()方法來發送,它是完全異步的。
所有的receivers(接收器)的執行順序不確定,因此所有的receivers(接收器)接收broadcast的順序不確定。
這種方式效率更高,但是BroadcastReceiver無法使用setResult系列、getResult系列及abortbroadcast(中止)系列API。
廣播不能被終止,數據不能被修改。
2.有序廣播
有序廣播,即從優先級別最高的廣播接收器開始接收,接收完了如果沒有丟棄,就下傳給下一個次高優先級別的廣播接收器進行處理,依次類推,直到最後。如果多個應用程序設置的優先級別相同,則誰先註冊的廣播,誰就可以優先接收到廣播。通過Context.sendorderBroadCast()方法來發送,sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras);,其中的參數resultReceiver,可以自己重寫一個類,作爲一個最終的receive 最後都能夠接收到廣播,最終的receiver 不需要再清單文件裏面配置,initialData可以作爲傳輸的數據
廣播可以被終止,數據傳輸過程中可以被修改。
參考 https://blog.csdn.net/ldc_123/article/details/52801218

  1. 全局廣播和本地廣播的區別(自我拓展:本地廣播和全局廣播的實現原理)

1、本地廣播:發送的廣播事件不被其他應用程序獲取,也不能響應其他應用程序發送的廣播事件。本地廣播只能被動態註冊,不能靜態註冊。動態註冊或方法時需要用到LocalBroadcastManager。
2、全局廣播:發送的廣播事件可被其他應用程序獲取,也能響應其他應用程序發送的廣播事件(可以通過 exported–是否監聽其他應用程序發送的廣播 在清單文件中控制) 全局廣播既可以動態註冊,也可以靜態註冊。
參考https://blog.csdn.net/look_future/article/details/79672760

  1. 靜態廣播和動態廣播的區別

動態註冊和靜態註冊的區別:

  1. 動態註冊的廣播會受Activity的生命週期的影響, 當Activity銷燬的時候,廣播就失效了。
  2. 而靜態註冊的廣播,即使Activity銷燬了,仍然可以收到廣播。更牛掰的是即使殺死進程,仍然可以收到廣播。

參考 https://www.cnblogs.com/lang-yu/p/6170325.html

  1. 冷啓動和熱啓動,關於啓動時間的日子打印應該在哪個方法裏面

這個題目其實是考察冷啓動和熱啓動的不同流程:
1.冷啓動和熱啓動的概念:
冷啓動:
在啓動應用時,系統中沒有該應用的進程,這時系統會創建一個新的進程分配給該應用;
熱啓動:
在啓動應用時,系統中已有該應用的進程(例:按back鍵、home鍵,應用雖然會退出,但是該應用的進程還是保留在後臺);

2.冷啓動、熱啓動的區別
冷啓動:系統沒有該應用的進程,需要創建一個新的進程分配給應用,所以會先創建和初始化Application類,再創建和初始化MainActivity類(包括一系列的測量、佈局、繪製),最後顯示在界面上。
熱啓動: 從已有的進程中來啓動,不會創建和初始化Application類,直接創建和初始化MainActivity類(包括一系列的測量、佈局、繪製),最後顯示在界面上。
3.冷啓動時間的計算
API19 之後,系統會出打印日誌輸出啓動的時間;
冷啓動時間 = 應用啓動(創建進程) —> 完成視圖的第一次繪製(Activity內容對用戶可見);
4.冷啓動流程
Zygote進程中fork創建出一個新的進程;
創建和初始化Application類、創建MainActivity;
inflate佈局、當onCreate/onStart/onResume方法都走完;
contentView的measure/layout/draw顯示在界面上;
總結:
Application構造方法 –> attachBaseContext() –> onCreate() –> Activity構造方法 –> onCreate() –> 配置主題中背景等屬性 –> onStart() –> onResume() –> 測量佈局繪製顯示在界面上。

  1. 冷啓動和熱啓動的時間是因爲 app 業務影響,還是因爲系統影響。(各自的啓動時間受什麼影響?)

這個題目承接上面的問題,考察的是冷啓動的優化問題:冷啓動受業務 app 的影響,我們應該:
1.減少在Application和第一個Activity的onCreate()方法的工作量;
2.不要讓Application參與業務的操作;
3.不要在Application進行耗時操作;
4.不要以靜態變量的方式在Application中保存數據;
5.減少佈局的複雜性和深度;
參考 >https://www.jianshu.com/p/eb3b410cce49

  1. MVVM 中是如何進行數據綁定的

MVVM 中的數據綁定依賴 DataBinding,分爲單向綁定和雙向綁定。
MVVM是更節省的設計模式,能實現雙向的數據綁定。
1.單向綁定是指View層(如EditText)上的數據改變會實時更新到Model層JavaBean中對應的屬性值(如username)上。或者,Model層的數據改變會實時更新到View層上的顯示,這樣我們稱之爲單向的數據綁定。
2.而雙向綁定呢,是指Model層和View層,無論那層數據改變都會實時更新到對方,Model層數據改變會更新View,同樣,View層數據改變會更新Model,這樣就稱之爲雙向的數據綁定。

  1. 消息傳遞機制 (Handler)

1.線程間通訊 ——— Handler,HandlerThread等。
2.組件間通信 ——— BroadcastReceiver,接口回調等。
3. 第三方通信 ——— EventBus,rxBus
4.進程間通信 ——— Content Provider ,Broadcast ,AIDL等。
5.長連接推送 ——— WebSocket,XMPP等。
參考https://blog.csdn.net/haoxuhong/article/details/80103030
Handler
一個Android應用程序被創建的時候都會創建一個UI主線程,但是有時我們會有一些比較耗時的操作,爲了防止阻塞UI主線程,我們會將耗時的操作放到子線程中進行處理,處理完之後操作UI,但是Android不允許子線程操作UI,違背了Android單線程模型的原則(即 Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行),所以Android通過Handler消息機制來實現線程之間的通訊。
Handler 機制的主要角色:
1.Message:消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。
2.Handler:處理者,負責Message的發送及處理。使用Handler時,需要實現handleMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等。
3.MessageQueue:消息隊列,用來存放Handler發送過來的消息,並按照FIFO規則執行。當然,存放Message並非實際意義的保存,而是將Message以鏈表的方式串聯起來的,等待Looper的抽取
4.Looper:消息泵,不斷地從MessageQueue中抽取Message執行。因此,一個MessageQueue需要一個Looper。
5.Thread:線程,負責調度整個消息循環,即消息循環的執行場所。

Handler 機制的主要運用:

sendEmptyMessage(int);//發送一個空的消息
sendMessage(Message);//發送消息,消息中可以攜帶參數
sendMessageAtTime(Message, long);//未來某一時間點發送消息
sendMessageDelayed(Message, long);//延時Nms發送消息

post(Runnable);//提交計劃任務馬上執行
postAtTime(Runnable, long);//提交計劃任務在未來的時間點執行
postDelayed(Runnable, long);//提交計劃任務延時Nms執行

Handler機制擴展:

Activity.runOnUiThread(Runnable)
View.post(Runnable)
以上也可以從子線程切換到主線程。

HandlerThread:

HandlerThread本質上就是一個普通Thread,只不過內部建立了Looper.

HandlerThread 的用法

 //創建一個線程,線程名字:handler-thread
        myHandlerThread = new HandlerThread( "handler-thread") ;
        //開啓一個線程
        myHandlerThread.start();
        //在這個線程中創建一個handler對象 主要這個handler是在子線程中循環接受消息的
        handler = new Handler( myHandlerThread.getLooper() ){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //這個方法是運行在 handler-thread 線程中的 ,可以執行耗時操作
                Log.d( "handler " , "消息: " + msg.what + "  線程: " +                Thread.currentThread().getName()  ) ;
 
            }
        };
 
        //在主線程給handler發送消息
        handler.sendEmptyMessage( 1 ) ;
 
        new Thread(new Runnable() {
            @Override
            public void run() {
             //在子線程給handler發送數據
             handler.sendEmptyMessage( 2 ) ;
            }
        }).start() ;
 
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
 
        //釋放資源
        myHandlerThread.quit() ;
    }

Looper的 quit 方法或 quitSafely 方法

相同點:

調用後,將不再接受新的事件加入消息隊列。

不同點

當我們調用Looper的quit方法時,實際上執行了MessageQueue中的removeAllMessagesLocked方法,該方法的作用是把MessageQueue消息池中所有的消息全部清空,無論是延遲消息(延遲消息是指通過sendMessageDelayed或通過postDelayed等方法發送的需要延遲執行的消息)還是非延遲消息。

當我們調用Looper的quitSafely方法時,實際上執行了MessageQueue中的removeAllFutureMessagesLocked方法,通過名字就可以看出,該方法只會清空MessageQueue消息池中所有的延遲消息,並將消息池中所有的非延遲消息派發出去讓Handler去處理,quitSafely相比於quit方法安全之處在於清空消息之前會派發所有的非延遲消息。

無論是調用了quit方法還是quitSafely方法只會,Looper就不再接收新的消息。即在調用了Looper的quit或quitSafely方法之後,消息循環就終結了,這時候再通過Handler調用sendMessage或post等方法發送消息時均返回false,表示消息沒有成功放入消息隊列MessageQueue中,因爲消息隊列已經退出了。

需要注意的是Looper的quit方法從API Level 1就存在了,但是Looper的quitSafely方法從API Level 18才添加進來。

  1. 如何在子線程中刷新視頻流

  2. 怎樣開啓一個多進程

在Android中說多進程一般是指一個應用中存在多個進程,在Android中使用多進程只有一種方法:給四大組件在AndroidMenifest中指定android:process屬性,除此之外別無他法(通過JNI在native層去fork一個進程也可以,不常用,不做介紹),所以我們不能給一個線程或者實體類指定其運行時所在的進程。

開啓多進程


<activity
    android:name="com.zhong.ActivityA" />
<activity
    android:name="com.zhong.ActivityB"
    android:process=":remote" />
<activity
    android:name="com.zhong.ActivityC"
    android:process="com.zhong.remote" />

上面三個Activity中:

ActivityA:未指明android:process屬性,它允許在默認進程中,進程名爲包名,即com.zhong;
ActivityB:設置android:process=":remote",系統會爲它創建一個單獨的進程,進程名爲包名+:remote,即com.zhong:remote,相當於android:process=“com.zhong:remote”;
ActivityC:設置android:process=“com.zhong.remote”,系統會爲它創建一個單獨的進程,進程名爲process設置的值,即com.zhong.remote

":remote"和"com.zhong.remote"區別:

“:remote"中”:"是指要在當前進程名前面附近包名的簡寫,“com.zhong.remote"是一種完整的寫法,進程名不會再附加包名;其次,”:"是屬於當前應用的私有進程,"com.zhong.remote"是全局進程,全局進程其他應用可以通過ShareUID方式跑在同一進程中(前提是兩個應用ShareUID相同並且簽名相同),這兩個應用可以相互訪問對方私有數據。

多進程運行機制

由於Android爲每個進程分配獨立的虛擬機,所以不同虛擬機訪問同一個類會產生不同的副本,因此運行在不同進程中的組件無法通過內存共享數據,所以一個應用使用多進程會出現一下幾個問題:

1.靜態成員和單例模式完全失效(不是同一塊內存,會產生不同的副本)
2.線程同步機制完全失效(不是同一塊內存,所以對象也不是同一個,因此類鎖、對象鎖也不是同一個,不能保證線程同步)
3.SharedPreferences 可靠性下降(SharedPreferences不支持多個進程同時寫,會有一定的機率丟失數據)
4.Application 多次創建(Android爲每個進程分配獨立的虛擬機,這個過程其實就是啓動一個應用,所以Application會被創建多次)

多進程小結:

Android中使用多進程一般是來分擔主進程的內存壓力,應用越做越大,需要的內存也越來越多,講一些獨立的組件放在不同的進程中,這樣可以減輕主進程的內存負擔;還有就是啓動一個Service,做一些守護或者耗時的操作。
參考 Android多進程模式

  1. Service與 Activity 怎樣交互

1.使用接口回調方式,activity實現相應的接口,service通過接口進行回調,比較靈活
2.使用廣播
參考 Android—Service與Activity的交互

  1. 直播視頻流是在哪個線程更新的,怎麼做到的,爲什麼?

是在子線程中更新的,使用 SurfaceView 來實現的。爲何SurfaceView能夠在非UI線程中刷新界面?

SurfaceView與View的刷新方法都是一樣的,通過lockCanvas和unlockCanvasAndPost方法來進行畫的,但SurfaceView能在UI線程中刷新,也能在其它線程中刷新,而View只能在UI線程中刷新,View的刷新有一個checkThread(在ViewRootImp.java中)的判斷,如果不是在UI線程中就會拋異常, 這是google人爲這樣設計的,不讓其它線程刷新View,SurfaceView就不會進行判斷,這樣它就可以在其它線程中進行刷新。

  1. 當前 Activity 持有哪些窗體

其實這個問題想問的是 Android - Activity 與 Window 與 View 之間的關係。

Window 是什麼?

Window 是 Android 中窗口的宏觀定義,主要是管理 View 的創建,以及與 ViewRootImpl 的交互,將 Activity 與 View 解耦。

Activity 與 PhoneWindow 與 DecorView 之間什麼關係?

一個 Activity 對應一個 Window 也就是 PhoneWindow,一個 PhoneWindow 持有一個 DecorView 的實例,DecorView 本身是一個 FrameLayout。
參考一篇文章看明白 Activity 與 Window 與 View 之間的關係

  1. 頁面間數據通信有哪些方式

1.如果頁面之間有直接關係,如Activity和在它之內的Fragment,可以直接通過接口的調用來傳遞數據。
優勢:直接,方便。 劣勢:代碼耦合性較高

2.如果是兩個Activity之間傳遞數據,有界面切換的過程的話,可以用startActivity
或startActivityForResult。用其中的intent參數攜帶數據。
優勢:一般用於初始化Activity和調用系統功能

3.如果頁面之間傳遞數據沒有頁面切換的過程,可以通過廣播的方式,sendBroadcast(intent);
要接受數據的頁面註冊這個廣播就行了。
優勢:代碼耦合性低,易重構,適用範圍廣。缺點:數據需要序列化和反序列化,代碼較多

4.通過存儲介質來分享數據,如頁面A將數據存入數據庫,SharedPreferences文件,Internet。頁面B通過讀取它們來得到數據。
優勢:數據保存時間長,不受到界面生命週期的影響 缺點:讀取速度較慢,需要異步操作

5.採用事件總線的方式,註冊和接收事件(數據),其中的代表者是EventBus,頁面需要指定和註冊接收事件的類型
優勢:不用序列化數據,適用範圍大 缺點:需要學習使用。

  1. recylcerView 相比 listview 有哪些優點

優缺點
1.ListView相比RecycleView的優點
a.ListView實現添加HeaderView和FooderView有直接的方法
b.分割線可以直接設置
c.ListView實現onItemClickListence和onItemLongClickListence有直接的方法

2.RecyclerView相比ListView的優點
a.封裝了ViewHodler,效率更高
b.可以添加增刪Item動畫、側滑功能等
c.支持局部更新,可見才更新,不可見不更新
d.插件式實現,各個功能模塊化,解耦性強,使用起來更方便

3.但RecyclerView不會替代ListView,因爲使用場景不同;但是會替代很多開元框架:橫向ListView、瀑布流等

Android RecyclerView的使用及和ListView比較的優缺點
ListView 與 RecyclerView 簡單對比
RecyclerView與ListView的異同
RecyclerView 和 ListView 性能和效果區別

  1. 平時碰到過哪些內存內存優化
    OOM、ANR、ML
    Android - 性能優化-內存優化

  2. 比較下 MVP 和 mvvm
    MVP 缺點:
    1.會增加大量的模板代碼
    2.更新一個很小的 UI 元素,也需要層層調用,流程比較繞
    MVVM 缺點:
    1.在佈局文件xml中加入了很多邏輯代碼,違背了展示和邏輯分離的原則,增加了複雜度,難以閱讀和維護
    2.單純的MVVM模式只能實現簡單的UI更新,無法實現諸如列表更新的功能,以及加載完成網絡數據後彈一個toast之類的功能
    3.不好定位 bug,很多問題無法直接定位到出問題的那一行,增加了工作量。
    Android開發模式:MVP Vs MVVM
    三種框架的比較—教你認清MVC,MVP和MVVM

  3. 工作中碰到的比較有價值的工作(或者碰到了哪些問題)

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