天天看點

Android 面試題筆記(一)

每日更新每日學習面試筆記,來自https://github.com/Moosphan/Android-Daily-Interview/

  • 1、自定義 Handler 時如何有效地避免記憶體洩漏問題?

    問題原因:一般非靜态内部類持有外部類的引用的情況下,造成外部類在使用完成後不能被系統回收記憶體,進而造成記憶體洩漏。這裡 Handler 持有外部類 Activity 的引用,一旦 Activity 被銷毀,而此時 Handler 依然持有 Activity 引用,就會造成記憶體洩漏。

解決方案:将 Handler 以靜态内部類的形式聲明,然後通過弱引用的方式讓 Handler 持有外部類 Activity 的引用,這樣就可以避免記憶體洩漏問題了:

1.自定義的靜态handler

2.可以加一個弱引用

3.還有一個主意的就是當你activity被銷毀的時候如果還有消息沒有發出去 就remove掉吧

4.removecallbacksandmessages去清除Message和Runnable 加null 寫在生命周的ondestroy()就行

private WeakHandler weakHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        weakHandler = new WeakHandler(this);
    }

    static class WeakHandler extends Handler {
        private final WeakReference<MainActivity> mActivity;

        WeakHandler(MainActivity activity) {
            mActivity = new WeakReference<>(activity);  // 弱引用
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity activity = mActivity.get();
            switch (msg.what) {
                case 1:
                    if (activity != null) {
                        activity.btnDemoOne.setText("ceshi");
                    }
                    break;
                default:
                    break;
            }

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        weakHandler.removeCallbacksAndMessages(null);
    }
           
  • 2、Activity 與 Fragment 之間常見的幾種通信方式?

通常,Fragment 與 Activity 通信存在三種情形:

  • Activity 操作内嵌的 Fragment
  • Fragment 操作宿主 Activity
  • Fragment 操作同屬 Activity中的其他 Fragment

在Android中我們可以通過以下幾種方式優雅地實作Activity和fragment之間的通信:

  • Handler
  • 廣播
  • EventBus
  • 接口回調

    見Activity 與 Fragment 之間的通信

    - 3、一般什麼情況下會導緻記憶體洩漏問題?

  • 内部類&匿名内部類執行個體無法釋放(有延遲時間等等),而内部類又持有外部類的強引用,導緻外部類無法釋放,這種匿名内部類常見于監聽器、Handler、Thread、TimerTask
  • 持有靜态的Context(Avtivity)和View
  • 資源使用完成後沒有關閉File,Cursor,Stream,Bitmap(調用recycle())等相關流的操作
  • 接收器、監聽器注冊沒取消,BraodcastReceiver,ContentObserver
  • 集合類記憶體洩漏,如果一個集合類是靜态的(緩存HashMap),隻有添加方法,沒有對應的删除方法,會導緻引用無法被釋放,引發記憶體洩漏。

順便說一下記憶體洩漏和記憶體溢出的差別

  • 記憶體溢出(Out of memory):系統會給每個APP配置設定記憶體也就是Heap size值,當APP所需要的記憶體大于了系統配置設定的記憶體,就會造成記憶體溢出;通俗點就是10L桶隻能裝10L水,但是你卻用來裝11L的水,那就有1L的水就會溢出
  • 記憶體洩漏(Memory leak):當一個對象不在使用了,本應該被垃圾回收器(JVM)回收,但是這個對象由于被其他正在使用的對象所持有,造成無法被回收的結果,通俗點就是系統把一定的記憶體值A借給程式,但是系統卻收不回完整的A值,那就是記憶體洩漏

    - 4、LaunchMode 的應用場景?

    LaunchMode 有四種,分别為 Standard,SingleTop,SingleTask 和 SingleInstance,每種模式的實作原理

  • android:launchMode="standard"可以存在多個執行個體,這是預設的啟動模式,系統總是會在目标棧中建立新的activity執行個體。Standard 模式是系統預設的啟動模式,一般我們 app 中大部分頁面都是由該模式的頁面構成的,比較常見的場景是:社交應用中,點選檢視使用者A資訊->檢視使用者A粉絲->在粉絲中挑選檢視使用者B資訊->檢視使用者A粉絲… 這種情況下一般我們需要保留使用者操作 Activity 棧的頁面所有執行順序。
  • android:launchMode=“singleTop” 如果這個 activity 執行個體已經存在目标棧的棧頂,系統會調用這個 activity 中的 onNewIntent() 方法,并傳遞 intent,而不會建立新的 activity 執行個體;如果不存在這個 activity 執行個體或者 activity 執行個體不在棧頂,則 SingleTop 和Standard 作用是一樣的。SingleTop 模式一般常見于社交應用中的通知欄行為功能,例如:App 使用者收到幾條好友請求的推送消息,需要使用者點選推送通知進入到請求者個人資訊頁,将資訊頁設定為 SingleTop 模式就可以增強複用性。
  • android:launchMode=“singleTask” 不會存在多個執行個體,如果棧中不存在 activity 執行個體,系統會在新棧的根部建立一個新的 activity;如果這個 activity 執行個體已經存在,系統會調用這個 activity的 onNewIntent() 方法而不會建立新的 activity 執行個體。SingleTask 模式一般用作應用的首頁,例如浏覽器首頁,使用者可能從多個應用啟動浏覽器,但主界面僅僅啟動一次,其餘情況都會走onNewIntent,并且會清空主界面上面的其他頁面。
  • android:launchMode=“singleInstance” 這種啟動模式比較特殊,因為它會啟用一個新的棧結構,将 Acitvity 放置于這個新的棧結構中,并保證不再有其他 Activity 執行個體進入,除此之外,SingleInstance模式和 SingleTask 模式是一樣的。SingleInstance 模式常應用于獨立棧操作的應用,如鬧鐘的提醒頁面,當你在A應用中看視訊時,鬧鐘響了,你點選鬧鐘提醒通知後進入提醒詳情頁面,然後點選傳回就再次回到A的視訊頁面,這樣就不會過多幹擾到使用者先前的操作了。
  • 5、如何實作多線程中的同步?

    線程間的同步問題一般借助于同步鎖 Synchronized 和 volatile 關鍵字實作:

public class Singleton{
    private volatile static Singleton mSingleton;
    private Singleton(){
    }
    public static Singleton getInstance(){
      if(mSingleton == null){
        synchronized(Singleton.class){
         if(mSingleton == null)
           mSingleton = new Singleton();
      }
    }
    return mSingleton;
  }
}
           
  • 6、Android 補間動畫和屬性動畫的差別?
  • 補間動畫

    補間動畫,主要是向View對象設定動畫效果,包括AlphaAnimation 、RotateAnimation 、ScaleAnimation 、TranslateAnimation 這4種效果,對應的xml标簽分别是alpha、rotate、scale、translate。通過為動畫設定初始和終止對應的值,根據插值器和duration計算動畫過程中間相應的值實作平滑運動,即設定初始和終止狀态,插值器來計算填補初始狀态到終止狀态間的動畫

  • 屬性動畫

    屬性動畫可以對任何對象的屬性做動畫而不僅僅是View,甚至可以沒有對象。除了作用對象進行擴充外,屬性動畫的效果也加強了,不僅能實作View動畫的4中效果,還能實作其它多種效果,這些效果都是通過ValuAnimator或ObjectAnimator、AnimatorSet等來實作的。

  • 7、ANR出現的場景及解決方案?

    在Android中,應用的響應性被活動管理器(Activity Manager)和視窗管理器(Window Manager)這兩個系統服務所監視。當使用者觸發了輸入事件(如鍵盤輸入,點選按鈕等),如果應用5秒内沒有響應使用者的輸入事件,那麼,Android會認為該應用無響應,便彈出ANR對話框。而彈出ANR異常,也主要是為了提升使用者體驗。

    解決方案是對于耗時的操作,比如通路網絡、通路資料庫等操作,需要開辟子線程,在子線程處理耗時的操作,主線程主要實作UI的操作

  • 8、談談 Handler 機制和原理?

    首先在UI線程我們建立了一個Handler執行個體對象,無論是匿名内部類還是自定義類生成的Handler執行個體對象,我們都需要對handleMessage方法進行重寫,在handleMessage方法中我們可以通過參數msg來寫接受消息過後UIi線程的邏輯處理,接着我們建立子線程,在子線程中需要更新UI的時候,建立一個Message對象,并且将消息的資料記錄在這個消息對象Message的内部,比如arg1,arg2,obj等,然後通過前面的Handler執行個體對象調用sendMessge方法把這個Message執行個體對象發送出去,之後這個消息會被存放于MessageQueue中等待被處理,此時MessageQueue的管家Looper正在不停的把MessageQueue存在的消息取出來,通過回調dispatchMessage方法将消息傳遞給Handler的handleMessage方法,最終前面提到的消息會被Looper從MessageQueue中取出來傳遞給handleMessage方法。

    問題擴充

    A. messageQueue.next 是阻塞式的取消息, 如果有 delay 會調用 nativeWake;

    那麼問題來了, 線程挂起了, 是挂起的 UI線程嗎? 答案是 YES, 為什麼我沒有察覺呢?

    還有就是 nativeWake 和 nativePollOnce 的實作原理;

B. looper.loop 既然是 while-true 為什麼不會卡死?

C. MessageQueue 是隊列嗎? 他是什麼資料結構呢?

D. handler 的postDelay, 時間準嗎? 答案是不準, 為什麼呢?

E. handler 的 postDelay 的時間是 system.currentTime 嗎? 答案是 NO, 你知道是什麼嗎?

F. 子線程run方法使用 handler 要先 looper.prepare(); 再 handler.post; 再 looper.loop();

那麼問題來了, looper.loop(); 之後 在 handler.post 消息, 還能收到嗎? 答案是 NO, 為什麼?

G. handler 這麼做到的 一個線程對應一個 looper, 答案是threadLocal, 你對ThreadLocal 有什麼了解嗎?

H. 假設先 postDelay 10ms, 再postDelay 1ms, 你簡單描述一下, 怎麼處理這2條消息?

I. 你知道主線程的Looper, 第一次被調用loop方法, 在什麼時候嗎? 哪一個類

J. 你對 IdleHandler 有多少了解?

K. 你了解 HandlerThread 嗎?

L. 你對 Message.obtain() 了解嗎, 或者你知道 怎麼維護消息池嗎;

  • 9、什麼是Sticky事件?

    在Android開發中,Sticky事件隻指事件消費者在事件釋出之後才注冊的也能接收到該事件的特殊類型。Android中就有這樣的執行個體,也就是Sticky Broadcast,即粘性廣播。正常情況下如果發送者發送了某個廣播,而接收者在這個廣播發送後才注冊自己的Receiver,這時接收者便無法接收到剛才的廣播,為此Android引入了StickyBroadcast,在廣播發送結束後會儲存剛剛發送的廣播(Intent),這樣當接收者注冊完Receiver後就可以接收到剛才已經釋出的廣播。這就使得我們可以預先處理一些事件,讓有消費者時再把這些事件投遞給消費者。

  • 10、抽象類與接口的差別?

    1.抽象類是用來捕捉子類的通用特性的 。它不能被執行個體化,隻能被用作子類的超類。抽象類是被用來建立繼承層級裡子類的模闆。

    2.接口是抽象方法的集合。如果一個類實作了某個接口,那麼它就繼承了這個接口的抽象方法。這就像契約模式,如果實作了這個接口,那麼就必須確定使用這些方法。接口隻是一種形式,接口自身不能做任何事情。

    大體差別如下:

  • 抽象類可以提供成員方法的實作細節,而接口中隻能存在 public 抽象方法;
  • 抽象類中的成員變量可以是各種類型的,而接口中的成員變量隻能是 public static final 類型的;
  • 接口中不能含有構造器、靜态代碼塊以及靜态方法,而抽象類可以有構造器、靜态代碼塊和靜态方法;
  • 一個類隻能繼承一個抽象類,而一個類卻可以實作多個接口;
  • 抽象類通路速度比接口速度要快,因為接口需要時間去尋找在類中具體實作的方法;
  • 如果你往抽象類中添加新的方法,你可以給它提供預設的實作。是以你不需要改變你現在的代碼。如果你往接口中添加方法,那麼你必須改變實作該接口的類。

    補充詳細見抽象類與接口的差別

  • 11、BroadcastReceiver 與 LocalBroadcastReceiver 有什麼差別?
  • BroadcastReceiver 是跨應用廣播,利用Binder機制實作,支援動态和靜态兩種方式注冊方式。
  • LocalBroadcastReceiver是應用内廣播,利用Handler實作,利用了IntentFilter的match功能,提供消息的釋出與接收功能,實作應用内通信,效率和安全性比較高,僅支援動态注冊。
/**
 * 自定義廣播
 */
public static final String LOGIN_ACTION = "com.archie.action.LOGIN_ACTION";
//廣播接收器
    private LoginBroadcastReceiver mReceiver = new LoginBroadcastReceiver();
 
    //注冊廣播方法
    private void registerLoginBroadcast(){
        IntentFilter intentFilter = new IntentFilter(LoginActivity.LOGIN_ACTION);
        LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver,intentFilter);
    }
 
    //取消注冊
    private void unRegisterLoginBroadcast(){
        LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
    }
