Android面試

[toc]

Android面試題

Android面試題除了Android基礎(chǔ)之外,更多的問(wèn)的是一些源碼級(jí)別的、原理這些等。所以想去大公司面試,一定要多看看源碼和實(shí)現(xiàn)方式,常用框架可以試試自己能不能手寫實(shí)現(xiàn)一下,鍛煉一下自己。

一、Android基礎(chǔ)知識(shí)點(diǎn)

四大組件是什么

  1. Activity
    其中Activity是我們最常使用的組件,也是我們最熟悉的組件,一般用于呈現(xiàn)頁(yè)面內(nèi)容,處理用戶交互等等

  2. Service
    Service是一個(gè)可以運(yùn)行于后臺(tái)的組件,我們一般用于處理一些不需要用戶知道,但是又必須比較長(zhǎng)時(shí)間存在的操作,比如下載

  3. ContentProvider
    主要用于進(jìn)程間通信,比如暴露某個(gè)APP的信息內(nèi)存給予另外一個(gè)APP獲取使用,比如獲取聯(lián)系人等等

  4. BroadcastReceiver
    廣播接受者主要用于接受廣播信息,像我們?nèi)粘J褂弥锌赡苡糜趦煞N情況:

    • APP內(nèi):界面間通信,例如退出app,可以發(fā)送自殺廣播
    • APP間:可以收到第三方發(fā)出的廣播,進(jìn)而進(jìn)行對(duì)應(yīng)的響應(yīng)操作。

四大組件的生命周期和簡(jiǎn)單用法

Activity
  1. 生命周期

oncreate()->onstart()->onresume()->onpause()->onstop()->ondestory()

  1. 簡(jiǎn)單用法
        /**創(chuàng)建App*/
        class someClass :Activity() 
        /**跳轉(zhuǎn)app*/
        startActivity(Intent(this@someActivity,targetClass::class.java))
Service
  1. 生命周期
  • startService()的生命周期:
    oncreate()->onstartComment()->onstart()->onDestory()
  • bindService()的生命周期:
    oncreate()->onbind()->onUnbind()->onDestroy()
  1. 簡(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)。
  public boolean bindService(Intent service, ServiceConnection conn,
                int flags)
BroadcastReceiver(分為2種):
  1. 簡(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()注銷),使用靈活,生命周期隨組件變化
