1. 程式人生 > >Android面試基礎題詳解1

Android面試基礎題詳解1

關於ANR

一、什麼是ANR?

ANR就是Application Not Responding,是安卓中彈出的一個對話方塊,讓使用者來選擇等待或者直接關閉程式。activity響應超過5秒,service響應超過10秒,都會出現ANR。

二、ANR產生的原因

剛才也說了,activity響應超過5秒,service響應超過10秒,都會出現ANR。那麼為什麼activity或者service會用這麼長時間來響應呢?最大的原因可能就是主執行緒被阻塞了。所以產生ANR的原因有以下兩個:

  • 主執行緒中存在耗時的計算
  • 主執行緒被IO操作阻塞(安卓4.0以後,網路IO禁止在主執行緒執行)

那麼安卓中有哪些是在主執行緒中執行的呢?

  • activity的所有生命週期方法;
  • service預設是在主執行緒的;、
  • broadcast中的onReceive()回撥方法;
  • AsyncTask中除了doInBackgroud方法,其他的四個方法都是在主執行緒中的;
  • Handler的handleMessage()和post(Runnable)方法,前提是這個Handler沒有使用子執行緒的looper。

基本上就是這麼多,所以在上述這些方法中是絕對不能做耗時操作的,否則就會出現ANR異常。

三、怎麼避免ANR?

避免ANR的總方針就是不要阻塞主執行緒,具體的方法有以下這些:

  • 使用AsyncTask來進行耗時操作;
  • 使用Thread或者HandlerThread來提高執行緒優先順序;
  • 使用Handler來處理工作執行緒;
  • 不要在Activity中執行耗時的計算。

一般做到以上幾步就可以避免ANR了。

關於Activity

一、簡述一下Activity的生命週期

  • 首先在回答這個問題之前,我們先來說一下什麼是Activity。 
    • Activity是安卓中與使用者進行互動的介面,它提供了一個介面,可以讓使用者進行點選、滑動等一系列操作。
  • 在瞭解了activity是什麼以後,我們還必須瞭解一下activity的四種狀態 
    • running狀態:這時activity處於活躍狀態,使用者可以與其進行互動,可以進行點選、滑動等操作,介面會做出響應。
    • pause狀態:這時activity處於可見狀態,但是失去觸控的能力。一般activity被一個透明的activity或者非全屏的activity覆蓋,此時被覆蓋的activity就處於pause狀態。如果記憶體緊張,會被系統回收。
    • stop狀態:此時的activity處於完全不可見狀態。一般是被另一個activity完全覆蓋時,被覆蓋的activity處於的狀態。如果記憶體緊張,會被系統回收。
    • killed狀態:此時的activity已經被系統回收,它儲存的資訊以及成員變數都會被銷燬。

在瞭解了以上內容以後,我們就可以來說一下activity的生命週期了。

activity一共有7個生命週期方法。

  • 當activity啟動的時候,它會經過如下生命週期方法,依次是:onCreate()–>onStart()–>onResume() 
    • onCreate():一般是完成介面以及資料的初始化工作,是activity啟動時第一個被呼叫的方法。
    • onStart():此時的activity處於可見狀態,但是還不可以與使用者進行互動。
    • onResume():此時的activity已經完全完成載入動作,使用者可以對其進行滑動、點選等操作。
  • 當點選Home鍵返回主介面的時候,此時的activity處於不可見狀態了,它會經過以下生命週期方法:onPause()–>onStop() 
    • onPause():此時activity還是處於可見狀態的,但是已經失去與使用者進行互動的能力了。與之對應的是onResume()方法。
    • onStop():此時的activity處於完全不可見狀態了。
  • 再次返回原Activity,由於之前已經快取過activity,所以再次回來不會走onCreate()方法,它的生命週期方法如下:onRestart()–>onStart()–>onResume() 
    • onRestart():從快取中重新載入activity會走此方法。
  • 退出activity,它會走如下生命週期方法:onPause()–>onStop()–>onDestroy() 
    • onDestroy():走到該方法表示activity已經被銷燬了,它的成員變數等已經被系統回收了。

二、Android任務棧