/**
     * 自定義廣播接受器,用來處理登入廣播
     */
    private class LoginBroadcastReceiver extends BroadcastReceiver{
 
        @Override
        public void onReceive(Context context, Intent intent) {
            //處理我們具體的邏輯,更新UI
        }
    }


/**
     * 發送我們的局部廣播
     */
    private void sendBroadcast(){
        LocalBroadcastManager.getInstance(this).sendBroadcast(
                new Intent(LOGIN_ACTION)
        );
    }
 
           

小結:

1、LocalBroadcastManager在建立單例傳參時,不用糾結context是取activity的還是Application的,它自己會取到tApplicationContext。

2、LocalBroadcastManager隻适用于代碼間的,因為它就是儲存接口BroadcastReceiver的對象,然後直接調用其onReceive方法。

3、LocalBroadcastManager注冊廣播後,當該其Activity或者Fragment不需要監聽時,記得要取消注冊,注意一點:注冊與取消注冊在activity或者fragment的生命周期中要保持一緻,例如onResume,onPause。

4、LocalBroadcastManager雖然 支援對同一個BroadcastReceiver可以注冊多個IntentFilter,但還是應該将所需要的action都放進一個 IntentFilter,即隻注冊一個IntentFilter,這隻是我個人的建議。

5、LocalBroadcastManager所發 送的廣播action,隻能與注冊到LocalBroadcastManager中BroadcastReceiver産生互動。如果你遇到了通過 LocalBroadcastManager發送的廣播,對面的BroadcastReceiver沒響應,很可能就是這個原因造成的。

  • 12、請簡要談一談單例模式?

    單例分為懶漢模式和惡漢模式,主要是雙檢查、靜态内部類、枚舉等

    懶漢模式有線程安全和非線程安全的差別

    實作線程安全的懶漢模式有多重 其中一種是加double check,一種是靜态内部類

/**
 * 雙重檢查
 */
public class SingletonDoubleCheck {
    private SingletonDoubleCheck() { }

    private static volatile SingletonDoubleCheck instance;//代碼1

    public static SingletonDoubleCheck getInc() {
        if (null == instance) {//代碼2
            synchronized (SingletonDoubleCheck.class) {
                if (null == instance) {//代碼3
                    instance = new SingletonDoubleCheck();//代碼4
                }
            }
        }
        return instance;
    }
}


/**
 * 靜态内部類實作單例
 * 
 */
public class SingleDemo4 {
    private static SingleDemo4 instance;

    private static class SingleDemo4Holder {
        private static final SingleDemo4 instance = new SingleDemo4();
    }

    private SingleDemo4() {
        if (instance != null) {
            throw new RuntimeException();
        }
    }

    /**
     * 調用這個方法的時候,JVM才加載靜态内部類,才初始化靜态内部類的類變量。由于由JVM初始化,保證了線程安全性,
     * 同時又實作了懶加載
     * @return
     */
    public static SingleDemo4 getInstance() {
        return SingleDemo4Holder.instance;
    }
}
           

更多詳細單例見單例的五種實作方式,及其性能分析

在代碼 在多線程中 兩個線程可能同時進入代碼2, synchronize保證隻有一個線程能進入下面的代碼,

此時一個線程A進入一個線程B在外等待, 當線程A完成代碼3 和代碼4之後,

線程B進入synchronized下面的方法, 線程B在代碼3的時候判斷不過,進而保證了多線程下 單例模式的線程安全,

另外要慎用單例模式,因為單例模式一旦初始化後 隻有程序退出才有可能被回收,如果一個對象不經常被使用,盡量不要使用單例,否則為了幾次使用,一直讓單例存在占用記憶體。

接着上一篇Android 面試題筆記(一)

13、Window和DecorView是什麼?DecorView又是如何和Window建立聯系的?

DecorView的作用

DecorView是頂級View,本質就是一個FrameLayout

包含了兩個部分,标題欄和内容欄

内容欄id是content,也就是activity中setContentView所設定的部分,最終将布局添加到id為content的FrameLayout中

擷取content:ViewGroup content = findViewById(R.android.id.content)

擷取設定的View:content.getChidlAt(0)

Window是什麼?

表示一個視窗的概念,是所有View的直接管理者,任何視圖都通過Window呈現(單擊事件由Window->DecorView->View; Activity的setContentView底層通過Window完成)

Window是一個抽象類,具體實作是PhoneWindow

建立Window需要通過WindowManager建立

WindowManager是外界通路Window的入口

Window具體實作位于WindowManagerService中

WindowManager和WindowManagerService的互動是通過IPC完成

DecorView又是如何和Window建立聯系的?

在Activity的啟動流程中,處理onResume()的相關方法中,将DecorView作為Window的成員變量儲存到Window内部

DecorView與Window建立聯系又有什麼用呢?例如Activity的onSaveInstanceState()進行資料儲存時,就通過window内部的DecorView觸發整個View樹進行狀态儲存

//ActivityThread.java

