diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index ea13b6d0e..a9790b200 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -2,6 +2,7 @@ plugins {
     id("com.android.application")
     id("org.jetbrains.kotlin.android")
     id("kotlin-parcelize")
+    kotlin("plugin.serialization") version "1.8.21"
 }
 
 /**
@@ -164,6 +165,7 @@ dependencies {
     implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
     implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
     implementation("info.debatty:java-string-similarity:2.0.0")
+    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
 }
 
 fun getVersion(): String {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index b9f975e2b..a9653475f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -100,7 +100,6 @@ class GameAdapter(private val activity: AppCompatActivity) :
             return oldItem.gameId == newItem.gameId
         }
 
-        @SuppressLint("DiffUtilEquals")
         override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
             return oldItem == newItem
         }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
index c5cde9d05..2a17653b2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
@@ -5,9 +5,11 @@ package org.yuzu.yuzu_emu.model
 
 import android.os.Parcelable
 import kotlinx.parcelize.Parcelize
+import kotlinx.serialization.Serializable
 import java.util.HashSet
 
 @Parcelize
+@Serializable
 class Game(
     val title: String,
     val description: String,
@@ -19,6 +21,18 @@ class Game(
     val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
     val keyLastPlayedTime get() = "${gameId}_LastPlayed"
 
+    override fun equals(other: Any?): Boolean {
+        if (other !is Game)
+            return false
+
+        return title == other.title
+                && description == other.description
+                && regions == other.regions
+                && path == other.path
+                && gameId == other.gameId
+                && company == other.company
+    }
+
     companion object {
         val extensions: Set<String> = HashSet(
             listOf(".xci", ".nsp", ".nca", ".nro")
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index 1d0846b08..5a35b14c9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -7,11 +7,16 @@ import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
+import androidx.preference.PreferenceManager
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
 import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.utils.GameHelper
+import java.util.Locale
 
 class GamesViewModel : ViewModel() {
     private val _games = MutableLiveData<List<Game>>(emptyList())
@@ -33,9 +38,30 @@ class GamesViewModel : ViewModel() {
     val searchFocused: LiveData<Boolean> get() = _searchFocused
 
     init {
+        // Retrieve list of cached games
+        val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+            .getStringSet(GameHelper.KEY_GAMES, emptySet())
+        if (storedGames!!.isNotEmpty()) {
+            val deserializedGames = mutableSetOf<Game>()
+            storedGames.forEach {
+                deserializedGames.add(Json.decodeFromString(it))
+            }
+            setGames(deserializedGames.toList())
+        }
         reloadGames(false)
     }
 
+    fun setGames(games: List<Game>) {
+        val sortedList = games.sortedWith(
+            compareBy(
+                { it.title.lowercase(Locale.getDefault()) },
+                { it.path }
+            )
+        )
+
+        _games.postValue(sortedList)
+    }
+
     fun setSearchedGames(games: List<Game>) {
         _searchedGames.postValue(games)
     }
@@ -60,7 +86,7 @@ class GamesViewModel : ViewModel() {
         viewModelScope.launch {
             withContext(Dispatchers.IO) {
                 NativeLibrary.resetRomMetadata()
-                _games.postValue(GameHelper.getGames())
+                setGames(GameHelper.getGames())
                 _isReloading.postValue(false)
 
                 if (directoryChanged) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 07d0cd3d8..afabfb2b9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -20,10 +20,8 @@ import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.adapters.GameAdapter
 import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
 import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
-import org.yuzu.yuzu_emu.model.Game
 import org.yuzu.yuzu_emu.model.GamesViewModel
 import org.yuzu.yuzu_emu.model.HomeViewModel
-import java.util.Locale
 
 class GamesFragment : Fragment() {
     private var _binding: FragmentGamesBinding? = null
@@ -81,7 +79,7 @@ class GamesFragment : Fragment() {
             binding.swipeRefresh.isRefreshing = isReloading
         }
         gamesViewModel.games.observe(viewLifecycleOwner) {
-            submitGamesList(it)
+            (binding.gridGames.adapter as GameAdapter).submitList(it)
             if (it.isEmpty()) {
                 binding.noticeText.visibility = View.VISIBLE
             } else {
@@ -91,7 +89,7 @@ class GamesFragment : Fragment() {
 
         gamesViewModel.shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
             if (shouldSwapData) {
-                submitGamesList(gamesViewModel.games.value!!)
+                (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!)
                 gamesViewModel.setShouldSwapData(false)
             }
         }
@@ -115,11 +113,6 @@ class GamesFragment : Fragment() {
         }
     }
 
-    private fun submitGamesList(gameList: List<Game>) {
-        val sortedList = gameList.sortedBy { it.title.lowercase(Locale.getDefault()) }
-        (binding.gridGames.adapter as GameAdapter).submitList(sortedList)
-    }
-
     override fun onDestroyView() {
         super.onDestroyView()
         _binding = null
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index b2499168e..f8f275b41 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -25,7 +25,6 @@ import androidx.navigation.ui.setupWithNavController
 import androidx.preference.PreferenceManager
 import com.google.android.material.color.MaterialColors
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.elevation.ElevationOverlayProvider
 import com.google.android.material.navigation.NavigationBarView
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index 9dd43343f..ba6b5783e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -6,19 +6,22 @@ package org.yuzu.yuzu_emu.utils
 import android.content.SharedPreferences
 import android.net.Uri
 import androidx.preference.PreferenceManager
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
 import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.model.Game
 import java.util.*
-import kotlin.collections.ArrayList
 
 object GameHelper {
     const val KEY_GAME_PATH = "game_path"
+    const val KEY_GAMES = "Games"
 
     private lateinit var preferences: SharedPreferences
 
-    fun getGames(): ArrayList<Game> {
-        val games = ArrayList<Game>()
+    fun getGames(): List<Game> {
+        val games = mutableListOf<Game>()
         val context = YuzuApplication.appContext
         val gamesDir =
             PreferenceManager.getDefaultSharedPreferences(context).getString(KEY_GAME_PATH, "")
@@ -44,7 +47,17 @@ object GameHelper {
             }
         }
 
-        return games
+        // Cache list of games found on disk
+        val serializedGames = mutableSetOf<String>()
+        games.forEach {
+            serializedGames.add(Json.encodeToString(it))
+        }
+        preferences.edit()
+            .remove(KEY_GAMES)
+            .putStringSet(KEY_GAMES, serializedGames)
+            .apply()
+
+        return games.toList()
     }
 
     private fun getGame(filePath: String): Game {
diff --git a/src/android/app/src/main/res/layout/fragment_games.xml b/src/android/app/src/main/res/layout/fragment_games.xml
index 8b6d0b3b6..a0568668a 100644
--- a/src/android/app/src/main/res/layout/fragment_games.xml
+++ b/src/android/app/src/main/res/layout/fragment_games.xml
@@ -20,7 +20,7 @@
             android:gravity="center"
             android:padding="@dimen/spacing_large"
             android:text="@string/empty_gamelist"
-            tools:visibility="gone" />
+            android:visibility="gone" />
 
         <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/grid_games"