任務棧是後進先出的棧結構,Android通過任務棧可以有序管理每一個Activity。任務棧並不是唯一的,一個App中可以有多個任務棧,但是,在某些情況下,一個Activity也可以獨享一個任務棧。

三、activity有幾種啟動模式,各有什麼區別?

1.Standard模式 :每次啟動Activity都會建立一個Activity例項,加入任務棧中,不會考慮任務棧中是否有相同的Activity。較為消耗資源。

2.SingleTop模式 :棧頂複用模式,如果新建立的Activity與當前的Activity一致,處於棧頂的話,就不會建立新的Activity,而是複用棧頂的Activity。

3.SingleTask模式 :任務棧複用模式,在新建Activity之前會檢測任務棧中是否有相同的Activity,有的話直接把Activity移到棧頂,這個Activity以上的Activity都會被移除和銷燬;沒有就新建一個。

4.SingleInstance模式 :在整個系統中,有且只有一個例項,而且這個Activity獨享任務棧。

四、Android程序優先順序

1.前臺程序 :處於前臺正在與使用者進行互動的Activity,或者在前臺繫結的Service。

2.可見程序 :可見但不可互動的Activity。

3.服務程序 :在後臺開啟的Service就是服務程序。

4.後臺程序 :當處於前臺的Activity,被按下Home鍵之後,該Activity會變成後臺程序,但後臺程序不會被立馬銷燬,系統會根據記憶體情況進行相應的回收。

5.空程序 :表示沒有活躍的元件,只是出於快取的目的而保留,可被隨時收回。

五、Scheme跳轉模式

android中的scheme是一種頁面內跳轉協議,是一種非常好的實現機制,通過定義自己的scheme協議,可以非常方便跳轉app中的各個頁面;通過scheme協議,伺服器可以定製化告訴app跳轉哪個頁面,可以通過通知欄訊息定製化跳轉頁面,可以通過H5頁面跳轉頁面等。

關於Fragment

一、Fragment為什麼被稱為第五大元件?

首先在使用頻率上,fragment不低於其他四大元件,而且它有自己的生命週期。使用fragment比activity更加節省記憶體,同時,它可以很靈活的載入到activity中,所以它被稱為第五大元件。雖然它有自己的生命週期,但是它必須依附於宿主activity存在。

二、Fragment載入到activity中的兩種方式?

  • 靜態載入:在activity的佈局檔案中以標籤的形式新增fragment。
  • 動態載入:在java檔案中以程式碼的形式載入fragmet。 
    • 1、獲取FragmentManager管理者物件;
    • 2、通過上述物件的beginTranscation()方法建立FragmentTranscation物件;
    • 3、通過transcation物件的add()/remove()/replace()/hide()/show()等方法來顯示或者移除fragment;
    • 4、通過transcation物件的commit()方法來提交。

總結起來就是如下兩行程式碼: 
getFragmentManager().beginTranscation().add() 
getFragmentManager().beginTranscation().commit()

注意: 
1、同一個fragment只能被add一次; 
2、同一個transcation只能被提交一次; 
3、transcation.addToBackStack(null)將碎片新增到返回棧中,此時按下返回鍵,不會退出程式,而是返回上一個碎片。

三、fragmentPagerAdapter和fragmentStatePagerAdapter的區別?

fragmentPagerAdapter在destroyItem()的時候呼叫的是detach()方法,只是把UI進行了分離,並沒有真正的移除fragment,所以只適用於頁面較少的情況。而fragmentStatePagerAdapter在切換頁面的時候呼叫的是remove()方法,是真正回收記憶體的,所以它適用於頁面較多的情況。

四、fragment的生命週期?

onAttach()–>onCreate()–>onCreateView()–>onViewCreate()–>activity.onCreate()–>onActivityCreate()–>activity.onStart()–>activity.onResume()–>activity.onPause()–>activity.onStop()–>onDestroyView()–>onDetach()

這裡寫圖片描述

五、fragment與activity之間的傳值通訊?

1、fragment向activity之間的傳值:

  • 在fragment中通過getActivity()獲取activity的物件,然後呼叫activity中的方法;
  • 介面回撥:在fragment中設定一個介面,然後讓activity實現這個介面,這樣就可以在介面中將資料傳遞給activity.

