2019年的面試條目總結
一.熟練對Android Q版本適配
存儲沙箱化, 每個應用訪問自己沙箱內的文件。訪問別的沙箱解決辦法類似于FileUriExposedException,用FileProvider將file轉換為content傳遞
設備唯一id獲取,谷歌開放了新的設備id獲取方式,目前不同手機獲取的方式不同以前的
READ_PHONE_STATE方式變?yōu)榱?code>READ_PRIVILEGED_PHONE_STATE非SDK接口的限制,設置禁止對非sdk接口的依賴,主要是一些第三方的熱修復,加固方案使用了這些被限制的SDK接口
限制了明文流量,只有使用https才可以訪問
二.安卓各版本的新特性
Android 6.0(M) ,完整的權限控制,危險權限需要動態(tài)獲取。 API 檢測是否有授權,申請授權,權限失敗解釋
Android7.0 (N) , 刪除了三種靜態(tài)廣播 網絡狀態(tài)新照片和新視頻
Android8.0(P),自適應的圖標類型,推送和通知的管理
三.Glide源碼解析
四.EventBus源碼解析
五.Arouter源碼解析
六.Retrofit源碼解析
Retrofit使用
Retrofit.create(Class)方法創(chuàng)建出Service 接口對象,使retrofit可以在Service中進行參數配置然而在
Retrofit.create()方法中,是使用Proxy.newProxyInstance()方法來創(chuàng)建Service接口實例,這個實例實現了所有的接口方法,調用實例的InvocationHandler成員變量的invoke()方法,InvocationHandler對象就會代理接口中的是設定。-
invoke()中的邏輯:loadServiceMethod(method)將interface中的方法信息讀取并初步分析生成ServiceMethodOkHttpCall<Object> okHttpCall = new OkhttpCall<>(serviceMethod,args)負責將ServiceMethod封裝到retrofit2.call對象,該對象在enqueue等的時候會調用okhttp.call對象,由它來進行網絡請求的發(fā)起在
serviceMethod.adapt(okHttpCall)中,serviceMethod里面的的callAdapter對象把okHttpCall轉為新的retrofit.call類型對象。這個call中,后臺線程發(fā)起的請求的返回中,調用主線程回調方法。還可以生成rxjava的Obervable等
七.Okhttp3源碼解析
首先創(chuàng)造出一個OkhttpClient進行網絡配置,然后創(chuàng)建一個Request傳入client,client創(chuàng)建一個call開始發(fā)起請求。
-
Dispatcher dispatcher:調度器,調度后臺發(fā)起的網絡請求,可設置主機請求數和后臺請求數List<Protocol> protocols:支持的應用層協(xié)議版本List<ConnectionSpec> connectionSpecs:應用層支持的Socket設置,即使用明文傳輸 還是TLSList<Interceptor> interceptors大多數的Interceptor都會配置在這里List<Interceptor> networkInterceptors:直接和網絡請求交互的Interceptor配置到這里CookieJar cookieJar:管理Cookie的控制器,提供了基礎的存取判斷,剩下的需要自己實現Cache cache:Cache 存儲的配置。默認是沒有,如果需要?,得??配置出 Cache 存儲的?件位置以及存儲空間上限。HostnameVerifier hostnameVerifier:?于驗證 HTTPS 握?過程中下載到的證書所屬者是否和??要訪問的主機名?致。Authenticator[???θ?nt?ke?t?] authenticator:用于自動重新認證。配置后收到401時會直接調用authenticator,重新發(fā)起請求 -
OKHttp3通過攔截鏈的設計,讓請求分成5個攔截器去處理,攔截器各司其職,擴展性非常高。攔截鏈是從自定義的攔截器開始,然后再到默認的5個攔截器。一般情況下我們想打印網絡請求日志,所以可以自定義Log攔截器,如果要給所有請求添加Header,同樣可以自定義Header攔截器。
- 失敗重試、重定向攔截器。
- 橋攔截器:主要是添加和刪除一些header
- 緩存攔截器:根據緩存策略,如果緩存可用,直接返回緩存數據。
- 連接池攔截器:連接池會緩存http鏈接,連接池的好處是復用連接,少了3次握手,所以請求會更快
- .真正訪問網絡的攔截器:內部使用okio去發(fā)請求
八.內存泄漏優(yōu)化
九.熟悉TCP/IP,http/https協(xié)議,熟悉用戶登錄和授權以及用戶行為分析等方式
十.熟悉對稱/非對稱加密、hash、base64的實現方式以及相關應用
十一.Handler和AsyncTask機制
handler機制
方法一:使用sendMessage()
步驟:
步驟一:主線程匿名內部類創(chuàng)建Handler對象
步驟二:創(chuàng)建消息對象
通過Message message=Message.obtain()創(chuàng)建一個message
步驟三:發(fā)送消息mHandler.sendMessage(msg);
原理:
UI線程創(chuàng)建一個Handler對象,然后在子線程中sendMessage發(fā)送到在主線程的MessageQueue中,最后通過Lopper(消息泵)對象去除MessageQueue中的消息,分發(fā)回Handler的handleMessage()中
AsyncTask機制
AsyncTask的原理 = 線程池 + Handler
線程池用于線程的調度、復用;handler用于異步通信
內部有一個SerialExecutor任務隊列線程池(任務調度)和THREAD_POOL_EXECUTOR執(zhí)行線程池(真正執(zhí)行任務的線程池)和內部handler(異步通信+消息傳遞),
類、方法的介紹
AsynTask屬于抽象類需實現子類
execute(Params... params):觸發(fā)執(zhí)行異步線程任務,必需在UI線程中調用
onPreExecute():執(zhí)行線程任務前的操作執(zhí)行任務前調用,如顯示進度條等操作
doInBackground(Params params):接受輸入參數,執(zhí)行任務中的耗時操作,返回執(zhí)行結果。
onPostExecute(Resule result):接收線程任務執(zhí)行結果,顯示到UI組件
onCancelled:將異步任務設置為取消狀態(tài),doInBackground()中判斷終止任務
點擊查看AsynTask原理具體說明
原理須知:
同步鎖修飾execute()從而保證AsyncTask中的任務是串行執(zhí)行的
十二.MVC以及MVP
MVC的步驟:
首先用戶觸摸View層(比如控件xml啥的都算view)
Controller層(Activity,Fragment)做出反應去請求
Model層(http, 數據庫,sp)的數據,然后返回數據給C層,C層收到數據后通知View層進行更新
MVP的步驟:
首先用戶觸摸View層(Activity,Fragment)如點擊請求網絡
然后view層調用Presenter(橋梁層)層去Model層(網絡請求數據,數據庫數據,sp數據,contentProvider)拿對應的數據
最后P層通過初始化時持有的View層引用將view的更新通知給V層
十三.組件化、插件化
組件化
插件化
將app拆分成很多塊,每個模塊都是一個apk,最終打包的時候講宿主apk和插件apk分開打包,插件apk通過動態(tài)下發(fā)到宿主apk。
用的是VirtualAPK
過程
1.宿主Module中的build.gradle中添加配置,apply plugin
2.application中進行初始化
3.適當位置加載插件
PluginManager pluginManager = PluginManager.getInstance(base);
File apk = new File(Environment.getExternalStorageDirectory(), "plugin.apk");
if (apk.exists()) {
try {
pluginManager.loadPlugin(apk);
} catch (Exception e) {
e.printStackTrace();
}
} else {
Toast.makeText(getApplicationContext(),"SDcard根目錄未檢測到plugin.apk插件", Toast.LENGTH_SHORT).show();
}
4.加載插件
public void click(View view){
Intent intent = new Intent();
intent.setClassName("com.emm.plugin","com.emm.plugin.Main2Activity");
tartActivity(intent);
}
原理:大致方案如下
原生apk就可作為插件加載,插件工程編譯生成apk后,即可通過宿主app加載,并在宿主中創(chuàng)建LoadedPlugin對象。再次啟動就會變得流暢
插件apk需要在宿主Apk中提前占坑,通過Hook Activity的啟動過程,“欺上瞞下”啟動插件Apk中的Activity,其他三大組件都是通過代理的方式。
十四.TCP/IP協(xié)議簇,http/https協(xié)議,用戶登錄授權、行為跟蹤
應用層 HTTP 、FTP
傳輸層 TCP、UDP
網絡層 IP、ICMP、ARP
數據鏈路層 FDDI、IEEE 802.1A
用戶登錄授權
Cookie是由服務器端生成,服務器需要客戶端保存的內容,放在Set-Cookieheaders里返回,客戶端會自動保存
客戶端保存的Cookie會在之后的所有請求里攜帶進Cookieheader里發(fā)回給服務器
客戶端保存Cookie是按照服務器域名來分類的
行為跟蹤
簡單的說就是Cookies追蹤,利用第三方cookie來實現這一的機制
很多第三方作為跨域提供了用戶的token
十五.工廠、模板、觀察者、適配器設計模式
工廠模式:寶馬和奔馳類都實現car接口,用CarFactory返回實例化接口car,相當于一個工廠可以生產不同類型的實例
模板模式:抽象模板角色類,實現了父類所聲明的基本方法。子類實現需要強制實現的邏輯
觀察者模式:
1.抽象被觀察者接口,聲明添加通知刪除觀察者方法
public interface Observerable {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
2.定義一個抽象觀察者接口
public interface Observer {
public void update(String message);
}
3.定義被觀察者,實現了Observerable接口,對接口方法進行了具體實現
public class WechatServer implements Observerable {
//注意到這個List集合的泛型參數為Observer接口,設計原則:面向接口編程而不是面向實現編程
private List<Observer> list;
private String message;
public WechatServer() {
list = new ArrayList<Observer>();
}
@Override
public void registerObserver(Observer o) {
list.add(o);
}
@Override
public void removeObserver(Observer o) {
if(!list.isEmpty())
list.remove(o);
}
//遍歷
@Override
public void notifyObserver() {
for(int i = 0; i < list.size(); i++) {
Observer oserver = list.get(i);
oserver.update(message);
}
}
public void setInfomation(String s) {
this.message = s;
System.out.println("微信服務更新消息: " + s);
//消息更新,通知所有觀察者
notifyObserver();
}
}
4.定義具體觀察者,微信公眾號具體觀察者為用戶User
public class User implements Observer {
private String name;
private String message;
public User(String name) {
this.name = name;
}
@Override
public void update(String message) {
this.message = message;
read();
}
public void read() {
System.out.println(name + " 收到推送消息: " + message);
}
}
5.編寫一個測試類,首先注冊了三個用戶,ZhangSan、LiSi、WangWu。公眾號發(fā)布了一條消息"PHP是世界上最好用的語言!",三個用戶都收到了消息。
用戶ZhangSan看到消息后頗為震驚,果斷取消訂閱,這時公眾號又推送了一條消息,此時用戶ZhangSan已經收不到消息,其他用戶
public class Test {
public static void main(String[] args) {
WechatServer server = new WechatServer();
Observer userZhang = new User("ZhangSan");
Observer userLi = new User("LiSi");
Observer userWang = new User("WangWu");
server.registerObserver(userZhang);
server.registerObserver(userLi);
server.registerObserver(userWang);
server.setInfomation("PHP是世界上最好用的語言!");
System.out.println("----------------------------------------------");
server.removeObserver(userZhang);
server.setInfomation("JAVA是世界上最好用的語言!");
}
}
適配器模式:
十六.viewStub、ViewPager2、CoordinatorLayout
ViewStub:寬高都為 0 的不可見 View. 通過延遲加載布局的方式優(yōu)化布局提升渲染性能,通過setVisibility或者直接調用inflate都會渲染布局文件
ViewPager2:核心為RecyclerView+LinerLayoutManager,所以支持橫向和豎向的滾動,viewpager2可以承載fragment,需要繼承它提供的FragmentStateAdapter,還可以完整支持notifyDataSetChanged()
CoordinatorLayout:
1.CoordinatorLayout繼承自viewgroup,但是使用類似于framLayout,只有CoordinatorLayout的直接子布局才能響應。
2.是需要CoordinateLayout+AppBarLayout+CollapingToolbarLayout結合才有效果。
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="15dp"
android:src="@drawable/add_2"/>
</android.support.design.widget.CoordinatorLayout>
- AppBarLayout設置屬性:layout_scrollFlags
-
scroll|exitUntilCollapsed,子控件可以向上滾動出NestedScrollView父布局時會折疊到頂端 -
scroll|enterAlways:只要向下滾動該布局就會顯示出來,向上滾動該布局就會收縮 -
scroll|enterAlwaysCollapsed:向下滾動到最低端時,該布局才會顯示出來
-
十七.jni使用
使用CMake進行ndk開發(fā),它是一種比nake更高級的編譯配置工具,通過CMakeLists.txt,可以控制生成的Makefile,從而控制編譯過程、生成源碼包、生成當前平臺安裝包。
使用流程
- 在java文件中創(chuàng)建本地方法
- build項目后自動生成
.h文件 - 創(chuàng)建.cpp文件,實現.h文件中的方法
- 配置Cmake文件,生成
.so文件
十八.屏幕適配
- 官方適配方案:
使用dp方式適配
使用資源目錄名加上-1920*1280等字段
百分比布局庫PercentRelativeLayout、PercentFrameLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#ff0000"
app:layout_heightPercent="30%"
app:layout_widthPercent="70%"
ConstraintLayout
layout_constraintHorizontal_chainStyle
Guideline
layout_constraintHorizontal_bias layout_constraintVertical_bias
- 開源適配方案:
今日頭條適配方案px=dp*density,動態(tài)修改設備的density值,在不同分辨率下達到相同的像素密度
十九.Arouter、RxJava
Arouter
RxJava
Single和Observerble分別作為被觀察者的區(qū)別
- 用法
subscribeOn(Schedule.io())上游切到子線程
observerOn(AndroidSchedule.mainThread())下游切到主線程 - 細節(jié)
onSubscribe(Disposable d)處在代碼所處在的線程,Disposable d表示丟棄的意思,可以拿到這個對象d 在onDestroy()中dispose - 怎么運行的?
怎么創(chuàng)建的
Single<String> single = Single.just("1");立即發(fā)送當前事件,然后檢驗傳入是否為null,然后onAssembly(new SingleJust<T>(item))雁過拔毛,SingleJust<T>繼承于Single<T>,重寫了唯一的抽象方法subscribeActual()中依次執(zhí)行了onSubscribe()、onSuccess()方法。在.subscribe()中執(zhí)行了SingleJust中的subscribeActual()方法,回調傳入的對象將被觀察者的東西發(fā)給觀察者。
map怎么創(chuàng)建的
同上,在.map()中先經過鉤子,然后onAssemble()中創(chuàng)建了一個SingleMap<T,R>,其構造參數中既指定了來源SingleSource<? extends T>也制定了去向Function<> mapper,然后在subscribeActual中進行對下游的操作。
source和mapper
二十.動畫、svg自定義控件
補間動畫:
主要通過xml中定義具有旋轉、縮放、平移、透明度的動畫
Animation animation = AnimationUtils.loadAnimation(MainActivity.this,R.anim.viewanimation);
textView.startAnimation(animation);
屬性動畫:
補間動畫只能定義兩個關鍵幀在透明(alpha)、旋轉(rorate)、位移(translate)和縮放(scale)這四個屬性的變換,但是屬性動畫可以定義任何屬性的變化。
補間動畫只能對 UI 組件執(zhí)行動畫,但屬性動畫可以對任何對象執(zhí)行動畫。
Animator: 提供創(chuàng)建屬性動畫的基類,基本不會直接使用這個類。
ValueAnimator:屬性動畫用到的主要的時間引擎,負責計算各個幀的屬性值。
ObjectAnimator: ValueAnimator 的子類,對指定對象的屬性執(zhí)行動畫。
AnimatorSet:Animator 的子類,用于組合多個 Animator。
二十一.https HTTP over SSL
定義:給HTTP增加一個安全層,用于保障HTTP的加密傳輸,可以接受的對稱加密,非對稱加密,hash算法
Https在與服務器進行數據交互之前,會與服務器進行一次通信(握手)
1、瀏覽器將自身支持的加密算法發(fā)送給服務端
2、服務端篩選出一套加密算法,以證書的形式發(fā)給瀏覽器
3、瀏覽器根驗證證書的合法性,據拿到的證書里的公鑰加密一串消息發(fā)給服務端
4、服務端使用私鑰解密信息,驗證哈希,并加密響應消息給瀏覽器
5、瀏覽器解密響應消息,并對消息進行驗證,如果驗證通過,則可以進行加密數據交互
觸摸事件的沖突解決
解決觸摸事件沖突:
外部攔截。ViewGroup重寫onInterceptTouchEvent方法,默認不攔截,事件往下分發(fā)給子View,若返回true,則攔截此次事件,將事件傳給ViewGroup的onTouchEvent處理。
內部攔截。重寫子View的dispatchTouchEvent方法,方法中調用getParent().requestDisallowInterceptTouchEvent(true)方法,傳true則代表不希望ViewGroup攔截事件,傳false則代表希望ViewGroup攔截事件。
內部攔截。子View重寫onTouchEvent方法,返回true,則子View消費該次事件,返回false,該次事件返回給ViewGroup的onTouchEvent處理。