Android 面試準備進行曲(Android 基礎知識)v1.1

update time 2019年9月18日 10:18:23

CSDN 地址(簡書不會整目錄,尷尬… -,-)

基礎部分

Activity生命週期

onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()
在這裏插入圖片描述
圖片簡要說明

  • 啓動 onCreate -> onStart -> onResume
  • 被覆蓋/ 回到當前界面 onPause -> / -> onResume
  • 在後臺 onPause -> onStop
  • 後退回到 onRestart -> onStart -> onResume
  • 退出 onPause -> onStop -> onDestory

另外說一下 其他兩個比較重要的 方法

  • onSaveInstanceState : (1)Activity被覆蓋或退居後臺,系統資源不足將其殺死,此方法會被調用;(2)在用戶改變屏幕方向時,此方法會被調用 系統先銷燬當前的Activity,然後再重建一個新的,調用此方法時,我們可以保存一些臨時數據;(3)在當前Activity跳轉到其他Activity或者按Home鍵回到主屏,自身退居後臺時, 系統調用此方法是爲了保存當前窗口各個View組件的狀態。onSaveInstanceState該方法調用在onStop之前,但和onPause沒有時序關係。 不過一般onSaveInstanceState() 保存臨時數據爲主,而 onPause() 適用於對數據的持久化保存。

  • onRestoreInstanceState : onRestoreInstanceState的調用順序是在onStart之後。主要用於 恢復一些onSaveInstanceState 方法中保存的數據

onStart()和onResume()/onPause()和onStop()的區別

onStart()與onStop()是從Activity是否可見這個角度調用的
onResume()和onPause()是從Activity是否顯示在前臺這個角度來回調的
在實際使用沒其他明顯區別。

Activity A 跳轉 Activity B的問題

Activity A啓動另一個Activity B會回調的方法:
Activity A的onPause() -->Activity B的onCreate()–>onStart()–>onResume()–>Activity A的onStop();

如果Activity B是完全透明的,則最後不會調用Activity A的onStop();如果是對話框Activity,則最後不會調用Activity A的onStop();

Activity 啓動流程

在這裏插入圖片描述
調用startActivity()後經過重重方法會轉移到ActivityManagerService的startActivity(),並通過一個IPC回到ActivityThread的內部類ApplicationThread中,並調用其scheduleLaunchActivity()將啓動Activity的消息發送並交由Handler H處理。
Handler H對消息的處理會調用handleLaunchActivity()->performLaunchActivity()得以完成Activity對象的創建和啓動。

參考地址:Activity啓動流程

Fragment 生命週期

Fragment從創建到銷燬整個生命週期中涉及到的方法依次爲:
onAttach()->onCreate()-> onCreateView()->onActivityCreated()->onStart()->onResume()->onPause()->onStop()->onDestroyView()->onDestroy()->onDetach(),
其中和Activity有不少名稱相同作用相似的方法,而不同的方法有:

onAttach():當Fragment和Activity建立關聯時調用

onCreateView():當Fragment創建視圖時調用

onActivityCreated():當與Fragment相關聯的Activity完成onCreate()之後調用

onDestroyView():在Fragment中的佈局被移除時調用

onDetach():當Fragment和Activity解除關聯時調用

Activity 與 Fragment 通信

  1. 對於Activity和Fragment之間的相互調用
    (1)Activity調用Fragment
    直接調用就好,Activity一般持有Fragment實例,或者通過Fragment id 或者tag獲取到Fragment實例
    (2)Fragment調用Activity
    通過activity設置監聽器到Fragment進行回調,或者是直接在fragment直接getActivity獲取到activity實例

  2. Activity如果更好的傳遞參數給Fragment
    如果直接通過普通方法的調用傳遞參數的話,那麼在fragment回收後恢復不能恢復這些數據。google給我們提供了一個方法 setArguments(bundle) 可以通過這個方法傳遞參數給fragment,然後在fragment中用getArguments獲取到。能保證在fragment銷燬重建後還能獲取到數據

Service 啓動及生命週期

service 啓動方式

  • 不可通信Service 。 通過startService()啓動,不跟隨調用者關閉而關閉
  • 可通信Service 。 通過bindService()方式進行啓動。跟隨調用者關閉而關閉