2、activity向fragment傳值:

  • 在 activity中建立Bundle資料包,通過putExtra()方法將資料放進去,然後呼叫fragment物件的setArguments(bundle)方法,在fragment中就可以通過getArguments()方法獲取資料。
  • 在activity中通過getFragmentManager.findFragmentById()獲取fragment物件,然後就可以呼叫fragment的方法了。

3、fragment與fragment傳值:

  • 在第一個fragment中呼叫getActivity().getFragmentManager().findFragmentById()獲取第二個fragment的物件,然後呼叫第二個fragment中的方法;
  • 介面回撥:在fragment1中設定一個介面,宿主activity實現這個介面,fragment1在介面中與activity實現通訊,然後在activity中的回撥方法中,實現與fragment2的通訊。

總結:fragment與activity通訊的關鍵點就是要拿到對方的物件,fragment通過getActivity()獲取activity的物件,activity通過getFragmentManager().getFragmentById()來獲取fragment物件。fragment與activity之間可以直接通訊,而fragment之間必須通過activity這個宿主來實現通訊。

關於service

一、什麼是Service?

Service是一個可以在後臺長時間執行操作而且沒有使用者介面的元件。它是執行在主執行緒中的,所以不能在Service中執行耗時操作。它可以由其他元件來啟動,比如activity或者broadcast,服務一旦啟動,就會一直在後臺執行,即使啟動它的元件被銷燬了,也不會影響它的執行。我們可以把service和activity進行繫結,這樣就可以實現兩者之間的資料互動。

二、service和thread的區別?

  • 定義:thread是程式執行的最小單元,我們可以通過開啟子執行緒來完成一些耗時操作,而service是後臺服務,是安卓中的一種機制,如果是本地的service,那麼它就執行在它所在的主執行緒,所以service中不能進行耗時操作。
  • 實際開發:不能在service中進行耗時操作,如果一定要進行,那麼也必須要開啟子執行緒,那為什麼不在activity中開啟子執行緒呢?因為activity很難對子執行緒進行把控,一旦activity銷燬了,那麼將很難獲得子執行緒的例項,容易造成記憶體洩漏。而service不一樣,即使activity被銷燬了,service依然可以在後臺執行。
  • 應用場景:如果我們需要進行耗時操作,那麼就必須要開啟子執行緒。而service是需要長時間在後臺執行,並且不需要使用者介面的情況下才會使用,比如後臺播放音樂,天氣預報的統計等。

三、service的兩種啟動方式以及區別?

  • startService:通過此種方式啟動的服務,會一直在後臺執行,除非手動關閉。 
    • 執行步驟如下:
    • 1、定義一個類繼承service;
    • 2、在manifest.xml檔案中註冊service;
    • 3、使用context的startService()啟動服務;
    • 4、如果不再使用,呼叫stopService()方法來停止該服務。
  • bindService:此時service和activity處於繫結狀態,兩者之間可以進行資料互動。 
    • 執行步驟如下:
    • 定義一個服務端繼承service,並且在類中實現IBinder介面的例項物件,並提供公共方法給客戶端呼叫;
    • 在onBind()方法中返回此Binder例項;
    • 客戶端在onServiceConnected()方法中接收Binder例項,並通過服務端提供的方法繫結服務。

接下來通過程式碼來深刻理解兩種啟動service的方法到底什麼區別。

  • 首先是startService
public class StartService extends Service {
    private static final String TAG = "StartService";

    /**
    * 首次建立服務時,系統將呼叫此方法執行一次性設定程式(在呼叫onStartCommand()或者onBind()之前),
    * 如果服務已在執行,則不會呼叫此方法,該方法只被呼叫一次。
    */

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "StartService Thread id is ==="+Thread.currentThread().getId() );
    }

    /**
    * 繫結服務的時候才會呼叫
    * 必須要實現的方法
    * @param intent
    * @return
    */

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
    * 每次通過startService()啟動Service時都會被回撥。
    * @param intent
    * @param flags
    * @param startId
    * @return  return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
    * 如果返回START_STICKY,那麼在系統記憶體空暇時,系統會嘗試重新啟動服務,而此時的intent為null;
    */

    @Override
    public int onStartCommand(Intent intent,  int flags, int startId) {
        System.out.println("onStartCommand invoke");

        Log.e(TAG, "StartService Thread id is ==="+Thread.currentThread().getId() );

        return super.onStartCommand(intent, flags, startId);
    }

    /**
    * 服務銷燬時的回撥
    */

    @Override
    public void onDestroy() {
        System.out.println("onDestroy invoke");
        super.onDestroy();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private Button mBtStop,mBtStart,mBtBind;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtStart= (Button) findViewById(R.id.btn_start);
        mBtStop= (Button) findViewById(R.id.btn_stop);

        Log.e(TAG, "MainActivity thread id is=="+Thread.currentThread().getId() );

        final Intent intent=new Intent(MainActivity.this,StartService.class);

        mBtStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService(intent);
            }
        });

        mBtStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopService(intent);
            }
        });



    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