final void handleResumeActivity(IBinder token, …) {

//1. 建立DecorView,設定為不可見INVISIBLE

View decor = r.window.getDecorView();

decor.setVisibility(View.INVISIBLE);

//2. 擷取到WindowManager, addView方法将DecorView添加到Window中

ViewManager wm = a.getWindowManager();

wm.addView(decor, l);

//3. 将DecorView設定為visible

r.activity.makeVisible();

}

  • 14、對于 Context,你了解多少?

    Context 宏觀來說是一個描述應用程式全局資訊的場景,當然,本質上來說,這個“場景”其實是一個抽象類詳細見Android Context 上下文 你必須知道的一切

  • 15、SharedPreferences 是線程安全的嗎?它的 commit 和 apply 方法有什麼差別?

    SharedPreferences 是線程安全的 程序不安全的, commit 是同步寫入有傳回值,apply是異步寫入。

    apply沒有傳回值而commit傳回boolean表明修改是否送出成功

    apply是将修改資料原子送出到記憶體, 而後異步真正送出到硬體磁盤, 而commit是同步的送出到硬體磁盤,是以,在多個并發的送出commit的時候,他們會等待正在處理的commit儲存到磁盤後在操作,進而降低了效率。而apply隻是原子的送出到内容,後面有調用apply的函數的将會直接覆寫前面的記憶體資料,這樣從一定程度上提高了很多效率。

    由于在一個程序中,sharedPreference是單執行個體,一般不會出現并發沖突,如果對送出的結果不關心的話,建議使用apply,當然需要確定送出成功且有後續操作的話,還是需要用commit的。

    - 16、HashMap 的實作原理?

  • 數組:存儲區間連續,占用記憶體嚴重,尋址容易,插入删除困難;
  • 連結清單:存儲區間離散,占用記憶體比較寬松,尋址困難,插入删除容易;
  • Hashmap: 綜合應用了這兩種資料結構,實作了尋址容易,插入删除也容易。

    更多可參考以下文章:

    HashMap 原理以及源碼解析

    HashMap 碰撞問題

    HashMap 中的負載因子

  • 17、簡述一下 Android 中 UI 的重新整理機制?

    應用層的:

  • 界面重新整理的本質流程

    通過ViewRootImpl的scheduleTraversals()進行界面的三大流程。

    調用到scheduleTraversals()時不會立即執行,而是将該操作儲存到待執行隊列中。并給底層的重新整理信号注冊監聽。

    當VSYNC信号到來時,會從待執行隊列中取出對應的scheduleTraversals()操作,并将其加入到主線程的消息隊列中。

    主線程從消息隊列中取出并執行三大流程: onMeasure()-onLayout()-onDraw()

  • 同步屏障的作用

    同步屏障用于阻塞住所有的同步消息(底層VSYNC的回調onVsync方法送出的消息是異步消息)

    用于保證界面重新整理功能的performTraversals()的優先執行。

  • 同步屏障的原理?

    主線程的Looper會一直循環調用MessageQueue的next方法并且取出隊列頭部的Message執行,遇到同步屏障(一種特殊消息)後會去尋找異步消息執行。如果沒有找到異步消息就會一直阻塞下去,除非将同步屏障取出,否則永遠不會執行同步消息。

    界面重新整理操作是異步消息,具有最高優先級

    我們發送的消息是同步消息,再多耗時操作也不會影響UI的重新整理操作

系統層的:

首先螢幕是 大約16.6ms重新整理一次(固定的),當界面需要改變時, CPU開始計算,将計算結果 賦予 GPU 的buffer緩存起來,等待重新整理時間的到來,然後根據buffer的資料重新整理界面。如果目前界面沒有變化,CPU不用計算,也不會給GPU的buffer指派啥的,這個buffer也就沒變化,等到重新整理時間的到來,會依舊根據buffer重新整理螢幕

結論是:界面改不改變都會重新整理界面,隻是在于CPU是否計算這點差別

UI重新整理卡頓,基本都在于卡在CPU計算這一環節,對于根據GPU 的buffer重新整理這一環節,在系統裡有很高的優先級

  • 18、Serializable和Parcelable的差別?

    Serializable是屬于Java自帶的,本質是使用了反射。序列化的過程比較慢,這種機制在序列化的時候會建立很多臨時的對象,比引起頻繁的GC。Parcelable 是屬于 Android 專用。不過不同于Serializable,Parcelable實作的原理是将一個完整的對象進行分解。而分解後的每一部分都是Intent所支援的資料類型。 如果在記憶體中使用建議Parcelable。持久化操作建議Serializable;目前AS安裝android parcelable code generator插件可直接生成Parcelable

  • 19、Android程序間的通信方式

    1、Bundle的使用

    可以看到Bundle實作了Parcelable 接口。

    優點:簡單易用

    缺點:隻能傳遞Bundle支援的資料類型

    使用場景:四大元件間的程序通訊

2.檔案共享

優點:簡單易用

缺點:不适合高并發的場景,不能做到即時通訊。

使用場景:無并發通路的情景,簡單的交換資料,實時性要求不高。

3.AIDI

優點:功能強大,支援一對多并發通信,支援實時通信。

缺點:一定要處理好線程同步的問題

使用場景:一對多進行通訊,有RPC(遠端過程調用協定)的需求

4.Messenger(信使)

優點:功能一般,支援一對多串行通信,支援實時通信。

缺點:不能很好的處理高并發場景,不支援RPC,資料通過Message進行傳輸,是以隻能支援Bundle支援的資料類型。

使用場景:低并發的一對多的實時通訊,沒有RPC的需求或者說沒有傳回結果的RPC(不調用服務端的相關方法)

5.ContentProvider

優點:主要用于資料通路,支援一對多的并發資料共享。

缺點:受限制,主要針對資料源的增删改查。

使用場景:一對多的資料共享。

6.Socket(套接字)

優點:功能強大,通過讀寫網絡傳輸位元組流,支援一對多的并發的實時通訊。

缺點:不支援直接的RPC(這裡我也不是很明白,間接的怎麼實作?)

使用場景:網絡的資料交換

20、請簡述一下String、StringBuffer和StringBuilder的差別?

  • String 為字元串常量,一旦建立不可以被修改,是線程安全的;String 類使用 final

    修飾符,不可以被繼承;String 的長度是不變的。适用于少量操作的字元串。

  • StringBuffer 為字元串變量,長度是可變的,線程安全。适用于多線程下在字元緩沖區進行大量字元串操作
  • StringBuilder 為字元串變量,長度是可變的,線程不安全。适用于單線程下在字元緩沖區進行大量字元串操作。
  • 字元串操作在執行速度:StringBuilder > StringBuffer > String

    21、請簡述從點選圖示開始app的啟動流程?

    ①點選桌面App圖示,Launcher程序采用Binder IPC向system_server程序發起startActivity請求;

    ②system_server程序接收到請求後,向zygote程序發送建立程序的請求;

    ③Zygote程序fork出新的子程序,即App程序;

    ④App程序,通過Binder IPC向sytem_server程序發起attachApplication請求;

    ⑤system_server程序在收到請求後,進行一系列準備工作後,再通過binder IPC向App程序發送scheduleLaunchActivity請求;

    ⑥App程序的binder線程(ApplicationThread)在收到請求後,通過handler向主線程發送LAUNCH_ACTIVITY消息;

    ⑦主線程在收到Message後,通過發射機制建立目标Activity,并回調Activity.onCreate()等方法。

    ⑧到此,App便正式啟動,開始進入Activity生命周期,執行完onCreate/onStart/onResume方法,UI渲染結束後便可以看到App的主界面。

  • 22、IntentService 的應用場景和使用姿勢?

    IntentService是Service的子類,比普通的Service增加了額外的功能。先看Service本身存在兩個問題:Service不會專門啟動一條單獨的程序,Service與他所在應用位于同一個程序中。

    Service也不是專門一條新程序,是以不應該在Service中直接處理耗時的任務。

    特點:

    IntentService會建立獨立的worker線程來處理所有的Intent請求;

    會建立獨立的worker線程來處理onHandleIntent()方法實作的代碼,無需處理多線程的問題;

    所有請求處理完成後,IntentService會自動停止,無需調用stopSelf()方法停止Service;

    為Service的onBind()提供預設實作,傳回null;

    為Service的onStartCommand提供預設實作,将請求Intent添加到隊列中;

    接着上一篇面試題Android 面試題筆記(二)

    23、IntentFilter是什麼?有哪些使用場景?

(1)IntentFilter是和intent相比對的,其中action,category,組成了比對規則。同時intentFilter還可以設定優先級,其中預設是0,範圍是【-1000,1000】,值越大優先級越高。并且IntentFilter多被通過AndroidManifest.xml的形式使用。

(2) 使用場景

activity的隐式啟動和廣播的比對

(3)IntentFilter的比對規則

IntentFilter的過濾資訊有action,category,data.一個元件可以包含多個intent-filter,一個intent隻要能完全比對一組intent-filter即可成功的啟動對應的元件。

24、回答一下什麼是強、軟、弱、虛引用以及它們之間的差別?

  • 強引用(StrongReference)

強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當記憶體空間不足,Java虛拟機甯願抛出 OutOfMemoryError 錯誤,使程式異常終止,也不會靠随意回收具有強引用的對象來解決記憶體不足的問題。

  • 軟引用(SoftReference)

如果一個對象隻具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些對象的記憶體。隻要垃圾回收器沒有回收它,該對象就可以被程式使用。軟引用可用來實作記憶體敏感的高速緩存(下文給出示例)。

軟引用可以和一個引用隊列 ReferenceQueue 聯合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛拟機就會把這個軟引用加入到與之關聯的引用隊列中。

  • 弱引用(WeakReference)

弱引用與軟引用的差別在于:隻具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的記憶體區域的過程中,一旦發現了隻具有弱引用的對象,不管目前記憶體空間足夠與否,都會回收它的記憶體。不過,由于垃圾回收器是一個優先級很低的線程,是以不一定會很快發現那些隻具有弱引用的對象。

弱引用可以和一個引用隊列 ReferenceQueue 聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛拟機就會把這個弱引用加入到與之關聯的引用隊列中。

  • 虛引用(PhantomReference)

“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。

虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個差別在于:虛引用必須和引用隊列 ReferenceQueue 聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的記憶體之前,把這個虛引用加入到與之 關聯的引用隊列中。

- 25、AsyncTask的優點和缺點?

優點:使用友善,既可以執行串行任務,也可以執行并行任務