以上兩種Servcie 默認都存在於調用者一樣的進程中,如果想要設置不一樣的進程中則需要在 AndroidManifest.xml 中 配置 android:process = Remote 屬性

生命週期 :

  • 通過startService()這種方式啓動的service,生命週期 :startService() --> onCreate()–> onStartConmon()–> onDestroy()。

需要注意幾個問題

1. 當我們通過startService被調用以後,多次在調用startService(),onCreate()方法也只會被調用一次,
2. 而onStartConmon()會被多次調用當我們調用stopService()的時候,onDestroy()就會被調用,從而銷燬服務。
 2. 當我們通過startService啓動時候,通過intent傳值,在onStartConmon()方法中獲取值的時候,一定要先判斷intent是否爲null。
  • 通過bindService()方式進行綁定,這種方式綁定service,生命週期走法:bindService–>onCreate()–>onBind()–>unBind()–>onDestroy()

bindService的優點
這種方式進行啓動service好處是更加便利activity中操作service,比如加入service中有幾個方法,a,b ,如果要在activity中調用,在需要在activity獲取ServiceConnection對象,通過ServiceConnection來獲取service中內部類的類對象,然後通過這個類對象就可以調用類中的方法,當然這個類需要繼承Binder對象

Service 通信方式

  1. 創建繼承Binder的內部類,重寫Service的onBind方法 返回 Binder 子類,重寫ServiceConnection,onServiceConnected時調用邏輯方法 綁定服務。

  2. 通過接口Iservice調用Service方法

IntentService對比Service

IntentService是Service的子類,是一個異步的,會自動停止的服務,很好解決了傳統的Service中處理完耗時操作忘記停止並銷燬Service的問題

優點:

  • 所有請求處理完成後,IntentService會自動停止,無需調用stopSelf()方法停止
  • IntentService不會阻塞UI線程,而普通Serveice會導致ANR異常
  • Intentservice若未執行完成上一次的任務,將不會新開一個線程,是等待之前的任務完成後,再執行新的任務,等任務完成後再次調用stopSelf()
  • 爲Service的onBind()提供默認實現,返回null;

提高service的優先級

  1. 在AndroidManifest.xml文件中對於intent-filter可以通過android:priority = “1000”這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,同時實用於廣播。
  2. onStartCommand方法,手動返回START_STICKY。
  3. 監聽系統廣播判斷Service狀態。
  4. Application加上Persistent屬性。
  5. 在onStartCommand裏面調用 startForeground()方法把Service提升爲前臺進程級別,然後再onDestroy裏面調用stopForeground ()方法。
  6. 在onDestroy方法裏發廣播重啓service。
    service +broadcast 方式,就是當service走ondestory的時候,發送一個自定義的廣播,當收到廣播的時候,重新啓動service。

延伸:進程保活(毒瘤)

黑色保活:不同的app進程,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒)
白色保活:啓動前臺Service
灰色保活:利用系統的漏洞啓動前臺Service

黑色保活
所謂黑色保活,就是利用不同的app進程使用廣播來進行相互喚醒。舉個3個比較常見的場景:
場景1:開機,網絡切換、拍照、拍視頻時候,利用系統產生的廣播喚醒app
場景2:接入第三方SDK也會喚醒相應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶。由此發散開去,就會直接觸發了下面的 場景3
場景3:假如你手機裏裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其他阿里系的app給喚醒了。(只是拿阿里打個比方,其實BAT系都差不多)

白色保活
白色保活手段非常簡單,就是調用系統api啓動一個前臺的Service進程,這樣會在系統的通知欄生成一個Notification,用來讓用戶知道有這樣一個app在運行着,哪怕當前的app退到了後臺。如下方的LBE和QQ音樂這樣:

灰色保活
灰色保活,這種保活手段是應用範圍最廣泛。它是利用系統的漏洞來啓動一個前臺的Service進程,與普通的啓動方式區別在於,它不會在系統通知欄處出現一個Notification,看起來就如同運行着一個後臺Service進程一樣。這樣做帶來的好處就是,用戶無法察覺到你運行着一個前臺進程(因爲看不到Notification),但你的進程優先級又是高於普通後臺進程的。那麼如何利用系統的漏洞呢,大致的實現思路和代碼如下:
思路一:API < 18,啓動前臺Service時直接傳入new Notification(); 思路二:API >= 18,同時啓動兩個id相同的前臺Service,然後再將後啓動的Service做stop處理
熟悉Android系統的童鞋都知道,系統出於體驗和性能上的考慮,app在退到後臺時系統並不會真正的kill掉這個進程,而是將其緩存起來。打開的應用越多,後臺緩存的進程也越多。在系統內存不足的情況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出內存來供給需要的app。這套殺進程回收內存的機制就叫 Low Memory Killer ,它是基於Linux內核的 OOM Killer(Out-Of-Memory killer)機制誕生。

思路二:後臺播放無聲音頻,模擬前臺服務,提高等級

思路三:1像素界面

思路四:在Activity的onDestroy()通過發送廣播,並在廣播接收器的onReceive()中啓動Service

進程的重要性,劃分5級:
前臺進程 (Foreground process)
可見進程 (Visible process)
服務進程 (Service process)
後臺進程 (Background process)
空進程 (Empty process)

什麼是oom_adj?它是linux內核分配給每個系統進程的一個值,代表進程的優先級,進程回收機制就是根據這個優先級來決定是否進行回收。對於oom_adj的作用,你只需要記住以下幾點即可:
進程的oom_adj越大,表示此進程優先級越低,越容易被殺回收;越小,表示進程優先級越高,越不容易被殺回收
普通app進程的oom_adj>=0,系統進程的oom_adj纔可能<0
有些手機廠商把這些知名的app放入了自己的白名單中,保證了進程不死來提高用戶體驗

Broadcast註冊方式與區別

Broadcast廣播,註冊方式主要有兩種.

  • 第一種是靜態註冊,也可成爲常駐型廣播,這種廣播需要在Androidmanifest.xml中進行註冊,這中方式註冊的廣播,不受頁面生命週期的影響,即使退出了頁面,也可以收到廣播這種廣播一般用於想開機自啓動啊等等,由於這種註冊的方式的廣播是常駐型廣播,所以會佔用CPU的資源。

  • 第二種是動態註冊,而動態註冊的話,是在代碼中註冊的,這種註冊方式也叫非常駐型廣播,收到生命週期的影響,退出頁面後,就不會收到廣播,我們通常運用在更新UI方面。這種註冊方式優先級較高。最後需要解綁,否則會內存泄露
    廣播是分爲有序廣播和無序廣播。

Broadcast 有幾種形式

普通廣播:一種完全異步執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,因此它們接收的先後是隨機的。

有序廣播:一種同步執行的廣播,在廣播發出之後,同一時刻只會有一個廣播接收器能夠收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播纔會繼續傳遞,所以此時的廣播接收器是有先後順序的,且優先級(priority)高的廣播接收器會先收到廣播消息。有序廣播可以被接收器截斷使得後面的接收器無法收到它。

本地廣播:發出的廣播只能夠在應用程序的內部進行傳遞,並且廣播接收器也只能接收本應用程序發出的廣播。

粘性廣播:這種廣播會一直滯留,當有匹配該廣播的接收器被註冊後,該接收器就會收到此條廣播。

部分Broadcast 之間的區別

BroadcastReceiver: 是可以跨應用廣播,利用Binder機制實現,支持動態和靜態兩種方式註冊方式。

LocalBroadcastReceiver: 是應用內廣播,利用Handler實現,利用了IntentFilter的match功能,提供消息的發佈與接收功能,實現應用內通信,效率和安全性比較高,僅支持動態註冊。

OrderedBroadcast : 調用sendOrderedBroadcast()發送,接收者會按照priority優先級從大到小進行排序,如優先級相同,先註冊,先處理
廣播接收者還能對廣播進行截斷和修改

ContentProvider

作爲四大組件之一,ContentProvider主要負責存儲和共享數據。與文件存儲、SharedPreferences存儲、SQLite數據庫存儲這幾種數據存儲方法不同的是,後者保存下的數據只能被該應用程序使用,而前者可以讓不同應用程序之間進行數據共享,它還可以選擇只對哪一部分數據進行共享,從而保證程序中的隱私數據不會有泄漏風險。

app中有幾個Context對象

先看一下源碼的解釋