-接下來是bindService

public class BindService extends Service {

    private static final String TAG = "BindService";
    private LocalBinder binder=new LocalBinder();
    private int count;
    private Thread thread;
    private boolean quit;

    /**
    * 建立Binder物件,返回給客戶端的Activity使用,提供資料交接的介面
    */
    public class LocalBinder extends Binder{
        //申明一個方法getService(),提供給客戶端呼叫
        BindService getService(){
            //返回當前物件LocalBinder,這樣我們就可以在客戶端呼叫Service的公共方法了。
            return BindService.this;
        }
    }

    /**
    * 把Binder類返回給客戶端
    * @param intent
    * @return
    */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "Service is invoke Created" );
        thread =new Thread(new Runnable() {
            @Override
            public void run() {
                while (!quit){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        });
        thread.start();

    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "Service is invoke Destroyed" );
        this.quit=true;
        super.onDestroy();
    }

    /**
    * 公共方法
    * @return
    */
    public int getCount(){
        return count;
    }

    /**
    * 解除繫結時呼叫
    * @param intent
    * @return
    */
    public boolean onUnbind(Intent intent){
        Log.e(TAG, " Service is invoke onUnbind" );
        return super.onUnbind(intent);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
public class BindActivity extends AppCompatActivity {
    private static final String TAG = "BindActivity";

    private Button btnBind,btnUnBind,btnGetDatas;

    private BindService mService;
    private ServiceConnection conn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bind);

        btnBind= (Button) findViewById(R.id.btn_Bind);
        btnUnBind= (Button) findViewById(R.id.btn_UnBind);
        btnGetDatas= (Button) findViewById(R.id.btn_GetDatas);

        //建立繫結物件
        final Intent intent=new Intent(this,BindService.class);

        //開啟繫結
        btnBind.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "繫結呼叫:bindService" );
                bindService(intent,conn, Service.BIND_AUTO_CREATE);
                Log.e(TAG, "繫結成功");
            }
        });

        //解除繫結
        btnUnBind.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "解除繫結呼叫:unbindService" );
                if(mService!=null){
                    mService=null;
                    unbindService(conn);
                }
            }
        });

        //獲取資料
        btnGetDatas.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mService!=null){
                    //通過繫結服務傳遞的Binder物件,獲取Service暴露出來的資料
                    Log.e(TAG, "從伺服器獲取資料 "+mService.getCount() );
                }else {
                    Log.e(TAG, "還沒有進行繫結,請先繫結");
                }
            }
        });

        /**
        * ServiceConnection代表與伺服器的連線,它只有兩個方法
        * @param savedInstanceState
        */
        conn=new ServiceConnection(){

            /**
            * 與伺服器互動的介面方法,繫結服務的時候被回撥,在這個方法中獲取繫結Service 傳遞過來的IBinder物件
            * 通過這個IBinder物件,實現宿主與Sevice的互動
            * @param name
            * @param service
            */
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(TAG, "繫結成功呼叫  onServiceConnected");
                BindService.LocalBinder binder= (BindService.LocalBinder) service;
                mService=binder.getService();
            }

            /**
            * 當取消繫結的時候被回撥,但正常情況下式不會被回撥的,只有當Service服務被意外銷燬時
            * 例如記憶體不足時這個方法才會被自動呼叫
            * @param name
            */
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, "繫結失敗呼叫  onServiceDisconnected");
            }
        };

    }

}