Optimize app startup speed

This commit is contained in:
ShirosakiMio 2024-09-09 10:33:58 +08:00
parent bc901558ef
commit 757444c6b4
10 changed files with 239 additions and 216 deletions

View File

@ -95,5 +95,11 @@ class AnimUtil {
this.interpolator = interpolator
return this
}
@JvmStatic
fun ObjectAnimator.startAfter(delayTime: Long) {
this.startDelay = delayTime
this.start()
}
}
}

View File

@ -0,0 +1,61 @@
package com.mio.util
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.util.Log
import android.util.Printer
class PerfUtil : Printer {
companion object {
@JvmStatic
fun install() {
Looper.getMainLooper().setMessageLogging(PerfUtil())
}
}
private val sampler = StackSampler(300)
private var isStarted = false
private var startTime = 0L
override fun println(x: String?) {
if (!isStarted) {
isStarted = true
startTime = System.currentTimeMillis()
sampler.startDump()
} else {
isStarted = false
val endTime = System.currentTimeMillis()
if (endTime - startTime > 300) {
Log.e("FCL PerfUtil", "block time = ${endTime - startTime}")
}
sampler.stopDump()
}
}
inner class StackSampler(val interval: Long) {
private val handler: Handler
private val runnable = Runnable {
val sb = StringBuilder()
Looper.getMainLooper().thread.stackTrace.forEach {
sb.append(it.toString())
sb.append("\n")
}
Log.e("FCL PerfUtil", sb.toString())
}
init {
val handlerThread = HandlerThread("")
handlerThread.start()
handler = Handler(handlerThread.looper)
}
fun startDump() {
handler.postDelayed(runnable, interval)
}
fun stopDump() {
handler.removeCallbacks(runnable)
}
}
}

View File