/**
* Interface to global information about an application environment.  This is
* an abstract class whose implementation is provided by
* the Android system.  It
* allows access to application-specific resources and classes, as well as
* up-calls for application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/
public abstract class Context {
    /**
     * File creation mode: the default mode, where the created file can only
     * be accessed by the calling application (or all applications sharing the
     * same user ID).
     * <a href="http://www.jobbole.com/members/[email protected]">@see</a> #MODE_WORLD_READABLE
     * <a href="http://www.jobbole.com/members/[email protected]">@see</a> #MODE_WORLD_WRITEABLE
     */
    public static final int MODE_PRIVATE = 0x0000;
 
    public static final int MODE_WORLD_WRITEABLE = 0x0002;
 
    public static final int MODE_APPEND = 0x8000;
 
    public static final int MODE_MULTI_PROCESS = 0x0004;
 
    }

源碼中的註釋是這麼來解釋Context的:Context提供了關於應用環境全局信息的接口。它是一個抽象類,它的執行被Android系統所提供。它允許獲取以應用爲特徵的資源和類型,是一個統領一些資源(應用程序環境變量等)的上下文。就是說,它描述一個應用程序環境的信息(即上下文);是一個抽象類,Android提供了該抽象類的具體實現類;通過它我們可以獲取應用程序的資源和類(包括應用級別操作,如啓動Activity,發廣播,接受Intent等)。

在這裏插入圖片描述
從上面的關係圖我們已經可以得出答案了,在應用程序中Context的具體實現子類就是:Activity,Service,Application。那麼Context數量=Activity數量+Service數量+1。當然如果你足夠細心,可能會有疑問:我們常說四大組件,這裏怎麼只有Activity,Service持有Context,那Broadcast Receiver,Content Provider呢?Broadcast Receiver,Content Provider並不是Context的子類,他們所持有的Context都是其他地方傳過去的,所以並不計入Context總數。

Application Context 啓動問題

如果我們用ApplicationContext去啓動一個LaunchMode爲standard的Activity的時候會報錯android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?這是因爲非Activity類型的Context並沒有所謂的任務棧,所以待啓動的Activity就找不到棧了。解決這個問題的方法就是爲待啓動的Activity指定FLAG_ACTIVITY_NEW_TASK標記位,這樣啓動的時候就爲它創建一個新的任務棧,而此時Activity是以singleTask模式啓動的。所有這種用Application啓動Activity的方式不推薦使用,Service同Application。

如何獲取 Context對象

1:View.getContext,返回當前View對象的Context對象,通常是當前正在展示的Activity對象。

2:Activity.getApplicationContext,獲取當前Activity所在的(應用)進程的Context對象,通常我們使用Context對象時,要優先考慮這個全局的進程Context。

4:Activity.this 返回當前的Activity實例,如果是UI控件需要使用Activity作爲Context對象,但是默認的Toast實際上使用ApplicationContext也可以。

根據Context 獲取活動狀態

參考博客

Android 5.0(Lollipop,API 21)之前,我們可以通過ActivityManager提供的getRunningTasks方法獲取當前設備所有處於運行狀態的應用信息,從而判斷自己的應用的運行狀態,包括topActivity信息

public static ComponentName getTopActivity(Context context){
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Service.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> runningTaskInfoList = activityManager.getRunningTasks(Integer.MAX_VALUE);
    for (ActivityManager.RunningTaskInfo taskInfo : runningTaskInfoList) {
        if (taskInfo.topActivity.getPackageName().equals(context.getPackageName())){
            return taskInfo.topActivity;
        }
    }
    return null;
}

Google 爲了提升 Android 系統的安全性,從 5.0 開始,廢棄了getRunningTasks方法,不再爲開發人員提供類似的服務,僅可作爲開發時的調試和展示。

我們還可以通過 Deprecated的getRunningAppProcesses方法,獲取處於“運行狀態”的應用進程信息。結合包名和importance屬性值的聯合判斷,即可判定應用是否處於前臺運行狀態

public static boolean isAppRunningForeground(Context context){
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Service.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfoList = activityManager.getRunningAppProcesses();
        if (runningAppProcessInfoList==null){
            return false;
        }
        for (ActivityManager.RunningAppProcessInfo processInfo : runningAppProcessInfoList) {
            if (processInfo.processName.equals(context.getPackageName())
                    && processInfo.importance==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
                return true;
            }
        }
        return false;
    }