全局廣播
  1. 普通廣播(最常用的那種)
  2. 系統(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
    ... ...
  3. 有序廣播
    有序廣播是指廣播按照一定的優(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)題為:

  1. 其他app可以發(fā)送相對(duì)應(yīng)的 intent-filter到我方App,導(dǎo)致我方錯(cuò)誤處理
  2. 其他app可以設(shè)置對(duì)應(yīng)的 intent-filter接收我方App發(fā)出的廣播,導(dǎo)致安全性問(wèn)題
    為解決這個(gè)問(wèn)題,我們可以使用本地廣播:
    1.方式一:
    1. 設(shè)置exported為false(默認(rèn)為true),使該廣播只在app內(nèi)傳遞
    2. 廣播傳遞和接收時(shí),添加響應(yīng)的permission
    3. 通過(guò)intent.setPackage(com.xxx.xxx)
  3. 方式二 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
  1. contentProvider 內(nèi)容提供者
  2. contentresolver 內(nèi)容解析器

Activity之間的通信方式

  1. 通過(guò)廣播的方式進(jìn)行通信
  2. 通過(guò)Intent的方式通信(startActivityForResult)
  3. 借助類的靜態(tài)成員/方法進(jìn)行通信
  4. 使用外部工具(sqlite/sharePreference/file/剪切板...)
  5. 全局變量(靜態(tài)配置類/Application)
  6. Service(IPC模型),也就是上文的bingService()
  7. 通過(guò)EventBus/rxBus 進(jìn)行通信

Activity各種情況下的生命周期

  1. 正常情況下是:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
  2. 被dialog遮罩情況下:
    1. 如果是自身的dialog那么不會(huì)走自己的生命周期
    2. 如果是其他組件傳遞的dialog,那么會(huì)走onpause()
  3. 可以看到我們前面有一個(gè)onRestart()的生命周期,這個(gè)生命周期我們不常用,具體是什么時(shí)機(jī)會(huì)被調(diào)用呢:
    1. home鍵,然后切換回來(lái)
    2. 跳轉(zhuǎn)到另外一個(gè)activity,然后back鍵
    3. 從本應(yīng)用跳轉(zhuǎn)到另外一個(gè)應(yīng)用,然后回來(lái)

... 其他

橫豎屏切換的時(shí)候,Activity 各種情況下的生命周期

2種情況:

  1. 不做任何配置,生命周期重新走一遍:完整流程為:onCreate()->onStart()->onResume()->onPause()->onSaveInstanceState()->onStop()->Ondestory()->onCreate()->onStart()->onRestoreInstanceState()->onResume()。
  2. 做配置:android:configChanges="orientation|screenSize"
    橫豎屏不走其他生命周期

其中onSaveInstanceState()可以保存用戶數(shù)據(jù),對(duì)應(yīng)的onRestoreInstanceState()可以讀取之前保存的用戶數(shù)據(jù)

Activity與Fragment之間生命周期比較

activity有7個(gè)生命周期,fragment有十一個(gè)生命周期


fragment以及activity生命周期
fragment以及activity生命周期

其中他們的關(guān)系是:

  1. 創(chuàng)建的時(shí)候:Activity 帶動(dòng) Fragment 走生命周期,表格關(guān)系為:
所屬 Activity Fragment
創(chuàng)建操作 onCreate onAttach/onCreate/onCreateView/onActivityCreated
創(chuàng)建操作 onStart onStart()
  1. 銷毀的時(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)方法

  1. 前臺(tái)切換到后臺(tái):onPause()->onStop()
    如果正常切換回來(lái),那么會(huì)走onRestart()->onStart()->onResume()
    如果很久沒切回來(lái),系統(tǒng)內(nèi)存緊急被回收了,那么回來(lái)會(huì)重新走一次生命周期
  2. 彈出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)?

  1. 把Fragment放到ViewPager里面去
  2. 把Fragment放到RecyclerView/ListView/GridView
    ... ...

fragment之間傳遞數(shù)據(jù)的方式?

  1. Intent傳值
  2. 廣播傳值
  3. 靜態(tài)調(diào)用
  4. 本地化存儲(chǔ)傳值
  5. 暴露接口/方法調(diào)用
  6. eventBus等之類的

Activity 怎么和Service 綁定?

通過(guò)BindService綁定,具體步驟
  1. 新建Activity,Service。Activity里面綁定Service。
  2. 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ū)別

  1. Application是全局的Context,可以用于需要長(zhǎng)期持久的contenxt操作
  2. 而Activity的Context只是屬于該Activity,如果被非法持有的話,很容易導(dǎo)致該Activity無(wú)法被回收,造成內(nèi)存泄露

Android屬性動(dòng)畫特性

如何導(dǎo)入外部數(shù)據(jù)庫(kù)?

  1. 把外部建好的數(shù)據(jù)庫(kù)文件放在raw中,
  2. 讀入該數(shù)據(jù)庫(kù)文件
  3. 寫入到data/data/batabases文件夾中
  4. 正常讀取

介紹下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ū)別

  1. 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ú)法正常反序列化的。

  1. 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
  1. 兩者區(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è)就好了

  1. android 5.0引入material design風(fēng)格
  2. android 6.0引入權(quán)限動(dòng)態(tài)管理
  3. android 7.0引入多窗口支持等
  4. android 8.0引入畫中畫等