缺點:預設使用串行任務執行效率低,不能充分利用多線程加快執行速度;如果使用并行任務執行,在任務特别多的時候會阻塞UI線程獲得CPU時間片,後續做線程收斂需要自定義AsynTask,将其設定為全局統一的線程池,改動量比較大

  • 26、對于面向對象的六大基本原則了解多少?
  • 單一職責(Single Responsibility Principle):一個類隻做一件事,可讀性提高
  • 裡式替換原則( Liskov Substitution Principle):依賴繼承和多态,就是能用父類的地方就可以用子類替換,用子類的但不能用父類。
  • 依賴倒置原則(Dependence Inversion Principle):依賴抽象,就是子產品之間的依賴通過抽象發生。
  • 開閉原則(Open-Close Principle):不管是實體類,子產品還是函數都應該遵循對擴充開放對修改關閉。還是要依賴封裝和繼承
  • 接口隔離原則(Interface Segregation Principle):一個類對另一個類的依賴應該建立在最小的接口上,如果接口太大,我們需要把它分割成一些更細小的接口,也是為了降低耦合性
  • 迪米特原則(Law of Demeter ):也稱最少知識原則,也就是說一個類應該對自己需要耦合或者調用的類知道的最少,隻需知道該方法即可,實作細節不必知道。
  • 27、LinearLayout, FrameLayout, RelativeLayout 哪個效率高, 為什麼?

    對于比較三者的效率那肯定是要在相同布局條件下比較繪制的流暢度及繪制過程,在這裡流暢度不好表達,并且受其他外部因素幹擾比較多,比如CPU、GPU等等,我說下在繪制過程中的比較,1、Fragment是從上到下的一個堆疊的方式布局的,那當然是繪制速度最快,隻需要将本身繪制出來即可,但是由于它的繪制方式導緻在複雜場景中直接是不能使用的,是以工作效率來說Fragment僅使用于單一場景,2、LinearLayout 在兩個方向上繪制的布局,在工作中使用頁比較多,繪制的時候隻需要按照指定的方向繪制,繪制效率比Fragment要慢,但使用場景比較多,3、RelativeLayout 它的沒個子控件都是需要相對的其他控件來計算,按照View樹的繪制流程、在不同的分支上要進行計算相對應的位置,繪制效率最低,但是一般工作中的布局使用較多,是以說這三者之間效率分開來講個有優勢、不足,那一起來講也是有優勢、不足,是以不能絕對的區分三者的效率

  • 28、請簡述一下 Android 7.0 的新特性?

    1.低電耗功能改進

    2.引入畫中畫功能

    3.引入“長按快捷方式”,即App Shortcuts

    4.引入混合模式,同時存在解釋執行/AOT/JIT,安裝應用時預設不全量編譯,使得安裝應用時間大大縮短

    5.引入了對私有平台庫限制,然而用一個叫做Nougat_dlfunctions的庫就行

    6.不推薦使用file:// URI傳遞資料,轉而推薦使用FileProvider

    7.快速回複通知

  • 29、 Android 8.0 的新特性

1、通知管道 — Notification Channels

2、畫中畫模式 — PIP

3、自适應圖示 — Adaptive Icons

4、定時作業排程

5、背景限制

6、廣播限制

7、背景位置限制

8、WebView API

9、多顯示器支援

10、 統一的布局外邊距和内邊距

11、指針捕獲

12、輸入和導航

13、新的 StrictMode 檢測程式

14、指紋手勢

15、更新的 ICU4J Android Framework API

  • 30、Android9.0新特性?

    1、室内WIFI定位

    2、“劉海”螢幕支援

    3、通知

    4、增強體驗

    5、通道設定、廣播以及免打擾

    6、多相機支援和相機更新

    7、新的圖檔解碼

    8、動畫

    9、HDR VP9視訊,HEIF圖像壓縮和媒體API

    10、JobScheduler中的資料成本敏感度

    11、神經網絡API 1.1

    12、改進表單自動填充

    13、安全增強

    14、Android 備份加密

  • 31、請談談你對 MVC 和 MVP 的了解?

1.MVC

使用者首先通過View發起互動,View調用Controller執行業務邏輯,Controller修改Model,然後View通過觀察者模式檢測到Model的變化(具體表現形式可以是Pub/Sub或者是觸發Events),重新整理界面顯示。

從這裡可以看出,主要業務邏輯都在Controller中,Controller會變得很重。MVC比較明顯的缺點:

View依賴特定的Model,無法元件化

View和Controller緊耦合,如果脫離Controller,View難以獨立應用(功能太少)

2.MVP

為了克服MVC的上述缺點,MVP應運而生。在MVP中,View和Model是沒有直接聯系的,所有操作都必須通過Presenter進行中轉。View向Presenter發起調用請求,Presenter修改Model,Model修改完成後通知Presenter,Presenter再調用View的相關接口重新整理界面。這樣,View就不需要監聽具體Model的變化了,隻需要提供接口給Presenter調用就可以了。MVP具有以下優點:

View可以元件化,不需要了解業務邏輯,隻需提供接口給Presenter

便于測試:隻需要給Presenter mock一個View,實作View的接口即可

- 32、談談Android的事件分發機制?

  • 會經過Activity->ViewGroup->view,一次往下傳遞事件,如果一直不攔截再回調回來。
  • 主要經過三個方法,dispatchTouchEvent(分發事件),oninterceptTouchEvent(是否攔截View中不存在),onTouchEvent(處理)。
  • 三個方法的用法是,先用dispatchTouchEvent來分發事件,然後用oninterceptTouchEvent來判斷是否攔截該任務(此方法在dispatchTouchEvent内部),如果不攔截直接dispatch向下回調,如果攔截就調用自己的onTouchEvent來處理事件。
  • 如果由setOnClickListener方法會先執行onClick.

    更多事件分發機制見講講 Android 的事件分發機制

  • 33、談談ArrayList和LinkedList的差別?

    ArrayList和LinkedList的大緻差別:

    1.ArrayList是實作了基于動态數組的資料結構,LinkedList是基于連結清單結構。

    2.對于随機通路的get和set方法,ArrayList要優于LinkedList,因為LinkedList要移動指針。

    3.對于新增和删除操作add和remove,LinkedList比較占優勢,因為ArrayList要移動資料。

    性能上的缺點:

    1.對ArrayList和LinkedList而言,在清單末尾增加一個元素所花的開銷都是固定的。對 ArrayList而言,主要是在内部數組中增加一項,指向所添加的元素,偶爾可能會導緻對數組重新進行配置設定;而對LinkedList而言,這個開銷是 統一的,配置設定一個内部Entry對象。

    2.在ArrayList集合中添加或者删除一個元素時,目前的清單所所有的元素都會被移動。而LinkedList集合中添加或者删除一個元素的開銷是固定的。

    3.LinkedList集合不支援 高效的随機随機通路(RandomAccess),因為可能産生二次項的行為。

    4.ArrayList的空間浪費主要展現在在list清單的結尾預留一定的容量空間,而LinkedList的空間花費則展現在它的每一個元素都需要消耗相當的空間

  • 34、handlerThread使用場景分析及原理?

當我們需要向子線程發送消息處理耗時操作時可使用handlerThread,詳細使用介紹見

handlerThread使用場景分析及源碼解析

  • 35、針對RecyclerView你做了哪些優化?

    1,減少view type的種類,如果樣式差别不大,可以公用一個布局。因為inflate調用比公用布局的繪制占用更多的性能。

    2,可以使用DiffUtil去重新整理資料,notifyDataSetChanged性能太低而且不會出發增删動畫。(子線程計算新舊資料,主線程重新整理recylerView)

    3,分頁加載

    4,有大量圖檔時,滾動停止加載圖檔,停止才通知adapter去加載

    5,設定合理的RecycledViewPool

    6,item的高度固定時setHasFixedSize(true)

    7,在ViewHolder中設定點選事件而不是在onBindViewHolder

  • 36,請說一下HashMap與HashTable的差別?

    HashMap和Hashtable的比較是Java面試中的常見問題,用來考驗程式員是否能夠正确使用集合類以及是否可以随機應變使用多種思路解決問題。HashMap的工作原理、ArrayList與Vector的比較以及這個問題是有關Java 集合架構的最經典的問題。Hashtable是個過時的集合類,存在于Java API中很久了。在Java 4中被重寫了,實作了Map接口,是以自此以後也成了Java集合架構中的一部分。Hashtable和HashMap在Java面試中相當容易被問到,甚至成為了集合架構面試題中最常被考的問題,是以在參加任何Java面試之前,都不要忘了準備這一題。

  • 1父類不同

第一個不同主要是曆史原因。Hashtable是基于陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實作。

public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable {…}

public class Hashtable<K, V> extends Dictionary<K, V> implements Map<K, V>, Cloneable, Serializable {…}

而HashMap繼承的抽象類AbstractMap實作了Map接口:

public abstract class AbstractMap<K, V> implements Map<K, V> {…}

  • 2 線程安全不一樣

Hashtable 中的方法是同步的,而HashMap中的方法在預設情況下是非同步的。在多線程并發的環境下,可以直接使用Hashtable,但是要使用HashMap的話就要自己增加同步處理了。

  • 3允不允許null值

Hashtable中,key和value都不允許出現null值,否則會抛出NullPointerException異常。

而在HashMap中,null可以作為鍵,這樣的鍵隻有一個;可以有一個或多個鍵所對應的值為null。當get()方法傳回null值時,即可以表示 HashMap中沒有該鍵,也可以表示該鍵所對應的值為null。是以,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應該用containsKey()方法來判斷。

  • 4周遊方式的内部實作上不同

Hashtable、HashMap都使用了 Iterator。而由于曆史原因,Hashtable還使用了Enumeration的方式 。

  • 5哈希值的使用不同

HashTable直接使用對象的hashCode。而HashMap重新計算hash值。

  • 6 内部實作方式的數組的初始大小和擴容的方式不一樣