@ -8,6 +8,8 @@ import android.os.StrictMode;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.mio.util.PerfUtil;
import java.lang.ref.WeakReference;
public class FCLApplication extends Application implements Application.ActivityLifecycleCallbacks {
@ -18,6 +20,7 @@ public class FCLApplication extends Application implements Application.ActivityL
// enabledStrictMode();
super.onCreate();
this.registerActivityLifecycleCallbacks(this);
PerfUtil.install();
}
public static Activity getCurrentActivity() {

View File

@ -9,10 +9,14 @@ import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.BounceInterpolator
import android.widget.RelativeLayout
import androidx.appcompat.app.AlertDialog
import androidx.core.view.forEach
import androidx.databinding.DataBindingUtil
import com.mio.util.AnimUtil
import com.mio.util.AnimUtil.Companion.interpolator
import com.mio.util.AnimUtil.Companion.startAfter
import com.tungsten.fcl.R
import com.tungsten.fcl.databinding.ActivityMainBinding
import com.tungsten.fcl.game.JarExecutorHelper
@ -246,6 +250,11 @@ class MainActivity : FCLActivity(), OnSelectListener, View.OnClickListener {
_uiManager?.onResume()
}
override fun onStart() {
super.onStart()
playAnim()
}
override fun onSelect(view: FCLMenuView) {
refreshMenuView(view)
bind.apply {
@ -394,41 +403,45 @@ class MainActivity : FCLActivity(), OnSelectListener, View.OnClickListener {
version
)
) {
val game = Profiles.getSelectedProfile().repository.getGameVersion(version)
.orElse(getString(R.string.message_unknown))
val libraries = StringBuilder(game)
val analyzer = LibraryAnalyzer.analyze(
Profiles.getSelectedProfile().repository.getResolvedPreservingPatchesVersion(version)
)
for (mark in analyzer) {
val libraryId = mark.libraryId
val libraryVersion = mark.libraryVersion
if (libraryId == LibraryType.MINECRAFT.patchId) continue
if (AndroidUtils.hasStringId(
this,
"install_installer_" + libraryId.replace("-", "_")
)
) {
libraries.append(", ").append(
AndroidUtils.getLocalizedText(
Schedulers.defaultScheduler().execute {
val game = Profiles.getSelectedProfile().repository.getGameVersion(version)
.orElse(getString(R.string.message_unknown))
val libraries = StringBuilder(game)
val analyzer = LibraryAnalyzer.analyze(
Profiles.getSelectedProfile().repository.getResolvedPreservingPatchesVersion(version)
)
for (mark in analyzer) {
val libraryId = mark.libraryId
val libraryVersion = mark.libraryVersion
if (libraryId == LibraryType.MINECRAFT.patchId) continue
if (AndroidUtils.hasStringId(
this,
"install_installer_" + libraryId.replace("-", "_")
)
)
if (libraryVersion != null) libraries.append(": ").append(
libraryVersion.replace(
"(?i)$libraryId".toRegex(), ""
) {
libraries.append(", ").append(
AndroidUtils.getLocalizedText(
this,
"install_installer_" + libraryId.replace("-", "_")
)
)
if (libraryVersion != null) libraries.append(": ").append(
libraryVersion.replace(
"(?i)$libraryId".toRegex(), ""
)
)
}
}
Schedulers.androidUIThread().execute {
bind.versionName.text = version
bind.versionHint.text = libraries.toString()
bind.icon.setBackgroundDrawable(
Profiles.getSelectedProfile().repository.getVersionIconImage(
version
)
)
}
}
bind.versionName.text = version
bind.versionHint.text = libraries.toString()
bind.icon.setBackgroundDrawable(
Profiles.getSelectedProfile().repository.getVersionIconImage(
version
)
)
} else {
bind.versionName.text = getString(R.string.version_no_version)
bind.versionHint.text = getString(R.string.version_manage)
@ -454,4 +467,21 @@ class MainActivity : FCLActivity(), OnSelectListener, View.OnClickListener {
})
}
}
private fun playAnim() {
bind.apply {
AnimUtil.playTranslationX(
leftMenu,
ThemeEngine.getInstance().getTheme().animationSpeed * 30L,
-100f,
0f
).interpolator(BounceInterpolator()).start()
AnimUtil.playTranslationX(
rightMenu,
ThemeEngine.getInstance().getTheme().animationSpeed * 30L,
100f,
0f
).interpolator(BounceInterpolator()).start()
}
}
}

View File

@ -41,9 +41,6 @@ public class SplashActivity extends FCLActivity {
public ConstraintLayout background;
private EulaFragment eulaFragment;
private RuntimeFragment runtimeFragment;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -103,21 +100,15 @@ public class SplashActivity extends FCLActivity {
FCLPath.loadPaths(this);
transFile();
Logging.start(Paths.get(FCLPath.LOG_DIR));
initFragments();
start();
}
private void initFragments() {
eulaFragment = new EulaFragment();
runtimeFragment = new RuntimeFragment();
}
public void start() {
SharedPreferences sharedPreferences = getSharedPreferences("launcher", MODE_PRIVATE);
if (sharedPreferences.getBoolean("isFirstLaunch", true)) {
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.frag_start_anim, R.anim.frag_stop_anim).replace(R.id.fragment, eulaFragment).commit();
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.frag_start_anim, R.anim.frag_stop_anim).replace(R.id.fragment, EulaFragment.class, null).commit();
} else {
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.frag_start_anim, R.anim.frag_stop_anim).replace(R.id.fragment, runtimeFragment).commit();
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.frag_start_anim, R.anim.frag_stop_anim).replace(R.id.fragment, RuntimeFragment.class, null).commit();
}
}

View File

@ -1,6 +1,5 @@
package com.tungsten.fcl.fragment
import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
@ -12,11 +11,9 @@ import com.tungsten.fcl.activity.SplashActivity
import com.tungsten.fcl.databinding.FragmentRuntimeBinding
import com.tungsten.fcl.util.RuntimeUtils
import com.tungsten.fclauncher.utils.FCLPath
import com.tungsten.fclcore.task.Schedulers
import com.tungsten.fclcore.util.io.FileUtils
import com.tungsten.fcllibrary.component.FCLFragment
import com.tungsten.fcllibrary.component.view.FCLButton
import com.tungsten.fcllibrary.component.view.FCLImageView
import com.tungsten.fcllibrary.component.view.FCLProgressBar
import com.tungsten.fcllibrary.util.LocaleUtils
import java.io.File
import java.io.IOException
@ -41,10 +38,12 @@ class RuntimeFragment : FCLFragment(), View.OnClickListener {
): View? {
val view = inflater.inflate(R.layout.fragment_runtime, container, false)
bind = FragmentRuntimeBinding.bind(view)
initState()
refreshDrawables()
check()
bind.install.setOnClickListener(this)
Schedulers.defaultScheduler().execute {
initState()
refreshDrawables()
check()
}
return view
}

View File

@ -19,6 +19,7 @@ package com.tungsten.fcl.setting;
import com.tungsten.fcl.game.FCLCacheRepository;
import com.tungsten.fclauncher.utils.FCLPath;
import com.tungsten.fclcore.task.Schedulers;
import com.tungsten.fclcore.util.CacheRepository;
public final class Settings {
@ -43,7 +44,7 @@ public final class Settings {
DownloadProviders.init();
Accounts.init();
Profiles.init();
Controllers.init();
Schedulers.defaultScheduler().execute(Controllers::init);
AuthlibInjectorServers.init();
CacheRepository.setInstance(FCLCacheRepository.REPOSITORY);

View File

@ -1,169 +0,0 @@
package com.tungsten.fcl.ui;
import android.content.Context;
import com.tungsten.fcl.R;
import com.tungsten.fcl.ui.account.AccountUI;
import com.tungsten.fcl.ui.controller.ControllerUI;
import com.tungsten.fcl.ui.download.DownloadUI;
import com.tungsten.fcl.ui.manage.ManageUI;
import com.tungsten.fcl.ui.main.MainUI;
import com.tungsten.fcl.ui.multiplayer.MultiplayerUI;
import com.tungsten.fcl.ui.setting.SettingUI;
import com.tungsten.fcl.ui.version.VersionUI;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fcllibrary.component.ui.FCLBaseUI;
import com.tungsten.fcllibrary.component.ui.FCLCommonUI;
import com.tungsten.fcllibrary.component.view.FCLUILayout;
import java.util.logging.Level;
public class UIManager {
private static UIManager instance;
public static UIManager getInstance() {
if (instance == null) {
throw new IllegalStateException("UIManager not initialized!");
}
return instance;
}
private final Context context;
private final FCLUILayout parent;
private MainUI mainUI;
private AccountUI accountUI;
private VersionUI versionUI;
private ManageUI manageUI;
private DownloadUI downloadUI;
private ControllerUI controllerUI;
private MultiplayerUI multiplayerUI;
private SettingUI settingUI;
private FCLBaseUI[] allUIs;
private FCLBaseUI currentUI;
private int loadedUI = 0;
public UIManager(Context context, FCLUILayout parent) {
this.context = context;
this.parent = parent;
}
public void init(UIListener listener) {
if (instance != null) {
Logging.LOG.log(Level.WARNING, "UIManager already initialized!");
return;
}
instance = this;
mainUI = new MainUI(context, parent, R.layout.ui_main);
accountUI = new AccountUI(context, parent, R.layout.ui_account);
versionUI = new VersionUI(context, parent, R.layout.ui_version);
manageUI = new ManageUI(context, parent, R.layout.ui_manage);
downloadUI = new DownloadUI(context, parent, R.layout.ui_download);
controllerUI = new ControllerUI(context, parent, R.layout.ui_controller);
multiplayerUI = new MultiplayerUI(context, parent, R.layout.ui_multiplayer);
settingUI = new SettingUI(context, parent, R.layout.ui_setting);
allUIs = new FCLBaseUI[] {
mainUI,
accountUI,
versionUI,
manageUI,
downloadUI,
controllerUI,
multiplayerUI,
settingUI
};
for (FCLBaseUI ui : allUIs) {
((FCLCommonUI) ui).addLoadingCallback(() -> {
loadedUI++;
if (loadedUI == allUIs.length) {
listener.onLoad();
}
});
}
}
public Context getContext() {
return context;
}
public FCLUILayout getParent() {
return parent;
}
public MainUI getMainUI() {
return mainUI;
}
public AccountUI getAccountUI() {
return accountUI;
}
public VersionUI getVersionUI() {
return versionUI;
}
public ManageUI getManageUI() {
return manageUI;
}
public DownloadUI getDownloadUI() {
return downloadUI;
}
public ControllerUI getControllerUI() {
return controllerUI;
}
public MultiplayerUI getMultiplayerUI() {
return multiplayerUI;
}
public SettingUI getSettingUI() {
return settingUI;
}
public FCLBaseUI getCurrentUI() {
return currentUI;
}
public void switchUI(FCLBaseUI ui) {
for (FCLBaseUI baseUI : allUIs) {
if (ui == baseUI) {
if (currentUI != null) {
currentUI.onStop();
}
ui.onStart();
currentUI = ui;
break;
}
}
}
public void registerDefaultBackEvent(Runnable runnable) {
FCLBaseUI.setDefaultBackEvent(runnable);
}
public void onBackPressed() {
if (currentUI != null) {
currentUI.onBackPressed();
}
}
public void onPause() {
for (FCLBaseUI baseUI : allUIs) {
baseUI.onPause();
}
}
public void onResume() {
for (FCLBaseUI baseUI : allUIs) {
baseUI.onResume();
}
}
}

View File

@ -0,0 +1,98 @@
package com.tungsten.fcl.ui
import android.content.Context
import com.tungsten.fcl.R
import com.tungsten.fcl.ui.account.AccountUI
import com.tungsten.fcl.ui.controller.ControllerUI
import com.tungsten.fcl.ui.download.DownloadUI
import com.tungsten.fcl.ui.main.MainUI
import com.tungsten.fcl.ui.manage.ManageUI
import com.tungsten.fcl.ui.multiplayer.MultiplayerUI
import com.tungsten.fcl.ui.setting.SettingUI
import com.tungsten.fcl.ui.version.VersionUI
import com.tungsten.fclcore.util.Logging
import com.tungsten.fcllibrary.component.ui.FCLBaseUI
import com.tungsten.fcllibrary.component.ui.FCLCommonUI
import com.tungsten.fcllibrary.component.view.FCLUILayout
import java.util.logging.Level
class UIManager(val context: Context, val parent: FCLUILayout) {
companion object {
@JvmStatic
lateinit var instance: UIManager
}
private var initialized = false
lateinit var mainUI: MainUI
val accountUI: AccountUI by lazy { AccountUI(context, parent, R.layout.ui_account) }
val versionUI: VersionUI by lazy { VersionUI(context, parent, R.layout.ui_version) }
val manageUI: ManageUI by lazy { ManageUI(context, parent, R.layout.ui_manage) }
val downloadUI: DownloadUI by lazy { DownloadUI(context, parent, R.layout.ui_download) }
val controllerUI: ControllerUI by lazy { ControllerUI(context, parent, R.layout.ui_controller) }
val multiplayerUI: MultiplayerUI by lazy {
MultiplayerUI(
context,
parent,
R.layout.ui_multiplayer
)
}
val settingUI: SettingUI by lazy { SettingUI(context, parent, R.layout.ui_setting) }
private val allUIList = mutableListOf<FCLBaseUI>()
var currentUI: FCLBaseUI? = null
fun init(listener: UIListener) {
if (initialized) {
Logging.LOG.log(Level.WARNING, "UIManager already initialized!")
return
}
instance = this
mainUI = MainUI(context, parent, R.layout.ui_main)
allUIList.add(mainUI)
mainUI.addLoadingCallback {
listener.onLoad()
}
}
fun switchUI(ui: FCLCommonUI) {
var isFirstAdd = false
if (!allUIList.contains(ui)) {
isFirstAdd = true
allUIList.add(ui)
}
for (baseUI in allUIList) {
if (ui === baseUI) {
currentUI?.onStop()
if (isFirstAdd) {
ui.addLoadingCallback {
ui.onStart()
}
} else {
ui.onStart()
}
currentUI = ui
break
}
}
}
fun registerDefaultBackEvent(runnable: Runnable?) {
FCLBaseUI.setDefaultBackEvent(runnable)
}
fun onBackPressed() {
currentUI?.onBackPressed()
}
fun onPause() {
for (baseUI in allUIList) {
baseUI.onPause()
}
}
fun onResume() {
for (baseUI in allUIList) {
baseUI.onResume()
}
}
}

View File

@ -68,6 +68,9 @@ public class ManageUI extends FCLMultiPageUI implements TabLayout.OnTabSelectedL
return;
}
if (pageManager == null) {
pageManager = new ManagePageManager(getContext(), container, ManagePageManager.PAGE_ID_MANAGE_MANAGE, null);
}
loadVersion(getVersion(), getProfile());
}