Requestlayout,onlayout,onDraw,DrawChild區(qū)別與聯(lián)系(個(gè)人看法,不一定正確)

  1. 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è)量、布局、繪制。
  2. 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
  3. onDraw同onLayout()
  4. DrawChild(Canvas canvas, View child, long drawingTime)也是個(gè)調(diào)度方法,調(diào)用了child.draw()方法
  5. 聯(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ū)別及使用

  1. 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樹的重繪(只繪制需要重繪的部分)
  2. postInvalidate 的方法與invalidate類似,不同的是postInvalidate可以在異步線程中調(diào)用,其內(nèi)部有一個(gè)Handler,發(fā)送了一個(gè)消息到主線程中,通知主線程進(jìn)行UI刷新。
  3. 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è)量、布局、繪制
  4. 區(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三者的差別

  1. 通用比喻:Acitivty像一個(gè)工匠(控制單元),Window像窗戶(承載模式),View像窗花(顯示視圖),LayoutInflater像剪刀,xml配置像窗花圖紙
  2. 三者關(guān)系
    1. 在Activity中調(diào)用attach,創(chuàng)建了一個(gè)Window
    2. 創(chuàng)建的window是其子類PhoneWindow,在attach中創(chuàng)建了PhoneWindow
    3. 在Activity中調(diào)用setContentView(R.layout.xxxxx)
    4. 其實(shí)實(shí)質(zhì)上是調(diào)用getWindow().setContentView()
    5. 調(diào)用phoneWindow中的setContentView方法
    6. 創(chuàng)建ParentView:作為ViewGroup的子類,實(shí)際上創(chuàng)建的是DecorView(作為Fraglayout的子類)
    7. 將制定的R.layout.xxxx進(jìn)行填充,通過(guò)布局填充器進(jìn)行填充(其中的parent就是DecorView)
    8. 調(diào)用到ViewGroup
    9. 調(diào)用viewGroup的remoAllView(),先將所有View移除,
    10. 添加新的View addView()

如何優(yōu)化自定義View

  1. 降低刷新頻率
    1. 減少不必要的調(diào)用invalidate()方法,避免頻繁刷新,最好可以調(diào)用四個(gè)參數(shù)的invalidate()方法,刷新指定View而不是刷新整個(gè)
    2. 減少不必要的layout()調(diào)用,因?yàn)檫@個(gè)方法需要遍歷整個(gè)View樹來(lái)獲取你真實(shí)的layout位置。如果真的必須經(jīng)常性地調(diào)用,那么你可以考慮寫一個(gè)特殊的ViewGroup
  2. 使用硬件加速
  3. 一些參數(shù)的初始化千萬(wàn)不要在onDraw里面初始化,最好的位置是構(gòu)造函數(shù)里面

低版本SDK如何實(shí)現(xiàn)高版本api?

  1. 使用support包的方法
  2. @TargetApi

描述一次網(wǎng)絡(luò)請(qǐng)求的流程

  1. 建立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
  2. Web瀏覽器向Web服務(wù)器發(fā)送請(qǐng)求命令
    一旦建立了TCP連接,Web瀏覽器就會(huì)向Web服務(wù)器發(fā)送請(qǐng)求命令。例如:GET/sample/hello.jsp HTTP/1.1。
  3. Web瀏覽器發(fā)送請(qǐng)求頭信息
    瀏覽器發(fā)送其請(qǐng)求命令之后,還要以頭信息的形式向Web服務(wù)器發(fā)送一些別的信息,之后瀏覽器發(fā)送了一空白行來(lái)通知服務(wù)器,它已經(jīng)結(jié)束了該頭信息的發(fā)送。
  4. 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)碼。
  5. Web服務(wù)器發(fā)送應(yīng)答頭信息
    正如客戶端會(huì)隨同請(qǐng)求發(fā)送關(guān)于自身的信息一樣,服務(wù)器也會(huì)隨同應(yīng)答向用戶發(fā)送關(guān)于它自己的數(shù)據(jù)及被請(qǐng)求的文檔。
  6. 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ù)。
  7. 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ì)象的理解(未完成)

  1. 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)

  1. 機(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)介
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容