HashTable中的hash數組初始大小是11,增加的方式是 old*2+1。HashMap中hash數組的預設大小是16,而且一定是2的指數。

  • 37,簡述一下自定義View的流程?

    自定義屬性;

    選擇和設定構造方法;

    重寫onMeasure()方法;

    重寫onDraw()方法;

    重寫onLayout()方法;

    重寫其他事件的方法(滑動監聽等);

    更多見自定義view的三種實作方式Android自定義View的三種實作方式

    38、談談線程死鎖,如何有效的避免線程死鎖?

    死鎖産生的條件

    一般來說,出現死鎖問題需要滿足以下條件

  • 互斥條件:一個資源每次隻能被一個線程使用
  • 請求與保證條件:一個線程因請求資源而阻塞時,對已獲得的資源保持不放
  • 不剝奪條件:線程已獲得的資源,在未使用完成之前,不能強行剝奪
  • 循環等待條件:若幹線程之間形成一種頭尾相接的循環等待資源關系

在JAVA程式設計中,有3中典型的死鎖類型:

  • 靜态的鎖順序死鎖
  • 動态的鎖順序死鎖
  • 協作對象之間發生的死鎖
  • 典型死鎖例子
  • 注意以下代碼都是錯誤代碼

1.靜态的鎖順序死鎖

class Test{
    final Object objA = new Object();
    final Object objB = new Object();
    
    public void a(){
        //注意這裡   先A後B
        synchronized(objA){
            synchronized(objB){
                //sth....
            }
        }
    }
    
    public void b(){
        //注意這裡    先B後A
        synchronized(objB){
            synchronized(objA){
                //sth....
            }
        }
    }
}
           

2.動态的鎖順序死鎖

動态的鎖順序死鎖是指兩個線程調用同一個方法時,傳入的參數颠倒造成的死鎖。如下情景,一個線程調用了transferMoney(轉賬)方法并傳入參數accountA,accountB;另一個線程調用了transferMoney方法并傳入參數accountB,accountA。此時就可能發生在靜态的鎖順序死鎖中存在的問題,即:第一個線程獲得了accountA鎖并等待accountB鎖,第二個線程獲得了accountB鎖并等待accountA鎖。

3.協作對象之間發生的死鎖

有時,死鎖并不會那麼明顯,比如兩個互相協作的類之間的死鎖,比如:一個線程調用了A對象的a方法,另一個線程調用了B對象的b方法。此時可能會發生,第一個線程持有A對象鎖并等待B對象鎖,另一個線程持有B對象鎖并等待A對象鎖。

  • 39、“equals”與“==”、“hashCode”的差別和使用場景?

    我們一般這麼了解

    equal比較的是内容

    == 比較的存儲位址或基本資料類型的數值比較(數學意義)

    hashCode 對記憶體配置設定的位置确定

使用場景

equal一般比較内容相等 比如字元串相等

==一般比較數值 或者null判斷

hashcode我們一般用來判斷來兩個對象是否相等,但這裡需要注意的是 兩個對象的hashcode相等,兩個對象不一定相等,兩個相等的對象hashcode一定相等。

我們為什麼要這樣判斷呢?

因為判斷兩個對象相等重寫equal的重載方法比較多,需要判斷 傳遞性、非空性、自反性、一緻性、對稱性

  • 40、談一談startService和bindService的差別,生命周期以及使用場景?

    1、生命周期上的差別

執行startService時,Service會經曆onCreate->onStartCommand。當執行stopService時,直接調用onDestroy方法。調用者如果沒有stopService,Service會一直在背景運作,下次調用者再起來仍然可以stopService。

執行bindService時,Service會經曆onCreate->onBind。這個時候調用者和Service綁定在一起。調用者調用unbindService方法或者調用者Context不存在了(如Activity被finish了),Service就會調用onUnbind->onDestroy。這裡所謂的綁定在一起就是說兩者共存亡了。

多次調用startService,該Service隻能被建立一次,即該Service的onCreate方法隻會被調用一次。但是每次調用startService,onStartCommand方法都會被調用。Service的onStart方法在API 5時被廢棄,替代它的是onStartCommand方法。

第一次執行bindService時,onCreate和onBind方法會被調用,但是多次執行bindService時,onCreate和onBind方法并不會被多次調用,即并不會多次建立服務和綁定服務。

2、調用者如何擷取綁定後的Service的方法

onBind回調方法将傳回給用戶端一個IBinder接口執行個體,IBinder允許用戶端回調服務的方法,比如得到Service運作的狀态或其他操作。我們需要IBinder對象傳回具體的Service對象才能操作,是以說具體的Service對象必須首先實作Binder對象。

3、既使用startService又使用bindService的情況

如果一個Service又被啟動又被綁定,則該Service會一直在背景運作。首先不管如何調用,onCreate始終隻會調用一次。對應startService調用多少次,Service的onStart方法便會調用多少次。Service的終止,需要unbindService和stopService同時調用才行。不管startService與bindService的調用順序,如果先調用unbindService,此時服務不會自動終止,再調用stopService之後,服務才會終止;如果先調用stopService,此時服務也不會終止,而再調用unbindService或者之前調用bindService的Context不存在了(如Activity被finish的時候)之後,服務才會自動停止。

那麼,什麼情況下既使用startService,又使用bindService呢?

如果你隻是想要啟動一個背景服務長期進行某項任務,那麼使用startService便可以了。如果你還想要與正在運作的Service取得聯系,那麼有兩種方法:一種是使用broadcast,另一種是使用bindService。前者的缺點是如果交流較為頻繁,容易造成性能上的問題,而後者則沒有這些問題。是以,這種情況就需要startService和bindService一起使用了。

另外,如果你的服務隻是公開一個遠端接口,供連接配接上的用戶端(Android的Service是C/S架構)遠端調用執行方法,這個時候你可以不讓服務一開始就運作,而隻是bindService,這樣在第一次bindService的時候才會建立服務的執行個體運作它,這會節約很多系統資源,特别是如果你的服務是遠端服務,那麼效果會越明顯(當然在Servcie建立的是偶會花去一定時間,這點需要注意)。

4、本地服務與遠端服務

本地服務依附在主程序上,在一定程度上節約了資源。本地服務因為是在同一程序,是以不需要IPC,也不需要AIDL。相應bindService會友善很多。缺點是主程序被kill後,服務變會終止。

遠端服務是獨立的程序,對應程序名格式為所在包名加上你指定的android:process字元串。由于是獨立的程序,是以在Activity所在程序被kill的是偶,該服務依然在運作。缺點是該服務是獨立的程序,會占用一定資源,并且使用AIDL進行IPC稍微麻煩一點。

對于startService來說,不管是本地服務還是遠端服務,我們需要做的工作都一樣簡單。

  • 41、synchronized和volatile關鍵字的差別?

    1.volatile本質是在告訴jvm目前變量在寄存器(工作記憶體)中的值是不确定的,需要從主存中讀取; synchronized則是鎖定目前變量,隻有目前線程可以通路該變量,其他線程被阻塞住。

    2.volatile僅能使用在變量級别;synchronized則可以使用在變量、方法、和類級别的

    volatile僅能實作變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性

    3.volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。

    4.volatile标記的變量不會被編譯器優化;synchronized标記的變量可以被編譯器優化

synchronized 可以保證原子性。他可以保證 在同一時刻,隻有一個線程可以通路被 synchronized 修飾的方法,或者代碼塊。

volatile 不能保證原子性。當時在使用這個關鍵字後。當被Volatitle 修飾字段的值發生改變後,其他線程會立刻知道這個值已經發生變化了。volatitle 可以保證可見性和有序性。

  • 42、什麼是冒泡排序?如何優化?

    冒泡排序算法原理:(從小到大排序)

    1.比較相鄰的元素。如果第一個比第二個大,就交換他們兩個

    2.對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對,交換一趟後,最後的元素會是最大的數

    3.針對所有的元素重複以上的步驟,除了最後一個

    4.持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較

優化方案1(定義一個變量l來儲存一趟交換中兩兩交換的次數,如果l==0,則說明排序已經完成,退出for循環)

優化方案2(假如有一個長度為50的數組,在一趟交換後,最後發生交換的位置是10,那麼這個位置之後的40個數必定已經有序了,記錄下這位置,下一趟交換隻要從數組頭部到這個位置就可以了)

定義一個變量n來儲存一趟交換中最後一次發生交換的位置,并把它傳遞給下一趟交換

/**
 * 排序思想:
 * 對一組數字進行從小到大或者從大到小的進行排序。
 * 它是通過讓相鄰的兩個元素進行比較,大的元素向下沉,小的元素向上冒
 * arr[0]與arr[1]進行比較,如果前者大于後者,則交換位置
 * 然後arr[1]與arr[2]進行比較,以此類推。當進行到n-1輪後,排序完成。
 */
import java.util.Arrays;
public class Sort {

    public static void main(String[] args){

        int arr[]= {100,90,101,23,13,75};
        int temp=0;
        for(int i=0;i<arr.length-1;i++) {
            for(int j=0;j<arr.length-1-i;j++) {
                if(arr[j]>arr[j+1]) {
                    temp=arr[j+1];
                    arr[j+1]=arr[j];
                    arr[j]=temp;
                }
            }
            System.out.println("第["+(i+1)+"]輪,排序結果:"+ Arrays.toString(arr));
        }
        System.out.print("================================");
        int arr2[]= {100,90,101,23,13,75};
        sort2(arr2);
    }

