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");
}
};
}
}