[toc]
Android面試題
Android面試題除了Android基礎(chǔ)之外,更多的問(wèn)的是一些源碼級(jí)別的、原理這些等。所以想去大公司面試,一定要多看看源碼和實(shí)現(xiàn)方式,常用框架可以試試自己能不能手寫實(shí)現(xiàn)一下,鍛煉一下自己。
一、Android基礎(chǔ)知識(shí)點(diǎn)
四大組件是什么
Activity
其中Activity是我們最常使用的組件,也是我們最熟悉的組件,一般用于呈現(xiàn)頁(yè)面內(nèi)容,處理用戶交互等等Service
Service是一個(gè)可以運(yùn)行于后臺(tái)的組件,我們一般用于處理一些不需要用戶知道,但是又必須比較長(zhǎng)時(shí)間存在的操作,比如下載ContentProvider
主要用于進(jìn)程間通信,比如暴露某個(gè)APP的信息內(nèi)存給予另外一個(gè)APP獲取使用,比如獲取聯(lián)系人等等-
BroadcastReceiver
廣播接受者主要用于接受廣播信息,像我們?nèi)粘J褂弥锌赡苡糜趦煞N情況:- APP內(nèi):界面間通信,例如退出app,可以發(fā)送自殺廣播
- APP間:可以收到第三方發(fā)出的廣播,進(jìn)而進(jìn)行對(duì)應(yīng)的響應(yīng)操作。
四大組件的生命周期和簡(jiǎn)單用法
Activity
- 生命周期
oncreate()->onstart()->onresume()->onpause()->onstop()->ondestory()
- 簡(jiǎn)單用法
/**創(chuàng)建App*/
class someClass :Activity()
/**跳轉(zhuǎn)app*/
startActivity(Intent(this@someActivity,targetClass::class.java))
Service
- 生命周期
- startService()的生命周期:
oncreate()->onstartComment()->onstart()->onDestory() - bindService()的生命周期:
oncreate()->onbind()->onUnbind()->onDestroy()
- 簡(jiǎn)單用法
- startService()
通過(guò)簡(jiǎn)單的startService()進(jìn)行service啟動(dòng),此后啟動(dòng)該Service的組件無(wú)法把控Service的生命周期,理論上此后該Service可以在后臺(tái)無(wú)期限運(yùn)行(實(shí)際根據(jù)情況該Service可能會(huì)在任意一個(gè)時(shí)刻被殺死,這里牽連到了另外一個(gè)知識(shí)點(diǎn):Service防殺)
我們可以在onStartCommand()里面做我們要做的操作,注意Service跟Activity一樣不可以做耗時(shí)操作,雖然運(yùn)行anr時(shí)間比Activity多了近一倍。 - bindService()
通過(guò)綁定的方式啟動(dòng)Service
綁定后,該Service與啟動(dòng)綁定操作的組件形成綁定,當(dāng)組件銷毀時(shí),該Service也隨著銷毀。
其中組件與Service形成一個(gè)典型的BC體系,Service相當(dāng)于服務(wù)器,組件可以通過(guò)IBinder像Service 發(fā)送請(qǐng)求并獲取回應(yīng)。
- startService()
public boolean bindService(Intent service, ServiceConnection conn,
int flags)
BroadcastReceiver(分為2種):
- 簡(jiǎn)單用法
- 靜態(tài)注冊(cè)(常駐廣播)
在AndroidManifest.xml中進(jìn)行注冊(cè),App啟動(dòng)的時(shí)候自動(dòng)注冊(cè)到系統(tǒng)中,不受任何組件生命周期影響,(即便應(yīng)用程序已經(jīng)關(guān)閉),但是 耗電,占內(nèi)存 - 動(dòng)態(tài)注冊(cè)(非常駐廣播)
在代碼中進(jìn)行注冊(cè),通過(guò)IntentFilter意圖過(guò)濾器篩選需要監(jiān)聽的廣播,
記得注銷(推薦在onResume()注冊(cè),在onPause()注銷),使用靈活,生命周期隨組件變化
- 靜態(tài)注冊(cè)(常駐廣播)
全局廣播
- 普通廣播(最常用的那種)
- 系統(tǒng)廣播
系統(tǒng)廣播無(wú)須開發(fā)者進(jìn)行發(fā)送,我們只需做好廣播接收器進(jìn)行接收即可,常用的幾個(gè)系統(tǒng)廣播為:
android.net.conn.CONNECTIVITY_CHANGE 監(jiān)聽網(wǎng)絡(luò)變化
Intent.ACTION_PACKAGE_ADDED 成功安裝apk
... ... - 有序廣播
有序廣播是指廣播按照一定的優(yōu)先級(jí)被廣播接受者依次接收,代碼實(shí)例
發(fā)送廣播
定義2個(gè)廣播接受者
動(dòng)態(tài)注冊(cè)2個(gè)廣播接受者,設(shè)置不同的優(yōu)先級(jí)
高優(yōu)先級(jí)的廣播接受者接受信息,篡改信息,塞回篡改后的信息
低優(yōu)先級(jí)廣播接收源信息,接收上一個(gè)廣播信息,分別打印出來(lái)
class MainActivity : AppCompatActivity() {
companion object {
fun sayLog(message: String) = Log.e("THIS IS THE TAG", message)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvMainSendBroadcast.setOnClickListener {
sendBroadcast()
}
// register 高等級(jí)的廣播 等級(jí)為100
val firstReceiver = BroadcastReceiverOrderFirst()
val intentFilter = IntentFilter()
intentFilter.addAction("lY_ACTION")
intentFilter.priority = 100
registerReceiver(firstReceiver, intentFilter)
// register 低等級(jí)的廣播 等級(jí)為50
val lowReceiver = BroadcastReceiverOrderLow()
val intentFilterLow = IntentFilter()
intentFilterLow.addAction("lY_ACTION")
intentFilterLow.priority = 50
registerReceiver(lowReceiver, intentFilterLow)
}
/**發(fā)送普通廣播*/
private fun sendBroadcast() {
val targetIntent = Intent()
val bundle = Bundle()
bundle.putString("message", "this is the def message")
targetIntent.action = "lY_ACTION"
targetIntent.putExtra("extra", bundle)
sendOrderedBroadcast(targetIntent, null)
}
/**
* 優(yōu)先級(jí)比較高的廣播接受者
*/
class BroadcastReceiverOrderFirst : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val bundle = intent?.getBundleExtra("extra")
sayLog("BroadcastReceiverOrderFirst has the message,the message is ${bundle?.getString("message")}")
// 修改攜帶的信息
bundle?.putString("message", " this is not the def message")
// 把修改好的信息put進(jìn)結(jié)果集
setResultExtras(bundle)
}
}
/**
* 優(yōu)先級(jí)比較低的廣播接受者
*/
class BroadcastReceiverOrderLow : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// 獲取源數(shù)據(jù)
val bundle = intent?.getBundleExtra("extra")
sayLog("BroadcastReceiverOrderLow has the message,the message is ${bundle?.getString("message")}")
// 獲取上一個(gè)廣播傳遞過(guò)來(lái)的數(shù)據(jù)
val bundleForPre=getResultExtras(true)
sayLog("BroadcastReceiverOrderLow has the message,the modify is ${bundleForPre?.getString("message")}")
}
}
}
}
本地廣播
上面所講的廣播都是全局廣播,全局廣播指的是,我在甲App發(fā)送了一條廣播,其中已App也可以收到這個(gè)廣播信息,存在問(wèn)題為:
- 其他app可以發(fā)送相對(duì)應(yīng)的 intent-filter到我方App,導(dǎo)致我方錯(cuò)誤處理
- 其他app可以設(shè)置對(duì)應(yīng)的 intent-filter接收我方App發(fā)出的廣播,導(dǎo)致安全性問(wèn)題
為解決這個(gè)問(wèn)題,我們可以使用本地廣播:
1.方式一:- 設(shè)置exported為false(默認(rèn)為true),使該廣播只在app內(nèi)傳遞
- 廣播傳遞和接收時(shí),添加響應(yīng)的permission
- 通過(guò)intent.setPackage(com.xxx.xxx)
- 方式二 LocalBroadcastManager:
/**發(fā)送普通廣播*/
private fun sendBroadcast() {
val targetIntent = Intent()
val bundle = Bundle()
bundle.putString("message", "this is the def message")
targetIntent.action = "lY_ACTION"
targetIntent.putExtra("extra", bundle)
val localBroadcastReceiver =LocalBroadcastManager.getInstance(this@MainActivity)
localBroadcastReceiver.sendBroadcast(targetIntent)
// register
val lowReceiver = BroadcastReceiverOrderLow()
val intentFilterLow = IntentFilter()
intentFilterLow.addAction("lY_ACTION")
intentFilterLow.priority = 50
localBroadcastReceiver.registerReceiver(lowReceiver,intentFilterLow)
}
ContentProvider
- contentProvider 內(nèi)容提供者
- contentresolver 內(nèi)容解析器
Activity之間的通信方式
- 通過(guò)廣播的方式進(jìn)行通信
- 通過(guò)Intent的方式通信(startActivityForResult)
- 借助類的靜態(tài)成員/方法進(jìn)行通信
- 使用外部工具(sqlite/sharePreference/file/剪切板...)
- 全局變量(靜態(tài)配置類/Application)
- Service(IPC模型),也就是上文的bingService()
- 通過(guò)EventBus/rxBus 進(jìn)行通信
Activity各種情況下的生命周期
- 正常情況下是:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
- 被dialog遮罩情況下:
- 如果是自身的dialog那么不會(huì)走自己的生命周期
- 如果是其他組件傳遞的dialog,那么會(huì)走onpause()
- 可以看到我們前面有一個(gè)onRestart()的生命周期,這個(gè)生命周期我們不常用,具體是什么時(shí)機(jī)會(huì)被調(diào)用呢:
- home鍵,然后切換回來(lái)
- 跳轉(zhuǎn)到另外一個(gè)activity,然后back鍵
- 從本應(yīng)用跳轉(zhuǎn)到另外一個(gè)應(yīng)用,然后回來(lái)
... 其他
橫豎屏切換的時(shí)候,Activity 各種情況下的生命周期
2種情況:
- 不做任何配置,生命周期重新走一遍:完整流程為:onCreate()->onStart()->onResume()->onPause()->onSaveInstanceState()->onStop()->Ondestory()->onCreate()->onStart()->onRestoreInstanceState()->onResume()。
- 做配置:android:configChanges="orientation|screenSize"
橫豎屏不走其他生命周期
其中onSaveInstanceState()可以保存用戶數(shù)據(jù),對(duì)應(yīng)的onRestoreInstanceState()可以讀取之前保存的用戶數(shù)據(jù)
Activity與Fragment之間生命周期比較
activity有7個(gè)生命周期,fragment有十一個(gè)生命周期
其中他們的關(guān)系是:
- 創(chuàng)建的時(shí)候:Activity 帶動(dòng) Fragment 走生命周期,表格關(guān)系為:
| 所屬 | Activity | Fragment |
|---|---|---|
| 創(chuàng)建操作 | onCreate | onAttach/onCreate/onCreateView/onActivityCreated |
| 創(chuàng)建操作 | onStart | onStart() |
- 銷毀的時(shí)候:Fragment 帶動(dòng) Activity 走生命周期,表格關(guān)系為:
| 所屬 | Fragment | Activity |
|---|---|---|
| 創(chuàng)建操作 | onPause() | onPause() |
| 創(chuàng)建操作 | onStop() | onStop() |
| 創(chuàng)建操作 | onDestroyView()/onDestroy()/onDetach() | onDestroy() |
Activity上有Dialog的時(shí)候按Home鍵時(shí)的生命周期
兩個(gè)Activity 之間跳轉(zhuǎn)時(shí)必然會(huì)執(zhí)行的是哪幾個(gè)方法?
假設(shè)有2個(gè)Activity A,B,在A里面激活B
A:調(diào)用onPause()
B:調(diào)用onCreate(),onStart(),onResume()
如果B是個(gè)正常的Activity,那么B會(huì)覆蓋A,那么A會(huì)走onStop()
如果B不能覆蓋A,那么A不會(huì)走onStop()
前臺(tái)切換到后臺(tái),然后再回到前臺(tái),Activity生命周期回調(diào)方法。彈出Dialog,生命值周期回調(diào)方法
- 前臺(tái)切換到后臺(tái):onPause()->onStop()
如果正常切換回來(lái),那么會(huì)走onRestart()->onStart()->onResume()
如果很久沒切回來(lái),系統(tǒng)內(nèi)存緊急被回收了,那么回來(lái)會(huì)重新走一次生命周期 - 彈出dialog
如果dialog是自身Activity彈出來(lái)的,則不會(huì)走生命周期
如果不是自身Activity彈出來(lái)的,則走onPause(),退出Dialog后走onRestart()->onResume()
Activity的四種啟動(dòng)模式對(duì)比
standard
這個(gè)是Activity的默認(rèn)啟動(dòng)方式,我們不需要額外的配置
在該配置下,啟動(dòng)一個(gè)Activity就會(huì)在該應(yīng)用的Activity棧中壓入一個(gè)Activity,返回的時(shí)候就直接把該Activity彈出棧。
singleTop
這個(gè)是棧頂復(fù)用模式
在該配置下,如果在Activity棧,棧頂是該Activity,那么會(huì)走onNewIntent()->onResume()
如果不是,那么就走正常的生命周期
singleInstance
這個(gè)是棧內(nèi)復(fù)用模式
在該配置下,如果在該Activity棧,棧內(nèi)存在該Activity(沒有要求是棧頂),那么會(huì)走onNewIntent()->onResume(),并且把位于該Activity上方的Activity全部出棧,使該Activity位于棧頂
singleTask
這個(gè)配置下,Activity獨(dú)享一個(gè)Activity棧。
Activity狀態(tài)保存與恢復(fù)
override fun onSaveInstanceState(outState: Bundle?){} 保存狀態(tài)
override fun onRestoreInstanceState(savedInstanceState: Bundle?){} 恢復(fù)狀態(tài)
Fragment狀態(tài)保存startActivityForResult是哪個(gè)類的方法,在什么情況下使用?
startActivityForResult是FragmentActivity的方法。
其實(shí)和Activity的startActivityForResult是一樣的,只不過(guò)需要注意在Fragment里面調(diào)用的話,直接使用:
val targetIntent = Intent(this@MyFragment.context, DialogActivity::class.java)
startActivityForResult(targetIntent, 1)
而不需要使用getActivity()/activity:
val targetIntent = Intent(this@MyFragment.context, DialogActivity::class.java)
activity.startActivityForResult(targetIntent, 1)
如何實(shí)現(xiàn)Fragment的滑動(dòng)?
- 把Fragment放到ViewPager里面去
- 把Fragment放到RecyclerView/ListView/GridView
... ...
fragment之間傳遞數(shù)據(jù)的方式?
- Intent傳值
- 廣播傳值
- 靜態(tài)調(diào)用
- 本地化存儲(chǔ)傳值
- 暴露接口/方法調(diào)用
- eventBus等之類的
Activity 怎么和Service 綁定?
通過(guò)BindService綁定,具體步驟
- 新建Activity,Service。Activity里面綁定Service。
- Service新建綁定類繼承Binder。
具體代碼如下:
Service
public class MyService extends Service {
private IBinder iBinder=new MyBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
class MyBinder extends Binder {
public MyService get() {
return MyService.this;
}
}
}
Activity
public class ServiceActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyConnection myConnection = new MyConnection();
Intent intent = new Intent(this, MyService.class);
bindService(intent, myConnection, Service.BIND_AUTO_CREATE);
}
private class MyConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService myService = ((MyService.MyBinder) service).get();
Log.e("lht", "onServiceConnected: " + myService.getPackageName());
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onBindingDied(ComponentName name) {
}
}
}
談?wù)勀銓?duì)ContentProvider的理解
說(shuō)說(shuō)ContentProvider、ContentResolver、ContentObserver 之間的關(guān)系
AlertDialog,popupWindow區(qū)別
AlertDialog是非阻塞式對(duì)話框,當(dāng)彈出來(lái)的時(shí)候,后臺(tái)還是可以繼續(xù)做事情的
popupWindow則是阻塞式的對(duì)話框
Application 和 Activity 的 Context 對(duì)象的區(qū)別
- Application是全局的Context,可以用于需要長(zhǎng)期持久的contenxt操作
- 而Activity的Context只是屬于該Activity,如果被非法持有的話,很容易導(dǎo)致該Activity無(wú)法被回收,造成內(nèi)存泄露
Android屬性動(dòng)畫特性
如何導(dǎo)入外部數(shù)據(jù)庫(kù)?
- 把外部建好的數(shù)據(jù)庫(kù)文件放在raw中,
- 讀入該數(shù)據(jù)庫(kù)文件
- 寫入到data/data/batabases文件夾中
- 正常讀取
介紹下SurfaceView
SurfaceView是View的子類,與普通的View的最大區(qū)別就是內(nèi)部實(shí)現(xiàn)了雙緩沖機(jī)制:就是使用主線程來(lái)負(fù)責(zé)UI的顯示和渲染線程做UI的渲染,這樣2個(gè)線程交替進(jìn)行,這樣做使得頁(yè)面顯示內(nèi)容的速度非??欤峭瑯拥?,也加大了對(duì)內(nèi)存和cpu的開銷。
序列化的作用,以及Android兩種序列化的區(qū)別
- Serializable接口
是一個(gè)空接口,為對(duì)象提供標(biāo)準(zhǔn)的序列化和反序列化操作。
其中有一個(gè)long類型的值為serialVersionUID,我們開發(fā)人員很少用到這個(gè),但是官方推薦是最好可以聲明這個(gè)屬性,主要用于校驗(yàn)序列化和反序列化。
serialVersionUID的詳細(xì)工作過(guò)程是這樣的:序列化的時(shí)候系統(tǒng)會(huì)把當(dāng)前類的serialVersionUID寫入序列化的二進(jìn)制文件中,當(dāng)反序列化的時(shí)候系統(tǒng)會(huì)檢測(cè)文件中的serialVersionUID是否和當(dāng)前類的serialVersionUID一致,如果一致就說(shuō)明序列化的類的版本和當(dāng)前類的版本是相同的,這個(gè)時(shí)候可以成功反序列化;否則說(shuō)明當(dāng)前類和反序列化的類相比發(fā)生了某些變化,比如成員變量的數(shù)量、類型發(fā)生了變化,這個(gè)時(shí)候是無(wú)法正常反序列化的。
- Parcelable接口
是androidSdk特意為序列化和反序列化開發(fā)出來(lái)的一個(gè)接口,相當(dāng)于Serializable具有更好的效率。
但是Parcelable接口使用起來(lái)比較麻煩。具有如此的屬性:
| 方法 | 功能 | 標(biāo)記位 |
|---|---|---|
| createFromParcel(Parcel in) | 從序列化后的對(duì)象中創(chuàng)建原始對(duì)象 | |
| newArray(int size) | 創(chuàng)建指定長(zhǎng)度的原始對(duì)象數(shù)組 | |
| User(Parcel in) | 從序列化后的對(duì)象中創(chuàng)建原始對(duì)象 | |
| writeToParcel(Parcel out,int flags) | 將當(dāng)前對(duì)象寫入序列化結(jié)構(gòu)中 | PARCALABLE_WRITE_RETURN_VALUE |
| describeContents | 返回當(dāng)前對(duì)象的內(nèi)容描述,幾乎所有情況都返回0,僅在當(dāng)前對(duì)象中存在文件描述符時(shí)返回1 | CONTENTS_FILE_DESCRIPTOR |
- 兩者區(qū)別
| 區(qū)別 | Serializable | Parcelable |
|---|---|---|
| 所屬API | JAVA API | Android SDK API |
| 原理 | 序列化和反序列化過(guò)程需要大量的I/O操作 | 序列化和反序列化過(guò)程不需要大量的I/O操作 |
| 開銷 | 開銷大 | 開銷小 |
| 效率 | 低 | 很高 |
| 使用場(chǎng)景 | 序列化到本地或者通過(guò)網(wǎng)絡(luò)傳輸 | 內(nèi)存序列化 |
二、Android源碼相關(guān)分析
Android動(dòng)畫框架實(shí)現(xiàn)原理
Android各個(gè)版本API的區(qū)別
其實(shí)只需要知道比較著名的幾個(gè)就好了
- android 5.0引入material design風(fēng)格
- android 6.0引入權(quán)限動(dòng)態(tài)管理
- android 7.0引入多窗口支持等
- android 8.0引入畫中畫等
Requestlayout,onlayout,onDraw,DrawChild區(qū)別與聯(lián)系(個(gè)人看法,不一定正確)
- RequestLayout():
子View調(diào)用requestLayout方法,會(huì)標(biāo)記當(dāng)前View及父容器,同時(shí)逐層向上提交,直到ViewRootImpl處理該事件,ViewRootImpl會(huì)調(diào)用三大流程,從measure開始,對(duì)于每一個(gè)含有標(biāo)記位的view及其子View都會(huì)進(jìn)行測(cè)量、布局、繪制。 - onLayout()只是一個(gè)調(diào)度方法,實(shí)質(zhì)調(diào)用的是layout(int l, int t, int r, int b)方法,其中的l,t,r,b分別表示該參數(shù)相對(duì)于父參數(shù)對(duì)應(yīng)參數(shù)的位置,當(dāng)需要重新布局時(shí)候,會(huì)依次上傳,直至rootView
- onDraw同onLayout()
- DrawChild(Canvas canvas, View child, long drawingTime)也是個(gè)調(diào)度方法,調(diào)用了child.draw()方法
- 聯(lián)系:Requestlayout會(huì)調(diào)用View樹的重繪機(jī)制,會(huì)分別調(diào)用需要重繪View的測(cè)量,布局,繪制3個(gè)過(guò)程,而且其中onlayout(),onDraw()會(huì)被分別依次調(diào)用,調(diào)用Requestlayout()方法的view會(huì)依次上報(bào)父View分別進(jìn)行測(cè)量,布局,繪制。而繪制的時(shí)候,子view會(huì)被父View通過(guò)DrawChild()啟動(dòng)目標(biāo)View的繪制。
invalidate和postInvalidate,以及他們和RequestLayout的區(qū)別及使用
- invalidate 該方法的調(diào)用會(huì)引發(fā)View樹的重繪,一般用于View內(nèi)部調(diào)用或者是需要刷新界面的時(shí)候,該方法有多個(gè)重載方法,但是最終都會(huì)調(diào)用到invalidateInternal方法,在該方法內(nèi)部,會(huì)經(jīng)過(guò)一系列的判斷之類的,判斷該View是否需要重繪,然后給View設(shè)置標(biāo)識(shí)位,然后把需要重繪的區(qū)域傳遞給父控件,調(diào)用父控件的invalidateChild方法,計(jì)算出自身需要繪制的參數(shù),經(jīng)過(guò)一系列傳遞后,最終會(huì)傳遞到ViewRootImpl,最終會(huì)觸發(fā)performTraversals方法,開始進(jìn)行View樹的重繪(只繪制需要重繪的部分)
- postInvalidate 的方法與invalidate類似,不同的是postInvalidate可以在異步線程中調(diào)用,其內(nèi)部有一個(gè)Handler,發(fā)送了一個(gè)消息到主線程中,通知主線程進(jìn)行UI刷新。
- RequestLayout 子View調(diào)用requestLayout方法,會(huì)標(biāo)記當(dāng)前View及父容器,同時(shí)逐層向上提交,直到ViewRootImpl處理該事件,ViewRootImpl會(huì)調(diào)用三大流程,從measure開始,對(duì)于每一個(gè)含有標(biāo)記位的view及其子View都會(huì)進(jìn)行測(cè)量、布局、繪制
-
區(qū)別:
3者區(qū)別
一般來(lái)說(shuō),如果確定你的View的位置屬性已經(jīng)不再滿足于現(xiàn)狀,需要改變,也就是LayoutParams已經(jīng)發(fā)生了改變了,也就是需要父布局對(duì)它進(jìn)行測(cè)量,布局,繪制 三個(gè)步驟的時(shí)候,調(diào)用requestLayout;
而invalidate/postInvalidate,則是在確定目標(biāo)View(往往是自身)不需要測(cè)量,布局,只需要重新繪制自身的時(shí)候調(diào)用,所以這種情況下invalidate/postInvalidate會(huì)比requestLayout更高效。
Activity-Window-View三者的差別
- 通用比喻:Acitivty像一個(gè)工匠(控制單元),Window像窗戶(承載模式),View像窗花(顯示視圖),LayoutInflater像剪刀,xml配置像窗花圖紙
- 三者關(guān)系
- 在Activity中調(diào)用attach,創(chuàng)建了一個(gè)Window
- 創(chuàng)建的window是其子類PhoneWindow,在attach中創(chuàng)建了PhoneWindow
- 在Activity中調(diào)用setContentView(R.layout.xxxxx)
- 其實(shí)實(shí)質(zhì)上是調(diào)用getWindow().setContentView()
- 調(diào)用phoneWindow中的setContentView方法
- 創(chuàng)建ParentView:作為ViewGroup的子類,實(shí)際上創(chuàng)建的是DecorView(作為Fraglayout的子類)
- 將制定的R.layout.xxxx進(jìn)行填充,通過(guò)布局填充器進(jìn)行填充(其中的parent就是DecorView)
- 調(diào)用到ViewGroup
- 調(diào)用viewGroup的remoAllView(),先將所有View移除,
- 添加新的View addView()
如何優(yōu)化自定義View
- 降低刷新頻率
- 減少不必要的調(diào)用invalidate()方法,避免頻繁刷新,最好可以調(diào)用四個(gè)參數(shù)的invalidate()方法,刷新指定View而不是刷新整個(gè)
- 減少不必要的layout()調(diào)用,因?yàn)檫@個(gè)方法需要遍歷整個(gè)View樹來(lái)獲取你真實(shí)的layout位置。如果真的必須經(jīng)常性地調(diào)用,那么你可以考慮寫一個(gè)特殊的ViewGroup
- 使用硬件加速
- 一些參數(shù)的初始化千萬(wàn)不要在onDraw里面初始化,最好的位置是構(gòu)造函數(shù)里面
低版本SDK如何實(shí)現(xiàn)高版本api?
- 使用support包的方法
- @TargetApi
描述一次網(wǎng)絡(luò)請(qǐng)求的流程
- 建立TCP連接
在HTTP工作開始之前,Web瀏覽器首先要通過(guò)網(wǎng)絡(luò)與Web服務(wù)器建立連接,該連接是通過(guò)TCP來(lái)完成的,該協(xié)議與IP協(xié)議共同構(gòu)建Internet,即著名的TCP/IP協(xié)議族,因此Internet又被稱作是TCP/IP網(wǎng)絡(luò)。HTTP是比TCP更高層次的應(yīng)用層協(xié)議,根據(jù)規(guī)則,只有低層協(xié)議建立之后才能進(jìn)行更高層協(xié)議的連接,因此,首先要建立TCP連接,一般TCP連接的端口號(hào)是80 - Web瀏覽器向Web服務(wù)器發(fā)送請(qǐng)求命令
一旦建立了TCP連接,Web瀏覽器就會(huì)向Web服務(wù)器發(fā)送請(qǐng)求命令。例如:GET/sample/hello.jsp HTTP/1.1。 - Web瀏覽器發(fā)送請(qǐng)求頭信息
瀏覽器發(fā)送其請(qǐng)求命令之后,還要以頭信息的形式向Web服務(wù)器發(fā)送一些別的信息,之后瀏覽器發(fā)送了一空白行來(lái)通知服務(wù)器,它已經(jīng)結(jié)束了該頭信息的發(fā)送。 - Web服務(wù)器應(yīng)答
客戶機(jī)向服務(wù)器發(fā)出請(qǐng)求后,服務(wù)器會(huì)客戶機(jī)回送應(yīng)答, HTTP/1.1 200 OK ,應(yīng)答的第一部分是協(xié)議的版本號(hào)和應(yīng)答狀態(tài)碼。 - Web服務(wù)器發(fā)送應(yīng)答頭信息
正如客戶端會(huì)隨同請(qǐng)求發(fā)送關(guān)于自身的信息一樣,服務(wù)器也會(huì)隨同應(yīng)答向用戶發(fā)送關(guān)于它自己的數(shù)據(jù)及被請(qǐng)求的文檔。 - Web服務(wù)器向?yàn)g覽器發(fā)送數(shù)據(jù)
Web服務(wù)器向?yàn)g覽器發(fā)送頭信息后,它會(huì)發(fā)送一個(gè)空白行來(lái)表示頭信息的發(fā)送到此為結(jié)束,接著,它就以Content-Type應(yīng)答頭信息所描述的格式發(fā)送用戶所請(qǐng)求的實(shí)際數(shù)據(jù)。 - Web服務(wù)器關(guān)閉TCP連接
一般情況下,一旦Web服務(wù)器向?yàn)g覽器發(fā)送了請(qǐng)求數(shù)據(jù),它就要關(guān)閉TCP連接,然后如果瀏覽器或者服務(wù)器在其頭信息加入了這行代碼:Connection:keep-alive
TCP連接在發(fā)送后將仍然保持打開狀態(tài),于是,瀏覽器可以繼續(xù)通過(guò)相同的連接發(fā)送請(qǐng)求。保持連接節(jié)省了為每個(gè)請(qǐng)求建立新連接所需的時(shí)間,還節(jié)約了網(wǎng)絡(luò)帶寬。
HttpUrlConnection 和 okhttp關(guān)系
4.4之后的的 HTTP URL connection 基于 OK HTTP實(shí)現(xiàn)
Bitmap對(duì)象的理解(未完成)
- Bitmap像素?cái)?shù)據(jù)
在Android 2.3.3(API 10)之前,Bitmap的像素?cái)?shù)據(jù)的內(nèi)存時(shí)分配在Native堆上的,而Bitmap對(duì)象的內(nèi)存則分配在Dalvik堆上的;
由于Native堆上的內(nèi)存時(shí)不受DVM管理的,如果想要回收Bitmap的所占用內(nèi)存的話,那么需要調(diào)用Bitmap.recyle()方法。
而API 10之后呢,谷歌將像素?cái)?shù)據(jù)的內(nèi)存分配也移到DVM堆上,由DVM管理,因此在dvm回收前;
只需要保證Bitmap對(duì)象不被任何GC Roots強(qiáng)引用就可以回收這部分內(nèi)存。
- looper架構(gòu)
- ActivityThread,AMS,WMS的工作原理
- 自定義View如何考慮機(jī)型適配
- 自定義View的事件
- AstncTask+HttpClient 與 AsyncHttpClient有什么區(qū)別?
- LaunchMode應(yīng)用場(chǎng)景
- AsyncTask 如何使用?
- SpareArray原理
- 請(qǐng)介紹下ContentProvider 是如何實(shí)現(xiàn)數(shù)據(jù)共享的?
- AndroidService與Activity之間通信的幾種方式
- IntentService原理及作用是什么?
- 說(shuō)說(shuō)Activity、Intent、Service 是什么關(guān)系
- ApplicationContext和ActivityContext的區(qū)別
- SP是進(jìn)程同步的嗎?有什么方法做到同步?
- 談?wù)劧嗑€程在Android中的使用
- 進(jìn)程和 Application 的生命周期
- 封裝View的時(shí)候怎么知道view的大小
- RecycleView原理
- AndroidManifest的作用與理解
三、常見的一些原理性問(wèn)題
Handler機(jī)制和底層實(shí)現(xiàn)
-
機(jī)制:
時(shí)序圖
主線程-->主線程Looper對(duì)象: 創(chuàng)建的時(shí)候自動(dòng)在創(chuàng)建了屬于自己的Looper對(duì)象
Note right of 主線程: 每個(gè)線程只能擁有一個(gè)looper對(duì)象
Note right of 主線程Looper對(duì)象: 自動(dòng)進(jìn)入循環(huán)狀態(tài)
Note right of 主線程Looper對(duì)象: 每個(gè)looper可以對(duì)應(yīng)很多個(gè)handler(多線程并發(fā)的條件)
主線程-->主線程MessageQueue:創(chuàng)建的時(shí)候自動(dòng)創(chuàng)建了屬于主線程的MessageQueue
主線程-->主線程Handler對(duì)象:創(chuàng)建的時(shí)候自動(dòng)綁定了主線程的looper對(duì)象
子線程-->主線程MessageQueue:把自己的消息通過(guò)Handler發(fā)送到MessageQueue中
主線程MessageQueue-->主線程Looper對(duì)象:無(wú)限循環(huán),沒有message則阻塞,有就拿出來(lái)處理
主線程Looper對(duì)象-->主線程:通過(guò)looper循環(huán)把消息拿出來(lái),進(jìn)行處理
- Handler、Thread和HandlerThread的差別
- handler發(fā)消息給子線程,looper怎么啟動(dòng)?
- 關(guān)于Handler,在任何地方new Handler 都是什么線程下?
- ThreadLocal原理,實(shí)現(xiàn)及如何保證Local屬性?
- 請(qǐng)解釋下在單線程模型中Message、Handler、Message Queue、Looper之間的關(guān)系
- 請(qǐng)描述一下View事件傳遞分發(fā)機(jī)制
- Touch事件傳遞流程
- 事件分發(fā)中的onTouch 和onTouchEvent 有什么區(qū)別,又該如何使用?
- View和ViewGroup分別有哪些事件分發(fā)相關(guān)的回調(diào)方法
- View刷新機(jī)制
- View繪制流程
- 自定義控件原理
- 自定義View如何提供獲取View屬性的接口?
- Android代碼中實(shí)現(xiàn)WAP方式聯(lián)網(wǎng)
- AsyncTask機(jī)制
- AsyncTask原理及不足
- 如何取消AsyncTask?
- 為什么不能在子線程更新UI?
- ANR產(chǎn)生的原因是什么?
- ANR定位和修正
- oom是什么?
- 什么情況導(dǎo)致oom?
- 有什么解決方法可以避免OOM?
- Oom 是否可以try catch?為什么?
- 內(nèi)存泄漏是什么?
- 什么情況導(dǎo)致內(nèi)存泄漏?
- 如何防止線程的內(nèi)存泄漏?
- 內(nèi)存泄露場(chǎng)的解決方法
- 內(nèi)存泄漏和內(nèi)存溢出區(qū)別?
- LruCache默認(rèn)緩存大小
- ContentProvider的權(quán)限管理(解答:讀寫分離,權(quán)限控制-精確到表級(jí),URL控制)
- 如何通過(guò)廣播攔截和abort一條短信?
- 廣播是否可以請(qǐng)求網(wǎng)絡(luò)?
- 廣播引起anr的時(shí)間限制是多少?
- 計(jì)算一個(gè)view的嵌套層級(jí)
- Activity棧
- Android線程有沒有上限?
- 線程池有沒有上限?
- ListView重用的是什么?
- Android為什么引入Parcelable?
- 有沒有嘗試簡(jiǎn)化Parcelable的使用?
四、開發(fā)中常見的一些問(wèn)題
- ListView 中圖片錯(cuò)位的問(wèn)題是如何產(chǎn)生的?
- 混合開發(fā)有了解嗎?
- 知道哪些混合開發(fā)的方式?說(shuō)出它們的優(yōu)缺點(diǎn)和各自使用場(chǎng)景?(解答:比如:RN,weex,H5,小程序,WPA等。做Android的了解一些前端js等還是很有好處的);
- 屏幕適配的處理技巧都有哪些?
- 服務(wù)器只提供數(shù)據(jù)接收接口,在多線程或多進(jìn)程條件下,如何保證數(shù)據(jù)的有序到達(dá)?
- 動(dòng)態(tài)布局的理解
- 怎么去除重復(fù)代碼?
- 畫出 Android 的大體架構(gòu)圖
- Recycleview和ListView的區(qū)別
- ListView圖片加載錯(cuò)亂的原理和解決方案
- 動(dòng)態(tài)權(quán)限適配方案,權(quán)限組的概念
- Android系統(tǒng)為什么會(huì)設(shè)計(jì)ContentProvider?
- 下拉狀態(tài)欄是不是影響activity的生命周期
- 如果在onStop的時(shí)候做了網(wǎng)絡(luò)請(qǐng)求,onResume的時(shí)候怎么恢復(fù)?
- Bitmap 使用時(shí)候注意什么?
- Bitmap的recycler()
- Android中開啟攝像頭的主要步驟
- ViewPager使用細(xì)節(jié),如何設(shè)置成每次只初始化當(dāng)前的Fragment,其他的不初始化?
- 點(diǎn)擊事件被攔截,但是想傳到下面的View,如何操作?
- 微信主頁(yè)面的實(shí)現(xiàn)方式
- 微信上消息小紅點(diǎn)的原理
- CAS介紹(這是阿里巴巴的面試題,我不是很了解,可以參考博客: CAS簡(jiǎn)介)