    /**
     * 優化思路:
     * 假如在第1輪比較當中,發現所有的元素都沒有進行交換,則說明此原資料就是有序的,不需要再進行排序
     * @param arr
     */
    public static void sort2(int arr[]){

        int temp=0;
        int flag=0;
        for(int i=0;i<arr.length-1;i++) {
            flag=0;
            for(int j=0;j<arr.length-1-i;j++) {
                if(arr[j]>arr[j+1]) {
                    temp=arr[j+1];
                    arr[j+1]=arr[j];
                    arr[j]=temp;
                    //如果有交換的行為,則flag=1
                    flag=1;
                }
            }
            //說明上面 内for循環中,沒有交換任何元素。
            if(flag==0) {
                break;
            }
            System.out.println("第["+(i+1)+"]輪,排序結果:"+Arrays.toString(arr));
        }

    }
}
           
  • 43、分别講講 final,static,synchronized 關鍵字可以修飾什麼,以及修飾後的作用?

    static

    static 方法

    static 方法一般稱作靜态方法,由于靜态方法不依賴于任何對象就可以進行通路,是以對于靜态方法來說,是沒有 this 的,因為它不依附于任何對象,既然都沒有對象,就談不上 this 了。

    public class StaticTest {

    public static void a(){

    }

    public static void main(String[]args){

    StaticTest.a();

    }

    }

    static 變量

    static 變量也稱作靜态變量,靜态變量和非靜态變量的差別是:靜态變量被所有的對象所共享,在記憶體中隻有一個副本,它當且僅當在類初次加載時會被初始化。而非靜态變量是對象所擁有的,在建立對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響。

    static 代碼塊

    static 關鍵字還有一個比較關鍵的作用就是 用來形成靜态代碼塊以優化程式性能。static 塊可以置于類中的任何地方,類中可以有多個 static 塊。在類初次被加載的時候,會按照 static 塊的順序來執行每個 static 塊,并且隻會執行一次。

    public class StaticTest {

    private static int a ;

    private static int b;

    static {

    a = 1;

    b = 2;

    }

    final

    final 變量

    凡是對成員變量或者本地變量(在方法中的或者代碼塊中的變量稱為本地變量)聲明為 final 的都叫作 final 變量。final 變量經常和 static 關鍵字一起使用,作為常量。

    private final int aa = 1;

    static {

    a = 1;

    b = 2;

    }

    private void init(){

    aa = 2;//報錯編譯器會提示 不能指派。。

    }

    final 方法

    final 也可以聲明方法。方法前面加上 final 關鍵字,代表這個方法不可以被子類的方法重寫。如果你認為一個方法的功能已經足夠完整了,子類中不需要改變的話,你可以聲明此方法為 final。final 方法比非 final 方法要快,因為在編譯的時候已經靜态綁定了,不需要在運作時再動态綁定。

    public static void main(String[]args){

    StaticTest.a();

    }

    class StaticTest2 extends StaticTest{

    public final void a(){ //這邊就會編譯器提示不能重寫

    }

    }

    **final 類 **

    其實更上面同個道理,使用 final 來修飾的類叫作 final 類。final 類通常功能是完整的,它們不能被繼承。Java 中有許多類是 final 的,譬如 String,Interger 以及其他包裝類。

    synchronized

    synchronized 是 Java 中解決并發問題的一種最常用的方法,也是最簡單的一種方法。synchronized 的作用主要有三個:

確定線程互斥的通路同步代碼

保證共享變量的修改能夠及時可見

有效解決重排序問題。

synchronized 方法

有效避免了類成員變量的通路沖突:

private synchronized void init(){

aa = 2;

}

synchronized 代碼塊

這時鎖就是對象,誰拿到這個鎖誰就可以運作它所控制的那段代碼。當有一個明确的對象作為鎖時,就可以這樣寫程式,但當沒有明确的對象作為鎖,隻是想讓一段代碼同步時,可以建立一個特殊的 instance 變量(它得是一個對象)來充當鎖。

public final void a(){

synchronized (lock){

//代碼

}

}

@Override

public void run() {

}

  • 44、什麼是 RemoteViews?使用場景有哪些?

    RemoteViews

    RemoteViews翻譯過來就是遠端視圖.顧名思義,RemoteViews不是目前程序的View,是屬于SystemServer程序.應用程式與RemoteViews之間依賴Binder實作了程序間通信.

    用法

    通常是在通知欄

//1.建立RemoteViews執行個體
        RemoteViews mRemoteViews=new RemoteViews("com.example.remoteviewdemo", R.layout.remoteview_layout);

        //2.建構一個打開Activity的PendingIntent
        Intent intent=new Intent(MainActivity.this,MainActivity.class);
        PendingIntent mPendingIntent=PendingIntent.getActivity(MainActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        //3.建立一個Notification
        mNotification = new Notification.Builder(this)
        .setSmallIcon(R.drawable.ic_launcher)
        .setContentIntent(mPendingIntent)
        .setContent(mRemoteViews)
        .build();

        //4.擷取NotificationManager
        manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        Button button1 = (Button) findViewById(R.id.button1);
        button1.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                //彈出通知
                manager.notify(1, mNotification);
            }
        });

           
  • 45、什麼是反射機制?反射機制的應用場景有哪些?

    Java 反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類中的所有屬性和方法,對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動态擷取的資訊以及動态調用對象的方法的功能稱為 Java 語言的反射機制。

    應用場景:

逆向代碼,例如反編譯

與注解相結合的架構,如 Retrofit

單純的反射機制應用架構,例如 EventBus(事件總線)

動态生成類架構 例如Gson

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;


/**
 * 對于任何一個類,我們都能夠知道這個類有哪些方法和屬性。對于任何一個對象,
 * 我們都能夠對它的方法和屬性進行調用。
 * 我們把這種動态擷取對象資訊和調用對象方法的功能稱之為 反射機制
 */
/**
 * 所謂反射其實是擷取類的位元組碼檔案,
 * 也就是.class檔案,那麼我們就可以通過Class這個對象進行擷取
 */
public class HookTest {

    public static void main(String[] args) {
        //第一種方式
        LoopTest loopTest = new LoopTest();
        Class aClass = loopTest.getClass();
        System.out.println(aClass.getName());
        //第二種方式
        Class aclass2 = LoopTest.class;
        System.out.println(aclass2.getName());
        //第三種方式
        try {
            Class aclass3 = Class.forName("LoopTest");
            System.out.println(aclass3.getName());
        }catch (ClassNotFoundException ex){
            ex.printStackTrace();
        }

        /**
         * 那麼這3中方式我們一般選用哪種方式呢?第一種已經建立了對象,那麼這個時候就不需要去進行反射了,
         * 顯得有點多此一舉。第二種需要導入類的包,依賴性太強。是以我們一般選中第三種方式。
         */

        /**
         * 三、通過反射擷取類的構造方法、方法以及屬性
         */

        /**
         * 1、擷取構造方法
         */
        Constructor[]constructors = aclass2.getConstructors();
        System.out.println("擷取構造方法:");
        for (Constructor constructor1 : constructors){
            System.out.println(constructor1.getName());
        }
        System.out.println("擷取類的屬性:");
        Field[] fields = aclass2.getFields();
        //88888
        System.out.println("擷取類的方法:");
        Method[]methods = aclass2.getMethods();
        for (Method method : methods){
            System.out.println(method.getName());
        }

        /**
         * 反射執行方法
         */

        try {   Class aclass4 = Class.forName("LoopTest");
            Method method   = aclass4.getDeclaredMethod("method",String.class);
            Constructor ct = aclass4.getConstructor(null);
            Object obj = ct.newInstance(null);
            method.invoke(obj,"反射調用");
        } catch (Exception e) {
            e.printStackTrace();
        }


        /**
         * Android中使用場景:其實很多用過的EventBus 、Retrofit 都有涉獵 可以去看看源碼
         */
    }

}

           

java反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能調用它的任意一個方法和屬性;這種動态擷取的資訊以及動态調用對象的方法的功能稱為java語言的反射機制。

靜态編譯:在編譯時确定類型,綁定對象。

動态編譯:在運作時确定類型,綁定對象。

反射機制的優缺點:

優點:運作期類型的判斷,動态加載類,提高代碼靈活度。

