⑴ android-ViewModel原理解析
在這四個方法中,其實唯一的區別就是要不要傳Factory,當沒有傳自定義的Factory的時候,則會傳入默認的Factory,我們看ViewModelProvider構造器的源碼和部分of方法的源碼:
在ViewModelProvider中需要傳入一個VieModelStore對象,這個對象是由ViewModelStoreOwner來提供的,而在Activity或者Fragment中,是由Activity和Fragment來提供的,因為ViewModelStoreOwner是一個介面,而AppCompatActivity的祖父ComponentActivity和Fragment均實現了ViewModelStoreOwner介面。
但是ViewModelProviders在新的lifecycle-extensions庫中,已經是屬於被棄用的。新版的API直接使用ViewModelProvider,而不是ViewModelProviders。
比如:
可以如下的方式在baseActivity中添加,由子類Activity調用:
創建ViewModel對象,首先就需要先初始化一個ViewModelProvider對象
可以看出,ViewModelProvider構造函數其實最終都是需要兩個參數,一個是ViewModelStoreOwner對象,一個是Factory。而ViewModelStoreOwner其實就是用來獲取一個ViewModelStore對象來保存ViewModel對象的。而Factory就是用來創建ViewModel對象的。
這個介面的主要實現的作用就是返回一個ViewModelStore對象。在Android中,Activity和Fragment都會實現該介面,並且實現getViewModelStore()方法。
比如Activity就是在FragmentActivity的父類ComponentActivity中實現了ViewModelStoreOwner介面
Fragment的ViewModelStore其實是由FragmentManager進行管理獲取
每個FragmentActivity都會有一個自己的FragmentManager對象,所以每個FragmentManagerViewModel對象,管理的是一個FragmentActivity中的所有的Fragment對應的ViewModel。具體看FragmentManagerViewModel的getViewModelStore方法
從這里可以看出,每個Fragment都會有自己的ViewModelStore對象,而ViewModelStore對象,是根據每個Fragment的唯一標識進行創建的。
ViewModelStore類對象,是每個Activity或者Fragment都有一個,目的是用於保存該頁面的ViewModel對象,方便ViewModel的管理
從ViewModelProvider的get方法中,可以看出,get方法傳入的是一個ViewModel.class的Class類型,然後通過這個類型,得到ViewModel的規范名稱。將ViewModel對象緩存在ViewModelStore中的HashMap中。而ViewModel的創建,其實是通過ViewModelProvider.Factory來實現的
ViewModelProviders的of方法,用於返回一個ViewModelProvider對象
從這里我們可以看到,如果傳入的Activity或者Fragment有方法實現,而factory為null的時候,則會通過創建對應的Factory,而如果沒有的實現,那麼就會調用NewInstanceFactory來創建對應的Factory,而NewInstanceFactory其實就是創建AndroidViewModelFactory對象。
最終ViewModel對象,其實就是通過AndroidViewModelFactory的create的方法實現來創建。一般就是通過Class.newInstance或者Class.getConstructor來創建對象。
而ViewModelProvider的第一個參數,其實最終傳入的是ViewModelStore對象,這個對象內部是通過一個HashMap來保存ViewModel對象
而新版的源碼,ViewModelStore對象是通過Fragment和FragmentActivity對象的getViewModelStore方法來獲取,而原先的HolderFragment的功能都移植到了Fragment中
HolderFragment通過設置setRetainInstance(true),使得自身能夠不受到屏幕旋轉等configuration
changes影響而存活,直到依附的Activity正常結束。
因為HolderFragment的生命周期,ViewModelStore對象保存在HolderFragment中,而ViewModel又存儲在ViewModelStore中,這就是為什麼我們說ViewModel類能夠讓數據在屏幕旋轉等配置信息改變導致UI重建的情況下不被銷毀。
ViewModelProvider的get方法:
在ViewModel中,有兩種Factory,Factory是的類型是由ViewModelProvider在初始化的時候創建的,所以是由ViewModelProvider決定Factory的類型。在ViewModelProvider中,有兩種Factory,一種是默認的Factory,默認的Factory是通過在ComponentActivity或者Fragment中實現介面,然後在()方法中初始化一個SavedStateViewModelFactory對象;另一種Factory則是NewInstanceFactory,這種是通過NewInstanceFactory.getInstance()的單例方式獲取。
其實就是通過ViewModel的Class對象,然後通過反射創建ViewModel對象,然後保存到ViewModelStore中的Map集合中
從ViewModelProvider的get方法可以看出,在ViewModelProvider的get方法中會根據Factory的類型,進行不同方法的調用。SavedStateViewModelFactory是實現了ViewModelProvider.KeyedFactory介面的,所以在創建ViewModel的時候,調用的是SavedStateViewModelFactory的create方法。
AndroidViewModel和ViewModel的構造器參數Class
ViewModel保存和恢復數據
ComponentActivity和Fragment都將數據的保存和恢復邏輯轉發給了SavedStateRegistryController。在在onCreate方法里通過調用performRestore來恢復數據,在onSaveInstanceState方法里通過調用performSave來保存數據。而SavedStateRegistryController中的SavedStateRegistry對象,就是實際進行數據的保存和恢復的,在SavedStateRegistry通過唯一的key獲取到一個SavedStateProvider,而SavedStateProvider其實就是返回需要保存的數據,將對應的需要緩存的數據一一返回,然後保存在系統緩存時的回調到onSaveInstanceState的方法參數Bundle中進行保存。
SavedStateRegistry.performSave()
該方法是由ComponentActivity的onSaveInstanceState方法觸發調用SavedStateRegistryController的performSave,進而調用的
在SavedStateRegistry恢復數據的時候,會把恢復後的數據都交給SavedStateHandle。希望保留的數據,可以通過兩種方式向mRegular保存數據。
在ComponentActivity恢復數據的時候,會通過SavedStateRegistryController.performSave在Activity的onSaveInstanceState方法中進行數據的保存,然後在ComponentActivity的onCreate方法中,通過調用SavedStateRegistryController.performRestore方法進行數據的恢復,這些恢復的數據都會保存在SavedStateHandleController對象中的SavedStateHandle屬性中,然後在Activity重新創建的時候,會通過反射創建對應的ViewModel對象的時候,將SavedStateHandleController中的SavedStateHandle賦值給對應的ViewModel進行數據恢復。
這塊的源碼分析可以參考:
從源碼看 Jetpack(7)-SavedStateHandle源碼詳解
這里其實就是直接使用Class的newInstance直接創建對象。Activity和Fragment一般都是使用SavedStateViewModelFactory創建ViewModel對象。
ViewModel的銷毀,要分為Activity和Fragment兩部分。
首先看下ViewModel在銷毀的時候做的事情
而ViewModel的clear()方法的調用,是在ViewModelStore中
Activity的銷毀,是通過Lifecycle監聽生命周期回調,當生命周期執行到onDestroy的時候,調用ViewModelStore的clear()方法進行ViewModel的銷毀。
看ComponentActivity中構造器中的實現:
Fragment的生命周期管理,如下:
Fragment的生命周期,首先會依次增大,然後在從onResume變成onPause的時候,就開始狀態碼減小。即先升再降的一個狀態變化。在當前狀態碼變成CREATED的時候,就會執行onDestroy。即調用
FragmentStateManager.destroy
在這里就會調用nonConfig.clearNonConfigState方法,nonConfig其實就是FragmentManagerViewModel對象。
FragmentManagerViewModel.clearNonConfigState
按照上面的邏輯,在Activity重建時會執行destory生命周期事件,那麼為什麼ViewModel沒有銷毀呢?
其實就是在屏幕旋轉的時候,AMS通過Binder回調Activity的()方法,這個時候就會進行數據的保存,保存到一個NonConfigurationInstances對象;而在屏幕翻轉結束之後,會再一次調用ViewModelProvider的構造函數,此時就會調用owner.getViewModelStore(),接著就會調用(),這里就會通過Activity中的NonConfigurationInstances對象取出保存的ViewModelStore對象。
所以數據保存就是通過()方法保存在NonConfigurationInstances對象,而再一次使用取出ViewModel的數據的時候,就是從nc對象中取出ViewModelStore對象,而ViewModelStore對象保存有ViewModel集合
通過對ComponentActivity的getViewModelStore()方法進行分析。可以找到這個問題的答案。
當mViewModelStore為null的時候,會從NonConfigurationInstances中獲取ViewModelStore對象。
其實在ComponentActivity和Activity中都會有一個NonConfigurationInstances類,而Activity中的NonConfigurationInstances類結構如下:
這里的Object activity其實就是保存的ComponentActivity中的NonConfigurationInstances類對象,看Activity的下面的方法:
activity這個Object對象,其實是通過()方法返回值賦值,而()方法的實現是在ComponentActivity中。
看ComponentActivity中的下面方法:
因為這里會在ComponentActivity中的NonConfigurationInstances類對象中保存ViewModelStore對象,所以這也是Activity重建時不會銷毀ViewModel的原因。
()方法除了被Activity的()調用以外,還會被LocalActivityManager的()方法調用
在分析ViewModel的銷毀過程時,我們看到Activity與Fragment存儲VieModel是分離的,那麼同一個Activity下的Fragment是如何共享ViewModel的呢?
其實共享的是Activity的ViewModel。
而具體的實現邏輯,其實就是在FragmentViewModelLazy.kt中的:
在Fragment中可以直接調用,這是一個Fragment的擴展函數,通過實現requireActivity().viewModelStore,獲取到了Activity的ViewModelStore對象後,這樣就可以實現了Fragment共用Activity的ViewModel,從而實現了Fragment之間共享ViewModel。
Fragment之間共享ViewModel,需要引入
⑵ android SQLiteDatabase涓鐨剈pdate鍑芥暟鐢ㄦ硶鏄浠涔堬紵
1路鍒涘緩鏁版嵁搴揂ndroid 涓嶈嚜鍔ㄦ彁渚涙暟鎹搴撱傚湪 Android 搴旂敤紼嬪簭涓浣跨敤 SQLite錛屽繀欏昏嚜宸卞壋寤烘暟鎹搴擄紝鐒跺悗鍒涘緩琛ㄣ佺儲寮曪紝濉鍏呮暟鎹銆侫ndroid 鎻愪緵浜 SQLiteOpenHelper 甯鍔╀綘鍒涘緩涓涓鏁版嵁搴擄紝浣犲彧瑕佺戶鎵 SQLiteOpenHelper 綾伙紝灝卞彲浠ヨ交鏉劇殑鍒涘緩鏁版嵁搴撱
2路SQLiteOpenHelper 綾繪牴鎹寮鍙戝簲鐢ㄧ▼搴忕殑闇瑕侊紝灝佽呬簡鍒涘緩鍜屾洿鏂版暟鎹搴撲嬌鐢ㄧ殑閫昏緫銆係QLiteOpenHelper 鐨勫瓙綾伙紝鑷沖皯闇瑕佸疄鐜頒笁涓鏂規硶錛氭瀯閫犲嚱鏁幫紝璋冪敤鐖剁被 SQLiteOpenHelper 鐨勬瀯閫犲嚱鏁般
3路榪欎釜鏂規硶闇瑕佸洓涓鍙傛暟錛
涓婁笅鏂囩幆澧冿紙渚嬪傦紝涓涓 Activity錛夛紝
鏁版嵁搴撳悕瀛楋紝
涓涓鍙閫夌殑娓告爣宸ュ巶錛堥氬父鏄 Null錛夛紝
涓涓浠h〃浣犳e湪浣跨敤鐨勬暟鎹搴撴ā鍨嬬増鏈鐨勬暣鏁般俹nCreate錛堬級鏂規硶錛屽畠闇瑕佷竴涓 SQLiteDatabase 瀵硅薄浣滀負鍙傛暟錛屾牴鎹闇瑕佸硅繖涓瀵硅薄濉鍏呰〃鍜屽垵濮嬪寲鏁版嵁銆俹nUpgrage() 鏂規硶錛屽畠闇瑕佷笁涓鍙傛暟錛屼竴涓 SQLiteDatabase 瀵硅薄錛屼竴涓鏃х殑鐗堟湰鍙峰拰涓涓鏂扮殑鐗堟湰鍙鳳紝榪欐牱浣犲氨鍙浠ユ竻妤氬備綍鎶婁竴涓鏁版嵁搴撲粠鏃х殑妯″瀷杞鍙樺埌鏂扮殑妯″瀷銆
⑶ Android中的線程狀態 - AsyncTask詳解
在操作系統中,線程是操作系統調度的最小單元,同時線程又是一種受限的系統資源,即線程不可能無限制地產生,並且 線程的創建和銷毀都會有相應的開銷。 當系統中存在大量的線程時,系統會通過會時間片輪轉的方式調度每個線程,因此線程不可能做到絕對的並行。
如果在一個進程中頻繁地創建和銷毀線程,顯然不是高效的做法。正確的做法是採用線程池,一個線程池中會緩存一定數量的線程,通過線程池就可以避免因為頻繁創建和銷毀線程所帶來的系統開銷。
AsyncTask是一個抽象類,它是由Android封裝的一個輕量級非同步類(輕量體現在使用方便、代碼簡潔),它可以在線程池中執行後台任務,然後把執行的進度和最終結果傳遞給主線程並在主線程中更新UI。
AsyncTask的內部封裝了 兩個線程池 (SerialExecutor和THREAD_POOL_EXECUTOR)和 一個Handler (InternalHandler)。
其中 SerialExecutor線程池用於任務的排隊,讓需要執行的多個耗時任務,按順序排列 , THREAD_POOL_EXECUTOR線程池才真正地執行任務 , InternalHandler用於從工作線程切換到主線程 。
1.AsyncTask的泛型參數
AsyncTask是一個抽象泛型類。
其中,三個泛型類型參數的含義如下:
Params: 開始非同步任務執行時傳入的參數類型;
Progress: 非同步任務執行過程中,返回下載進度值的類型;
Result: 非同步任務執行完成後,返回的結果類型;
如果AsyncTask確定不需要傳遞具體參數,那麼這三個泛型參數可以用Void來代替。
有了這三個參數類型之後,也就控制了這個AsyncTask子類各個階段的返回類型,如果有不同業務,我們就需要再另寫一個AsyncTask的子類進行處理。
2.AsyncTask的核心方法
onPreExecute()
這個方法會在 後台任務開始執行之間調用,在主線程執行。 用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。
doInBackground(Params...)
這個方法中的所有代碼都會 在子線程中運行,我們應該在這里去處理所有的耗時任務。
任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果。 注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress...)方法來完成。
onProgressUpdate(Progress...)
當在後台任務中調用了publishProgress(Progress...)方法後,這個方法就很快會被調用,方法中攜帶的參數就是在後台任務中傳遞過來的。 在這個方法中可以對UI進行操作,在主線程中進行,利用參數中的數值就可以對界面元素進行相應的更新。
onPostExecute(Result)
當doInBackground(Params...)執行完畢並通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作為參數傳遞到此方法中, 可以利用返回的數據來進行一些UI操作,在主線程中進行,比如說提醒任務執行的結果,以及關閉掉進度條對話框等。
上面幾個方法的調用順序:
onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()
如果不需要執行更新進度則為onPreExecute() --> doInBackground() --> onPostExecute(),
除了上面四個方法,AsyncTask還提供了onCancelled()方法, 它同樣在主線程中執行,當非同步任務取消時,onCancelled()會被調用,這個時候onPostExecute()則不會被調用 ,但是要注意的是, AsyncTask中的cancel()方法並不是真正去取消任務,只是設置這個任務為取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記為中斷,需要在線程內部進行標記判斷然後中斷線程。
3.AsyncTask的簡單使用
這里我們模擬了一個下載任務,在doInBackground()方法中去執行具體的下載邏輯,在onProgressUpdate()方法中顯示當前的下載進度,在onPostExecute()方法中來提示任務的執行結果。如果想要啟動這個任務,只需要簡單地調用以下代碼即可:
4.使用AsyncTask的注意事項
①非同步任務的實例必須在UI線程中創建,即AsyncTask對象必須在UI線程中創建。
②execute(Params... params)方法必須在UI線程中調用。
③不要手動調用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。
④不能在doInBackground(Params... params)中更改UI組件的信息。
⑤一個任務實例只能執行一次,如果執行第二次將會拋出異常。
先從初始化一個AsyncTask時,調用的構造函數開始分析。
這段代碼雖然看起來有點長,但實際上並沒有任何具體的邏輯會得到執行,只是初始化了兩個變數,mWorker和mFuture,並在初始化mFuture的時候將mWorker作為參數傳入。mWorker是一個Callable對象,mFuture是一個FutureTask對象,這兩個變數會暫時保存在內存中,稍後才會用到它們。 FutureTask實現了Runnable介面,關於這部分內容可以看這篇文章。
mWorker中的call()方法執行了耗時操作,即result = doInBackground(mParams);,然後把執行得到的結果通過postResult(result);,傳遞給內部的Handler跳轉到主線程中。在這里這是實例化了兩個變數,並沒有開啟執行任務。
那麼mFuture對象是怎麼載入到線程池中,進行執行的呢?
接著如果想要啟動某一個任務,就需要調用該任務的execute()方法,因此現在我們來看一看execute()方法的源碼,如下所示:
調用了executeOnExecutor()方法,具體執行邏輯在這個方法裡面:
可以 看出,先執行了onPreExecute()方法,然後具體執行耗時任務是在exec.execute(mFuture),把構造函數中實例化的mFuture傳遞進去了。
exec具體是什麼?
從上面可以看出具體是sDefaultExecutor,再追溯看到是SerialExecutor類,具體源碼如下:
終於追溯到了調用了SerialExecutor 類的execute方法。SerialExecutor 是個靜態內部類,是所有實例化的AsyncTask對象公有的,SerialExecutor 內部維持了一個隊列,通過鎖使得該隊列保證AsyncTask中的任務是串列執行的,即多個任務需要一個個加到該隊列中,然後執行完隊列頭部的再執行下一個,以此類推。
在這個方法中,有兩個主要步驟。
①向隊列中加入一個新的任務,即之前實例化後的mFuture對象。
②調用 scheleNext()方法,調用THREAD_POOL_EXECUTOR執行隊列頭部的任務。
由此可見SerialExecutor 類僅僅為了保持任務執行是串列的,實際執行交給了THREAD_POOL_EXECUTOR。
THREAD_POOL_EXECUTOR又是什麼?
實際是個線程池,開啟了一定數量的核心線程和工作線程。然後調用線程池的execute()方法。執行具體的耗時任務,即開頭構造函數中mWorker中call()方法的內容。先執行完doInBackground()方法,又執行postResult()方法,下面看該方法的具體內容:
該方法向Handler對象發送了一個消息,下面具體看AsyncTask中實例化的Hanlder對象的源碼:
在InternalHandler 中,如果收到的消息是MESSAGE_POST_RESULT,即執行完了doInBackground()方法並傳遞結果,那麼就調用finish()方法。
如果任務已經取消了,回調onCancelled()方法,否則回調 onPostExecute()方法。
如果收到的消息是MESSAGE_POST_PROGRESS,回調onProgressUpdate()方法,更新進度。
InternalHandler是一個靜態類,為了能夠將執行環境切換到主線程,因此這個類必須在主線程中進行載入。所以變相要求AsyncTask的類必須在主線程中進行載入。
到此為止,從任務執行的開始到結束都從源碼分析完了。
AsyncTask的串列和並行
從上述源碼分析中分析得到,默認情況下AsyncTask的執行效果是串列的,因為有了SerialExecutor類來維持保證隊列的串列。如果想使用並行執行任務,那麼可以直接跳過SerialExecutor類,使用executeOnExecutor()來執行任務。
四、AsyncTask使用不當的後果
1.)生命周期
AsyncTask不與任何組件綁定生命周期,所以在Activity/或者Fragment中創建執行AsyncTask時,最好在Activity/Fragment的onDestory()調用 cancel(boolean);
2.)內存泄漏
3.) 結果丟失
屏幕旋轉或Activity在後台被系統殺掉等情況會導致Activity的重新創建,之前運行的AsyncTask(非靜態的內部類)會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將不再生效。
自己是從事了七年開發的Android工程師,不少人私下問我,2019年Android進階該怎麼學,方法有沒有?
沒錯,年初我花了一個多月的時間整理出來的學習資料,希望能幫助那些想進階提升Android開發,卻又不知道怎麼進階學習的朋友。【 包括高級UI、性能優化、架構師課程、NDK、Kotlin、混合式開發(ReactNative+Weex)、Flutter等架構技術資料 】,希望能幫助到您面試前的復習且找到一個好的工作,也節省大家在網上搜索資料的時間來學習。
⑷ Android錛氭渶鍏ㄩ潰鐨 Webview 璇﹁В
WebView鏄涓涓鍩轟簬webkit寮曟搸銆佸睍鐜皐eb欏甸潰鐨勬帶浠躲
涓鑸鏉ヨ碬ebview鍙鍗曠嫭浣跨敤錛屽彲鑱斿悎鍏跺瓙綾諱竴璧蜂嬌鐢錛屾墍浠ユ帴涓嬫潵錛屾垜浼氫粙緇嶏細
甯歌佺敤娉曪細Back閿鎺у埗緗戦〉鍚庨
閰嶇疆姝ラ1錛氭坊鍔犺塊棶緗戠粶鏉冮檺 錛圓ndroidManifest.xml錛
閰嶇疆姝ラ2錛氱敓鎴愪竴涓猈ebView緇勪歡錛堟湁涓ょ嶆柟寮忥級
閰嶇疆姝ラ3錛氳繘琛岄厤緗-鍒╃敤WebSettings瀛愮被 錛堝父瑙佹柟娉曪級
甯歌佺敤娉曪細璁劇疆WebView緙撳瓨
娉ㄦ剰錛 姣忎釜 Application 鍙璋冪敤涓嬈 WebSettings.setAppCachePath()錛學ebSettings.setAppCacheMaxSize()
甯歌佹柟娉1錛歴houldOverrideUrlLoading()
甯歌佹柟娉2錛歰nPageStarted()
甯歌佹柟娉3錛歰nPageFinished()
甯歌佹柟娉4錛歰nLoadResource()
甯歌佹柟娉5錛歰nReceivedError錛堬級
甯歌佹柟娉6錛歰nReceivedSslError()
甯歌佹柟娉1錛 onProgressChanged錛堬級
甯歌佹柟娉2錛 onReceivedTitle錛堬級
鍏蜂綋璇風湅鎴戝啓鐨勬枃絝 Android WebView涓嶫S鐨勪氦浜掓柟寮 鏈鍏ㄩ潰奼囨
3.4.1 涓嶅湪xml涓瀹氫箟 Webview 錛岃屾槸鍦ㄩ渶瑕佺殑鏃跺欏湪Activity涓鍒涘緩錛屽苟涓擟ontext浣跨敤 getApplicationgContext()
3.4.2 鍦 Activity 閿姣侊紙 WebView 錛夌殑鏃跺欙紝鍏堣 WebView 鍔犺澆null鍐呭癸紝鐒跺悗縐婚櫎 WebView錛屽啀閿姣 WebView錛屾渶鍚庣疆絀恆
姝ラ1錛氭坊鍔犺塊棶緗戠粶鏉冮檺
AndroidManifest.xml
姝ラ2錛氫富甯冨矓
activity_main.xml
姝ラ3錛氭牴鎹闇瑕佸疄鐜扮殑鍔熻兘浠庤屼嬌鐢ㄧ浉搴旂殑瀛愮被鍙婂叾鏂規硶錛堟敞閲婂緢娓呮氫簡錛
MainActivity.java