當然 上述的辦法也不是官網推薦的 要不然也不會有現在出來的 Lifecycle 。

關於如何通過 ActivityLifecycleCallbacks 管理Activity 聲明週期的

ActivityLifecycleCallbacks

jetpack 推薦寫法

JetPack Lifecycle 生命週期組件

如何避免因爲Context 造成內存泄漏

一般Context造成的內存泄漏,幾乎都是當Context銷燬的時候,卻因爲被引用導致銷燬失敗,而Application的Context對象可以理解爲隨着進程存在的,所以我們總結出使用Context的正確姿勢:

1:當Application的Context能搞定的情況下,並且生命週期長的對象,優先使用Application的Context。

2:不要讓生命週期長於Activity的對象持有到Activity的引用。

3:儘量不要在Activity中使用非靜態內部類,因爲非靜態內部類會隱式持有外部類實例的引用,如果使用靜態內部類,將外部實例引用作爲弱引用持有。

getApplication()和getApplicationContext() 區別

其實我們通過程序打印 兩個方法獲得的對象
Application本身就是一個Context,所以這裏獲取getApplicationContext()得到的結果就是Application本身的實例。那麼問題來了,既然這兩個方法得到的結果都是相同的,那麼Android爲什麼要提供兩個功能重複的方法呢?

實際上這兩個方法在作用域上有比較大的區別。getApplication()方法的語義性非常強,一看就知道是用來獲取Application實例的,但是這個方法只有在Activity和Service中才能調用的到。那麼也許在絕大多數情況下我們都是在Activity或者Service中使用Application的,但是如果在一些其它的場景,比如BroadcastReceiver中也想獲得Application的實例,這時就可以藉助getApplicationContext()方法了。

理解Activity,View,Window三者關係

Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示視圖)LayoutInflater像剪刀,Xml配置像窗花圖紙。
1:Activity構造的時候會初始化一個Window,準確的說是PhoneWindow。
2:這個PhoneWindow有一個“ViewRoot”,這個“ViewRoot”是一個View或者說ViewGroup,是最初始的根視圖。
3:“ViewRoot”通過addView方法來一個個的添加View。比如TextView,Button等
4:這些View的事件監聽,是由WindowManagerService來接受消息,並且回調Activity函數。比如onClickListener,onKeyDown等。

四種LaunchMode及其使用場景

此處延伸:棧(First In Last Out)與隊列(First In First Out)的區別
棧與隊列的區別:

隊列先進先出,棧先進後出
對插入和刪除操作的"限定"。 棧是限定只能在表的一端進行插入和刪除操作的線性表。 隊列是限定只能在表的一端進行插入和在另一端進行刪除操作的線性表。
遍歷數據速度不同

standard 模式
這是默認模式,每次激活Activity時都會創建Activity實例,並放入任務棧中。使用場景:大多數Activity。
singleTop 模式
如果在任務的棧頂正好存在該Activity的實例,就重用該實例( 會調用實例的 onNewIntent() ),否則就會創建新的實例並放入棧頂,即使棧中已經存在該Activity的實例,只要不在棧頂,都會創建新的實例。使用場景如新聞類或者閱讀類App的內容頁面。
singleTask 模式
如果在棧中已經有該Activity的實例,就重用該實例(會調用實例的 onNewIntent() )。重用時,會讓該實例回到棧頂,因此在它上面的實例將會被移出棧。如果棧中不存在該實例,將會創建新的實例放入棧中。使用場景如瀏覽器的主界面。不管從多少個應用啓動瀏覽器,只會啓動主界面一次,其餘情況都會走onNewIntent,並且會清空主界面上面的其他頁面。
singleInstance 模式
在一個新棧中創建該Activity的實例,並讓多個應用共享該棧中的該Activity實例。一旦該模式的Activity實例已經存在於某個棧中,任何應用再激活該Activity時都會重用該棧中的實例( 會調用實例的 onNewIntent() )。其效果相當於多個應用共享一個應用,不管誰激活該 Activity 都會進入同一個應用中。使用場景如鬧鈴提醒,將鬧鈴提醒與鬧鈴設置分離。singleInstance不要用於中間頁面,如果用於中間頁面,跳轉會有問題,比如:A -> B (singleInstance) -> C,完全退出後,在此啓動,首先打開的是B。