缺點:性能瓶頸:反射相當于一系列解釋操作,通知 JVM 要做的事情,性能比直接的java代碼要慢很多。

  • 46、Java 中使用多線程的方式有哪些?

    1、繼承Thread類建立線程

    Thread類本質上是實作了Runnable接口的一個執行個體,代表一個線程的執行個體。啟動線程的唯一方法就是通過Thread類的start()執行個體方法。start()方法是一個native方法,它将啟動一個新線程,并執行run()方法。這種方式實作多線程很簡單,通過自己的類直接extend Thread,并複寫run()方法,就可以啟動新線程并執行自己定義的run()方法。

    2、實作Runnable接口建立線程

    如果自己的類已經extends另一個類,就無法直接extends Thread,此時,可以實作一個Runnable接口

    3、實作Callable接口通過FutureTask包裝器來建立Thread線程

    Callable接口(也隻有一個方法

    4、4、使用ExecutorService、Callable、Future實作有傳回結果的線程

ExecutorService、Callable、Future三個接口實際上都是屬于Executor架構。傳回結果的線程是在JDK1.5中引入的新特征,有了這種特征就不需要再為了得到傳回值而大費周折了。

可傳回值的任務必須實作Callable接口。類似的,無傳回值的任務必須實作Runnable接口

  • 47、請簡述一下什麼是 Kotlin?它有哪些特性?

    設計理念

    1、建立一種相容Java的語言

    2、讓它比Java更安全,能夠靜态檢測常見的陷阱。如:引用空指針

    3、讓它比Java更簡潔,通過支援variable type inference,higher-order functions (closures),extension functions,mixins and first-class delegation等實作。

    4、讓它比最成熟的競争對手Scala語言更加簡單。

    Kotlin優勢

    1、簡潔: 大大減少樣闆代碼的數量。

    2、安全: 避免空指針異常等整個類的錯誤。

    3、互操作性: 充分利用 JVM、Android 和浏覽器的現有庫。

    4、工具友好: 可用任何 Java IDE 或者使用指令行建構。

kotlin和java都是運作在java虛拟機的語言。編譯後都會生成.class檔案。而虛拟機運作的正是.class檔案。是以兩者都可以用來寫Android。再說說個人的一些看法。java作為一門相對時間長一點的語言。相對來說更萬能一些。基本上能完成所有的開發場景。而且,因為時間夠久,相對來說問題也很少,雖然大家都吐槽分号,類型轉換,空指針這些傻瓜操作,但是我并沒有覺得不寫這些就能對我的開發有質的的提升,唯一讓我想學kt的動力就是google的Android執行個體将來要用kt寫。而kotlin作為一門新語言,有他自己的優點,也有一些缺點。具體什麼缺點大家看下面的文章吧。

`public class LruCachePhoto {

/**

* 圖檔 緩存技術的核心類,用于緩存下載下傳好的所有圖檔,

* 在程式記憶體達到設定值後會将最少最近使用的圖檔移除掉

*/

private LruCache<String, Bitmap> mMenoryCache;

public LruCachePhoto() {

//擷取應用最大可用記憶體

int maxMemory = (int) Runtime.getRuntime().maxMemory();

//設定 緩存檔案大小為 程式最大可用記憶體的 1/8

int cacheSize = maxMemory / 8;

mMenoryCache = new LruCache<String, Bitmap>(cacheSize) {

@Override

protected int sizeOf(String key, Bitmap value) {

return value.getByteCount();

}

};

}

/**

* 從 LruCache 中擷取一張圖檔,如果不存在 就傳回 null

*

* @param key LurCache 的鍵,這裡是 圖檔的位址

* @return 傳回對應的 Bitmap對象,找不到則為 null

*/

public Bitmap getBitmapFromMemoryCache(String key) {

return mMenoryCache.get(key);

}

/**

* 添加一張圖檔

* * @param key key

* @param bitmap bitmap

*/

public void addBitmapToCache(String key, Bitmap bitmap) {

if (getBitmapFromMemoryCache(key) == null) {

mMenoryCache.put(key, bitmap);

}

}

}`

  • 57、談談怎麼給apk瘦身?

    (1)res目錄優化:将png格式轉webp或svg格式,

    保真壓縮圖檔:可以使用一些圖檔壓縮網站或者工具壓縮你的資源檔案吧,例如TinyPng、ImageOptim、Zopfli、智圖等。

    (2)使用lint删除無用資源:在多人開發過程中,通常都會有漏删無用資源的問題,圖檔資源也不例外,例如需要删除一個子產品的代碼時,很容易就會漏删資源檔案,是以可以定期使用lint檢測出無用的資源檔案,原理這裡不作介紹,使用方法非常簡單,可以直接在AS裡面使用,如下圖所示。注意:lint檢查出來的資源都是無直接引用的,是以如果我們通過getIdentifier()方法引用檔案時,lint也會标記為無引用,是以删除時注意不要删除通過getIdentifier()引用的資源。

    (3)方法:Analyze -> Run Inspection by Name -> 輸入:Unused resources -> 跳出彈框選擇範圍即可

    (4)去掉無用資源:打開shrinkResources

    shrinkResources是在編譯過程中用來檢測并删除無用資源檔案,也就是沒有引用的資源,minifyEnabled 這個是用來開啟删除無用代碼,比如沒有引用到的代碼,是以如果需要知道資源是否被引用就要配合minifyEnabled使用,隻有兩者都為true時才會起到真正的删除無效代碼和無引用資源的目的。打開方式也是非常簡單,在build.gralde檔案裡面打開即可:

    android {

    buildTypes{

    minifyEnabled true

    shrinkResources true

    }

    }

    (5)Proguard代碼混淆:

    Proguard是一款免費的Java類檔案壓縮器、優化器和混淆器,Android Studio已經內建了這個工具,隻要經過簡單的配置,即可完成,如下代碼所示,在build.gradle裡面設定minifyEnabled為ture,同時在proguardFiles指向proguard的規則檔案即可。

    android {

    buildTypes{

    minifyEnabled true

    proguardFiles ‘proguard.cfg’

    }

    }

  • 58、JVM、Dalvik、ART三者的原理和差別?

    JVM:是Java Virtual Machine的縮寫,其并不是指某個特定的虛拟機實作,而指任何能夠運作Java位元組碼(class檔案)的虛拟機實作,比如oracle的Hotspot VM

Dalvik:是Google寫的一個用于Android的虛拟機,但其嚴格來說并不算JVM(沒有遵守Java虛拟機規範,比如其位元組碼格式是dex而非class)

該虛拟機在5.0時被ART替代

ART:是Android Runtime的縮寫,嚴格來說并不是一個虛拟機,在4.4~6.0時采用安裝時全部編譯為機器碼的方式實作,7.0時預設不全部編譯,采用解釋執行+JIT+空閑時AOT以改善安裝耗時

ART在安卓4.4時加入,5.0取代dalvik作為唯一實作直到現在。

  • 59、談談你是如何優化App啟動過程的?

    (1)盡量不要在Application裡做耗時操作,能放子線程的放子線程,能延後初始化的延後

    (2)啟動頁可以做成一個view在首頁面加載,同時首頁面的一些操作可以在這個過程中開始初始化

    (3)啟動頁的view層級盡量簡單

  • 60、談一談單例模式,建造者模式,工廠模式的使用場景?如何合理選擇?

    (1)單例模式,一般是指将消耗記憶體、屬性和對象支援全局公用的對象,應該設定為單例模式,如持久化處理(網絡、檔案等)

    (2)建造者模式,一般見于開發的架構或者屬性時可以直接鍊式設定屬性,比如我們看到的AlertDialog,一般用在某些輔助類(如BRVAH的BaseViewHolder)或者開發的架構的時候友善連續設定多個屬性和調用多個方法。

    (3)工廠模式,一般用于業務的實體建立,在建立的過程中考慮到後期的擴充。在Android源碼中比較常見的有BitmapFactory

    LayoutInflater.Factory,在實體編碼的過程中,比如BRVAH的多布局,如果資料類型比較多或者後期需要擴充,則可以通過工廠布局的方式,将實作MultiItemEntity

    接口的實體通過工廠模式建立:
  • 61、談談布局優化的技巧?

    1、降低Overdraw(過度繪制),減少不必要的背景繪制

    2、減少嵌套層次及控件個數

    3、使用Canvas的clipRect和clipPath方法限制View的繪制區域

    4、通過imageDrawable方法進行設定避免ImageView的background和imageDrawable重疊

    5、借助ViewStub按需延遲加載

    6、選擇合适的布局類型

    7、熟悉API盡量借助系統現有的屬性來實作一些UI效果

    8、盡量減少控件個數,對 TextView 左邊或者右邊有圖檔可是試用 drawableLeft,drawableRight

  • 61、說一下線程的幾種狀态?

    1.初始(NEW) ,建立線程對象

    2.運作(RUNNABLE),此時就緒且正在運作一起稱為運作

    3.阻塞(BLOCKED),線程阻塞

    4.等待(WAITING),等待中斷等操作

    5.逾時等待(TIMED_WAITING),可以指定時間傳回,不一定需要操作

    6.終止(TERMINATED),線程執行完畢

  • 62、簡單介紹下ContentProvider是如何實作資料共享的?

    使用 ContentProvider 可以将資料共享給其他應用,讓除本應用之外的應用也可以通路本應用的資料。它的底層是用 SQLite 資料庫實作的,是以其對資料做的各種操作都是以 Sql 實作,隻是在上層提供的是 Uri,使用者隻需要關心操作資料的 uri 就可以了,ContentProvider 可以實作不同 app 之間共享。詳細使用見ContentProvider跨程式共享資料(一)

  • 63、談談App的電量優化?

    (1)GPS

——使用要謹慎,如精确度不高可用WiFi定位或者基站定位,可用;非要用的話,注意定位資料的複用和定位頻率的門檻值

(2)Process和Service

——按需啟動,用完就退出

(3)網絡資料互動

——減少網絡網絡請求次數和資料量;WiFi比手機網絡省電

(4)CPU

——減少I/O操作(包括資料庫操作),減少大量的計算

(5)減少手機硬體互動

——使用頻率優化和選擇低功耗模式

(6)避免輪循。可以利用推送。如果非要輪循,合理的設定頻率。

應用處于背景時,避免某些資料的傳輸,比如感應器,定位,視訊緩存。

頁面銷毀時,取消掉網絡請求。

限制通路頻率,失敗後不要無限的重連。

合理的選擇定位精度和頻率。

使用緩存。如果資料變化周期比較長,可以出一個配置接口,用于記錄那些接口有變化。沒變化的直接用緩存。

減少廣播的使用頻率。可以用觀察者,startActivityForResult等代替。

  • 64、Java 線程中notify 和 notifyAll有什麼差別?

    當線程狀态為等待、逾時等待會調用notify 和 notifyAll方法通知線程更改狀态,此時

    當線程數量為1時,notify 和 notifyAll的效果一樣,會喚醒一個線程,并擷取鎖

    當線程數量大于1時,notify會喚醒一個線程,并擷取鎖,notifyAll會喚醒所有線程并根據算法選取其中一個線程擷取鎖,差別在于此時使用notify可能會出現死鎖的情況

  • 65、談一談你對binder的機制的了解?

    Binder機制:

    1.為了保證程序空間不被其他程序破壞或幹擾,Linux中的程序是互相獨立或互相隔離的。

    2.程序空間分為使用者空間和核心空間。使用者空間不可以進行資料互動;核心空間可以進行資料互動,所有程序共用一個核心空間。

    3.Binder機制相對于Linux内傳統的程序間通信方式:(1)性能更好;Binder機制隻需要拷貝資料一次,管道、消息隊列、Socket等都需要拷貝資料兩次;而共享記憶體雖然不需要拷貝,但實作複雜度高。(2)安全性更高;Binder機制通過UID/PID在核心空間添加了身份辨別,安全性更高。

    4.Binder跨程序通信機制:基于C/S架構,由Client、Server、Server Manager和Binder驅動組成。

    5.Binder驅動實作的原理:通過記憶體映射,即系統調用了mmap()函數。

    6.Server Manager的作用:管理Service的注冊和查詢。

    7.Binder驅動的作用:(1)傳遞程序間的資料,通過系統調用mmap()函數;(2)實作線程的控制,通過Binder驅動的線程池,并由Binder驅動自身進行管理。

    8.Server程序會建立很多線程處理Binder請求,這些線程采用Binder驅動的線程池,由Binder驅動自身進行管理。一個程序的Binder線程池預設最大是16個,超過的請求會阻塞等待空閑的線程。

    9.Android中進行程序間通信主要通過Binder類(已經實作了IBinder接口),即具備了跨程序通信的能力。

  • 66、什麼是線程池?如何建立一個線程池?

    線程池:

    1.線程池:建立多個線程,并管理線程,為線程配置設定任務并執行。

    2.使用線程池的好處:多個線程的建立會占用過多的系統資源,造成死鎖或OOM

    3.線程池的作用:(1)可以複用建立好的線程,減少線程的建立或銷毀的開銷;(2)提高響應速度,當任務到達時,不需要等待就可以立即執行;(3)可有效控制最大并發的線程數,提高系統資源的使用率。防止死鎖或OOM;(4)可以提供定時和定期的執行方式。

    4.線程池參數:corePoolSize(核心線程數)、maximumPoolSize(最大線程數)、workQueue(阻塞隊列)、keepAliveTime(保活時間)、threadFactory(線程工廠,用于生成線程)。

    5.線程池送出任務:有兩個方法可以向線程池送出任務,分别是execute()和submit()。

    execute():用于送出不需要傳回值的任務,無法判斷任務是否被線程執行成功。

    submit():用于送出需要傳回值的任務,會傳回一個future類型的對象,來判斷任務是否執行成功,還可以通過future的get()方法擷取傳回值,get()方法會阻塞目前線程直到任務完成。

    5.線程池的工作流程:

    (1)有新任務時,判斷目前線程數是否超過corePoolSize,如果小于corePoolSize,即使有空閑線程可以執行任務,也會建立一個新的線程用來執行該任務;

    (2)如果超過corePoolSize,就把任務放在workQueue(阻塞隊列)中,等待被執行,前提是workQueue是有界隊列;

    (3)如果workQueue滿了,判斷目前線程數是否小于maximumPoolSize,如果小于maximumPoolSize就建立一個線程用來執行任務。

    (4)如果目前線程數大于maximumPoolSize,就會執行線程池的飽和政策。

    6.線程池的飽和政策:(1)預設政策:直接抛出異常;(2)用調用者所在的線程(送出任務的那個線程)執行任務;(3)丢棄阻塞隊列中最靠前的任務,執行目前任務;(4)直接丢棄任務。

    7.線程池的狀态:

    (1)RUNNING:接收送出的任務。

    (2)SHUTDOWN:不再接收新送出的任務,繼續處理阻塞隊列中的任務。

    (3)STOP:不再接收新的任務,也不會處理阻塞隊列中的任務,并會終止正在執行的任務。

    (4)TIDYING:所有的任務已終止,ctl記錄的任務數量為0,會執行鈎子函數terminated()。

    (5)TERMINATED:線程池徹底終止。

    8.關閉線程池的方法:ThreadPoolExecutor提供了兩個方法,用于線程池的關閉,分别是shutdown()和shutdownNow()。

    原理:都是循環周遊線程池的工作線程,然後依次調用線程的intercept()方法來中斷線程。

    shutdown():将線程池狀态設定為SHUTDOWN。

    shutdownNow():将線程池狀态設定為STOP。

  • 67、給View設定的透明度的三種方法

    1,java代碼實作

text = (TextView) findViewById(R.id.text);
text.getBackground().setAlpha(12);
           

setAlpha()的括号中可以填0–255之間的數字。數字越大,越不透明。

注意點:在5.0以上系統時,有些機型會出現莫名其妙的顔色值不起作用,變成透明了,也就是用此方法會導緻其他共用一個資源的布局(例如:@color/white)透明度也跟着改變。

比如text用上述方法設定成透明後,項目中,其他用到text顔色值的控件,都變成透明了。

原因:在布局中多個控件同時使用一個資源的時候,這些控件會共用一個狀态,例如ColorState,如果你改變了一個控件的狀态,其他的控件都會接收到相同的通知。這時我們可以使用mutate()方法使該控件狀态不定,這樣不定狀态的控件就不會共享自己的狀态了。

text.getBackground().mutate().setAlpha(12);
           

2,在xml布局中進行設定

<TextView
        android:id="@ id/text"
        android:text="Hello World!"
        android:background="#FFFFFF"
        android:layout_width="match_parent"
        android:alpha="0.6"
        android:layout_height="100dp" />
           

android:alpha的值為0~1之間的數。數字越大,越不透明。1表示完全不透明,0表示完全透明。

3,在xml布局中通過android:background設定

<TextView
        android:id="@ id/text"
        android:text="Hello World!"
        android:background="#52FFFFFF"
        android:layout_width="match_parent"
        android:layout_height="100dp" />
           

顔色和不透明度 (alpha) 值以十六進制表示法表示。任何一種顔色的值範圍都是 0 到 255(00 到 ff)。對于 alpha,00 表示完全透明,ff 表示完全不透明。android:background的值的格式為”#AARRGGBB”。AA即透明度,R、G、B是紅綠藍三色。每一位均為0–F的十六位數。其中透明度的數值越大,越不透明

java代碼

//java代碼生成的對應表
for (int i = 100; i>=0; i--) {
   double j = (i / 100.0d);
   int alpha = (int) Math.round(255-j * 255);
   String hex = Integer.toHexString(alpha).toUpperCase();
   if (hex.length() == 1) hex = "0" + hex;
   int percent = (int) (j*100);
   System.out.println(String.format("%d%% — %s", percent, hex));
}
           

透明度對照表

透明度 16進制表示

100% 00(全透明)

99% 03

98% 05

97% 07

96% 0A

95% 0D

94% 0F

93% 12

92% 14

91% 17

90% 1A

89% 1C

88% 1E

87% 21

86% 24

85% 26

84% 29

83% 2B

82% 2E

81% 30

80% 33

79% 36

78% 38

77% 3B

76% 3D

75% 40

74% 42

73% 45

72% 47

71% 4A

70% 4D

69% 4F

68% 52

67% 54

66% 57

65% 59

64% 5C

63% 5E

62% 61

61% 63

60% 66

59% 69

58% 6B

57% 6E

56% 70

55% 73

54% 75

53% 78

52% 7A

51% 7D

50% 80

49% 82

48% 85

47% 87

46% 8A

45% 8C

44% 8F

43% 91

42% 94

41% 96

40% 99

39% 9C

38% 9E

37% A1

36% A3

35% A6

34% A8

33% AB

32% AD

31% B0

30% B3

29% B5

28% B8

27% BA

26% BD

25% BF

24% C2

23% C4

22% C7

21% C9

20% CC

19% CF

18% D1

17% D4

16% D6

15% D9

14% DB

13% DE

12% E0

11% E3

10% E6

9% E8

8% EB

7% ED

6% F0

5% F2

4% F5

3% F7

2% FA

1% FC

0% FF(完全不透明)

不透明度對照表

不透明度—十六進制值

100% — FF(完全不透明)

99% — FC

98% — FA

97% — F7

96% — F5

95% — F2

94% — F0

93% — ED

92% — EB

91% — E8

90% — E6

89% — E3

88% — E0

87% — DE

86% — DB

85% — D9

84% — D6

83% — D4

82% — D1

81% — CF

80% — CC

79% — C9

78% — C7

77% — C4

76% — C2

75% — BF

74% — BD

73% — BA

72% — B8

71% — B5

70% — B3

69% — B0

68% — AD

67% — AB

66% — A8

65% — A6

64% — A3

63% — A1

62% — 9E

61% — 9C

60% — 99

59% — 96

58% — 94

57% — 91

56% — 8F

55% — 8C

54% — 8A

53% — 87

52% — 85

51% — 82

50% — 80

49% — 7D

48% — 7A

47% — 78

46% — 75

45% — 73

44% — 70

43% — 6E

42% — 6B

41% — 69

40% — 66

39% — 63

38% — 61

37% — 5E

36% — 5C

35% — 59

34% — 57

33% — 54

32% — 52

31% — 4F

30% — 4D

29% — 4A

28% — 47

27% — 45

26% — 42

25% — 40

24% — 3D

23% — 3B

22% — 38

21% — 36

20% — 33

19% — 30

18% — 2E

17% — 2B

16% — 29

15% — 26

14% — 24

13% — 21

12% — 1F

11% — 1C

10% — 1A

9% — 17

8% — 14

7% — 12

6% — 0F

5% — 0D

4% — 0A

3% — 08

2% — 05

1% — 03

0% — 00(全透明)

繼續閱讀