فهرست منبع

feat: 应用启动优化 (#41)

详见
https://fikvmzrrhfl.feishu.cn/wiki/SfumwJ9Eqi5Obzky6TXc9HUZnoc?from=from_copylink
DoggyZhang 10 ماه پیش
والد
کامیت
b24aa7ea80
57فایلهای تغییر یافته به همراه1329 افزوده شده و 579 حذف شده
  1. 8 9
      app/build.gradle
  2. 1 0
      app/dependencies/releaseRuntimeClasspath.txt
  3. 4 10
      app/src/main/AndroidManifest.xml
  4. 200 87
      app/src/main/java/com/adealink/weparty/App.kt
  5. 39 31
      app/src/main/java/com/adealink/weparty/MainActivity.kt
  6. 1 5
      app/src/main/java/com/adealink/weparty/Routers.kt
  7. 25 5
      app/src/main/java/com/adealink/weparty/commonui/BaseFragment.kt
  8. 18 0
      app/src/main/java/com/adealink/weparty/debug/DebugExt.kt
  9. 1 1
      app/src/main/java/com/adealink/weparty/deeplink/DeepLinkConfig.kt
  10. 29 6
      app/src/main/java/com/adealink/weparty/module/call/CallModule.kt
  11. 2 4
      app/src/main/java/com/adealink/weparty/module/call/ICallService.kt
  12. 2 3
      app/src/main/java/com/adealink/weparty/module/message/IMessageService.kt
  13. 29 6
      app/src/main/java/com/adealink/weparty/module/message/MessageModule.kt
  14. 3 4
      app/src/main/java/com/adealink/weparty/module/task/RoomTaskMonitor.kt
  15. 2 1
      app/src/main/java/com/adealink/weparty/module/task/UserTaskManager.kt
  16. 12 8
      app/src/main/java/com/adealink/weparty/module/userlist/HomeUserListFragment.kt
  17. 2 0
      app/src/main/java/com/adealink/weparty/module/userlist/fragment/HomeNewUserListFragment.kt
  18. 2 0
      app/src/main/java/com/adealink/weparty/module/userlist/fragment/HomeOnLineUserListFragment.kt
  19. 0 2
      app/src/main/java/com/adealink/weparty/sound/bg/BackgroundMusicManager.kt
  20. 0 134
      app/src/main/java/com/adealink/weparty/splash/SplashActivity.kt
  21. 160 0
      app/src/main/java/com/adealink/weparty/ui/MainStartUpFragment.kt
  22. 16 163
      app/src/main/java/com/adealink/weparty/ui/home/BaseHomeFragment.kt
  23. 136 0
      app/src/main/java/com/adealink/weparty/ui/splash/SplashFragment.kt
  24. 6 6
      app/src/main/java/com/adealink/weparty/ui/tab/HomeTab.kt
  25. 0 13
      app/src/main/res/layout/activity_main.xml
  26. 1 8
      app/src/main/res/layout/fragment_home_user_list.xml
  27. 6 0
      app/src/main/res/layout/fragment_splash.xml
  28. 0 0
      app/src/main/res/layout/fragment_splash_banner.xml
  29. 1 4
      app/src/main/res/values/themes.xml
  30. 1 0
      frame/startup/.gitignore
  31. 62 0
      frame/startup/build.gradle
  32. 0 0
      frame/startup/consumer-rules.pro
  33. 21 0
      frame/startup/proguard-rules.pro
  34. 4 0
      frame/startup/src/main/AndroidManifest.xml
  35. 25 0
      frame/startup/src/main/java/com/adealink/frame/startup/IStartUpTask.kt
  36. 3 0
      frame/startup/src/main/java/com/adealink/frame/startup/Tags.kt
  37. 21 0
      frame/startup/src/main/java/com/adealink/frame/startup/base/TaskInterface.kt
  38. 192 0
      frame/startup/src/main/java/com/adealink/frame/startup/dispatcher/AppStartTaskDispatcher.kt
  39. 62 0
      frame/startup/src/main/java/com/adealink/frame/startup/executor/TaskExecutorManager.kt
  40. 22 0
      frame/startup/src/main/java/com/adealink/frame/startup/runnable/AppStartTaskRunnable.kt
  41. 50 0
      frame/startup/src/main/java/com/adealink/frame/startup/task/AppStartTask.kt
  42. 68 0
      frame/startup/src/main/java/com/adealink/frame/startup/util/AppStartTaskSortUtil.kt
  43. 3 0
      frame/startup/src/main/java/com/adealink/frame/startup/util/model/TaskSortModel.kt
  44. 0 6
      module/call/src/main/AndroidManifest.xml
  45. 15 3
      module/call/src/main/java/com/adealink/weparty/call/CallServiceImpl.kt
  46. 5 3
      module/call/src/main/java/com/adealink/weparty/call/manager/CallLoginManager.kt
  47. 0 1
      module/call/src/main/java/com/adealink/weparty/call/manager/CallManager.kt
  48. 6 5
      module/call/src/main/java/com/adealink/weparty/call/view/floatview/calling/CallingFloatView.kt
  49. 20 45
      module/call/src/main/java/com/tencent/qcloud/tuikit/tuicallkit/internal/ServiceInitializer.kt
  50. 3 0
      module/message/src/main/java/com/adealink/weparty/message/MessageHomeFragment.kt
  51. 16 2
      module/message/src/main/java/com/adealink/weparty/message/MessageServiceImpl.kt
  52. 2 2
      module/message/src/main/java/com/adealink/weparty/message/manager/IMessageManager.kt
  53. 13 2
      module/message/src/main/java/com/adealink/weparty/message/manager/MessageManager.kt
  54. 2 0
      module/moment/src/main/java/com/adealink/weparty/moment/usermoment/MomentFragment.kt
  55. 3 0
      module/profile/src/main/java/com/adealink/weparty/profile/me/MeFragment.kt
  56. 3 0
      module/room/src/main/java/com/adealink/weparty/room/roomlist/RoomListFragment.kt
  57. 1 0
      settings.gradle

+ 8 - 9
app/build.gradle

@@ -60,11 +60,8 @@ android {
         multiDexEnabled true
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 //        resConfigs "zh", "zh_TW", "en", "ar", "hi", "tr", "th", "bn", "vi", "ur", "id", "ms", "tl", "pt", "es", "ru", "kk", "ky", "tk", "tg", "uz"
-        resConfigs "zh","en","ar"
-        buildConfigField("boolean", "OFFICIAL", project.OFFICIAL)
-        buildConfigField("boolean", "IS_RELEASE", hookConfigByLocalProperties("IS_RELEASE", IS_RELEASE))
-        buildConfigField("String", "HTTPS_WEB_HOST", '"https://web.yoki.chat"')
-        buildConfigField("String", "AUTH_APPLE_PATH", '"/web/yoki-auth/apple"')
+        resConfigs "zh", "en", "ar"
+
         ndk {
             abiFilters "armeabi-v7a"
             abiFilters "arm64-v8a"
@@ -78,8 +75,6 @@ android {
         }
 
 
-
-
         def jvc = project.hasProperty('JKS_VERSION_CODE') ? project.JKS_VERSION_CODE : ""
         def jvn = project.hasProperty('JKS_VERSION_NAME') ? project.JKS_VERSION_NAME : ""
         def localVC = hookConfigByLocalProperties("VERSION_CODE", VERSION_CODE)
@@ -103,6 +98,8 @@ android {
 
         buildConfigField("boolean", "OFFICIAL", project.OFFICIAL)
         buildConfigField("boolean", "IS_RELEASE", hookConfigByLocalProperties("IS_RELEASE", IS_RELEASE))
+        buildConfigField("boolean", "ENABLE_TRACE", hookConfigByLocalProperties("ENABLE_TRACE", ENABLE_TRACE))
+
         buildConfigField("String", "HTTPS_WEB_HOST", '"https://web.yoki.chat"')
         buildConfigField("String", "AUTH_APPLE_PATH", '"/web/yoki-auth/apple"')
         buildConfigField("String", "AGORA_APP_ID", '"a5233b5e42534b77bbabf2ca8ac95215"')
@@ -140,7 +137,6 @@ android {
     }
 
 
-
     buildTypes {
         debug {
             minifyEnabled false
@@ -162,6 +158,7 @@ android {
             }
         }
     }
+
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_17
         targetCompatibility JavaVersion.VERSION_17
@@ -427,6 +424,8 @@ dependencies {
 
     api project(":frame:room")
     api project(":frame:imkit")
+    api project(':frame:startup')
+
     api libs.frame.locale
     api libs.frame.push
     api libs.frame.media
@@ -442,7 +441,7 @@ dependencies {
 
     implementation libs.toolargetool
 
-    implementation (libs.rangeseekbar)
+    implementation(libs.rangeseekbar)
 
 //    debugApi "com.tuzhenlei:crashhandler:1.0.1"
 //    debugApi 'cat.ereza:customactivityoncrash:2.3.0'

+ 1 - 0
app/dependencies/releaseRuntimeClasspath.txt

@@ -258,6 +258,7 @@ com.wenext.android:frame-statistics:5.1.5
 com.wenext.android:frame-storage:5.1.5-yoki
 com.wenext.android:frame-tceffect:5.1.6-yoki-beta
 com.wenext.android:frame-tcturing:5.1.4
+com.wenext.android:frame-trace-api:1.0.0
 com.wenext.android:frame-util:5.1.5-yoki-beta-2
 com.wenext.android:frame-zero:5.1.4
 com.wenext.android:retrofit:5.1.4

+ 4 - 10
app/src/main/AndroidManifest.xml

@@ -87,9 +87,11 @@
         </receiver>
 
         <activity
-            android:name=".splash.SplashActivity"
+            android:name=".MainActivity"
             android:exported="true"
-            android:theme="@style/SplashActivityTheme">
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait"
+            android:theme="@style/MainActivityTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -98,14 +100,6 @@
                 <action android:name="${applicationId}.GO_MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
-        </activity>
-
-        <activity
-            android:name=".MainActivity"
-            android:exported="true"
-            android:launchMode="singleTask"
-            android:screenOrientation="portrait"
-            android:theme="@style/MainActivityTheme">
             <intent-filter>
                 <action android:name="DISPATCH_ROUTER" />
                 <category android:name="android.intent.category.DEFAULT" />

+ 200 - 87
app/src/main/java/com/adealink/weparty/App.kt

@@ -42,6 +42,8 @@ import com.adealink.frame.router.manager.deeplinkRouterManager
 import com.adealink.frame.security.createSecurityService
 import com.adealink.frame.share.initShareManager
 import com.adealink.frame.sound.createSoundPlayer
+import com.adealink.frame.startup.dispatcher.AppStartTaskDispatcher
+import com.adealink.frame.startup.task.AppStartTask
 import com.adealink.frame.statistics.report.initStat
 import com.adealink.frame.storageService
 import com.adealink.frame.util.ActivityLifecycleCallbacksExt
@@ -81,7 +83,6 @@ import com.adealink.weparty.media.MediaManager
 import com.adealink.weparty.module.attribution.AttributionModule
 import com.adealink.weparty.module.call.CallModule
 import com.adealink.weparty.module.message.MessageModule
-import com.adealink.weparty.module.operation.OperationModule
 import com.adealink.weparty.network.INetworkManager
 import com.adealink.weparty.network.NetworkConfig
 import com.adealink.weparty.network.NetworkManager
@@ -172,77 +173,199 @@ class App : SplitCompatApplication(), ActivityLifecycleCallbacksExt {
         logTime(TAG_TIME_APP_START, "app attachBaseContext end")
     }
 
-    override fun onCreate() {
-        super.onCreate()
-        logTime(TAG_TIME_APP_START, "app onCreate start")
-        initAPMService(APMConfig())
-        securityService.init()
-        initXLog()
-        initStorageService(StorageConfig())
-        installCrashProtector(CrashConfig())
-        initJsonConfig(JsonConfig())
-        initShareManager(ShareConfig())
-        AAB.init(AABConfig())
-        initStat { StatConfig() }
-        initCrash(this) //依赖xlog,stat,crash protector
-        googleService.init()
-        initRouter()
-        initLanguage()
-        initNetwork()
-        initIM()
-        registerNetworkReceiver()
-        imageService.init(ImageConfig())
-        initOssService { ossService }
-        initJSBridgeManager { jsBridgeManager }
-        initWebResourceLoader(WebResourceConfig())
-        initDownloadService { downloadService }
-        initEffect { effect }
-        WEUI.init(this)
-        WindowManagerProxy.init()
-        initTCEffect()
-        initSVGA()
-        initFirebaseAnalytics()
-        initDeeplinkConfig()
-        initPush()
-        initFB()
-        initPayerMax()
-        initAPM(this)
-        networkService.fetchNetAntiBanConfig()
-        deviceIdService.updateDeviceId()
-        TCTuringManager.init(TCTuringConfig())
-        initAttribution()
-        initAutoSize()
-        initDebugKit()
-        soundPlayer.prepare()
-        initTooLargeTool()
-        initCall()
-        urlConfigService.init()
-        OperationModule.init()
-        logTime(TAG_TIME_APP_START, "app onCreate end")
-    }
-
-    private fun initTooLargeTool() {
-        TooLargeTool.startLogging(this, DefaultFormatter(), object : Logger {
-            override fun log(msg: String) {
-                Log.i(TAG_TOO_LARGE_TOOL, msg)
-            }
+    abstract inner class MainThreadStartTask : AppStartTask() {
+        override val isRunOnMainThread: Boolean = true
+    }
 
-            override fun logException(e: Exception) {
-                Log.e(TAG_TOO_LARGE_TOOL, "logException", e)
-            }
-        })
+    /**
+     * 所有模块最基础启动任务,必须执行完该任务才能执行其他启动任务
+     * - 日志
+     * - 组件/模块配置
+     */
+    inner class BaseStartTask : MainThreadStartTask() {
+
+        override fun run() {
+            initXLog()
+            initAPMService(APMConfig())
+            initAPM(this@App)
+            initStat { StatConfig() }
+            installCrashProtector(CrashConfig())
+            initCrash(this@App) //依赖xlog,stat,crash protector
+        }
+    }
+
+    inner class SecondStartTask : MainThreadStartTask() {
+        override fun run() {
+            initJsonConfig(JsonConfig())
+            AAB.init(AABConfig())
+        }
+    }
+
+
+    abstract inner class SubStartTask : AppStartTask() {
+        override val isRunOnMainThread: Boolean = false
+
+        override fun needWait(): Boolean {
+            return true
+        }
+
+        override val dependsTaskList: List<Class<out AppStartTask>>
+            get() = listOf(BaseStartTask::class.java)
+    }
+
+
+    /**
+     * 轻量任务1(每一项任务耗时控制在100ms内)
+     * (耗时可通过trace导出)
+     */
+    inner class LightTask1 : SubStartTask() {
+        override fun run() {
+            languageManager.init()
+            Router.config = RouterConfig()
+            registerNetworkReceiver()
+            securityService.init()
+            initStorageService(StorageConfig())
+            googleService.init()
+            initShareManager(ShareConfig())
+            initOssService { ossService }
+            initJSBridgeManager { jsBridgeManager }
+            initWebResourceLoader(WebResourceConfig())
+        }
     }
 
-    private fun initRouter() {
-        Router.config = RouterConfig()
+    inner class LightTask2 : SubStartTask() {
+        override fun run() {
+            initDownloadService { downloadService }
+            initEffect { effect }
+            WEUI.init(this@App)
+            WindowManagerProxy.init()
+            initTCEffect()
+        }
     }
 
-    private fun initNetwork() {
-        networkManager.init(this)
+    inner class LightTask3 : SubStartTask() {
+        override fun run() {
+            initSVGA()
+            initFirebaseAnalytics()
+            initDeeplinkConfig()
+            initPush()
+            initFB()
+            initPayerMax()
+            networkService.fetchNetAntiBanConfig()
+            deviceIdService.updateDeviceId()
+            initAutoSize()
+            soundPlayer.prepare()
+            urlConfigService.init()
+        }
     }
 
-    private fun initLanguage() {
-        languageManager.init()
+
+    /**
+     * 重任务(任务耗时>30ms)
+     */
+    inner class InitNetwork : SubStartTask() {
+        override fun run() {
+            networkManager.init(this@App)
+        }
+    }
+
+    inner class InitIM : SubStartTask() {
+        override fun run() {
+            imService.init(
+                this@App,
+                InitOption.Builder()
+                    .setAreaCode(
+                        when (AppBaseInfo.isProdEnv) {
+                            true -> InitOption.AreaCode.SG
+                            else -> InitOption.AreaCode.BJ
+                        }
+                    )
+                    .setMainProcess(true)
+                    .enablePush(false)
+                    .enableSyncEmptyTopConversation(true)
+                    .build()
+            )
+            MessageModule.appOnCreateMainTask(this@App)
+        }
+    }
+
+    //1v1呼叫主线程
+    inner class InitCall1 : SubStartTask() {
+        override val isRunOnMainThread: Boolean = true
+
+        override fun run() {
+            CallModule.appOnCreateMainTask(this@App)
+        }
+    }
+
+    //1v1呼叫子线程
+    inner class InitCall2 : SubStartTask() {
+        override fun run() {
+            CallModule.appOnCreateSubTask(this@App)
+        }
+
+        override val dependsTaskList: List<Class<out AppStartTask>>
+            get() = listOf(
+                *super.dependsTaskList.toTypedArray(),
+                InitCall1::class.java
+            )
+    }
+
+    inner class InitImageService : SubStartTask() {
+        override fun run() {
+            imageService.init(ImageConfig())
+        }
+    }
+
+    inner class InitTCTuringManager : SubStartTask() {
+        override fun run() {
+            TCTuringManager.init(TCTuringConfig())
+        }
+    }
+
+    inner class InitAttribution : SubStartTask() {
+        override fun run() {
+            initAttribution()
+        }
+    }
+
+    inner class InitDebugKit : SubStartTask() {
+        override val isRunOnMainThread: Boolean = true
+
+        override fun run() {
+            initDebugKit()
+        }
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        val appOnCreateTs = SystemClock.elapsedRealtime()
+        android.util.Log.d(TAG_TIME_APP_START, "app onCreate start")
+
+        AppStartTaskDispatcher.create().apply {
+            //基础任务
+            addAppStartTask(BaseStartTask())
+            addAppStartTask(SecondStartTask())
+            //轻量任务
+            addAppStartTask(LightTask1())
+            addAppStartTask(LightTask2())
+            addAppStartTask(LightTask3())
+            //重量任务
+            addAppStartTask(InitNetwork())
+            addAppStartTask(InitImageService())
+            addAppStartTask(InitAttribution())
+            addAppStartTask(InitTCTuringManager())
+            addAppStartTask(InitIM())
+            addAppStartTask(InitCall1())
+            addAppStartTask(InitCall2())
+            //其他任务
+            if (!AppBaseInfo.isRelease) {
+                addAppStartTask(InitDebugKit())
+            }
+
+        }.start().await()
+
+        android.util.Log.d(TAG_TIME_APP_START, "app onCreate end, cost:${SystemClock.elapsedRealtime() - appOnCreateTs}")
     }
 
     private fun initXLog() {
@@ -362,7 +485,10 @@ class App : SplitCompatApplication(), ActivityLifecycleCallbacksExt {
     }
 
     private fun initDebugKit() {
-        if (BuildConfig.IS_RELEASE.not() && DebugPrefs.showPerformanceFloatView) {
+        if (AppBaseInfo.isRelease) {
+            return
+        }
+        if (DebugPrefs.showPerformanceFloatView) {
             floatKitManager.install(this, true)
             floatKitManager.onMainIconDoubleClick = {
                 AppUtil.currentActivity?.let {
@@ -371,28 +497,15 @@ class App : SplitCompatApplication(), ActivityLifecycleCallbacksExt {
             }
             performanceDataController.start()
         }
-    }
-
-    private fun initIM() {
-        imService.init(
-            this,
-            InitOption.Builder()
-                .setAreaCode(
-                    when (AppBaseInfo.isProdEnv) {
-                        true -> InitOption.AreaCode.SG
-                        else -> InitOption.AreaCode.BJ
-                    }
-                )
-                .setMainProcess(true)
-                .enablePush(false)
-                .enableSyncEmptyTopConversation(true)
-                .build()
-        )
-        MessageModule.init(this)
-    }
+        TooLargeTool.startLogging(this, DefaultFormatter(), object : Logger {
+            override fun log(msg: String) {
+                Log.i(TAG_TOO_LARGE_TOOL, msg)
+            }
 
-    private fun initCall() {
-        CallModule.init(this)
+            override fun logException(e: Exception) {
+                Log.e(TAG_TOO_LARGE_TOOL, "logException", e)
+            }
+        })
     }
 
     override fun onTerminate() {

+ 39 - 31
app/src/main/java/com/adealink/weparty/MainActivity.kt

@@ -4,36 +4,30 @@ import android.content.Intent
 import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import android.view.WindowManager
+import androidx.fragment.app.FragmentContainerView
 import com.adealink.frame.aab.util.getCompatColor
 import com.adealink.frame.log.Log
 import com.adealink.frame.push.data.KEY_PUSH_DEEPLINK
 import com.adealink.frame.push.data.KEY_PUSH_ID
 import com.adealink.frame.push.data.KEY_PUSH_MESSAGE_TYPE
-import com.adealink.frame.push.manager.pushService
 import com.adealink.frame.router.Router
 import com.adealink.frame.router.annotation.BindExtra
 import com.adealink.frame.router.annotation.RouterUri
 import com.adealink.frame.router.manager.deeplinkRouterManager
-import com.adealink.frame.statistics.BaseStatEvent
 import com.adealink.weparty.commonui.BaseActivity
 import com.adealink.weparty.module.account.Account
 import com.adealink.weparty.module.account.AccountModule
-import com.adealink.weparty.module.attribution.AttributionModule
-import com.adealink.weparty.module.profile.ProfileModule
-import com.adealink.weparty.module.share.ShareModule
-import com.adealink.weparty.push.NotificationUtil
 import com.adealink.weparty.push.PushStatEvent
 import com.adealink.weparty.stat.manager.serveReportManager
 import com.adealink.weparty.stat.reportAppOpenIfNeed
 import com.adealink.weparty.stat.reportEnterHome
 import com.adealink.weparty.storage.AppPref
+import com.adealink.weparty.ui.MainStartUpFragment
 import com.adealink.weparty.ui.home.HomeFragment
 import com.adealink.weparty.ui.home.util.HomeLocalService
+import com.adealink.weparty.ui.splash.SplashFragment
 import com.adealink.weparty.update.updateManager
 import com.adealink.weparty.util.goLocalLinkPage
-import com.google.firebase.crashlytics.ktx.crashlytics
-import com.google.firebase.crashlytics.ktx.setCustomKeys
-import com.google.firebase.ktx.Firebase
 import com.qmuiteam.qmui.widget.util.QMUIStatusBarHelper
 
 @RouterUri(path = [AppModule.Main.PATH], desc = "首页")
@@ -41,6 +35,9 @@ class MainActivity : BaseActivity() {
 
     companion object {
         private const val TAG = "MainActivity"
+
+        //显示splash页面
+        private var showSplash = true
     }
 
     enum class DispatchFrom {
@@ -61,6 +58,7 @@ class MainActivity : BaseActivity() {
 
     override fun onBeforeCreate() {
         super.onBeforeCreate()
+        android.util.Log.d("MainActivity", "onCreate")
         Router.bind(this)
         HomeLocalService.homeTabVisitCount++
     }
@@ -82,7 +80,9 @@ class MainActivity : BaseActivity() {
         window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
 
         QMUIStatusBarHelper.setStatusBarLightMode(this)
-        setContentView(R.layout.activity_main)
+        setContentView(FragmentContainerView(this).apply {
+            id = R.id.fl_content
+        })
         dispatch(intent, DispatchFrom.ON_CREATE)
     }
 
@@ -90,19 +90,6 @@ class MainActivity : BaseActivity() {
         super.initOthers()
         reportAppOpenIfNeed(false)
         serveReportManager.reportFirebaseAppInstanceId()
-        if (AccountModule.isLogin()) {
-            AccountModule.checkRemoteVirtualAppConfig()
-            AttributionModule.reportAttributionData()
-            BaseStatEvent.setUserId(ProfileModule.getMyUid())
-            ShareModule.reportUserType()
-            pushService.getPushTokenAndReport()
-
-            Firebase.crashlytics.setCustomKeys {
-                key("uid", AccountModule.uid)
-                key("BuildType", BuildConfig.BUILD_TYPE)
-                key("IS_RELEASE", BuildConfig.IS_RELEASE)
-            }
-        }
     }
 
     override fun onResume() {
@@ -120,6 +107,11 @@ class MainActivity : BaseActivity() {
     }
 
     private fun dispatch(intent: Intent?, from: DispatchFrom) {
+        if (showSplash) {
+            showSplash = false
+            inflateSplashFragment()
+            return
+        }
         when {
             needGoLoginActivity() -> {
                 Router.build(this, Account.Login.PATH)
@@ -137,13 +129,11 @@ class MainActivity : BaseActivity() {
                 return
             }
         }
-        initView()
-        if (from == DispatchFrom.ON_CREATE) {
-            NotificationUtil.requestPostNotificationPermission(this)
-        }
+        inflateHomeFragment()
         intent ?: return
         val pushDeeplink = intent.getStringExtra(KEY_PUSH_DEEPLINK)
         if (!pushDeeplink.isNullOrEmpty()) {
+            Log.d(TAG, "dispatchPath, pushDeeplink:$pushDeeplink")
             PushStatEvent(PushStatEvent.Action.CLICK).apply {
                 type to (intent.extras?.getString(KEY_PUSH_MESSAGE_TYPE) ?: "1")
                 pushId to (intent.extras?.getString(KEY_PUSH_ID) ?: "1")
@@ -155,18 +145,22 @@ class MainActivity : BaseActivity() {
 
         val dispatchPath = intent.getStringExtra(deeplinkRouterManager.getDispatchPathKey())
         if (!dispatchPath.isNullOrEmpty()) {
-            Log.d(TAG, "dispatchPath:$dispatchPath")
+            Log.d(TAG, "dispatchPath, path:$dispatchPath")
             if (dispatchPath.startsWith(Router.getDeepLink(AppModule.Main.PATH))) {
                 return
             }
 
             dispatchRouter = true
             Router.build(this, dispatchPath).putExtras(intent).start()
+            return
         }
-    }
 
-    private fun initView() {
-        inflateHomeFragment()
+        val splashBannerUrl = intent.getStringExtra(AppModule.Main.EXTRA_SPLASH_JUMP_URL)
+        if (!splashBannerUrl.isNullOrEmpty()) {
+            Log.d(TAG, "dispatchPath, splashBannerUrl:$splashBannerUrl")
+            goLocalLinkPage(this, splashBannerUrl)
+            return
+        }
     }
 
     private fun needGoLoginActivity(): Boolean {
@@ -177,6 +171,18 @@ class MainActivity : BaseActivity() {
         return AppPref.needRegister
     }
 
+    private fun inflateSplashFragment() {
+        var splashFragment =
+            supportFragmentManager.findFragmentByTag(SplashFragment.TAG) as? SplashFragment
+        if (splashFragment?.isAdded == true) {
+            return
+        }
+        splashFragment = splashFragment ?: SplashFragment()
+        supportFragmentManager.beginTransaction()
+            .replace(R.id.fl_content, splashFragment, SplashFragment.TAG)
+            .commit()
+    }
+
     private fun inflateHomeFragment() {
         var homeFragment =
             supportFragmentManager.findFragmentByTag(HomeFragment.TAG) as? HomeFragment
@@ -192,6 +198,8 @@ class MainActivity : BaseActivity() {
             .replace(R.id.fl_content, homeFragment, HomeFragment.TAG)
             .commit()
         reportEnterHome()
+
+        MainStartUpFragment.inject(this)
     }
 
 }

+ 1 - 5
app/src/main/java/com/adealink/weparty/Routers.kt

@@ -7,12 +7,8 @@ interface AppModule {
             const val EXTRA_ACCOUNT_LOGIN="extra_account_login"
             const val EXTRA_MAIN_TAB = "tab" //控制首页默认展示的tab
             const val EXTRA_MAIN_SUB_TAB = "subTab" //控制首页默认展示的子tab
-        }
-    }
 
-    interface Splash {
-        companion object {
-            const val PATH = "/splash"
+            const val EXTRA_SPLASH_JUMP_URL = "splash_jump_url" //Splash页面跳转URL
         }
     }
 }

+ 25 - 5
app/src/main/java/com/adealink/weparty/commonui/BaseFragment.kt

@@ -20,7 +20,10 @@ open class BaseFragment : Fragment {
 
     constructor(@LayoutRes contentLayoutId: Int) : super(contentLayoutId)
 
+    open val isLazyInit: Boolean = false //是否懒加载UI
     open val isLazyLoad: Boolean = false //是否懒加载数据
+        get() = isLazyInit || field
+
     open val loadDataWhenResume: Boolean = false //是否每次onResume加载数据
 
     override fun onAttach(context: Context) {
@@ -31,25 +34,42 @@ open class BaseFragment : Fragment {
     @CallSuper
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-        initViews()
-        initComponents()
-        observeViewModel()
-        if (!isLazyLoad){
+        Log.d("BaseFragment", "BaseFragment($this), onViewCreated")
+        if (!isLazyInit) {
+            Log.d("BaseFragment", "BaseFragment($this), onViewCreated, init")
+            initViews()
+            initComponents()
+            observeViewModel()
+        }
+
+        if (!isLazyLoad) {
+            Log.d("BaseFragment", "BaseFragment($this), onViewCreated, loadData")
             loadData()
         }
     }
 
+    private var isFirstInit = true
     private var isFirstResume = true
 
     override fun onResume() {
         super.onResume()
-        Log.i("BaseFragment", "onResume: ${javaClass.simpleName}")
+        Log.i("BaseFragment", "BaseFragment($this), onResume: ${javaClass.simpleName}")
+        if (isLazyInit && isFirstInit) {
+            isFirstInit = false
+            Log.d("BaseFragment", "BaseFragment($this), onResume, init")
+            initViews()
+            initComponents()
+            observeViewModel()
+        }
+
         if (loadDataWhenResume) {
+            Log.d("BaseFragment", "BaseFragment($this), onResume, loadData")
             loadData()
             return
         }
         if (isLazyLoad && isFirstResume) {
             isFirstResume = false
+            Log.d("BaseFragment", "BaseFragment($this), onResume, loadData")
             loadData()
         }
     }

+ 18 - 0
app/src/main/java/com/adealink/weparty/debug/DebugExt.kt

@@ -0,0 +1,18 @@
+package com.adealink.weparty.debug
+
+import android.os.Trace
+import com.adealink.weparty.BuildConfig
+
+const val traceEnable = BuildConfig.ENABLE_TRACE
+
+inline fun <T> trace(sectionName: String, block: () -> T): T {
+    if(!traceEnable){
+       return block()
+    }
+    Trace.beginSection(sectionName)
+    try {
+        return block()
+    } finally {
+        Trace.endSection()
+    }
+}

+ 1 - 1
app/src/main/java/com/adealink/weparty/deeplink/DeepLinkConfig.kt

@@ -37,7 +37,7 @@ class DeepLinkConfig: IDeepLinkConfig {
     }
 
     override fun getSplashRouterPath(): String {
-        return AppModule.Splash.PATH
+        return AppModule.Main.PATH
     }
 
     override fun isHomeActivityExists(): Boolean {

+ 29 - 6
app/src/main/java/com/adealink/weparty/module/call/CallModule.kt

@@ -1,5 +1,6 @@
 package com.adealink.weparty.module.call
 
+import android.app.Activity
 import android.app.Application
 import androidx.lifecycle.ViewModelStoreOwner
 import com.adealink.frame.aab.BaseDynamicModule
@@ -25,8 +26,6 @@ object CallModule : BaseDynamicModule<ICallService>(ICallService::class), ICallS
 
     override fun emptyService(): ICallService {
         return object : ICallService {
-            override fun init(application: Application) {
-            }
 
             override fun call(uid: Long, mediaType: MediaType, callerSource: CallerSource, onSuccess: (() -> Unit)?, onFail: ((errCode: Int, errMsg: String?) -> Unit)?) {
             }
@@ -85,11 +84,19 @@ object CallModule : BaseDynamicModule<ICallService>(ICallService::class), ICallS
                 }
             }
 
-        }
-    }
+            override fun appOnCreateMainTask(application: Application) {
+            }
+
+            override fun appOnCreateSubTask(application: Application) {
+            }
+
+            override fun activityOnCreateMainTask() {
+            }
+
+            override fun activityOnCreateSubTask() {
+            }
 
-    override fun init(application: Application) {
-        getService().init(application)
+        }
     }
 
     override fun call(uid: Long, mediaType: MediaType, callerSource: CallerSource, onSuccess: (() -> Unit)?, onFail: ((errCode: Int, errMsg: String?) -> Unit)?) {
@@ -116,4 +123,20 @@ object CallModule : BaseDynamicModule<ICallService>(ICallService::class), ICallS
         return getService().getMediaOperator()
     }
 
+    override fun appOnCreateMainTask(application: Application) {
+        return getService().appOnCreateMainTask(application)
+    }
+
+    override fun appOnCreateSubTask(application: Application) {
+        return getService().appOnCreateSubTask(application)
+    }
+
+    override fun activityOnCreateMainTask() {
+        return getService().activityOnCreateMainTask()
+    }
+
+    override fun activityOnCreateSubTask() {
+        return getService().activityOnCreateSubTask()
+    }
+
 }

+ 2 - 4
app/src/main/java/com/adealink/weparty/module/call/ICallService.kt

@@ -1,18 +1,16 @@
 package com.adealink.weparty.module.call
 
-import android.app.Application
 import androidx.lifecycle.ViewModelStoreOwner
 import com.adealink.frame.aab.IService
 import com.adealink.frame.media.IMediaOperatorGet
+import com.adealink.frame.startup.IStartUpTask
 import com.adealink.weparty.commonui.widget.floatview.data.IFloatData
 import com.adealink.weparty.commonui.widget.floatview.view.BaseFloatView
 import com.adealink.weparty.module.call.data.CallerSource
 import com.adealink.weparty.module.call.data.MediaType
 import com.adealink.weparty.module.call.viewmodel.ICallViewModel
 
-interface ICallService : IService<ICallService>, IMediaOperatorGet {
-
-    fun init(application: Application)
+interface ICallService : IService<ICallService>, IMediaOperatorGet, IStartUpTask {
 
     fun call(
         uid: Long,

+ 2 - 3
app/src/main/java/com/adealink/weparty/module/message/IMessageService.kt

@@ -1,10 +1,10 @@
 package com.adealink.weparty.module.message
 
-import android.app.Application
 import androidx.lifecycle.ViewModelStoreOwner
 import com.adealink.frame.aab.IService
 import com.adealink.frame.base.Rlt
 import com.adealink.frame.imkit.manager.UnReadMessageManager
+import com.adealink.frame.startup.IStartUpTask
 import com.adealink.weparty.commonui.widget.floatview.data.IFloatData
 import com.adealink.weparty.commonui.widget.floatview.view.BaseFloatView
 import com.adealink.weparty.module.message.data.CustomerInfo
@@ -12,8 +12,7 @@ import com.adealink.weparty.module.message.viewmodel.IMessageViewModel
 import io.rong.imlib.IRongCoreCallback
 import io.rong.imlib.model.Conversation
 
-interface IMessageService : IService<IMessageService> {
-    fun init(application: Application)
+interface IMessageService : IService<IMessageService>, IStartUpTask {
     fun getMessageViewModel(owner: ViewModelStoreOwner): IMessageViewModel?
 
     /**

+ 29 - 6
app/src/main/java/com/adealink/weparty/module/message/MessageModule.kt

@@ -1,5 +1,6 @@
 package com.adealink.weparty.module.message
 
+import android.app.Activity
 import android.app.Application
 import androidx.lifecycle.ViewModelStoreOwner
 import com.adealink.frame.aab.BaseDynamicModule
@@ -22,9 +23,6 @@ object MessageModule : BaseDynamicModule<IMessageService>(IMessageService::class
     override val moduleNameResId: Int
         get() = R.string.module_message
 
-    override fun init(application: Application) {
-        getService().init(application)
-    }
 
     override fun getMessageViewModel(owner: ViewModelStoreOwner): IMessageViewModel? {
         return getService().getMessageViewModel(owner)
@@ -74,12 +72,25 @@ object MessageModule : BaseDynamicModule<IMessageService>(IMessageService::class
         return getService().checkIsCustomerService(targetId, cache)
     }
 
+    override fun appOnCreateMainTask(application: Application) {
+        return getService().appOnCreateMainTask(application)
+    }
+
+    override fun appOnCreateSubTask(application: Application) {
+        return getService().appOnCreateSubTask(application)
+    }
+
+    override fun activityOnCreateMainTask() {
+        return getService().activityOnCreateMainTask()
+    }
+
+    override fun activityOnCreateSubTask() {
+        return getService().activityOnCreateSubTask()
+    }
+
     override fun emptyService(): IMessageService {
 
         return object : IMessageService {
-            override fun init(application: Application) {
-
-            }
 
             override fun getMessageViewModel(owner: ViewModelStoreOwner): IMessageViewModel? {
                 return null
@@ -131,6 +142,18 @@ object MessageModule : BaseDynamicModule<IMessageService>(IMessageService::class
             override fun getService(): IMessageService? {
                 return null
             }
+
+            override fun appOnCreateMainTask(application: Application) {
+            }
+
+            override fun appOnCreateSubTask(application: Application) {
+            }
+
+            override fun activityOnCreateMainTask() {
+            }
+
+            override fun activityOnCreateSubTask() {
+            }
         }
 
     }

+ 3 - 4
app/src/main/java/com/adealink/weparty/module/task/RoomTaskMonitor.kt

@@ -16,15 +16,14 @@ object RoomTaskMonitor : IRoomListener {
 
     private const val TAG = "RoomTaskMonitor"
 
-    init {
-        RoomModule.registerListener(this)
-    }
-
     private var stayInRoomCountDownTimer: CountDownTimer? = null
     private var onMicCountDownTimer: CountDownTimer? = null
     private var stayInRoomLastTime = UserTaskManager.DEFAULT_LIVE_TASK_TIME
     private var onMicLastTime = UserTaskManager.DEFAULT_MIC_TASK_TIME //剩余时间
 
+    fun init() {
+        RoomModule.registerListener(this)
+    }
 
     override fun onRoomIn(roomId: Long, flowStateInfo: FlowStateInfo) {
         super.onRoomIn(roomId, flowStateInfo)

+ 2 - 1
app/src/main/java/com/adealink/weparty/module/task/UserTaskManager.kt

@@ -15,6 +15,7 @@ import com.adealink.frame.network.ISocketNotify
 import com.adealink.frame.util.AppUtil
 import com.adealink.frame.util.ONE_MINUTE
 import com.adealink.frame.util.differentDays
+import com.adealink.frame.util.runOnUiThread
 import com.adealink.weparty.App
 import com.adealink.weparty.commonui.dialogchain.DialogTaskManager
 import com.adealink.weparty.commonui.dialogchain.dialogtask.CommonTaskRewardDialogTask
@@ -459,7 +460,7 @@ object UserTaskManager : BaseFrame<IUserTaskListener>(), IUserTaskManager {
 
     fun init() {
         App.instance.networkService.subscribeNotify(commonTaskCompleteNotify)
-        allTaskCodeConfig.observeForever(observer)
+        runOnUiThread { allTaskCodeConfig.observeForever(observer) }
     }
 
     override fun clear() {

+ 12 - 8
app/src/main/java/com/adealink/weparty/module/userlist/HomeUserListFragment.kt

@@ -1,6 +1,7 @@
 package com.adealink.weparty.module.userlist
 
 import android.os.Bundle
+import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.view.updateLayoutParams
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
@@ -53,9 +54,8 @@ class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list), ISc
             HomeUserTab(
                 HomeUserTabType.NewUsers,
                 getCompatString(R.string.common_new)
-            ) { HomeNewUserListFragment() },
-
-            )
+            ) { HomeNewUserListFragment() }
+        )
     }
     private val pageAdapter by fastLazy {
         PageAdapter()
@@ -64,6 +64,8 @@ class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list), ISc
     @BindExtra(name = AppModule.Main.EXTRA_MAIN_SUB_TAB)
     var subTabKey: String? = null
 
+    override val isLazyInit: Boolean = true
+
     override fun onCreate(savedInstanceState: Bundle?) {
         Router.bind(this)
         super.onCreate(savedInstanceState)
@@ -72,16 +74,18 @@ class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list), ISc
 
     override fun initViews() {
         super.initViews()
-        binding.spaceStatusBar.updateLayoutParams {
-            height = DisplayUtil.getStatusBarHeight(requireActivity())
+        binding.clTop.updateLayoutParams<ConstraintLayout.LayoutParams> {
+            topMargin = DisplayUtil.getStatusBarHeight(requireActivity())
         }
 
+        binding.viewPager.offscreenPageLimit = 1
+        binding.viewPager.isSaveEnabled = false
         binding.viewPager.adapter = pageAdapter
 
         val defaultPos = if (subTabKey == "online") {
-            tabList.indexOfFirst { it.type == HomeUserTabType.Online }
+            tabList.indexOfFirst { it.type == HomeUserTabType.Online }.takeIf { it != -1 } ?: 0
         } else if (subTabKey == "new") {
-            tabList.indexOfFirst { it.type == HomeUserTabType.NewUsers }
+            tabList.indexOfFirst { it.type == HomeUserTabType.NewUsers }.takeIf { it != -1 } ?: 0
         } else {
             0
         }
@@ -97,7 +101,7 @@ class HomeUserListFragment : BaseFragment(R.layout.fragment_home_user_list), ISc
 
         binding.btnFilter.onClick {
             val currentItem = binding.viewPager.currentItem
-            val currentFragment= pageAdapter.getFragment(this,currentItem) as? HomeBaseUserListFragment ?:return@onClick
+            val currentFragment = pageAdapter.getFragment(this, currentItem) as? HomeBaseUserListFragment ?: return@onClick
             val tabType = currentFragment.tabType()
             UserFilterDialog.newInstance(tabType).show(childFragmentManager)
         }

+ 2 - 0
app/src/main/java/com/adealink/weparty/module/userlist/fragment/HomeNewUserListFragment.kt

@@ -7,5 +7,7 @@ import com.adealink.weparty.module.userlist.viewmodel.HomeUserTabType
  * Date: 2025/2/24
  */
 class HomeNewUserListFragment: HomeBaseUserListFragment() {
+    override val isLazyInit: Boolean = true
+
     override fun tabType(): HomeUserTabType =  HomeUserTabType.NewUsers
 }

+ 2 - 0
app/src/main/java/com/adealink/weparty/module/userlist/fragment/HomeOnLineUserListFragment.kt

@@ -7,5 +7,7 @@ import com.adealink.weparty.module.userlist.viewmodel.HomeUserTabType
  * Date: 2025/2/24
  */
 class HomeOnLineUserListFragment : HomeBaseUserListFragment() {
+    override val isLazyInit: Boolean = false
+
     override fun tabType(): HomeUserTabType = HomeUserTabType.Online
 }

+ 0 - 2
app/src/main/java/com/adealink/weparty/sound/bg/BackgroundMusicManager.kt

@@ -10,7 +10,6 @@ import com.adealink.frame.sound.data.SoundType
 import com.adealink.frame.util.ActivityLifecycleCallbacksExt
 import com.adealink.frame.util.AppUtil
 import com.adealink.weparty.App
-import com.adealink.weparty.AppModule
 import com.adealink.weparty.module.account.Account
 import com.adealink.weparty.module.sound.data.Sound
 
@@ -30,7 +29,6 @@ class BackgroundMusicManager : IBackgroundMusicManager, ActivityLifecycleCallbac
 
         private val noBGMActivities: Set<String> by lazy {
             hashSetOf<String>().apply {
-                Router.getClazz(AppModule.Splash.PATH)?.let { this.add(it.name) }
                 Router.getClazz(Account.Login.PATH)?.let { this.add(it.name) }
                 Router.getClazz(Account.Phone.INPUT)?.let { this.add(it.name) }
                 Router.getClazz(Account.Phone.LOGIN)?.let { this.add(it.name) }

+ 0 - 134
app/src/main/java/com/adealink/weparty/splash/SplashActivity.kt

@@ -1,134 +0,0 @@
-package com.adealink.weparty.splash
-
-import android.content.Intent
-import android.os.Bundle
-import android.widget.ImageView
-import androidx.lifecycle.lifecycleScope
-import com.adealink.frame.aab.util.getCompatString
-import com.adealink.frame.download.manager.downloadManager
-import com.adealink.frame.mvvm.view.viewBinding
-import com.adealink.frame.router.Router
-import com.adealink.frame.router.annotation.RouterUri
-import com.adealink.weparty.AppModule
-import com.adealink.weparty.R
-import com.adealink.weparty.commonui.BaseActivity
-import com.adealink.weparty.commonui.ext.gone
-import com.adealink.weparty.commonui.ext.show
-import com.adealink.weparty.constant.DEFAULT_SKIP_TIME
-import com.adealink.weparty.constant.SECOND
-import com.adealink.weparty.databinding.ActivitySplashBinding
-import com.adealink.weparty.effect.AnimExtraConfig
-import com.adealink.weparty.module.account.Account
-import com.adealink.weparty.module.account.AccountModule
-import com.adealink.weparty.module.operation.OperationModule
-import com.adealink.weparty.storage.file.FilePath
-import com.adealink.weparty.util.goLocalLinkPage
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-
-@RouterUri(path = [AppModule.Splash.PATH], desc = "闪屏页")
-class SplashActivity : BaseActivity() {
-    private val binding by viewBinding(ActivitySplashBinding::inflate)
-
-    override fun onAfterSuperCreate(): Boolean {
-        if (!isTaskRoot) {
-            finish()
-            return false
-        }
-
-        return true
-    }
-
-    override fun initViews() {
-        super.initViews()
-        setContentView(binding.root)
-
-        val banner = OperationModule.getSplashBanner()
-        if (banner == null) {
-            jump()
-            return
-        }
-
-        binding.tvSkip.show()
-        binding.tvSkip.setOnClickListener {
-            jump()
-        }
-
-        if (banner.isEffectIcon()) {
-            val savePath = downloadManager.getDownloadedResourcePath(
-                FilePath.effectPath ?: FilePath.backupEffectPath, banner.icon
-            )
-            if (savePath.isNullOrBlank()) {
-                jump()
-                return
-            }
-
-            binding.ivSplash.gone()
-            binding.weAnimView.show()
-            binding.weAnimView.setFile(
-                savePath,
-                extraConfig = AnimExtraConfig(scaleType = ImageView.ScaleType.CENTER_CROP),
-                onPlayEnd = { jump() })
-
-            binding.tvSkip.text = getCompatString(R.string.common_skip)
-        } else {
-            binding.ivSplash.setImageURI(banner.icon)
-
-            val skipTime = banner.showConfigParamInfo?.skipTime ?: DEFAULT_SKIP_TIME
-            binding.tvSkip.text = getCompatString(R.string.common_skip_after_second, skipTime)
-            preformTimer(skipTime)
-        }
-
-        binding.root.setOnClickListener {
-            if (banner.url.isBlank()) {
-                return@setOnClickListener
-            }
-
-            jump(banner.url)
-        }
-    }
-
-    private fun preformTimer(skipTime: Int) {
-        if (skipTime <= 0) {
-            jump()
-            return
-        }
-
-        var count = 0
-        lifecycleScope.launch {
-            while (count < skipTime) {
-                delay(SECOND)
-                count++
-                binding.tvSkip.text =
-                    getCompatString(R.string.common_skip_after_second, skipTime - count)
-
-                if (count == skipTime) {
-                    jump()
-                }
-            }
-        }
-    }
-
-    private fun jump(url: String? = null) {
-        if (AccountModule.isLogin().not()) {
-            Router.build(this, Account.Login.PATH)
-                .putExtras(intent?.extras ?: Bundle())
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
-                .start()
-            finish()
-            return
-        }
-
-        Router.build(this, AppModule.Main.PATH)
-            .putExtras(intent?.extras ?: Bundle())
-            .start()
-
-        if (url?.isNotBlank() == true) {
-            binding.root.postDelayed({
-                goLocalLinkPage(this, url)
-            }, 200)
-        }
-
-        finish()
-    }
-}

+ 160 - 0
app/src/main/java/com/adealink/weparty/ui/MainStartUpFragment.kt

@@ -0,0 +1,160 @@
+package com.adealink.weparty.ui
+
+import android.os.Bundle
+import android.os.SystemClock
+import com.adealink.frame.base.fastLazy
+import com.adealink.frame.log.Log
+import com.adealink.frame.push.manager.pushService
+import com.adealink.frame.startup.executor.TaskExecutorManager
+import com.adealink.frame.statistics.BaseStatEvent
+import com.adealink.frame.util.AppUtil
+import com.adealink.frame.util.removeUiCallbacks
+import com.adealink.frame.util.runOnUiThread
+import com.adealink.weparty.BuildConfig
+import com.adealink.weparty.commonui.BaseActivity
+import com.adealink.weparty.commonui.BaseFragment
+import com.adealink.weparty.config.globalConfigManager
+import com.adealink.weparty.log.manager.logManager
+import com.adealink.weparty.module.account.AccountModule
+import com.adealink.weparty.module.anchor.data.FromScene
+import com.adealink.weparty.module.attribution.AttributionModule
+import com.adealink.weparty.module.backpack.BackpackModule
+import com.adealink.weparty.module.couple.CoupleModule
+import com.adealink.weparty.module.emotion.EmotionModule
+import com.adealink.weparty.module.family.FamilyModule
+import com.adealink.weparty.module.game.GameModule
+import com.adealink.weparty.module.gift.GiftModule
+import com.adealink.weparty.module.level.LevelModule
+import com.adealink.weparty.module.medal.MedalModule
+import com.adealink.weparty.module.message.MessageModule
+import com.adealink.weparty.module.operation.OperationModule
+import com.adealink.weparty.module.profile.ProfileModule
+import com.adealink.weparty.module.room.RoomModule
+import com.adealink.weparty.module.share.ShareModule
+import com.adealink.weparty.module.skin.SkinModule
+import com.adealink.weparty.module.task.RoomTaskMonitor
+import com.adealink.weparty.module.task.UserTaskManager
+import com.adealink.weparty.module.task.invite.InviteRewardManager
+import com.adealink.weparty.module.wallet.WalletModule
+import com.adealink.weparty.webview.manager.PreloadWebViewManager
+import com.adealink.weparty.webview.manager.PreloadWebViewWorker
+import com.google.firebase.crashlytics.ktx.crashlytics
+import com.google.firebase.crashlytics.ktx.setCustomKeys
+import com.google.firebase.ktx.Firebase
+
+/**
+ * 主界面初始化
+ */
+class MainStartUpFragment : BaseFragment() {
+
+    private val roomAttrViewModel by fastLazy { RoomModule.getRoomAttrViewModel(this.requireActivity()) }
+    private val familyInfoViewModel by fastLazy { FamilyModule.getFamilyInfoViewModel(this.requireActivity()) }
+    private val countryViewModel by fastLazy { ProfileModule.getCountryViewModel(this) }
+
+    private val importantTask by fastLazy {
+        Runnable {
+            TaskExecutorManager.instance.subThreadPoolExecutor.execute {
+                importantLoad()
+            }
+        }
+    }
+
+    private fun importantLoad() {
+        if (AccountModule.isLogin()) {
+            AccountModule.checkRemoteVirtualAppConfig()
+            AttributionModule.reportAttributionData()
+            BaseStatEvent.setUserId(ProfileModule.getMyUid())
+            ShareModule.reportUserType()
+            pushService.getPushTokenAndReport()
+            Firebase.crashlytics.setCustomKeys {
+                key("uid", AccountModule.uid)
+                key("BuildType", BuildConfig.BUILD_TYPE)
+                key("IS_RELEASE", BuildConfig.IS_RELEASE)
+            }
+        }
+
+        logManager.subscribeReportLogNotify()
+        AccountModule.refreshToken()
+        ProfileModule.reportPhoneModel(FromScene.STARTUP.scene)
+        roomAttrViewModel?.getMyRoomInfo(true)
+        MessageModule.activityOnCreateMainTask()
+        BackpackModule.checkGiftBackpack()
+        RoomModule.init()
+        GameModule.checkPlayingGame()
+        countryViewModel?.getCountryList(true)
+    }
+
+    private val minorTask by fastLazy {
+        Runnable {
+            TaskExecutorManager.instance.subThreadPoolExecutor.execute {
+                minorLoad()
+            }
+        }
+    }
+
+    private fun minorLoad() {
+        WalletModule.fetchCurrency()
+        WalletModule.queryAndHandleUnDealPurchases()
+        LevelModule.pullLevelConfigs()
+        CoupleModule.pullCoupleConfig()
+        WalletModule.init()
+        FamilyModule.pullFamilyLevelConfig()
+        ProfileModule.pullUserNoteNameData()
+        globalConfigManager.getAllGlobalConfig(true)
+    }
+
+    private val otherTask by fastLazy {
+        Runnable {
+            TaskExecutorManager.instance.subThreadPoolExecutor.execute {
+                otherLoad()
+            }
+        }
+    }
+
+    private fun otherLoad() {
+        GiftModule.getGifts(true)
+        EmotionModule.getEmotionPackages(true)
+        MedalModule.init()
+        SkinModule.getCurrentSkin(true)
+        familyInfoViewModel?.getApplyJoinFamilyUnHandleNum()
+        UserTaskManager.refreshTaskConfig()
+        PreloadWebViewWorker.startPreloadWorker(AppUtil.appContext)
+        ProfileModule.checkChatAchievement()
+        UserTaskManager.init()
+        InviteRewardManager.init()
+        RoomTaskMonitor.init()
+        OperationModule.init()
+        OperationModule.checkSuperSupporterWhatsAppFillStatus()
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val startTs = SystemClock.elapsedRealtime()
+        Log.i(TAG, "StartUpFragment.onCreate() start")
+        runOnUiThread(importantTask)
+        runOnUiThread(minorTask, 2000L)
+        runOnUiThread(otherTask, 3000L)
+        Log.i(TAG, "StartUpFragment.onCreate() end, cost: ${SystemClock.elapsedRealtime() - startTs}")
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        PreloadWebViewManager.destroy()
+        removeUiCallbacks(importantTask)
+        removeUiCallbacks(minorTask)
+        removeUiCallbacks(otherTask)
+    }
+
+    companion object {
+        private const val TAG = "StartUpFragment"
+
+        @JvmStatic
+        fun inject(activity: BaseActivity) {
+            val manager = activity.supportFragmentManager
+            if (manager.findFragmentByTag(TAG) == null) {
+                manager.beginTransaction().add(MainStartUpFragment(), TAG).commit()
+                manager.executePendingTransactions()
+            }
+        }
+    }
+}

+ 16 - 163
app/src/main/java/com/adealink/weparty/ui/home/BaseHomeFragment.kt

@@ -14,9 +14,6 @@ import androidx.viewpager2.widget.ViewPager2
 import com.adealink.frame.base.fastLazy
 import com.adealink.frame.log.Log
 import com.adealink.frame.router.Router
-import com.adealink.frame.util.AppUtil
-import com.adealink.frame.util.removeUiCallbacks
-import com.adealink.frame.util.runOnUiThread
 import com.adealink.weparty.AppModule
 import com.adealink.weparty.R
 import com.adealink.weparty.commonui.BaseFragment
@@ -24,27 +21,10 @@ import com.adealink.weparty.commonui.dialogchain.DialogTaskManager
 import com.adealink.weparty.commonui.dialogchain.dialogtask.MedalAchieveDialogTask
 import com.adealink.weparty.commonui.dialogfragment.BaseDialogFragment
 import com.adealink.weparty.commonui.recycleview.adapter.BaseTabFragmentStateAdapter
-import com.adealink.weparty.commonui.toast.util.showToast
 import com.adealink.weparty.commonui.widget.EmptyFragment
-import com.adealink.weparty.config.globalConfigManager
 import com.adealink.weparty.databinding.LayoutMainTabNormalBinding
 import com.adealink.weparty.location.constant.TAG_LOCATION_REPORT
 import com.adealink.weparty.location.viewmodel.LocationViewModel
-import com.adealink.weparty.log.manager.logManager
-import com.adealink.weparty.module.account.AccountModule
-import com.adealink.weparty.module.anchor.AnchorModule
-import com.adealink.weparty.module.anchor.data.AnchorMessage
-import com.adealink.weparty.module.anchor.data.AnchorMessageReplyCode
-import com.adealink.weparty.module.anchor.data.AnchorMessageType
-import com.adealink.weparty.module.anchor.data.FromScene
-import com.adealink.weparty.module.backpack.BackpackModule
-import com.adealink.weparty.module.couple.CoupleModule
-import com.adealink.weparty.module.emotion.EmotionModule
-import com.adealink.weparty.module.family.FamilyModule
-import com.adealink.weparty.module.game.GameModule
-import com.adealink.weparty.module.gift.GiftModule
-import com.adealink.weparty.module.level.LevelModule
-import com.adealink.weparty.module.level.data.TAG_LEVEL
 import com.adealink.weparty.module.medal.Medal
 import com.adealink.weparty.module.medal.MedalModule
 import com.adealink.weparty.module.medal.data.MedalData
@@ -54,16 +34,10 @@ import com.adealink.weparty.module.operation.Operation
 import com.adealink.weparty.module.operation.OperationModule
 import com.adealink.weparty.module.operation.newuser.HomeBannerEntranceFloatViewComp
 import com.adealink.weparty.module.profile.Profile
-import com.adealink.weparty.module.profile.ProfileModule
 import com.adealink.weparty.module.room.Room
-import com.adealink.weparty.module.room.RoomModule
-import com.adealink.weparty.module.skin.SkinModule
 import com.adealink.weparty.module.task.DailySignInComp
 import com.adealink.weparty.module.task.HomeIncomeViewComp
 import com.adealink.weparty.module.task.HomeTaskCountDownViewComp
-import com.adealink.weparty.module.task.RoomTaskMonitor
-import com.adealink.weparty.module.task.UserTaskManager
-import com.adealink.weparty.module.task.invite.InviteRewardManager
 import com.adealink.weparty.module.userlist.UserList
 import com.adealink.weparty.module.wallet.WalletModule
 import com.adealink.weparty.ui.IScrollManager
@@ -73,8 +47,6 @@ import com.adealink.weparty.ui.tab.HOME_TABS
 import com.adealink.weparty.ui.tab.HomeTab
 import com.adealink.weparty.ui.tab.ITabManager
 import com.adealink.weparty.ui.tab.Tab
-import com.adealink.weparty.webview.manager.PreloadWebViewManager
-import com.adealink.weparty.webview.manager.PreloadWebViewWorker
 import com.google.android.material.tabs.TabLayout
 import com.google.android.material.tabs.TabLayoutMediator
 
@@ -90,14 +62,9 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
     abstract val vpContent: ViewPager2
     abstract val tlTab: TabLayout
 
-    protected val roomAttrViewModel by fastLazy { RoomModule.getRoomAttrViewModel(this.requireActivity()) }
     private val locationViewModel by viewModels<LocationViewModel>()
-    private val anchorViewModel by fastLazy { AnchorModule.getAnchorViewModel(this) }
-    private val familyInfoViewModel by fastLazy { FamilyModule.getFamilyInfoViewModel(this.requireActivity()) }
     private val medalViewModel by fastLazy { MedalModule.getMedalViewModel(this) }
-    protected val operationViewModel by fastLazy { OperationModule.getOperationViewModel(this.requireActivity()) }
-    private val countryViewModel by fastLazy { ProfileModule.getCountryViewModel(this) }
-
+    private val operationViewModel by fastLazy { OperationModule.getOperationViewModel(this.requireActivity()) }
     private lateinit var homePagerAdapter: HomeProfilePageAdapter
     private val gestureDetector: GestureDetector by lazy {
         GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
@@ -180,6 +147,14 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
         }
     }
 
+    override fun initComponents() {
+        super.initComponents()
+        homeBannerEntranceFloatViewComp = HomeBannerEntranceFloatViewComp(this).also { it.attach() }
+        HomeTaskCountDownViewComp(this).attach()
+        HomeIncomeViewComp(this).attach()
+        DailySignInComp(this).attach()
+    }
+
     fun updateTabView(
         tabView: TabLayout.Tab?,
         isSelected: Boolean,
@@ -199,103 +174,10 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
 
     open fun onConfigureTab(tab: Tab, binding: LayoutMainTabNormalBinding) {}
 
-    private val importantLoad by fastLazy {
-        Runnable {
-            importantLoad()
-        }
-    }
-
-    @CallSuper
-    open fun importantLoad() {
-        AccountModule.refreshToken()
-        roomAttrViewModel?.getMyRoomInfo(true)
-//        anchorViewModel?.getAnchorMessages()
-        OperationModule.checkSuperSupporterWhatsAppFillStatus()
-        BackpackModule.checkGiftBackpack()
-        RoomModule.init()
-        GameModule.checkPlayingGame()
-        countryViewModel?.getCountryList(true)
-    }
-
-    private val minorLoad by fastLazy {
-        Runnable {
-            minorLoad()
-        }
-    }
-
-    @CallSuper
-    open fun minorLoad() {
-        WalletModule.fetchCurrency()
-        WalletModule.queryAndHandleUnDealPurchases()
-        LevelModule.pullLevelConfigs()
-        CoupleModule.pullCoupleConfig()
-        ProfileModule.reportPhoneModel(FromScene.STARTUP.scene)
-        WalletModule.init()
-        FamilyModule.pullFamilyLevelConfig()
-        ProfileModule.pullUserNoteNameData()
-    }
-
-    private val otherLoad by fastLazy {
-        Runnable {
-            otherLoad()
-        }
-    }
-
-    private var homeBannerEntranceFloatViewComp:HomeBannerEntranceFloatViewComp?=null
-
-    @CallSuper
-    open fun otherLoad() {
-        GiftModule.getGifts(true)
-        EmotionModule.getEmotionPackages(true)
-        MedalModule.init()
-        OperationModule.init()
-        SkinModule.getCurrentSkin(true)
-        familyInfoViewModel?.getApplyJoinFamilyUnHandleNum()
-
-        //todo:先放延迟加载
-        UserTaskManager.refreshTaskConfig()
-        HomeTaskCountDownViewComp(this).attach()
-        HomeIncomeViewComp(this).attach()
-        DailySignInComp(this).attach()
-        homeBannerEntranceFloatViewComp = HomeBannerEntranceFloatViewComp(this)
-        homeBannerEntranceFloatViewComp?.attach()
-
-        preloadWebViewResource()
-        ProfileModule.checkChatAchievement()
-    }
-
-    @CallSuper
-    override fun loadData() {
-        globalConfigManager.getAllGlobalConfig(true)
-        logManager.subscribeReportLogNotify()
-        runOnUiThread(importantLoad)
-        runOnUiThread(minorLoad, 2000L)
-        runOnUiThread(otherLoad, 3000L)
-        UserTaskManager.init()//初始化
-        InviteRewardManager.init()//初始化
-        RoomTaskMonitor //初始化
-    }
+    private var homeBannerEntranceFloatViewComp: HomeBannerEntranceFloatViewComp? = null
 
     override fun observeViewModel() {
         super.observeViewModel()
-        anchorViewModel?.anchorMessageLD?.observe(viewLifecycleOwner,
-            object : Observer<AnchorMessage> {
-                override fun onChanged(value: AnchorMessage) {
-                    handleAnchorMessage(value)
-                }
-            }
-        )
-        observeMedal()
-        operationViewModel?.rateAppNotifyLD?.observe(viewLifecycleOwner,
-            object : Observer<Unit> {
-                override fun onChanged(value: Unit) {
-                    showRateAppDialog()
-                }
-            }
-        )
-    }
-
-    private fun observeMedal() {
         medalViewModel?.medalAchieveLiveData?.observe(viewLifecycleOwner,
             object : Observer<MedalData> {
                 override fun onChanged(it: MedalData) {
@@ -313,23 +195,13 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
                 }
             }
         )
-    }
-
-    private fun handleAnchorMessage(message: AnchorMessage) {
-        when (AnchorMessageType.map(message.messageType)) {
-            AnchorMessageType.SUPER_GIFT_ENERGY_BOX, AnchorMessageType.MESSAGE_BECOME_SUPER_SUPPORTER -> {
-                OperationModule.handleOperationMessage(
-                    childFragmentManager,
-                    message
-                ) { msg, replayCode ->
-                    replyAnchorMessage(msg, replayCode)
+        operationViewModel?.rateAppNotifyLD?.observe(viewLifecycleOwner,
+            object : Observer<Unit> {
+                override fun onChanged(value: Unit) {
+                    showRateAppDialog()
                 }
             }
-
-            else -> {
-                Log.d(TAG_LEVEL, "handleAnchorMessage: $message")
-            }
-        }
+        )
     }
 
     private fun showRateAppDialog() {
@@ -339,13 +211,6 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
 
     }
 
-    private fun replyAnchorMessage(message: AnchorMessage, replyCode: AnchorMessageReplyCode) {
-        anchorViewModel?.replyAnchorMessage(message.messageId, message.messageType, replyCode)
-            ?.observe(viewLifecycleOwner) {
-                showToast(it)
-            }
-    }
-
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         locationViewModel.reportLocation().observe(viewLifecycleOwner) {
@@ -353,18 +218,6 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
         }
     }
 
-    private fun preloadWebViewResource() {
-        PreloadWebViewWorker.startPreloadWorker(AppUtil.appContext)
-    }
-
-    override fun onDestroy() {
-        super.onDestroy()
-        removeUiCallbacks(importantLoad)
-        removeUiCallbacks(minorLoad)
-        removeUiCallbacks(otherLoad)
-        PreloadWebViewManager.destroy()
-    }
-
     internal inner class HomeProfilePageAdapter : BaseTabFragmentStateAdapter(this) {
         override fun getTabName(pos: Int): String {
             return ""
@@ -398,7 +251,7 @@ abstract class BaseHomeFragment : BaseFragment, ITabManager {
                 }
             }.apply {
                 //把数据传递给子Fragment
-                arguments=Bundle().apply {
+                arguments = Bundle().apply {
                     putString(AppModule.Main.EXTRA_MAIN_SUB_TAB, subTabKey)
                 }
             }

+ 136 - 0
app/src/main/java/com/adealink/weparty/ui/splash/SplashFragment.kt

@@ -0,0 +1,136 @@
+package com.adealink.weparty.ui.splash
+
+import android.os.Bundle
+import android.os.SystemClock
+import android.util.Log
+import android.view.LayoutInflater
+import android.widget.FrameLayout
+import android.widget.ImageView
+import androidx.lifecycle.lifecycleScope
+import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.download.manager.downloadManager
+import com.adealink.frame.ext.isViewBindingValid
+import com.adealink.frame.mvvm.view.viewBinding
+import com.adealink.frame.router.Router
+import com.adealink.weparty.AppModule
+import com.adealink.weparty.R
+import com.adealink.weparty.commonui.BaseActivity
+import com.adealink.weparty.commonui.BaseFragment
+import com.adealink.weparty.commonui.ext.gone
+import com.adealink.weparty.commonui.ext.show
+import com.adealink.weparty.constant.DEFAULT_SKIP_TIME
+import com.adealink.weparty.constant.SECOND
+import com.adealink.weparty.databinding.FragmentSplashBannerBinding
+import com.adealink.weparty.databinding.FragmentSplashBinding
+import com.adealink.weparty.effect.AnimExtraConfig
+import com.adealink.weparty.module.account.Account
+import com.adealink.weparty.module.operation.OperationModule
+import com.adealink.weparty.module.operation.banner.data.BannerInfo
+import com.adealink.weparty.storage.file.FilePath
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+class SplashFragment : BaseFragment(R.layout.fragment_splash) {
+
+    companion object {
+        const val TAG = "SplashFragment"
+    }
+
+    val binding by viewBinding(FragmentSplashBinding::bind)
+
+    override fun initViews() {
+        super.initViews()
+        val banner = OperationModule.getSplashBanner()
+        if (banner == null) {
+            jump()
+            return
+        }
+        inflateSplashLayout(banner)
+    }
+
+    override fun loadData() {
+        super.loadData()
+        val loadStartTs = SystemClock.elapsedRealtime()
+        Log.d("SplashActivity", "preLoadActivity($loadStartTs)")
+        Router.getRouterInstance<BaseActivity>(Account.Login.PATH)
+        Router.getRouterInstance<BaseActivity>(AppModule.Main.PATH)
+        val loadEndTs = SystemClock.elapsedRealtime()
+        Log.d("SplashActivity", "preLoadActivity${loadEndTs}, cost:${loadEndTs - loadStartTs}")
+    }
+
+    private fun inflateSplashLayout(banner: BannerInfo) {
+        val bannerBinding = FragmentSplashBannerBinding.inflate(LayoutInflater.from(context), null, false)
+        binding.flContent.addView(bannerBinding.root, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT))
+        bannerBinding.tvSkip.show()
+        bannerBinding.tvSkip.setOnClickListener {
+            jump()
+        }
+        if (banner.isEffectIcon()) {
+            val savePath = downloadManager.getDownloadedResourcePath(
+                FilePath.effectPath ?: FilePath.backupEffectPath, banner.icon
+            )
+            if (savePath.isNullOrBlank()) {
+                jump()
+                return
+            }
+
+            bannerBinding.ivSplash.gone()
+            bannerBinding.weAnimView.show()
+            bannerBinding.weAnimView.setFile(
+                savePath,
+                extraConfig = AnimExtraConfig(scaleType = ImageView.ScaleType.CENTER_CROP),
+                onPlayEnd = { jump() })
+
+            bannerBinding.tvSkip.text = getCompatString(R.string.common_skip)
+        } else {
+            bannerBinding.ivSplash.setImageURI(banner.icon)
+
+            val skipTime = banner.showConfigParamInfo?.skipTime ?: DEFAULT_SKIP_TIME
+            bannerBinding.tvSkip.text = getCompatString(R.string.common_skip_after_second, skipTime)
+            preformTimer(skipTime) {
+                if (isViewBindingValid()) {
+                    bannerBinding.tvSkip.text = getCompatString(R.string.common_skip_after_second, it)
+                }
+            }
+        }
+
+        bannerBinding.root.setOnClickListener {
+            if (banner.url.isBlank()) {
+                return@setOnClickListener
+            }
+
+            jump(banner.url)
+        }
+    }
+
+    private fun jump(url: String? = null) {
+        val activity = activity ?: return
+        val bundle = activity.intent?.extras ?: Bundle()
+        if (!url.isNullOrEmpty()) {
+            bundle.putString(AppModule.Main.EXTRA_SPLASH_JUMP_URL, url)
+        }
+        Router.build(activity, AppModule.Main.PATH)
+            .putExtras(bundle)
+            .start()
+    }
+
+    private fun preformTimer(skipTime: Int, onTimerUpdate: (time: Int) -> Unit) {
+        if (skipTime <= 0) {
+            jump()
+            return
+        }
+
+        var count = 0
+        lifecycleScope.launch {
+            while (count < skipTime) {
+                delay(SECOND)
+                count++
+                onTimerUpdate.invoke(skipTime - count)
+
+                if (count == skipTime) {
+                    jump()
+                }
+            }
+        }
+    }
+}

+ 6 - 6
app/src/main/java/com/adealink/weparty/ui/tab/HomeTab.kt

@@ -41,25 +41,25 @@ enum class HomeTab(val tab: String) {
     MOMENT("moment")
 }
 
-var ROOM_LIST_TAB = Tab(
+val ROOM_LIST_TAB = Tab(
     HomeTab.ROOM_LIST,
     UriUtil.getUriForResourceId(R.drawable.app_tab_room_list_ic).toString(),
     UriUtil.getUriForResourceId(R.drawable.app_tab_room_list_selected_ic).toString()
 ) { Router.getRouterInstance<BaseFragment>(Room.RoomList.PATH) ?: EmptyFragment() }
 
-var ME_TAB = Tab(
+val ME_TAB = Tab(
     HomeTab.ME,
     UriUtil.getUriForResourceId(R.drawable.app_tab_me_ic).toString(),
     UriUtil.getUriForResourceId(R.drawable.app_tab_me_selected_ic).toString()
 ) { Router.getRouterInstance<BaseFragment>(Profile.Me.PATH) ?: EmptyFragment() }
 
-var MESSAGE_TAB = Tab(
+val MESSAGE_TAB = Tab(
     HomeTab.MESSAGE,
     UriUtil.getUriForResourceId(R.drawable.app_tab_message_ic).toString(),
     UriUtil.getUriForResourceId(R.drawable.app_tab_message_selected_ic).toString()
 ) { Router.getRouterInstance<BaseFragment>(Message.Home.PATH) ?: EmptyFragment() }
 
-var MOMENT_TAB = Tab(
+val MOMENT_TAB = Tab(
     HomeTab.MOMENT,
     UriUtil.getUriForResourceId(R.drawable.app_tab_moment_ic).toString(),
     UriUtil.getUriForResourceId(R.drawable.app_tab_moment_selected_ic).toString()
@@ -67,7 +67,7 @@ var MOMENT_TAB = Tab(
     Router.getRouterInstance<BaseFragment>(Moment.Home.PATH) ?: EmptyFragment()
 }
 
-var USER_LIST_TAB = Tab(
+val USER_LIST_TAB = Tab(
     HomeTab.USER_LIST,
     UriUtil.getUriForResourceId(R.drawable.app_tab_userlist_ic).toString(),
     UriUtil.getUriForResourceId(R.drawable.app_tab_userlist_selected_ic).toString()
@@ -78,6 +78,6 @@ var USER_LIST_TAB = Tab(
 /**
  * 首页HomeTab配置
  */
-var HOME_TABS = mutableListOf(
+val HOME_TABS = listOf(
     USER_LIST_TAB, MOMENT_TAB, ROOM_LIST_TAB, MESSAGE_TAB, ME_TAB
 )

+ 0 - 13
app/src/main/res/layout/activity_main.xml

@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/container"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <androidx.fragment.app.FragmentContainerView
-        android:id="@+id/fl_content"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-
-</androidx.constraintlayout.widget.ConstraintLayout>

+ 1 - 8
app/src/main/res/layout/fragment_home_user_list.xml

@@ -4,19 +4,12 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <Space
-        android:id="@+id/space_status_bar"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        app:layout_constraintTop_toTopOf="parent" />
-
-
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/cl_top"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/space_status_bar">
+        app:layout_constraintTop_toTopOf="parent">
 
         <com.adealink.weparty.commonui.widget.CommonTabLayout
             android:id="@+id/common_tab_layout"

+ 6 - 0
app/src/main/res/layout/fragment_splash.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/fl_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />

+ 0 - 0
app/src/main/res/layout/activity_splash.xml → app/src/main/res/layout/fragment_splash_banner.xml


+ 1 - 4
app/src/main/res/values/themes.xml

@@ -10,11 +10,8 @@
         <item name="default_dot_style">@style/CommonRedDot</item>
     </style>
 
-    <style name="SplashActivityTheme" parent="AppTheme">
-        <item name="android:windowBackground">@drawable/bg_splash</item>
-    </style>
-
     <style name="MainActivityTheme" parent="AppTheme">
+        <item name="android:windowBackground">@drawable/bg_splash</item>
         <item name="android:windowFullscreen">true</item>
     </style>
 

+ 1 - 0
frame/startup/.gitignore

@@ -0,0 +1 @@
+/build

+ 62 - 0
frame/startup/build.gradle

@@ -0,0 +1,62 @@
+plugins {
+    id 'com.android.library'
+    id 'org.jetbrains.kotlin.android'
+    id 'kotlin-parcelize'
+}
+
+android {
+    namespace 'com.adealink.frame.startup'
+    compileSdk libs.versions.compileSdk.get().toInteger()
+
+    defaultConfig {
+        minSdk libs.versions.minSdk.get().toInteger()
+        targetSdk libs.versions.targetSdk.get().toInteger()
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles "consumer-rules.pro"
+        ndk {
+            abiFilters "armeabi-v7a"
+            abiFilters "arm64-v8a"
+        }
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_17
+        targetCompatibility JavaVersion.VERSION_17
+    }
+    kotlinOptions {
+        jvmTarget = JavaVersion.VERSION_17.majorVersion
+    }
+}
+
+dependencies {
+    //kotlin
+    implementation libs.kotlin.stdlib
+
+    //androidx
+    implementation libs.androidx.core.ktx
+
+    //frame
+    compileOnly libs.gson
+
+    //frame
+    compileOnly platform(libs.frame.bom)
+    compileOnly libs.frame.data
+    compileOnly libs.frame.zero
+    compileOnly libs.frame.base
+    compileOnly libs.frame.util
+    compileOnly libs.frame.coroutine
+    compileOnly libs.frame.log
+    compileOnly libs.frame.media
+
+    //test
+    testImplementation libs.junit
+    androidTestImplementation libs.androidx.junit
+    androidTestImplementation libs.androidx.espresso.core
+}

+ 0 - 0
frame/startup/consumer-rules.pro


+ 21 - 0
frame/startup/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 4 - 0
frame/startup/src/main/AndroidManifest.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest>
+
+</manifest>

+ 25 - 0
frame/startup/src/main/java/com/adealink/frame/startup/IStartUpTask.kt

@@ -0,0 +1,25 @@
+package com.adealink.frame.startup
+
+import android.app.Application
+
+interface IStartUpTask {
+    /**
+     * 应用启动(主线程任务)
+     */
+    fun appOnCreateMainTask(application: Application)
+
+    /**
+     * 应用启动(子线程任务)
+     */
+    fun appOnCreateSubTask(application: Application)
+
+    /**
+     * 页面启动(主线程任务)
+     */
+    fun activityOnCreateMainTask()
+
+    /**
+     * 页面启动(子线程任务)
+     */
+    fun activityOnCreateSubTask()
+}

+ 3 - 0
frame/startup/src/main/java/com/adealink/frame/startup/Tags.kt

@@ -0,0 +1,3 @@
+package com.adealink.frame.startup
+
+const val TAG_START_UP = "tag_start_up"

+ 21 - 0
frame/startup/src/main/java/com/adealink/frame/startup/base/TaskInterface.kt

@@ -0,0 +1,21 @@
+package com.adealink.frame.startup.base
+
+import android.os.Process
+import androidx.annotation.IntRange
+import com.adealink.frame.startup.task.AppStartTask
+import java.util.concurrent.Executor
+
+interface TaskInterface {
+    //线程的优先级
+    @IntRange(from = Process.THREAD_PRIORITY_FOREGROUND.toLong(), to = Process.THREAD_PRIORITY_LOWEST.toLong())
+    fun priority(): Int
+
+    //执行任务所在的线程池
+    fun runOnExecutor(): Executor
+
+    //所依赖的父亲们,父亲们执行完了,孩子才能执行
+    val dependsTaskList: List<Class<out AppStartTask>>
+
+    //在非主线程执行的Task是否需要在被调用await的时候等待,默认不需要,返回true即在Application的onCreate中阻塞,直到该任务执行完
+    fun needWait(): Boolean
+}

+ 192 - 0
frame/startup/src/main/java/com/adealink/frame/startup/dispatcher/AppStartTaskDispatcher.kt

@@ -0,0 +1,192 @@
+package com.adealink.frame.startup.dispatcher
+
+import android.os.Looper
+import android.util.Log
+import com.adealink.frame.base.AppBaseInfo
+import com.adealink.frame.startup.TAG_START_UP
+import com.adealink.frame.startup.runnable.AppStartTaskRunnable
+import com.adealink.frame.startup.task.AppStartTask
+import com.adealink.frame.startup.util.AppStartTaskSortUtil.getSortResult
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicInteger
+
+class AppStartTaskDispatcher private constructor() {
+    //存放每个Task  (key= Class < ? extends AppStartTask>)
+    private val mTaskHashMap = HashMap<Class<out AppStartTask>, AppStartTask>()
+
+    //每个Task的孩子 (key= Class < ? extends AppStartTask>)
+    private val mTaskChildHashMap = HashMap<Class<out AppStartTask>, MutableList<Class<out AppStartTask>>>()
+
+    //通过Add添加进来的所有任务
+    private val mStartTaskList: MutableList<AppStartTask> = ArrayList()
+
+    //拓扑排序后的所有任务
+    private var mSortTaskList: List<AppStartTask>? = null
+
+    //拓扑排序后的主线程的任务
+    private val mSortMainThreadTaskList: MutableList<AppStartTask> = ArrayList()
+
+    //拓扑排序后的子线程的任务
+    private val mSortThreadPoolTaskList: MutableList<AppStartTask> = ArrayList()
+
+    //需要等待的任务总数,用于阻塞
+    private var mCountDownLatch: CountDownLatch? = null
+
+    //需要等待的任务总数,用于CountDownLatch
+    private val mNeedWaitCount = AtomicInteger()
+
+    //所有的任务开始时间,结束时间
+    private var mStartTime: Long = 0
+    private var mFinishTime: Long = 0
+
+    //所有阻塞任务的总超时时间
+    private var mAllTaskWaitTimeOut = WAITING_TIME
+
+    fun setAllTaskWaitTimeOut(allTaskWaitTimeOut: Long): AppStartTaskDispatcher {
+        mAllTaskWaitTimeOut = allTaskWaitTimeOut
+        return this
+    }
+
+    fun addAppStartTask(appStartTask: AppStartTask?): AppStartTaskDispatcher {
+        if (appStartTask == null) {
+            throw RuntimeException("addAppStartTask() 传入的appStartTask为null")
+        }
+        mStartTaskList.add(appStartTask)
+        if (ifNeedWait(appStartTask)) {
+            mNeedWaitCount.getAndIncrement()
+        }
+        return this
+    }
+
+    fun start(): AppStartTaskDispatcher {
+        if (Looper.getMainLooper() != Looper.myLooper()) {
+            throw RuntimeException("start方法必须在主线程调用")
+        }
+        mStartTime = System.currentTimeMillis()
+        //拓扑排序,拿到排好序之后的任务队列
+        mSortTaskList = getSortResult(mStartTaskList, mTaskHashMap, mTaskChildHashMap)
+        initRealSortTask()
+        printSortTask()
+        mCountDownLatch = CountDownLatch(mNeedWaitCount.get())
+
+        Log.d(TAG_START_UP, "start(), mStartTime:$mStartTime, mNeedWaitCount:${mNeedWaitCount.get()}")
+        dispatchAppStartTask()
+        return this
+    }
+
+    //分别处理主线程和子线程的任务
+    private fun initRealSortTask() {
+        val sortTaskList = mSortTaskList
+        if (sortTaskList.isNullOrEmpty()) {
+            return
+        }
+        for (appStartTask in sortTaskList) {
+            if (appStartTask.isRunOnMainThread) {
+                mSortMainThreadTaskList.add(appStartTask)
+            } else {
+                mSortThreadPoolTaskList.add(appStartTask)
+            }
+        }
+    }
+
+    //输出排好序的Task
+    private fun printSortTask() {
+        if(AppBaseInfo.isRelease){
+           return
+        }
+        val sb = StringBuilder()
+        sb.append("当前所有任务排好的顺序为:")
+        val sortTaskList = mSortTaskList ?: emptyList()
+        for (i in sortTaskList.indices) {
+            val taskName = sortTaskList[i].javaClass.simpleName
+            if (i != 0) {
+                sb.append("--->")
+            }
+            sb.append(taskName)
+        }
+        Log.i(TAG_START_UP, sb.toString())
+
+        val mainTaskSb = StringBuilder()
+        mainTaskSb.append("    主线程任务顺序: ")
+        for (i in mSortMainThreadTaskList.indices) {
+            val taskName = mSortMainThreadTaskList[i].javaClass.simpleName
+            if (i != 0) {
+                mainTaskSb.append("--->")
+            }
+            mainTaskSb.append(taskName)
+        }
+        Log.i(TAG_START_UP, mainTaskSb.toString())
+
+        val threadTaskSb = StringBuilder()
+        threadTaskSb.append("    子线程任务顺序: ")
+        for (i in mSortThreadPoolTaskList.indices) {
+            val taskName = mSortThreadPoolTaskList[i].javaClass.simpleName
+            if (i != 0) {
+                threadTaskSb.append("--->")
+            }
+            threadTaskSb.append(taskName)
+        }
+        Log.i(TAG_START_UP, threadTaskSb.toString())
+
+
+    }
+
+    //发送任务
+    private fun dispatchAppStartTask() {
+        //再发送非主线程的任务
+        for (appStartTask in mSortThreadPoolTaskList) {
+            appStartTask.runOnExecutor().execute(AppStartTaskRunnable(appStartTask, this))
+        }
+        //再发送主线程的任务,防止主线程任务阻塞,导致子线程任务不能立刻执行
+        for (appStartTask in mSortMainThreadTaskList) {
+            AppStartTaskRunnable(appStartTask, this).run()
+        }
+    }
+
+    //通知Children一个前置任务已完成
+    fun setNotifyChildren(appStartTask: AppStartTask) {
+        val arrayList = mTaskChildHashMap[appStartTask.javaClass]
+        if (arrayList != null && arrayList.size > 0) {
+            for (aclass in arrayList) {
+                mTaskHashMap[aclass]?.notifyDependFinish()
+            }
+        }
+    }
+
+    //标记已经完成的Task
+    fun markAppStartTaskFinish(appStartTask: AppStartTask) {
+        if (ifNeedWait(appStartTask)) {
+            mCountDownLatch?.countDown()
+            mNeedWaitCount.getAndDecrement()
+        }
+        Log.i(TAG_START_UP, "markAppStartTaskFinish, ${appStartTask.javaClass.simpleName}, mNeedWaitCount:${mNeedWaitCount.get()}")
+    }
+
+    //是否需要等待,主线程的任务本来就是阻塞的,所以不用管
+    private fun ifNeedWait(task: AppStartTask): Boolean {
+        return !task.isRunOnMainThread && task.needWait()
+    }
+
+    //等待,阻塞主线程
+    fun await() {
+        try {
+            val countDownLatch = mCountDownLatch ?: throw RuntimeException("在调用await()之前,必须先调用start()")
+            Log.i(TAG_START_UP, "await(), mAllTaskWaitTimeOut:$mAllTaskWaitTimeOut")
+            countDownLatch.await(mAllTaskWaitTimeOut, TimeUnit.MILLISECONDS)
+
+            mFinishTime = System.currentTimeMillis() - mStartTime
+            Log.i(TAG_START_UP, "启动耗时:$mFinishTime")
+        } catch (e: InterruptedException) {
+            e.printStackTrace()
+        }
+    }
+
+    companion object {
+        //所有任务需要等待的时间
+        private const val WAITING_TIME = 10_000L
+        fun create(): AppStartTaskDispatcher {
+            return AppStartTaskDispatcher()
+        }
+    }
+}

+ 62 - 0
frame/startup/src/main/java/com/adealink/frame/startup/executor/TaskExecutorManager.kt

@@ -0,0 +1,62 @@
+package com.adealink.frame.startup.executor
+
+import com.adealink.frame.coroutine.dispatcher.Dispatcher
+import com.adealink.frame.thread.NamedThreadFactory
+import java.util.concurrent.BlockingQueue
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.RejectedExecutionHandler
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
+import kotlin.math.min
+
+class TaskExecutorManager private constructor() {
+    //获得cpu密集型线程池,因为占据CPU的时间片过多的话会影响性能,所以这里控制了最大并发,防止主线程的时间片减少
+    //CPU 密集型任务的线程池
+    val mainThreadPoolExecutor: ThreadPoolExecutor
+
+    //获得io密集型线程池,有好多任务其实占用的CPU time非常少,所以使用缓存线程池,基本上来着不拒
+    // IO 密集型任务的线程池
+    val subThreadPoolExecutor: ExecutorService
+
+    //线程池队列
+    private val mPoolWorkQueue: BlockingQueue<Runnable> = LinkedBlockingQueue()
+
+    // 这个是为了保障任务超出BlockingQueue的最大值,且线程池中的线程数已经达到MAXIMUM_POOL_SIZE时候,还有任务到来会采取任务拒绝策略,这里定义的策略就是
+    //再开一个缓存线程池去执行。当然BlockingQueue默认的最大值是int_max,所以理论上这里是用不到的
+    private val mHandler = RejectedExecutionHandler { r, executor -> Executors.newCachedThreadPool().execute(r) }
+
+    //初始化线程池
+    init {
+        //CPU 密集型任务的线程池
+        mainThreadPoolExecutor = ThreadPoolExecutor(
+            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS.toLong(), TimeUnit.SECONDS,
+            mPoolWorkQueue, NamedThreadFactory("startup-cpu", Thread.NORM_PRIORITY), mHandler
+        )
+        mainThreadPoolExecutor.allowCoreThreadTimeOut(true)
+
+        //io密集型线程池
+        subThreadPoolExecutor = Dispatcher.wenextThreadPoolExecutor
+        //Executors.newCachedThreadPool(NamedThreadFactory("startup-io", Thread.NORM_PRIORITY))
+    }
+
+    companion object {
+        val instance: TaskExecutorManager by lazy {
+            TaskExecutorManager()
+        }
+
+        //CPU 核数
+        private val CPU_COUNT = Runtime.getRuntime().availableProcessors()
+
+        //线程池线程数
+        private val CORE_POOL_SIZE = max(2.0, min((CPU_COUNT - 1).toDouble(), 5.0)).toInt()
+
+        //线程池线程数的最大值
+        private val MAXIMUM_POOL_SIZE = CORE_POOL_SIZE
+
+        //线程空置回收时间
+        private const val KEEP_ALIVE_SECONDS = 5
+    }
+}

+ 22 - 0
frame/startup/src/main/java/com/adealink/frame/startup/runnable/AppStartTaskRunnable.kt

@@ -0,0 +1,22 @@
+package com.adealink.frame.startup.runnable
+
+import android.os.Process
+import android.util.Log
+import com.adealink.frame.startup.TAG_START_UP
+import com.adealink.frame.startup.dispatcher.AppStartTaskDispatcher
+import com.adealink.frame.startup.task.AppStartTask
+
+class AppStartTaskRunnable(private val mAppStartTask: AppStartTask, private val mAppStartTaskDispatcher: AppStartTaskDispatcher) : Runnable {
+    override fun run() {
+        if (!mAppStartTask.isRunOnMainThread) {
+            Process.setThreadPriority(mAppStartTask.priority())
+        }
+        mAppStartTask.waitToNotify()
+        val startTime = System.currentTimeMillis()
+        Log.d(TAG_START_UP, "${mAppStartTask::class.java} start")
+        mAppStartTask.run()
+        Log.d(TAG_START_UP, "${mAppStartTask::class.java} end, cost ${System.currentTimeMillis() - startTime}")
+        mAppStartTaskDispatcher.setNotifyChildren(mAppStartTask)
+        mAppStartTaskDispatcher.markAppStartTaskFinish(mAppStartTask)
+    }
+}

+ 50 - 0
frame/startup/src/main/java/com/adealink/frame/startup/task/AppStartTask.kt

@@ -0,0 +1,50 @@
+package com.adealink.frame.startup.task
+
+import android.os.Process
+import com.adealink.frame.startup.base.TaskInterface
+import com.adealink.frame.startup.executor.TaskExecutorManager
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+
+abstract class AppStartTask : TaskInterface {
+
+    //是否在主线程执行
+    abstract val isRunOnMainThread: Boolean
+
+    override val dependsTaskList: List<Class<out AppStartTask>>
+        get() = emptyList()
+
+    // 当前Task依赖的Task数量(等父亲们执行完了,孩子才能执行),默认没有依赖
+    private val mDepends = CountDownLatch(dependsTaskList.size)
+
+    //当前Task等待,让父亲Task先执行
+    fun waitToNotify() {
+        try {
+            mDepends.await()
+        } catch (e: InterruptedException) {
+            e.printStackTrace()
+        }
+    }
+
+    override fun priority(): Int {
+        return Process.THREAD_PRIORITY_BACKGROUND
+    }
+
+    //执行任务代码
+    abstract fun run()
+
+    //他的父亲们执行完了一个
+    fun notifyDependFinish() {
+        mDepends.countDown()
+    }
+
+    override fun runOnExecutor(): Executor {
+        return TaskExecutorManager.instance.subThreadPoolExecutor
+    }
+
+    override fun needWait(): Boolean {
+        return false
+    }
+
+
+}

+ 68 - 0
frame/startup/src/main/java/com/adealink/frame/startup/util/AppStartTaskSortUtil.kt

@@ -0,0 +1,68 @@
+package com.adealink.frame.startup.util
+
+import com.adealink.frame.startup.task.AppStartTask
+import com.adealink.frame.startup.util.model.TaskSortModel
+import java.util.ArrayDeque
+import java.util.Deque
+
+
+object AppStartTaskSortUtil {
+    /**
+     * 拓扑排序
+     * taskIntegerHashMap每个Task的入度(key= Class < ? extends AppStartTask>)
+     * taskHashMap每个Task            (key= Class < ? extends AppStartTask>)
+     * taskChildHashMap每个Task的孩子  (key= Class < ? extends AppStartTask>)
+     * deque 入度为0的Task
+     */
+    fun getSortResult(
+        startTaskList: List<AppStartTask>,
+        taskHashMap: HashMap<Class<out AppStartTask>, AppStartTask>,
+        taskChildHashMap: HashMap<Class<out AppStartTask>, MutableList<Class<out AppStartTask>>>
+    ): List<AppStartTask> {
+        val sortTaskList: MutableList<AppStartTask> = ArrayList()
+        val taskIntegerHashMap = HashMap<Class<out AppStartTask>, TaskSortModel>()
+        val deque: Deque<Class<out AppStartTask>> = ArrayDeque()
+        for (task in startTaskList) {
+            if (!taskIntegerHashMap.containsKey(task.javaClass)) {
+                taskHashMap[task.javaClass] = task
+                taskIntegerHashMap[task.javaClass] = TaskSortModel(task.dependsTaskList.size)
+                taskChildHashMap[task.javaClass] = ArrayList()
+                //入度为0的队列
+                if (taskIntegerHashMap[task.javaClass]?.taskIn == 0) {
+                    deque.offer(task.javaClass)
+                }
+            } else {
+                throw RuntimeException("任务重复了: " + task.javaClass)
+            }
+        }
+        //把孩子都加进去
+        for (task in startTaskList) {
+            for (aclass in task.dependsTaskList) {
+                taskChildHashMap[aclass]?.add(task.javaClass)
+            }
+        }
+        //循环去除入度0的,再把孩子入度变成0的加进去
+        while (!deque.isEmpty()) {
+            val aclass = deque.poll() ?: break
+
+            taskHashMap[aclass]?.let {
+                sortTaskList.add(it)
+            }
+
+            taskChildHashMap[aclass]?.let {
+                for (classChild in it) {
+                    taskIntegerHashMap[classChild]?.let { sortModel ->
+                        sortModel.taskIn -= 1
+                        if (sortModel.taskIn == 0) {
+                            deque.offer(classChild)
+                        }
+                    }
+                }
+            }
+        }
+        if (sortTaskList.size != startTaskList.size) {
+            throw RuntimeException("出现环了")
+        }
+        return sortTaskList
+    }
+}

+ 3 - 0
frame/startup/src/main/java/com/adealink/frame/startup/util/model/TaskSortModel.kt

@@ -0,0 +1,3 @@
+package com.adealink.frame.startup.util.model
+
+class TaskSortModel(@JvmField var taskIn: Int)

+ 0 - 6
module/call/src/main/AndroidManifest.xml

@@ -53,12 +53,6 @@
             android:screenOrientation="portrait"
             android:theme="@style/AppTheme" />
 
-        <provider
-            android:name="com.tencent.qcloud.tuikit.tuicallkit.internal.ServiceInitializer"
-            android:authorities="${applicationId}.ServiceInitializer"
-            android:enabled="true"
-            android:exported="false" />
-
         <receiver
             android:name=".view.floatview.incoming.IncomingCallReceiver"
             android:enabled="true"

+ 15 - 3
module/call/src/main/java/com/adealink/weparty/call/CallServiceImpl.kt

@@ -1,5 +1,6 @@
 package com.adealink.weparty.call
 
+import android.app.Activity
 import android.app.Application
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStoreOwner
@@ -37,6 +38,7 @@ import com.tencent.cloud.tuikit.engine.common.TUICommonDefine
 import com.tencent.qcloud.tim.push.TIMPushManager
 import com.tencent.qcloud.tuicore.TUIConstants
 import com.tencent.qcloud.tuicore.TUICore
+import com.tencent.qcloud.tuikit.tuicallkit.internal.ServiceInitializer
 import com.tencent.qcloud.tuikit.tuicallkit.manager.EngineManager
 import com.tencent.qcloud.tuikit.tuicallkit.state.TUICallState
 import kotlinx.coroutines.suspendCancellableCoroutine
@@ -45,13 +47,23 @@ import kotlinx.coroutines.withContext
 @RegisterService(ICallService::class)
 class CallServiceImpl : ICallService {
 
-    override fun init(application: Application) {
+    override fun appOnCreateMainTask(application: Application) {
+        ServiceInitializer.createInstance(application)
         WenextUICallKitImpl.createInstance(application)
+        registerFCMDataMessageNotifyEvent()
+        TIMPushManager.getInstance().forceUseFCMPushChannel(true)
+    }
+
+    override fun appOnCreateSubTask(application: Application) {
         callLoginManager.init(application)
         callPingManager.init(application)
         callManager.init()
-        registerFCMDataMessageNotifyEvent()
-        TIMPushManager.getInstance().forceUseFCMPushChannel(true)
+    }
+
+    override fun activityOnCreateMainTask() {
+    }
+
+    override fun activityOnCreateSubTask() {
     }
 
     private fun registerFCMDataMessageNotifyEvent() {

+ 5 - 3
module/call/src/main/java/com/adealink/weparty/call/manager/CallLoginManager.kt

@@ -54,9 +54,11 @@ class CallLoginManager : BaseFrame<ICallLoginListener>(),
 
     private fun handleLogout() {
         Log.d(TAG_CALL_LOGIN, "handleLogout")
-        callManager.hangup(null)
-        callPingManager.stopCallPing()
-        logout()
+        if (isLogin()) {
+            callManager.hangup(null)
+            callPingManager.stopCallPing()
+            logout()
+        }
     }
 
     private fun reLogin() {

+ 0 - 1
module/call/src/main/java/com/adealink/weparty/call/manager/CallManager.kt

@@ -66,7 +66,6 @@ val callManager: ICallManager by lazy { CallManager() }
  * 1. 发起通话
  * 2. 应答通话
  * 3. 监听通话行为
- * 4. 加入媒体房(语聊房的一些通知需要加入房间后才能送达)
  */
 class CallManager : BaseFrame<ICallListener>(), ICallManager {
 

+ 6 - 5
module/call/src/main/java/com/adealink/weparty/call/view/floatview/calling/CallingFloatView.kt

@@ -6,6 +6,7 @@ import android.view.WindowManager
 import com.adealink.frame.aab.util.getCompatDimensionPixelSize
 import com.adealink.frame.log.Log
 import com.adealink.frame.router.Router
+import com.adealink.frame.util.AppUtil
 import com.adealink.frame.util.DisplayUtil
 import com.adealink.weparty.call.R
 import com.adealink.weparty.call.constant.TAG_CALL_FLOAT_VIEW
@@ -15,7 +16,6 @@ import com.adealink.weparty.commonui.widget.floatview.data.FloatWindowType
 import com.adealink.weparty.commonui.widget.floatview.view.BaseDragFloatView
 import com.adealink.weparty.module.call.Call
 import com.tencent.cloud.tuikit.engine.call.TUICallDefine
-import com.tencent.qcloud.tuicore.ServiceInitializer
 import com.tencent.qcloud.tuicore.TUICore
 import com.tencent.qcloud.tuikit.tuicallkit.data.Constants
 import com.tencent.qcloud.tuikit.tuicallkit.state.TUICallState
@@ -52,10 +52,11 @@ class CallingFloatView(floatData: CallingFloatData) :
         callingView.setOnClickListener {
             cancelCallingView()
             if (TUICallState.instance.selfUser.get().callStatus.get() != TUICallDefine.Status.None) {
-                val context = ServiceInitializer.getAppContext()
-                Router.build(context, Call.Call.PATH)
-                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                    .start()
+                AppUtil.currentActivity?.let {
+                    Router.build(context, Call.Call.PATH)
+                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                        .start()
+                }
             }
         }
     }

+ 20 - 45
module/call/src/main/java/com/tencent/qcloud/tuikit/tuicallkit/internal/ServiceInitializer.kt

@@ -1,11 +1,7 @@
 package com.tencent.qcloud.tuikit.tuicallkit.internal
 
 import android.app.Activity
-import android.content.ContentProvider
-import android.content.ContentValues
 import android.content.Context
-import android.database.Cursor
-import android.net.Uri
 import com.adealink.frame.log.Log
 import com.adealink.frame.router.Router
 import com.adealink.frame.util.ActivityLifecycleCallbacksExt
@@ -15,21 +11,32 @@ import com.adealink.weparty.call.WenextUICallKitImpl
 import com.adealink.weparty.call.constant.TAG_CALL
 import com.adealink.weparty.call.constant.TAG_CALL_FLOW
 import com.adealink.weparty.commonui.widget.floatview.WindowManagerProxy
-import com.adealink.weparty.module.call.Call
-import com.adealink.weparty.commonui.widget.floatview.FloatViewFactory
 import com.adealink.weparty.commonui.widget.floatview.data.FloatWindowType
+import com.adealink.weparty.module.call.Call
 import com.tencent.qcloud.tuicore.TUIConstants
 import com.tencent.qcloud.tuicore.TUICore
 import com.tencent.qcloud.tuicore.TUILogin
 import com.tencent.qcloud.tuikit.tuicallkit.data.Constants
 
-/**
- * `TUICallKit` uses `ContentProvider` to be registered with `TUICore`.
- * (`TUICore` is the connection and communication class of each component)
- */
-class ServiceInitializer : ContentProvider(), ActivityLifecycleCallbacksExt {
-    fun init(context: Context?) {
-        val callingService: TUICallKitService = TUICallKitService.sharedInstance(context!!)
+
+class ServiceInitializer private constructor(context: Context) : ActivityLifecycleCallbacksExt {
+
+    companion object {
+        private var instance: ServiceInitializer? = null
+        fun createInstance(context: Context): ServiceInitializer {
+            if (null == instance) {
+                synchronized(ServiceInitializer::class.java) {
+                    if (null == instance) {
+                        instance = ServiceInitializer(context)
+                    }
+                }
+            }
+            return instance!!
+        }
+    }
+
+    init {
+        val callingService: TUICallKitService = TUICallKitService.sharedInstance(context)
         TUICore.registerService(TUIConstants.TUICalling.SERVICE_NAME, callingService)
 
         val audioRecordService = TUIAudioMessageRecordService(context)
@@ -88,36 +95,4 @@ class ServiceInitializer : ContentProvider(), ActivityLifecycleCallbacksExt {
         }
         return false
     }
-
-    override fun onCreate(): Boolean {
-        val appContext = context!!.applicationContext
-        init(appContext)
-        return false
-    }
-
-    override fun query(
-        uri: Uri, projection: Array<String>?, selection: String?,
-        selectionArgs: Array<String>?, sortOrder: String?
-    ): Cursor? {
-        return null
-    }
-
-    override fun getType(uri: Uri): String? {
-        return null
-    }
-
-    override fun insert(uri: Uri, values: ContentValues?): Uri? {
-        return null
-    }
-
-    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
-        return 0
-    }
-
-    override fun update(
-        uri: Uri, values: ContentValues?, selection: String?,
-        selectionArgs: Array<String>?
-    ): Int {
-        return 0
-    }
 }

+ 3 - 0
module/message/src/main/java/com/adealink/weparty/message/MessageHomeFragment.kt

@@ -39,6 +39,9 @@ import com.adealink.weparty.R as APP_R
 @RouterUri(path = [Message.Home.PATH], desc = "消息首页")
 class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home), IScrollManager {
 
+    override val isLazyInit: Boolean = true
+    override val isLazyLoad: Boolean = true
+
     companion object {
         var backFromConversationActivity = false
         var hasSendMessage = false

+ 16 - 2
module/message/src/main/java/com/adealink/weparty/message/MessageServiceImpl.kt

@@ -1,5 +1,6 @@
 package com.adealink.weparty.message
 
+import android.app.Activity
 import android.app.Application
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStoreOwner
@@ -24,12 +25,25 @@ import io.rong.imlib.model.Conversation
 @RegisterService(IMessageService::class)
 class MessageServiceImpl : IMessageService {
 
-    override fun init(application: Application) {
+    override fun appOnCreateMainTask(application: Application) {
         IMConfigCenter.init()
         imConnectManager.init()
-        messageManager.init()
+        messageManager.appOnCreateMainTask(application)
     }
 
+    override fun appOnCreateSubTask(application: Application) {
+        messageManager.appOnCreateSubTask(application)
+    }
+
+    override fun activityOnCreateMainTask() {
+        messageManager.activityOnCreateMainTask()
+    }
+
+    override fun activityOnCreateSubTask() {
+        messageManager.activityOnCreateSubTask()
+    }
+
+
     override fun getMessageViewModel(owner: ViewModelStoreOwner): IMessageViewModel {
         return ViewModelProvider(owner, MessageViewModelFactory())[MessageViewModel::class.java]
     }

+ 2 - 2
module/message/src/main/java/com/adealink/weparty/message/manager/IMessageManager.kt

@@ -4,14 +4,14 @@ import com.adealink.frame.base.Rlt
 import com.adealink.frame.frame.IBaseFrame
 import com.adealink.frame.network.data.Res
 import com.adealink.frame.router.Router
+import com.adealink.frame.startup.IStartUpTask
 import com.adealink.weparty.message.listener.IMessageListener
 import com.adealink.weparty.module.account.Account
 import com.adealink.weparty.module.message.data.CustomerInfo
 import com.adealink.weparty.module.message.data.SessionInfo
 import com.adealink.weparty.module.room.Room
 
-interface IMessageManager : IBaseFrame<IMessageListener> {
-    fun init()
+interface IMessageManager : IBaseFrame<IMessageListener>, IStartUpTask {
     suspend fun batchQuerySayHiState(uidSet: Set<Long>): Rlt<Map<Long, Boolean>>
     fun getSayHiState(uid: Long): Boolean
     /**

+ 13 - 2
module/message/src/main/java/com/adealink/weparty/message/manager/MessageManager.kt

@@ -1,5 +1,7 @@
 package com.adealink.weparty.message.manager
 
+import android.app.Activity
+import android.app.Application
 import android.graphics.Bitmap
 import android.text.Spannable
 import android.text.SpannableString
@@ -255,14 +257,23 @@ class MessageManager : BaseFrame<IMessageListener>(), IMessageManager,
         App.instance.networkService.subscribeNotify(im1v1ContinuousSendMessageDeductNotify)
     }
 
-    override fun init() {
-        IMUserInfoManager.setUserInfoProvider(this)
+    override fun appOnCreateMainTask(application: Application) {
         IMService.innerService.setMessageInterceptor(messageInterceptor)
         IMService.innerService.addOnReceiveMessageListener(onReceiveMessageListener)
         IMService.innerService.addMessageExpansionListener(messageExpansionListener)
+    }
+
+    override fun appOnCreateSubTask(application: Application) {
+    }
+
+    override fun activityOnCreateMainTask() {
+        IMUserInfoManager.setUserInfoProvider(this)
         CoupleModule.addListener(this)
     }
 
+    override fun activityOnCreateSubTask() {
+    }
+
     override suspend fun sendQuickMessage(
         toUid: Long,
         message1: String?,

+ 2 - 0
module/moment/src/main/java/com/adealink/weparty/moment/usermoment/MomentFragment.kt

@@ -43,6 +43,7 @@ import kotlinx.coroutines.launch
 @RouterUri(path = [Moment.Home.PATH], desc = "主页Moment Tab")
 class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener, IScrollManager {
 
+    override val isLazyInit: Boolean = true
     override val isLazyLoad: Boolean = true
 
     private val binding by viewBinding(FragmentMomentBinding::bind)
@@ -76,6 +77,7 @@ class MomentFragment : BaseFragment(R.layout.fragment_moment), IMomentListener,
             height = DisplayUtil.getStatusBarHeight(requireActivity())
         }
 
+        binding.momentListViewPage.offscreenPageLimit = 1
         binding.momentListViewPage.adapter = pageAdapter
         binding.momentListViewPage.isSaveEnabled = false
         (binding.momentListViewPage.getChildAt(0) as? RecyclerView)?.setOverScrollModeToNever()

+ 3 - 0
module/profile/src/main/java/com/adealink/weparty/profile/me/MeFragment.kt

@@ -83,6 +83,9 @@ import com.adealink.weparty.R as APP_R
 @RouterUri(path = [Profile.Me.PATH], desc = "首页我的")
 class MeFragment : BaseFragment(R.layout.fragment_me), IScrollManager {
 
+    override val isLazyInit: Boolean = true
+    override val isLazyLoad: Boolean = true
+
     private val binding by viewBinding(FragmentMeBinding::bind)
     private val profileViewModel by viewModels<ProfileViewModel> { ProfileViewModelFactory() }
     private val walletViewModel by fastLazy { WalletModule.getWalletViewModel(this) }

+ 3 - 0
module/room/src/main/java/com/adealink/weparty/room/roomlist/RoomListFragment.kt

@@ -32,6 +32,9 @@ import com.adealink.weparty.ui.listener.setOnGameClick
 @RouterUri(path = [Room.RoomList.PATH], desc = "首页房间列表")
 class RoomListFragment : BaseFragment(R.layout.fragment_room_list), IScrollManager {
 
+    override val isLazyInit: Boolean = true
+    override val isLazyLoad: Boolean = true
+
     companion object {
         const val TAB_INDEX_MINE = 0
         const val TAB_INDEX_POPULAR = 1

+ 1 - 0
settings.gradle

@@ -63,6 +63,7 @@ rootProject.name = "yoki"
 include ':app'
 
 include ':frame:room'
+include ':frame:startup'
 include ':frame:imkit'
 
 include ':module:wallet'