數據存儲

Android中提供哪些數據持久存儲的方法?

File 文件存儲:寫入和讀取文件的方法和 Java中實現I/O的程序一樣。

SharedPreferences存儲:一種輕型的數據存儲方式,常用來存儲一些簡單的配置
信息,本質是基於XML文件存儲key-value鍵值對數據。

SQLite數據庫存儲:一款輕量級的關係型數據庫,它的運算速度非常快,佔用資源很少,在存儲大量複雜的關係型數據的時可以使用。

ContentProvider:四大組件之一,用於數據的存儲和共享,不僅可以讓不同應用程序之間進行數據共享,還可以選擇只對哪一部分數據進行共享,可保證程序中的隱私數據不會有泄漏風險。

SharePreferences 相關問題

  1. SharePreferences是一種輕型的數據存儲方式,適用於存儲一些簡單的配置信息,如int、string、boolean、float和long。由於系統對SharedPreferences的讀/寫有一定的緩存策略,即在內存中有一份該文件的緩存,因此在多進程模式下,其讀/寫會變得不可靠,甚至丟失數據。

  2. context.getSharedPreferences()開始追蹤的話,可以去到ContextImpl的getSharedPreferences(),最終發現SharedPreferencesImpl這個SharedPreferences的實現類,在代碼中可以看到讀寫操作時都有大量的synchronized,因此它是線程安全

  3. 由於進程間是不能內存共享的,每個進程操作的SharedPreferences都是一個單獨的實例,這導致了多進程間通過SharedPreferences來共享數據是不安全的,這個問題只能通過多進程間其它的通信方式或者是在確保不會同時操作SharedPreferences數據的前提下使用SharedPreferences來解決。

SharePreferences 注意事項及優化辦法

  1. 第一次getSharePreference會讀取磁盤文件,異步讀取,寫入到內存中,後續的getSharePreference都是從內存中拿了。
  2. 第一次讀取完畢之前 所有的get/set請求都會被卡住 等待讀取完畢後再執行,所以第一次讀取會有ANR風險。
  3. 所有的get都是從內存中讀取。
  4. 提交都是 寫入到內存和磁盤中 。apply跟commit的區別在於
    apply 是內存同步 然後磁盤異步寫入任務放到一個單線程隊列中 等待調用。方法無返回 即void
    commit 內存同步 只不過要等待磁盤寫入結束才返回 直接返回寫入成功狀態 true or false
  5. 從 Android N 開始, 不再支持 MODE_WORLD_READABLE & MODE_WORLD_WRITEABLE. 一旦指定, 會拋異常 。也不要用MODE_MULTI_PROCESS 遲早被放棄。
    8.每次commit/apply都會把全部數據一次性寫入到磁盤,即沒有增量寫入的概念 。 所以單個文件千萬不要太大 否則會嚴重影響性能。

建議用微信的第三方MMKV來替代SharePreference

SP源碼解析

SQLite 相關問題

  • 使用事務做批量操作:
    使用SQLiteDatabase的beginTransaction()方法開啓一個事務,將批量操作SQL語句轉化成SQLiteStatement並進行批量操作,結束後endTransaction()

  • 及時關閉Cursor,避免內存泄漏

  • 耗時操作異步化:數據庫的操作屬於本地IO,通常比較耗時,建議將這些耗時操作放入異步線程中處理

  • ContentValues的容量調整:ContentValues內部採用HashMap來存儲Key-Value數據,ContentValues初始容量爲8,擴容時翻倍。因此建議對ContentValues填入的內容進行估量,設置合理的初始化容量,減少不必要的內部擴容操作

  • 使用索引加快檢索速度:對於查詢操作量級較大、業務對要求查詢要求較高的推薦使用索引

多線程 多併發情況下 數據存儲相關問題

暫時提到 能想到的 辦法,後續還會補充

單獨以 原生 DB 操作方式爲例:

  1. 單例在操作多個數據庫的時候 聲明唯一的操作類。
  2. 使用 枷鎖 保證 併發情況下的 讀寫順序。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章