├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ └── android_startup_diagram.png
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_common.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── activity_more.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── rousetime
│ │ │ │ └── sample
│ │ │ │ ├── provider
│ │ │ │ └── MultipleProcessStartupProvider.kt
│ │ │ │ ├── Constants.kt
│ │ │ │ ├── startup
│ │ │ │ ├── SampleSyncOneStartup.kt
│ │ │ │ ├── SampleSyncThreeStartup.kt
│ │ │ │ ├── SampleFirstStartup.kt
│ │ │ │ ├── SampleAsyncTwoStartup.kt
│ │ │ │ ├── SampleAsyncFiveStartup.kt
│ │ │ │ ├── SampleAsyncSixStartup.kt
│ │ │ │ ├── multiple
│ │ │ │ │ ├── SampleMultipleThirdStartup.kt
│ │ │ │ │ ├── SampleMultipleFirstStartup.kt
│ │ │ │ │ ├── SampleMultipleFourthStartup.kt
│ │ │ │ │ ├── SampleMultipleSecondStartup.kt
│ │ │ │ │ ├── SampleMultipleSixthStartup.kt
│ │ │ │ │ └── SampleMultipleFifthStartup.kt
│ │ │ │ ├── priority
│ │ │ │ │ ├── SamplePrioritySecondStartup.kt
│ │ │ │ │ ├── SamplePriorityThirdStartup.kt
│ │ │ │ │ └── SamplePriorityFirstStartup.kt
│ │ │ │ ├── SampleManualDispatchStartup.kt
│ │ │ │ ├── SampleFourthStartup.kt
│ │ │ │ ├── SampleSyncFourStartup.kt
│ │ │ │ ├── SampleSyncTwoStartup.kt
│ │ │ │ ├── SampleSyncFiveStartup.kt
│ │ │ │ ├── SampleAsyncFourStartup.kt
│ │ │ │ ├── SampleAsyncOneStartup.kt
│ │ │ │ ├── SampleAsyncThreeStartup.kt
│ │ │ │ ├── SampleAsyncSevenStartup.kt
│ │ │ │ ├── SampleThirdStartup.kt
│ │ │ │ ├── SampleSecondStartup.kt
│ │ │ │ └── SampleStartupProviderConfig.kt
│ │ │ │ ├── SampleMoreActivity.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── SampleApplication.kt
│ │ │ │ ├── MultipleProcessService.kt
│ │ │ │ └── SampleCommonActivity.kt
│ │ ├── aidl
│ │ │ └── com
│ │ │ │ └── rousetime
│ │ │ │ └── sample
│ │ │ │ ├── IServiceListenerInterface.aidl
│ │ │ │ └── IMultipleProcessServiceInterface.aidl
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── rousetime
│ │ │ └── android_startup
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── rousetime
│ │ └── android_startup
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── android-startup
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── rousetime
│ │ │ │ └── android_startup
│ │ │ │ ├── model
│ │ │ │ ├── ResultModel.kt
│ │ │ │ ├── LoggerLevel.kt
│ │ │ │ ├── CostTimesModel.kt
│ │ │ │ ├── StartupSortStore.kt
│ │ │ │ ├── StartupProviderStore.kt
│ │ │ │ └── StartupConfig.kt
│ │ │ │ ├── executor
│ │ │ │ ├── StartupExecutor.kt
│ │ │ │ └── ExecutorManager.kt
│ │ │ │ ├── annotation
│ │ │ │ ├── MultipleProcess.kt
│ │ │ │ └── ThreadPriority.kt
│ │ │ │ ├── provider
│ │ │ │ ├── StartupProviderConfig.kt
│ │ │ │ └── StartupProvider.kt
│ │ │ │ ├── execption
│ │ │ │ └── StartupException.kt
│ │ │ │ ├── extensions
│ │ │ │ └── StartupExtensions.kt
│ │ │ │ ├── StartupListener.kt
│ │ │ │ ├── dispatcher
│ │ │ │ ├── ManagerDispatcher.kt
│ │ │ │ ├── Dispatcher.kt
│ │ │ │ └── StartupManagerDispatcher.kt
│ │ │ │ ├── utils
│ │ │ │ ├── ProcessUtils.kt
│ │ │ │ ├── StartupLogUtils.kt
│ │ │ │ └── StartupCostTimesUtils.kt
│ │ │ │ ├── manager
│ │ │ │ └── StartupCacheManager.kt
│ │ │ │ ├── AndroidStartup.kt
│ │ │ │ ├── Startup.kt
│ │ │ │ ├── run
│ │ │ │ └── StartupRunnable.kt
│ │ │ │ ├── StartupInitializer.kt
│ │ │ │ ├── sort
│ │ │ │ └── TopologySort.kt
│ │ │ │ └── StartupManager.kt
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── rousetime
│ │ │ └── android_startup
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── rousetime
│ │ └── android_startup
│ │ └── ExampleInstrumentedTest.kt
├── consumer-rules.pro
├── proguard-rules.pro
├── maven.gradle
├── build.gradle
├── install.gradle
├── bintray.gradle
└── maven-center.gradle
├── keystore
├── images
├── qq.png
├── wx.jpg
└── android_startup_diagram.png
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── .gitignore
├── gradlew.bat
├── gradlew
├── LICENSE
├── README-ch.md
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/android-startup/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/keystore
--------------------------------------------------------------------------------
/images/qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/images/qq.png
--------------------------------------------------------------------------------
/images/wx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/images/wx.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':android-startup'
2 | include ':app'
3 | rootProject.name = "android-startup"
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/images/android_startup_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/images/android_startup_diagram.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/android_startup_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/idisfkj/android-startup/HEAD/app/src/main/res/drawable-xxhdpi/android_startup_diagram.png
--------------------------------------------------------------------------------
/android-startup/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | /
5 |
--------------------------------------------------------------------------------
/android-startup/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
2 | -keep public class * extends com.rousetime.android_startup.AndroidStartup { *; }
3 | -keep class * implements com.rousetime.android_startup.provider.StartupProviderConfig { *; }
4 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/model/ResultModel.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.model
2 |
3 | /**
4 | * Created by idisfkj on 7/19/21.
5 | */
6 | data class ResultModel(val result: T?)
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jul 23 09:41:34 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/android-startup/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | android.startup
4 | android.startup.provider.config
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/model/LoggerLevel.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.model
2 |
3 | /**
4 | * Created by idisfkj on 2020/7/24.
5 | * Email: idisfkj@gmail.com.
6 | */
7 | enum class LoggerLevel(val level: Int) {
8 | NONE(0),
9 | ERROR(1),
10 | DEBUG(2)
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/provider/MultipleProcessStartupProvider.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.provider
2 |
3 | import com.rousetime.android_startup.provider.StartupProvider
4 |
5 | /**
6 | * Created by idisfkj on 2020/9/14.
7 | * Email: idisfkj@gmail.com.
8 | */
9 | class MultipleProcessStartupProvider : StartupProvider()
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/aidl/com/rousetime/sample/IServiceListenerInterface.aidl:
--------------------------------------------------------------------------------
1 | // IServiceListenerInterface.aidl
2 | package com.rousetime.sample;
3 |
4 | // Declare any non-default types here with import statements
5 |
6 | interface IServiceListenerInterface {
7 |
8 | void onCompleted(String result, long totalMainThreadCostTime);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/executor/StartupExecutor.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.executor
2 |
3 | import java.util.concurrent.Executor
4 |
5 | /**
6 | * Created by idisfkj on 2020/7/23.
7 | * Email: idisfkj@gmail.com.
8 | */
9 | interface StartupExecutor {
10 |
11 | fun createExecutor(): Executor
12 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/annotation/MultipleProcess.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.annotation
2 |
3 | /**
4 | * Created by idisfkj on 2020/9/14.
5 | * Email: idisfkj@gmail.com.
6 | */
7 | @MustBeDocumented
8 | @Retention
9 | @Target(AnnotationTarget.CLASS)
10 | annotation class MultipleProcess(vararg val process: String)
11 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/provider/StartupProviderConfig.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.provider
2 |
3 | import com.rousetime.android_startup.model.StartupConfig
4 |
5 | /**
6 | * Created by idisfkj on 2020/7/28.
7 | * Email: idisfkj@gmail.com.
8 | */
9 | interface StartupProviderConfig {
10 |
11 | fun getConfig(): StartupConfig
12 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/execption/StartupException.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.execption
2 |
3 | /**
4 | * Created by idisfkj on 2020/7/28.
5 | * Email: idisfkj@gmail.com.
6 | */
7 | internal class StartupException : RuntimeException {
8 |
9 | constructor(message: String?) : super(message)
10 |
11 | constructor(t: Throwable) : super(t)
12 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/model/CostTimesModel.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.model
2 |
3 | /**
4 | * Created by idisfkj on 2020/8/10.
5 | * Email: idisfkj@gmail.com.
6 | */
7 | data class CostTimesModel(
8 | val name: String,
9 | val callOnMainThread: Boolean,
10 | val waitOnMainThread: Boolean,
11 | val startTime: Long,
12 | var endTime: Long = 0L
13 | )
14 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/annotation/ThreadPriority.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.annotation
2 |
3 | import android.os.Process
4 |
5 | /**
6 | * Created by idisfkj on 2020/9/16.
7 | * Email: idisfkj@gmail.com.
8 | */
9 | @MustBeDocumented
10 | @Retention
11 | @Target(AnnotationTarget.CLASS)
12 | annotation class ThreadPriority(val priority: Int = Process.THREAD_PRIORITY_DEFAULT)
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/model/StartupSortStore.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.model
2 |
3 | import com.rousetime.android_startup.Startup
4 |
5 | /**
6 | * Created by idisfkj on 2020/7/27.
7 | * Email: idisfkj@gmail.com.
8 | */
9 | data class StartupSortStore(
10 | val result: MutableList>,
11 | val startupMap: Map>,
12 | val startupChildrenMap: Map>
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/aidl/com/rousetime/sample/IMultipleProcessServiceInterface.aidl:
--------------------------------------------------------------------------------
1 | // IMultipleProcessServiceInterface.aidl
2 | package com.rousetime.sample;
3 |
4 | // Declare any non-default types here with import statements
5 | import com.rousetime.sample.IServiceListenerInterface;
6 |
7 | interface IMultipleProcessServiceInterface {
8 |
9 | void addServiceListener(IServiceListenerInterface serviceListener);
10 |
11 | void initStartup();
12 |
13 | void clear();
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/test/java/com/rousetime/android_startup/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/model/StartupProviderStore.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.model
2 |
3 | import com.rousetime.android_startup.AndroidStartup
4 | import com.rousetime.android_startup.provider.StartupProviderConfig
5 |
6 | /**
7 | * Created by idisfkj on 2020/7/28.
8 | * Email: idisfkj@gmail.com.
9 | */
10 | data class StartupProviderStore(
11 | val result: List>,
12 | val config: StartupProviderConfig?
13 | )
14 |
--------------------------------------------------------------------------------
/android-startup/src/test/java/com/rousetime/android_startup/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample
2 |
3 | /**
4 | * Created by idisfkj on 2020/8/17.
5 | * Email: idisfkj@gmail.com.
6 | */
7 | object Constants {
8 | const val SYNC_AND_SYNC = "sync_and_sync"
9 | const val SYNC_AND_ASYNC = "sync_and_async"
10 | const val ASYNC_AND_SYNC = "async_and_sync"
11 | const val ASYNC_AND_ASYNC = "async_and_async"
12 | const val ASYNC_AND_ASYNC_AWAIT_MAIN_THREAD = "async_and_async_await_main_thread"
13 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/extensions/StartupExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.extensions
2 |
3 | import com.rousetime.android_startup.Startup
4 |
5 | /**
6 | * Created by idisfkj on 2020/8/10.
7 | * Email: idisfkj@gmail.com.
8 | */
9 |
10 | private const val DEFAULT_KEY = "com.rousetime.android_startup.defaultKey"
11 |
12 | internal fun Class>.getUniqueKey(): String {
13 | return "$DEFAULT_KEY:$name"
14 | }
15 |
16 | internal fun String.getUniqueKey(): String = "$DEFAULT_KEY:$this"
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleSyncOneStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 |
6 | /**
7 | * Created by idisfkj on 2020/8/17.
8 | * Email: idisfkj@gmail.com.
9 | */
10 | class SampleSyncOneStartup: AndroidStartup() {
11 |
12 | override fun create(context: Context): String? {
13 | return "sync one"
14 | }
15 |
16 | override fun callCreateOnMainThread(): Boolean = true
17 |
18 | override fun waitOnMainThread(): Boolean = false
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleSyncThreeStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 |
6 | /**
7 | * Created by idisfkj on 2020/8/18.
8 | * Email : idisfkj@gmail.com.
9 | */
10 | class SampleSyncThreeStartup : AndroidStartup() {
11 | override fun callCreateOnMainThread(): Boolean = true
12 |
13 | override fun create(context: Context): String? {
14 | return "sync three"
15 | }
16 |
17 | override fun waitOnMainThread(): Boolean = false
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleFirstStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 |
6 | /**
7 | * Created by idisfkj on 2020/7/24.
8 | * Email: idisfkj@gmail.com.
9 | */
10 | class SampleFirstStartup : AndroidStartup() {
11 |
12 | override fun callCreateOnMainThread(): Boolean = true
13 |
14 | override fun waitOnMainThread(): Boolean = false
15 |
16 | override fun create(context: Context): String? {
17 | return this.javaClass.simpleName
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/StartupListener.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import com.rousetime.android_startup.model.CostTimesModel
4 |
5 | /**
6 | * Created by idisfkj on 2020/8/10.
7 | * Email: idisfkj@gmail.com.
8 | */
9 | interface StartupListener {
10 |
11 | /**
12 | * call when all startup completed.
13 | * @param totalMainThreadCostTime cost times of main thread.
14 | * @param costTimesModels list of cost times for every startup.
15 | */
16 | fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List)
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleAsyncTwoStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 |
6 | /**
7 | * Created by idisfkj on 2020/8/17.
8 | * Email: idisfkj@gmail.com.
9 | */
10 | class SampleAsyncTwoStartup: AndroidStartup() {
11 |
12 | override fun create(context: Context): String? {
13 | Thread.sleep(3000)
14 | return "async two"
15 | }
16 |
17 | override fun callCreateOnMainThread(): Boolean = false
18 |
19 | override fun waitOnMainThread(): Boolean = false
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleAsyncFiveStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 |
6 | /**
7 | * Created by idisfkj on 2020/8/18.
8 | * Email : idisfkj@gmail.com.
9 | */
10 | class SampleAsyncFiveStartup: AndroidStartup() {
11 |
12 | override fun callCreateOnMainThread(): Boolean = false
13 |
14 | override fun create(context: Context): String? {
15 | Thread.sleep(1000)
16 | return "async five"
17 | }
18 |
19 | override fun waitOnMainThread(): Boolean = false
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleAsyncSixStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 |
6 | /**
7 | * Created by idisfkj on 2020/8/18.
8 | * Email : idisfkj@gmail.com.
9 | */
10 | class SampleAsyncSixStartup: AndroidStartup() {
11 |
12 | override fun callCreateOnMainThread(): Boolean = false
13 |
14 | override fun create(context: Context): String? {
15 | Thread.sleep(2000)
16 | return "async six"
17 | }
18 |
19 | override fun waitOnMainThread(): Boolean = false
20 |
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/multiple/SampleMultipleThirdStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.multiple
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.annotation.MultipleProcess
6 |
7 | /**
8 | * Created by idisfkj on 2020/9/14.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | @MultipleProcess(":multiple.test")
12 | class SampleMultipleThirdStartup : AndroidStartup() {
13 |
14 | override fun create(context: Context): String? {
15 | return SampleMultipleThirdStartup::class.java.name
16 | }
17 |
18 | override fun callCreateOnMainThread(): Boolean = false
19 |
20 | override fun waitOnMainThread(): Boolean = false
21 |
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/multiple/SampleMultipleFirstStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.multiple
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.annotation.MultipleProcess
6 |
7 | /**
8 | * Created by idisfkj on 2020/9/14.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | @MultipleProcess(":multiple.provider")
12 | class SampleMultipleFirstStartup : AndroidStartup() {
13 |
14 | override fun create(context: Context): String? {
15 | return SampleMultipleFirstStartup::class.java.simpleName
16 | }
17 |
18 | override fun callCreateOnMainThread(): Boolean = false
19 |
20 | override fun waitOnMainThread(): Boolean = false
21 |
22 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/dispatcher/ManagerDispatcher.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.dispatcher
2 |
3 | import com.rousetime.android_startup.Startup
4 | import com.rousetime.android_startup.model.StartupSortStore
5 |
6 | /**
7 | * Created by idisfkj on 2020/7/27.
8 | * Email: idisfkj@gmail.com.
9 | */
10 | interface ManagerDispatcher {
11 |
12 | /**
13 | * dispatch prepare
14 | */
15 | fun prepare()
16 |
17 | /**
18 | * dispatch startup to executing.
19 | */
20 | fun dispatch(startup: Startup<*>, sortStore: StartupSortStore)
21 |
22 | /**
23 | * notify children when dependency startup completed.
24 | */
25 | fun notifyChildren(dependencyParent: Startup<*>, result: Any?, sortStore: StartupSortStore)
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/priority/SamplePrioritySecondStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.priority
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 |
6 | /**
7 | * Created by idisfkj on 2020/9/16.
8 | * Email: idisfkj@gmail.com.
9 | */
10 | class SamplePrioritySecondStartup : AndroidStartup() {
11 |
12 | override fun create(context: Context): String? {
13 | val i = buildString {
14 | repeat(1000000) {
15 | append("$it")
16 | }
17 | }
18 | return SamplePrioritySecondStartup::class.java.simpleName
19 | }
20 |
21 | override fun callCreateOnMainThread(): Boolean = false
22 |
23 | override fun waitOnMainThread(): Boolean = false
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleManualDispatchStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 |
6 | /**
7 | * Created by idisfkj on 2020/8/18.
8 | * Email: idisfkj@gmail.com.
9 | */
10 | class SampleManualDispatchStartup : AndroidStartup() {
11 |
12 | override fun create(context: Context): String? {
13 | Thread {
14 | Thread.sleep(2000)
15 | // manual dispatch
16 | onDispatch()
17 | }.start()
18 | return "manual dispatch"
19 | }
20 |
21 | override fun callCreateOnMainThread(): Boolean = true
22 |
23 | override fun waitOnMainThread(): Boolean = false
24 |
25 | override fun manualDispatch(): Boolean = true
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/multiple/SampleMultipleFourthStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.multiple
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.annotation.MultipleProcess
6 |
7 | /**
8 | * Created by idisfkj on 2020/9/15.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | @MultipleProcess(":multiple.process.service", ":multiple.test")
12 | class SampleMultipleFourthStartup : AndroidStartup() {
13 |
14 | override fun create(context: Context): String? {
15 | Thread.sleep(1000)
16 | return SampleMultipleFourthStartup::class.java.simpleName
17 | }
18 |
19 | override fun callCreateOnMainThread(): Boolean = false
20 |
21 | override fun waitOnMainThread(): Boolean = false
22 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/SampleMoreActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.view.View
6 | import androidx.appcompat.app.AppCompatActivity
7 |
8 | /**
9 | * Created by idisfkj on 2020/8/12.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | class SampleMoreActivity : AppCompatActivity() {
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.activity_more)
17 | }
18 |
19 | fun onClick(view: View) {
20 | goToCommonActivity(view.id)
21 | }
22 |
23 | private fun goToCommonActivity(id: Int) {
24 | startActivity(Intent(this, SampleCommonActivity::class.java).apply {
25 | putExtra("id", id)
26 | })
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/rousetime/android_startup/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.rousetime.android_startup", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/android-startup/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/android-startup/src/androidTest/java/com/rousetime/android_startup/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.rousetime.android_startup.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/dispatcher/Dispatcher.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.dispatcher
2 |
3 | /**
4 | * Created by idisfkj on 2020/7/27.
5 | * Email: idisfkj@gmail.com.
6 | */
7 | interface Dispatcher {
8 |
9 | /**
10 | * Return true call the create function on main thread otherwise false.
11 | */
12 | fun callCreateOnMainThread(): Boolean
13 |
14 | /**
15 | * Return true block the main thread until the startup completed otherwise false.
16 | *
17 | * Note: If the function [callCreateOnMainThread] return true, main thread default block.
18 | */
19 | fun waitOnMainThread(): Boolean
20 |
21 | /**
22 | * To wait dependencies startup completed.
23 | */
24 | fun toWait()
25 |
26 | /**
27 | * To notify the startup when dependencies startup completed.
28 | */
29 | fun toNotify()
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/priority/SamplePriorityThirdStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.priority
2 |
3 | import android.content.Context
4 | import android.os.Process
5 | import com.rousetime.android_startup.AndroidStartup
6 | import com.rousetime.android_startup.annotation.ThreadPriority
7 |
8 | /**
9 | * Created by idisfkj on 2020/9/16.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | @ThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
13 | class SamplePriorityThirdStartup : AndroidStartup() {
14 | override fun create(context: Context): String? {
15 | val i = buildString {
16 | repeat(1000000) {
17 | append("$it")
18 | }
19 | }
20 | return SamplePriorityThirdStartup::class.java.simpleName
21 | }
22 |
23 | override fun callCreateOnMainThread(): Boolean = false
24 |
25 | override fun waitOnMainThread(): Boolean = false
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/priority/SamplePriorityFirstStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.priority
2 |
3 | import android.content.Context
4 | import android.os.Process
5 | import com.rousetime.android_startup.AndroidStartup
6 | import com.rousetime.android_startup.annotation.ThreadPriority
7 |
8 | /**
9 | * Created by idisfkj on 2020/9/16.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | @ThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO)
13 | class SamplePriorityFirstStartup : AndroidStartup() {
14 |
15 | override fun create(context: Context): String? {
16 | val i = buildString {
17 | repeat(1000000) {
18 | append("$it")
19 | }
20 | }
21 | return SamplePriorityFirstStartup::class.java.simpleName
22 | }
23 |
24 | override fun callCreateOnMainThread(): Boolean = false
25 |
26 | override fun waitOnMainThread(): Boolean = false
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleFourthStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 |
7 | /**
8 | * Created by idisfkj on 2020/7/24.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | class SampleFourthStartup : AndroidStartup() {
12 |
13 | override fun callCreateOnMainThread(): Boolean = false
14 |
15 | override fun waitOnMainThread(): Boolean = false
16 |
17 | override fun create(context: Context): Any? {
18 | Thread.sleep(100)
19 | return null
20 | }
21 |
22 | override fun dependenciesByName(): List {
23 | return listOf(
24 | "com.rousetime.sample.startup.SampleFirstStartup",
25 | "com.rousetime.sample.startup.SampleSecondStartup",
26 | "com.rousetime.sample.startup.SampleThirdStartup"
27 | )
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/multiple/SampleMultipleSecondStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.multiple
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 | import com.rousetime.android_startup.annotation.MultipleProcess
7 |
8 | /**
9 | * Created by idisfkj on 2020/9/14.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | @MultipleProcess(":multiple.provider")
13 | class SampleMultipleSecondStartup : AndroidStartup() {
14 |
15 | override fun create(context: Context): String? {
16 | return SampleMultipleSecondStartup::class.java.simpleName
17 | }
18 |
19 | override fun callCreateOnMainThread(): Boolean = false
20 |
21 | override fun waitOnMainThread(): Boolean = false
22 |
23 | override fun dependencies(): List>>? {
24 | return arrayListOf(SampleMultipleFirstStartup::class.java)
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleSyncFourStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 |
7 | /**
8 | * Created by idisfkj on 2020/8/17.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | class SampleSyncFourStartup: AndroidStartup() {
12 |
13 | private var mResult: String? = null
14 |
15 | override fun create(context: Context): String? {
16 | return "$mResult + sync four"
17 | }
18 |
19 | override fun callCreateOnMainThread(): Boolean = true
20 |
21 | override fun waitOnMainThread(): Boolean = false
22 |
23 | override fun dependenciesByName(): List {
24 | return listOf("com.rousetime.sample.startup.SampleAsyncTwoStartup")
25 | }
26 |
27 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
28 | mResult = result as? String?
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleSyncTwoStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 |
7 | /**
8 | * Created by idisfkj on 2020/8/17.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | class SampleSyncTwoStartup: AndroidStartup() {
12 |
13 | private var mResult: String? = null
14 |
15 | override fun create(context: Context): String? {
16 | return "$mResult + sync two"
17 | }
18 |
19 | override fun callCreateOnMainThread(): Boolean = true
20 |
21 | override fun waitOnMainThread(): Boolean = false
22 |
23 | override fun dependenciesByName(): List {
24 | return listOf("com.rousetime.sample.startup.SampleSyncOneStartup")
25 | }
26 |
27 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
28 | mResult = result as? String?
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/multiple/SampleMultipleSixthStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.multiple
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 | import com.rousetime.android_startup.annotation.MultipleProcess
7 |
8 | /**
9 | * Created by idisfkj on 2020/9/15.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | @MultipleProcess(":multiple.process.service")
13 | class SampleMultipleSixthStartup : AndroidStartup() {
14 |
15 | override fun create(context: Context): String? {
16 | return SampleMultipleSixthStartup::class.java.simpleName
17 | }
18 |
19 | override fun callCreateOnMainThread(): Boolean = false
20 |
21 | override fun waitOnMainThread(): Boolean = true
22 |
23 | override fun dependenciesByName(): List {
24 | return listOf("com.rousetime.sample.startup.multiple.SampleMultipleFifthStartup")
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/multiple/SampleMultipleFifthStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup.multiple
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 | import com.rousetime.android_startup.annotation.MultipleProcess
7 |
8 | /**
9 | * Created by idisfkj on 2020/9/15.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | @MultipleProcess(":multiple.process.service")
13 | class SampleMultipleFifthStartup : AndroidStartup() {
14 |
15 | override fun create(context: Context): String? {
16 | return SampleMultipleFifthStartup::class.java.simpleName
17 | }
18 |
19 | override fun callCreateOnMainThread(): Boolean = false
20 |
21 | override fun waitOnMainThread(): Boolean = false
22 |
23 | override fun dependenciesByName(): List {
24 | return listOf("com.rousetime.sample.startup.multiple.SampleMultipleFourthStartup")
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleSyncFiveStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 |
7 | /**
8 | * Created by idisfkj on 2020/8/18.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | class SampleSyncFiveStartup : AndroidStartup() {
12 |
13 | private var mResult: String? = null
14 |
15 | override fun create(context: Context): String? {
16 | return "$mResult + sync five"
17 | }
18 |
19 | override fun callCreateOnMainThread(): Boolean = true
20 |
21 | override fun waitOnMainThread(): Boolean = false
22 |
23 | override fun dependenciesByName(): List {
24 | return listOf("com.rousetime.sample.startup.SampleManualDispatchStartup")
25 | }
26 |
27 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
28 | mResult = result as? String?
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleAsyncFourStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 |
7 | /**
8 | * Created by idisfkj on 2020/8/18.
9 | * Email : idisfkj@gmail.com.
10 | */
11 | class SampleAsyncFourStartup : AndroidStartup() {
12 |
13 | private var mResult: String? = null
14 |
15 | override fun callCreateOnMainThread(): Boolean = false
16 |
17 | override fun create(context: Context): String? {
18 | Thread.sleep(1000)
19 | return "$mResult + async four"
20 | }
21 |
22 | override fun waitOnMainThread(): Boolean = true
23 |
24 | override fun dependenciesByName(): List {
25 | return listOf("com.rousetime.sample.startup.SampleAsyncSixStartup")
26 | }
27 |
28 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
29 | mResult = result as? String?
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleAsyncOneStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 |
7 | /**
8 | * Created by idisfkj on 2020/8/17.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | class SampleAsyncOneStartup : AndroidStartup() {
12 |
13 | private var mResult: String? = null
14 |
15 | override fun create(context: Context): String? {
16 | Thread.sleep(2000)
17 | return "$mResult + async one"
18 | }
19 |
20 | override fun callCreateOnMainThread(): Boolean = false
21 |
22 | override fun waitOnMainThread(): Boolean = false
23 |
24 | override fun dependenciesByName(): List {
25 | return listOf("com.rousetime.sample.startup.SampleSyncThreeStartup")
26 | }
27 |
28 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
29 | mResult = result as? String?
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleAsyncThreeStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 |
7 | /**
8 | * Created by idisfkj on 2020/8/18.
9 | * Email : idisfkj@gmail.com.
10 | */
11 | class SampleAsyncThreeStartup : AndroidStartup() {
12 |
13 | private var mResult: String? = null
14 |
15 | override fun callCreateOnMainThread(): Boolean = false
16 |
17 | override fun create(context: Context): String? {
18 | Thread.sleep(2000)
19 | return "$mResult + async three"
20 | }
21 |
22 | override fun waitOnMainThread(): Boolean = false
23 |
24 | override fun dependenciesByName(): List {
25 | return listOf("com.rousetime.sample.startup.SampleAsyncFiveStartup")
26 | }
27 |
28 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
29 | mResult = result as? String?
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleAsyncSevenStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.AndroidStartup
5 | import com.rousetime.android_startup.Startup
6 |
7 | /**
8 | * Created by idisfkj on 2020/8/18.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | class SampleAsyncSevenStartup : AndroidStartup() {
12 |
13 | private var mResult: String? = null
14 |
15 | override fun create(context: Context): String? {
16 | Thread.sleep(3000)
17 | return "$mResult + async seven"
18 | }
19 |
20 | override fun callCreateOnMainThread(): Boolean = false
21 |
22 | override fun waitOnMainThread(): Boolean = false
23 |
24 | override fun dependenciesByName(): List {
25 | return listOf("com.rousetime.sample.startup.SampleManualDispatchStartup")
26 | }
27 |
28 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
29 | mResult = result as? String?
30 | }
31 | }
--------------------------------------------------------------------------------
/android-startup/maven.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | bintrayRepo = 'maven'
3 | bintrayName = 'android-startup'
4 | publishedGroupId = 'com.rousetime.android'
5 | libraryName = 'android-startup'
6 | artifact = 'android-startup'
7 | libraryDescription = 'The Android Startup library provides a straightforward, performant way to initialize components at application startup. Both library developers and app developers can use Android Startup to streamline startup sequences and explicitly set the order of initialization'
8 | siteUrl = 'https://github.com/idisfkj/android-startup'
9 | gitUrl = 'https://github.com/idisfkj/android-startup.git'
10 | libraryVersion = '1.0.6'
11 | developerId = 'idisfkj'
12 | developerName = 'idisfkj'
13 | developerEmail = 'idisfkj@gmail.com'
14 | licenseName = 'The Apache Software License, Version 2.0'
15 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
16 | allLicenses = ["Apache-2.0"]
17 | }
18 |
19 | apply from: 'install.gradle'
20 | apply from: 'bintray.gradle'
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleThirdStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import android.util.Log
5 | import com.rousetime.android_startup.AndroidStartup
6 | import com.rousetime.android_startup.Startup
7 |
8 | /**
9 | * Created by idisfkj on 2020/7/24.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | class SampleThirdStartup : AndroidStartup() {
13 |
14 | override fun callCreateOnMainThread(): Boolean = false
15 |
16 | override fun waitOnMainThread(): Boolean = false
17 |
18 | override fun create(context: Context): Long? {
19 | Thread.sleep(3000)
20 | return 10L
21 | }
22 |
23 | override fun dependenciesByName(): List {
24 | return listOf(
25 | "com.rousetime.sample.startup.SampleFirstStartup",
26 | "com.rousetime.sample.startup.SampleSecondStartup"
27 | )
28 | }
29 |
30 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
31 | Log.d("SampleThirdStartup", "onDependenciesCompleted: ${startup::class.java.simpleName}, $result")
32 | }
33 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/utils/ProcessUtils.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.utils
2 |
3 | import android.app.ActivityManager
4 | import android.content.Context
5 | import android.os.Process
6 |
7 | /**
8 | * Created by idisfkj on 2020/9/14.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | internal object ProcessUtils {
12 |
13 | private fun getProcessName(context: Context): String {
14 | val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
15 | val myPid = Process.myPid()
16 | am.runningAppProcesses.forEach {
17 | if (it.pid == myPid) {
18 | return it.processName
19 | }
20 | }
21 | return ""
22 | }
23 |
24 | fun isMainProcess(context: Context): Boolean = getProcessName(context) == context.packageName
25 |
26 | fun isMultipleProcess(context: Context, processName: Array): Boolean {
27 | processName.forEach {
28 | if (getProcessName(context) == "${context.packageName}$it") {
29 | return true
30 | }
31 | }
32 | return false
33 | }
34 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleSecondStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.content.Context
4 | import android.util.Log
5 | import com.rousetime.android_startup.AndroidStartup
6 | import com.rousetime.android_startup.Startup
7 | import com.rousetime.android_startup.executor.ExecutorManager
8 | import java.util.concurrent.Executor
9 |
10 | /**
11 | * Created by idisfkj on 2020/7/24.
12 | * Email: idisfkj@gmail.com.
13 | */
14 | class SampleSecondStartup : AndroidStartup() {
15 |
16 | override fun callCreateOnMainThread(): Boolean = false
17 |
18 | override fun waitOnMainThread(): Boolean = true
19 |
20 | override fun create(context: Context): Boolean {
21 | Thread.sleep(5000)
22 | return true
23 | }
24 |
25 | override fun createExecutor(): Executor {
26 | return ExecutorManager.instance.cpuExecutor
27 | }
28 |
29 | override fun dependenciesByName(): List {
30 | return listOf("com.rousetime.sample.startup.SampleFirstStartup")
31 | }
32 |
33 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {
34 | Log.d("SampleSecondStartup", "onDependenciesCompleted: ${startup::class.java.simpleName}, $result")
35 | }
36 | }
--------------------------------------------------------------------------------
/android-startup/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply from: 'maven.gradle'
5 | apply from: "maven-center.gradle"
6 |
7 | android {
8 | compileSdkVersion Versions.compile_sdk_version
9 |
10 | defaultConfig {
11 | minSdkVersion Versions.min_sdk_version
12 | targetSdkVersion Versions.target_sdk_version
13 | versionCode Versions.version_code
14 | versionName Versions.version_name
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | consumerProguardFiles "consumer-rules.pro"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: "libs", include: ["*.jar"])
30 |
31 | implementation Dependencies.kotlin_stdlib
32 | implementation Dependencies.core_ktx
33 | implementation Dependencies.appcompat
34 |
35 | testImplementation Dependencies.junit
36 | androidTestImplementation Dependencies.ext_junit
37 | androidTestImplementation Dependencies.espresso_core
38 | }
--------------------------------------------------------------------------------
/android-startup/install.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.dcendents.android-maven'
2 |
3 | group = publishedGroupId // Maven Group ID for the artifact
4 |
5 | install {
6 | repositories.mavenInstaller {
7 | // This generates POM.xml with proper parameters
8 | pom {
9 | project {
10 | packaging 'aar'
11 | groupId publishedGroupId
12 | artifactId artifact
13 |
14 | // Add your description here
15 | name libraryName
16 | description libraryDescription
17 | url siteUrl
18 |
19 | // Set your license
20 | licenses {
21 | license {
22 | name licenseName
23 | url licenseUrl
24 | }
25 | }
26 | developers {
27 | developer {
28 | id developerId
29 | name developerName
30 | email developerEmail
31 | }
32 | }
33 | scm {
34 | connection gitUrl
35 | developerConnection gitUrl
36 | url siteUrl
37 |
38 | }
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/startup/SampleStartupProviderConfig.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample.startup
2 |
3 | import android.util.Log
4 | import com.rousetime.android_startup.StartupListener
5 | import com.rousetime.android_startup.model.CostTimesModel
6 | import com.rousetime.android_startup.model.LoggerLevel
7 | import com.rousetime.android_startup.model.StartupConfig
8 | import com.rousetime.android_startup.provider.StartupProviderConfig
9 | import com.rousetime.sample.SampleApplication
10 |
11 | /**
12 | * Created by idisfkj on 2020/7/28.
13 | * Email: idisfkj@gmail.com.
14 | */
15 | class SampleStartupProviderConfig : StartupProviderConfig {
16 |
17 | override fun getConfig(): StartupConfig =
18 | StartupConfig.Builder()
19 | .setLoggerLevel(LoggerLevel.DEBUG) // default LoggerLevel.NONE
20 | .setAwaitTimeout(12000L) // default 10000L
21 | .setOpenStatistics(true) // default true
22 | .setListener(object : StartupListener {
23 | override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List) {
24 | // can to do cost time statistics.
25 | SampleApplication.costTimesLiveData.value = costTimesModels
26 | Log.d("StartupTrack", "onCompleted: ${costTimesModels.size}")
27 | }
28 | })
29 | .build()
30 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 | /build
24 |
25 | # Local configuration file (sdk path, etc)
26 | local.properties
27 |
28 | # Proguard folder generated by Eclipse
29 | proguard/
30 |
31 | # Log Files
32 | *.log
33 |
34 | # Android Studio Navigation editor temp files
35 | .navigation/
36 |
37 | # Android Studio captures folder
38 | captures/
39 |
40 | # IntelliJ
41 | *.iml
42 | .idea
43 | # Keystore files
44 | # Uncomment the following lines if you do not want to check your keystore files in.
45 | #*.jks
46 | #*.keystore
47 |
48 | # External native build folder generated in Android Studio 2.2 and later
49 | .externalNativeBuild
50 | .cxx/
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | # google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | # fastlane
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 | fastlane/readme.md
66 |
67 | # Version control
68 | vcs.xml
69 |
70 | # lint
71 | lint/intermediates/
72 | lint/generated/
73 | lint/outputs/
74 | lint/tmp/
75 | # lint/reports/
76 | .DS_Store
77 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | android-startup
3 | Initializing!
4 | Get SampleSecondStartup From Cache If Exist
5 | Get Startup From Cache If Exist
6 | Clear SampleSecondStartup Cache
7 | Clear Cache
8 | Clear Cache Success
9 | Result Of SampleSecondStartup From Cache: %b
10 | SampleSecondStartup not initialized, being initializing…
11 | Sample Startup not initialized, being initializing…
12 | More
13 | Sync And Sync
14 | Sync And Async
15 | Async And Sync
16 | Async And Async
17 | Async And Async Await Main Thread
18 | Manual Dispatch
19 | Thread Priority
20 | Multiple Process
21 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | signingConfigs {
7 | release {
8 | storeFile file('../keystore')
9 | storePassword 'android'
10 | keyAlias 'android'
11 | keyPassword 'android'
12 | }
13 | }
14 | compileSdkVersion Versions.compile_sdk_version
15 |
16 | defaultConfig {
17 | applicationId "com.rousetime.sample"
18 | minSdkVersion Versions.min_sdk_version
19 | targetSdkVersion Versions.target_sdk_version
20 | versionCode Versions.version_code
21 | versionName Versions.version_name
22 |
23 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
24 | }
25 |
26 | buildTypes {
27 | release {
28 | minifyEnabled true
29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
30 | signingConfig signingConfigs.release
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: "libs", include: ["*.jar"])
37 | implementation project(':android-startup')
38 |
39 | // implementation Dependencies.android_startup
40 |
41 | implementation Dependencies.kotlin_stdlib
42 | implementation Dependencies.core_ktx
43 | implementation Dependencies.appcompat
44 | implementation Dependencies.constraint_layout
45 |
46 | testImplementation Dependencies.junit
47 | androidTestImplementation Dependencies.ext_junit
48 | androidTestImplementation Dependencies.espresso_core
49 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/model/StartupConfig.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.model
2 |
3 | import com.rousetime.android_startup.StartupListener
4 |
5 | /**
6 | * Created by idisfkj on 2020/7/31.
7 | * Email: idisfkj@gmail.com.
8 | */
9 | class StartupConfig private constructor(
10 | val loggerLevel: LoggerLevel,
11 | val awaitTimeout: Long,
12 | val listener: StartupListener?,
13 | val openStatistic: Boolean? = true
14 | ) {
15 |
16 | class Builder {
17 | private var mLoggerLevel: LoggerLevel? = null
18 | private var mAwaitTimeout: Long? = null
19 | private var mListener: StartupListener? = null
20 | private var mOpenStatistics: Boolean? = true
21 |
22 | companion object {
23 | const val AWAIT_TIMEOUT = 10000L
24 | }
25 |
26 | fun setLoggerLevel(level: LoggerLevel) = apply {
27 | mLoggerLevel = level
28 | }
29 |
30 | fun setAwaitTimeout(timeoutMilliSeconds: Long) = apply {
31 | mAwaitTimeout = timeoutMilliSeconds
32 | }
33 |
34 | fun setListener(listener: StartupListener) = apply {
35 | mListener = listener
36 | }
37 |
38 | fun setOpenStatistics(openStatistic: Boolean) = apply {
39 | mOpenStatistics = openStatistic
40 | }
41 |
42 | fun build(): StartupConfig {
43 | return StartupConfig(
44 | mLoggerLevel ?: LoggerLevel.NONE,
45 | mAwaitTimeout ?: AWAIT_TIMEOUT,
46 | mListener,
47 | mOpenStatistics
48 | )
49 | }
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/manager/StartupCacheManager.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.manager
2 |
3 | import com.rousetime.android_startup.Startup
4 | import com.rousetime.android_startup.model.ResultModel
5 | import com.rousetime.android_startup.model.StartupConfig
6 | import java.util.concurrent.ConcurrentHashMap
7 |
8 | /**
9 | * Created by idisfkj on 2020/8/11.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | class StartupCacheManager {
13 |
14 | /**
15 | * Save initialized components result.
16 | */
17 | private val mInitializedComponents = ConcurrentHashMap>, ResultModel<*>>()
18 | var initializedConfig: StartupConfig? = null
19 | private set
20 |
21 | companion object {
22 | @JvmStatic
23 | val instance by lazy { StartupCacheManager() }
24 | }
25 |
26 | /**
27 | * save result of initialized component.
28 | */
29 | internal fun saveInitializedComponent(zClass: Class>, result: ResultModel<*>) {
30 | mInitializedComponents[zClass] = result
31 | }
32 |
33 | /**
34 | * check initialized.
35 | */
36 | fun hadInitialized(zClass: Class>): Boolean = mInitializedComponents.containsKey(zClass)
37 |
38 | @Suppress("UNCHECKED_CAST")
39 | fun obtainInitializedResult(zClass: Class>): T? = mInitializedComponents[zClass]?.result as? T?
40 |
41 | fun remove(zClass: Class>) {
42 | mInitializedComponents.remove(zClass)
43 | }
44 |
45 | fun clear() {
46 | mInitializedComponents.clear()
47 | }
48 |
49 | /**
50 | * save initialized config.
51 | */
52 | internal fun saveConfig(config: StartupConfig?) {
53 | initializedConfig = config
54 | }
55 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/AndroidStartup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import com.rousetime.android_startup.dispatcher.Dispatcher
4 | import com.rousetime.android_startup.executor.ExecutorManager
5 | import java.util.concurrent.CountDownLatch
6 | import java.util.concurrent.Executor
7 |
8 | /**
9 | * Created by idisfkj on 2020/7/23.
10 | * Email: idisfkj@gmail.com.
11 | */
12 | abstract class AndroidStartup : Startup {
13 |
14 | private val mWaitCountDown by lazy { CountDownLatch(getDependenciesCount()) }
15 | private val mObservers by lazy { mutableListOf() }
16 |
17 | override fun toWait() {
18 | try {
19 | mWaitCountDown.await()
20 | } catch (e: InterruptedException) {
21 | e.printStackTrace()
22 | }
23 | }
24 |
25 | override fun toNotify() {
26 | mWaitCountDown.countDown()
27 | }
28 |
29 | override fun createExecutor(): Executor = ExecutorManager.instance.ioExecutor
30 |
31 | override fun dependencies(): List>>? {
32 | return null
33 | }
34 |
35 | override fun dependenciesByName(): List? {
36 | return null
37 | }
38 |
39 | override fun getDependenciesCount(): Int {
40 | if (dependenciesByName().isNullOrEmpty()) return dependencies()?.size ?: 0
41 | return dependenciesByName()?.size ?: 0
42 | }
43 |
44 | override fun onDependenciesCompleted(startup: Startup<*>, result: Any?) {}
45 |
46 | override fun manualDispatch(): Boolean = false
47 |
48 | override fun registerDispatcher(dispatcher: Dispatcher) {
49 | mObservers.add(dispatcher)
50 | }
51 |
52 | override fun onDispatch() {
53 | mObservers.forEach {
54 | it.toNotify()
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/executor/ExecutorManager.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.executor
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import java.util.concurrent.*
6 | import kotlin.math.max
7 | import kotlin.math.min
8 |
9 | /**
10 | * Created by idisfkj on 2020/7/23.
11 | * Email: idisfkj@gmail.com.
12 | */
13 | class ExecutorManager {
14 |
15 | var cpuExecutor: ThreadPoolExecutor
16 | private set
17 |
18 | var ioExecutor: ExecutorService
19 | private set
20 |
21 | var mainExecutor: Executor
22 | private set
23 |
24 | private val handler = RejectedExecutionHandler { _, _ -> Executors.newCachedThreadPool(Executors.defaultThreadFactory()) }
25 |
26 | companion object {
27 |
28 | @JvmStatic
29 | val instance by lazy { ExecutorManager() }
30 |
31 | private val CPU_COUNT = Runtime.getRuntime().availableProcessors()
32 | private val CORE_POOL_SIZE = max(2, min(CPU_COUNT - 1, 5))
33 | private val MAX_POOL_SIZE = CORE_POOL_SIZE
34 | private const val KEEP_ALIVE_TIME = 5L
35 | }
36 |
37 | init {
38 | cpuExecutor = ThreadPoolExecutor(
39 | CORE_POOL_SIZE,
40 | MAX_POOL_SIZE,
41 | KEEP_ALIVE_TIME,
42 | TimeUnit.SECONDS,
43 | LinkedBlockingDeque(),
44 | Executors.defaultThreadFactory(),
45 | handler
46 | ).apply {
47 | allowCoreThreadTimeOut(true)
48 | }
49 |
50 | ioExecutor = Executors.newCachedThreadPool(Executors.defaultThreadFactory())
51 |
52 | mainExecutor = object : Executor {
53 | private val handler = Handler(Looper.getMainLooper())
54 |
55 | override fun execute(command: Runnable) {
56 | handler.post(command)
57 | }
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
20 |
32 |
33 |
42 |
43 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/Startup.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.dispatcher.Dispatcher
5 | import com.rousetime.android_startup.executor.StartupExecutor
6 |
7 | /**
8 | * Created by idisfkj on 2020/7/23.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | interface Startup : Dispatcher, StartupExecutor {
12 |
13 | /**
14 | * Contains all of the necessary operations to initialize the component.
15 | * and returns an instance of `T`
16 | *
17 | * @param [context]
18 | */
19 | fun create(context: Context): T?
20 |
21 | /**
22 | * Returns a list of the other [Startup] objects that the initializer depends on.
23 | */
24 | @Deprecated("Used dependenciesByName instead", ReplaceWith("dependenciesByName()"))
25 | fun dependencies(): List>>?
26 |
27 | /**
28 | * Returns a list of the other [Startup] Class Name that the initializer depends on.
29 | */
30 | fun dependenciesByName(): List?
31 |
32 | /**
33 | * Returns size of depends on.
34 | */
35 | fun getDependenciesCount(): Int
36 |
37 | /**
38 | * Called whenever there is a dependency completion.
39 | *
40 | * @param [startup] dependencies [startup].
41 | * @param [result] of dependencies startup.
42 | */
43 | fun onDependenciesCompleted(startup: Startup<*>, result: Any?)
44 |
45 | /**
46 | * Returns true that manual to dispatch. but must be call [onDispatch], in order to notify children that dependencies startup completed.
47 | */
48 | fun manualDispatch(): Boolean
49 |
50 | /**
51 | * Register dispatcher when [manualDispatch] return true.
52 | */
53 | fun registerDispatcher(dispatcher: Dispatcher)
54 |
55 | /**
56 | * Start to dispatch when [manualDispatch] return true.
57 | */
58 | fun onDispatch()
59 |
60 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/provider/StartupProvider.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.provider
2 |
3 | import android.content.ContentProvider
4 | import android.content.ContentValues
5 | import android.database.Cursor
6 | import android.net.Uri
7 | import com.rousetime.android_startup.StartupInitializer
8 | import com.rousetime.android_startup.StartupManager
9 | import com.rousetime.android_startup.execption.StartupException
10 |
11 | /**
12 | * Created by idisfkj on 2020/7/28.
13 | * Email: idisfkj@gmail.com.
14 | */
15 | open class StartupProvider : ContentProvider() {
16 |
17 | override fun onCreate(): Boolean {
18 | context.takeIf { context -> context != null }?.let {
19 | val store = StartupInitializer.instance.discoverAndInitialize(it, this::class.java.name)
20 | StartupManager.Builder()
21 | .setConfig(store.config?.getConfig())
22 | .addAllStartup(store.result)
23 | .build(it)
24 | .start()
25 | .await()
26 | } ?: throw StartupException("Context cannot be null.")
27 |
28 | return true
29 | }
30 |
31 | override fun insert(uri: Uri, values: ContentValues?): Uri? {
32 | throw IllegalStateException("Not allowed.")
33 | }
34 |
35 | override fun query(uri: Uri, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?): Cursor? {
36 | return null
37 | }
38 |
39 | override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int {
40 | throw IllegalStateException("Not allowed.")
41 | }
42 |
43 | override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int {
44 | throw IllegalStateException("Not allowed.")
45 | }
46 |
47 | override fun getType(uri: Uri): String? {
48 | throw IllegalStateException("Not allowed.")
49 | }
50 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/run/StartupRunnable.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.run
2 |
3 | import android.content.Context
4 | import android.os.Process
5 | import androidx.core.os.TraceCompat
6 | import com.rousetime.android_startup.Startup
7 | import com.rousetime.android_startup.annotation.ThreadPriority
8 | import com.rousetime.android_startup.dispatcher.ManagerDispatcher
9 | import com.rousetime.android_startup.manager.StartupCacheManager
10 | import com.rousetime.android_startup.model.ResultModel
11 | import com.rousetime.android_startup.model.StartupSortStore
12 | import com.rousetime.android_startup.utils.StartupCostTimesUtils
13 | import com.rousetime.android_startup.utils.StartupLogUtils
14 |
15 | /**
16 | * Created by idisfkj on 2020/7/27.
17 | * Email: idisfkj@gmail.com.
18 | */
19 | internal class StartupRunnable(
20 | private val context: Context,
21 | private val startup: Startup<*>,
22 | private val sortStore: StartupSortStore,
23 | private val dispatcher: ManagerDispatcher
24 | ) : Runnable {
25 |
26 | override fun run() {
27 | Process.setThreadPriority(startup::class.java.getAnnotation(ThreadPriority::class.java)?.priority ?: Process.THREAD_PRIORITY_DEFAULT)
28 | startup.toWait()
29 | StartupLogUtils.d { "${startup::class.java.simpleName} being create." }
30 |
31 | TraceCompat.beginSection(startup::class.java.simpleName)
32 | StartupCostTimesUtils.recordStart { Triple(startup::class.java, startup.callCreateOnMainThread(), startup.waitOnMainThread()) }
33 | val result = startup.create(context)
34 | StartupCostTimesUtils.recordEnd { startup::class.java }
35 | TraceCompat.endSection()
36 |
37 | // To save result of initialized component.
38 | StartupCacheManager.instance.saveInitializedComponent(startup::class.java, ResultModel(result))
39 | StartupLogUtils.d { "${startup::class.java.simpleName} was completed." }
40 |
41 | dispatcher.notifyChildren(startup, result, sortStore)
42 | }
43 | }
--------------------------------------------------------------------------------
/android-startup/bintray.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.jfrog.bintray'
2 |
3 | version = libraryVersion
4 |
5 | if (project.hasProperty("android")) { // Android libraries
6 | task sourcesJar(type: Jar) {
7 | archiveClassifier.convention('sources')
8 | archiveClassifier.set('sources')
9 | from android.sourceSets.main.java.srcDirs
10 | }
11 |
12 | task javadoc(type: Javadoc) {
13 | source = android.sourceSets.main.java.srcDirs
14 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
15 | }
16 | } else { // Java libraries
17 | task sourcesJar(type: Jar, dependsOn: classes) {
18 | archiveClassifier.convention('sources')
19 | archiveClassifier.set('sources')
20 | from sourceSets.main.allSource
21 | }
22 | }
23 |
24 | task javadocJar(type: Jar, dependsOn: javadoc) {
25 | archiveClassifier.convention('javadoc')
26 | archiveClassifier.set('javadoc')
27 | from javadoc.destinationDir
28 | }
29 |
30 | artifacts {
31 | // archives javadocJar
32 | archives sourcesJar
33 | }
34 |
35 | // Bintray
36 | Properties properties = new Properties()
37 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
38 |
39 | bintray {
40 | user = properties.getProperty("bintray.user")
41 | key = properties.getProperty("bintray.apikey")
42 |
43 | configurations = ['archives']
44 | pkg {
45 | repo = bintrayRepo
46 | name = bintrayName
47 | desc = libraryDescription
48 | websiteUrl = siteUrl
49 | vcsUrl = gitUrl
50 | licenses = allLicenses
51 | publish = true
52 | publicDownloadNumbers = true
53 | version {
54 | desc = libraryDescription
55 | gpg {
56 | sign = true //Determines whether to GPG sign the files. The default is false
57 | passphrase = properties.getProperty("bintray.gpg.password")
58 | //Optional. The passphrase for GPG signing'
59 | }
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
33 |
34 |
37 |
38 |
41 |
42 |
43 |
44 |
49 |
50 |
53 |
54 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/utils/StartupLogUtils.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.utils
2 |
3 | import android.util.Log
4 | import com.rousetime.android_startup.model.LoggerLevel
5 | import java.util.*
6 |
7 | /**
8 | * Created by idisfkj on 2020/7/24.
9 | * Email: idisfkj@gmail.com.
10 | */
11 | internal object StartupLogUtils {
12 |
13 | private const val TAG = "StartupTrack"
14 | var level: LoggerLevel = LoggerLevel.NONE
15 |
16 | fun e(block: () -> String) {
17 | if (level >= LoggerLevel.ERROR) print(Log.ERROR, TAG, block())
18 | }
19 |
20 | fun d(block: () -> String) {
21 | if (level >= LoggerLevel.DEBUG) print(Log.DEBUG, TAG, block())
22 | }
23 |
24 | /**
25 | * Print log to console (solve Android console loss of long log records)
26 | *
27 | * @param priority
28 | * @param tag
29 | * @param content
30 | */
31 | private fun print(priority: Int, tag: String?, content: String) {
32 | if (content.length < 1000) {
33 | Log.println(priority, tag, content)
34 | return
35 | }
36 |
37 | // The maximum number of bytes to print at one time.
38 | val maxByteNum = 4000
39 |
40 | var bytes = content.toByteArray()
41 |
42 | // Print directly out of range.
43 | if (maxByteNum >= bytes.size) {
44 | Log.println(priority, tag, content)
45 | return
46 | }
47 |
48 | // Section print counting.
49 | var count = 1
50 |
51 | // In the range of the array, the loop is segmented.
52 | while (maxByteNum < bytes.size) {
53 | // Capture a string by byte length.
54 | val subStr = cutStr(bytes, maxByteNum)
55 |
56 | val desc = String.format("Block printing(%s):%s", count++, subStr)
57 | Log.println(priority, tag, desc)
58 |
59 | // Intercepts an array of bytes not yet printed.
60 | bytes = bytes.copyOfRange(subStr!!.toByteArray().size, bytes.size)
61 |
62 | /*if (count == 10) {
63 | break;
64 | }*/
65 | }
66 |
67 | Log.println(priority, tag, String.format("Block printing(%s):%s", count, String(bytes)))
68 | }
69 |
70 |
71 | /**
72 | * Capture a byte array as a string by byte length.
73 | *
74 | * @param bytes
75 | * @param subLength
76 | * @return
77 | */
78 | private fun cutStr(bytes: ByteArray?, subLength: Int): String? {
79 | if (bytes == null || subLength < 1) {
80 | return null
81 | }
82 |
83 | if (subLength >= bytes.size) {
84 | return String(bytes)
85 | }
86 |
87 | // Copy out a fixed - length byte array and convert it to a string.
88 | val subStr = String(Arrays.copyOf(bytes, subLength))
89 |
90 | // To avoid the end character being split, subtract 1 here to keep the string intact.
91 | return subStr.substring(0, subStr.length - 1)
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.os.Handler
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.lifecycle.Observer
8 | import com.rousetime.android_startup.StartupManager
9 | import com.rousetime.android_startup.manager.StartupCacheManager
10 | import com.rousetime.sample.startup.SampleFirstStartup
11 | import com.rousetime.sample.startup.SampleSecondStartup
12 | import com.rousetime.sample.startup.SampleStartupProviderConfig
13 | import kotlinx.android.synthetic.main.activity_main.*
14 |
15 | class MainActivity : AppCompatActivity() {
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setContentView(R.layout.activity_main)
19 |
20 | get.setOnClickListener {
21 | if (StartupCacheManager.instance.hadInitialized(SampleSecondStartup::class.java)) {
22 | content.text = getString(
23 | R.string.sample_second_startup_result_from_cache,
24 | StartupCacheManager.instance.obtainInitializedResult(SampleSecondStartup::class.java)
25 | )
26 | } else {
27 | // show initialize tips
28 | content.text = getString(R.string.sample_second_startup_not_initialized)
29 |
30 | // because SampleSecondStartup need to block on main thread.
31 | // in order to show initialize tips,to delay a frame times.
32 | Handler().postDelayed({
33 | StartupManager.Builder()
34 | .setConfig(SampleStartupProviderConfig().getConfig())
35 | .addStartup(SampleFirstStartup())
36 | .addStartup(SampleSecondStartup())
37 | .build(this)
38 | .start()
39 | .await()
40 | }, 16)
41 | }
42 | }
43 |
44 | clear.setOnClickListener {
45 | StartupCacheManager.instance.remove(SampleSecondStartup::class.java)
46 | content.text = getString(R.string.clear_cache_success)
47 | }
48 |
49 | more.setOnClickListener {
50 | startActivity(Intent(this, SampleMoreActivity::class.java))
51 | }
52 |
53 | SampleApplication.costTimesLiveData.observe(this, Observer {
54 | content.text = buildString {
55 | append("Startup Completed: ")
56 | append("\n")
57 | append("\n")
58 | it.forEach {
59 | append("\n")
60 | append("Startup Name: ${it.name}")
61 | append("\n")
62 | append("CallOnMainThread: ${it.callOnMainThread}")
63 | append("\n")
64 | append("WaitOnMainThread: ${it.waitOnMainThread}")
65 | append("\n")
66 | append("Cost times: ${it.endTime - it.startTime} ms")
67 | append("\n")
68 | }
69 | }
70 | })
71 | }
72 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
24 |
25 |
36 |
37 |
48 |
49 |
57 |
58 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/utils/StartupCostTimesUtils.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.utils
2 |
3 | import com.rousetime.android_startup.Startup
4 | import com.rousetime.android_startup.extensions.getUniqueKey
5 | import com.rousetime.android_startup.manager.StartupCacheManager
6 | import com.rousetime.android_startup.model.CostTimesModel
7 | import java.util.concurrent.ConcurrentHashMap
8 |
9 | /**
10 | * Created by idisfkj on 2020/8/10.
11 | * Email: idisfkj@gmail.com.
12 | */
13 | internal object StartupCostTimesUtils {
14 |
15 | val costTimesMap = ConcurrentHashMap()
16 |
17 | private const val ACCURACY = 1000 * 1000L
18 |
19 | var startTime = 0L
20 | var endTime: Long? = null
21 |
22 | val mainThreadTimes
23 | get() = (endTime ?: System.nanoTime()) - startTime
24 |
25 | fun recordStart(block: () -> Triple>, Boolean, Boolean>) {
26 | if (checkOpenStatistics()) {
27 | block().run {
28 | costTimesMap[first.getUniqueKey()] = CostTimesModel(
29 | first.simpleName,
30 | second,
31 | third,
32 | System.nanoTime() / ACCURACY
33 | )
34 | }
35 | }
36 | }
37 |
38 | fun recordEnd(block: () -> Class>) {
39 | if (checkOpenStatistics()) {
40 | costTimesMap[block().getUniqueKey()]?.let {
41 | it.endTime = System.nanoTime() / ACCURACY
42 | }
43 | }
44 | }
45 |
46 | fun clear() {
47 | if (checkOpenStatistics()) {
48 | endTime = null
49 | costTimesMap.clear()
50 | }
51 | }
52 |
53 | fun printAll() {
54 | StartupLogUtils.d { buildString {
55 | append("startup cost times detail:")
56 | append("\n")
57 | append("|=================================================================")
58 | costTimesMap.values.forEach {
59 | append("\n")
60 | append("| Startup Name | ${it.name}")
61 | append("\n")
62 | append("| ----------------------- | --------------------------------------")
63 | append("\n")
64 | append("| Call On Main Thread | ${it.callOnMainThread}")
65 | append("\n")
66 | append("| ----------------------- | --------------------------------------")
67 | append("\n")
68 | append("| Wait On Main Thread | ${it.waitOnMainThread}")
69 | append("\n")
70 | append("| ----------------------- | --------------------------------------")
71 | append("\n")
72 | append("| Cost Times | ${it.endTime - it.startTime} ms")
73 | append("\n")
74 | append("|=================================================================")
75 | }
76 | append("\n")
77 | append("| Total Main Thread Times | ${mainThreadTimes / ACCURACY} ms")
78 | append("\n")
79 | append("|=================================================================")
80 | } }
81 | }
82 |
83 | private fun checkOpenStatistics() = StartupCacheManager.instance.initializedConfig?.openStatistic == true
84 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/dispatcher/StartupManagerDispatcher.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.dispatcher
2 |
3 | import android.content.Context
4 | import com.rousetime.android_startup.Startup
5 | import com.rousetime.android_startup.StartupListener
6 | import com.rousetime.android_startup.executor.ExecutorManager
7 | import com.rousetime.android_startup.extensions.getUniqueKey
8 | import com.rousetime.android_startup.manager.StartupCacheManager
9 | import com.rousetime.android_startup.model.StartupSortStore
10 | import com.rousetime.android_startup.run.StartupRunnable
11 | import com.rousetime.android_startup.utils.StartupCostTimesUtils
12 | import com.rousetime.android_startup.utils.StartupLogUtils
13 | import java.util.concurrent.CountDownLatch
14 | import java.util.concurrent.atomic.AtomicInteger
15 |
16 | /**
17 | * Created by idisfkj on 2020/8/14.
18 | * Email: idisfkj@gmail.com.
19 | */
20 | internal class StartupManagerDispatcher(
21 | private val context: Context,
22 | private val needAwaitCount: AtomicInteger,
23 | private val awaitCountDownLatch: CountDownLatch?,
24 | private val startupSize: Int,
25 | private val listener: StartupListener?
26 | ) : ManagerDispatcher {
27 |
28 | private var count: AtomicInteger? = null
29 |
30 | override fun prepare() {
31 | count = AtomicInteger()
32 | StartupCostTimesUtils.clear()
33 | }
34 |
35 | override fun dispatch(startup: Startup<*>, sortStore: StartupSortStore) {
36 | StartupLogUtils.d { "${startup::class.java.simpleName} being dispatching, onMainThread ${startup.callCreateOnMainThread()}." }
37 |
38 | if (StartupCacheManager.instance.hadInitialized(startup::class.java)) {
39 | val result = StartupCacheManager.instance.obtainInitializedResult(startup::class.java)
40 |
41 | StartupLogUtils.d { "${startup::class.java.simpleName} was completed, result from cache." }
42 |
43 | notifyChildren(startup, result, sortStore)
44 | } else {
45 | val runnable = StartupRunnable(context, startup, sortStore, this)
46 | if (!startup.callCreateOnMainThread()) {
47 | startup.createExecutor().execute(runnable)
48 | } else {
49 | runnable.run()
50 | }
51 | }
52 | }
53 |
54 | override fun notifyChildren(dependencyParent: Startup<*>, result: Any?, sortStore: StartupSortStore) {
55 | // immediately notify main thread,Unblock the main thread.
56 | if (dependencyParent.waitOnMainThread() && !dependencyParent.callCreateOnMainThread()) {
57 | needAwaitCount.decrementAndGet()
58 | awaitCountDownLatch?.countDown()
59 | }
60 |
61 | sortStore.startupChildrenMap[dependencyParent::class.java.getUniqueKey()]?.forEach {
62 | sortStore.startupMap[it]?.run {
63 | onDependenciesCompleted(dependencyParent, result)
64 |
65 | if (dependencyParent.manualDispatch()) {
66 | dependencyParent.registerDispatcher(this)
67 | } else {
68 | toNotify()
69 | }
70 | }
71 | }
72 | val size = count?.incrementAndGet() ?: 0
73 | if (size == startupSize) {
74 | StartupCostTimesUtils.printAll()
75 | listener?.let {
76 | ExecutorManager.instance.mainExecutor.execute {
77 | it.onCompleted(StartupCostTimesUtils.mainThreadTimes, StartupCostTimesUtils.costTimesMap.values.toList())
78 | }
79 | }
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/SampleApplication.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample
2 |
3 | import android.app.Application
4 | import android.net.Uri
5 | import android.util.Log
6 | import androidx.lifecycle.MutableLiveData
7 | import com.rousetime.android_startup.StartupListener
8 | import com.rousetime.android_startup.StartupManager
9 | import com.rousetime.android_startup.manager.StartupCacheManager
10 | import com.rousetime.android_startup.model.CostTimesModel
11 | import com.rousetime.android_startup.model.LoggerLevel
12 | import com.rousetime.android_startup.model.StartupConfig
13 | import com.rousetime.sample.startup.SampleFirstStartup
14 | import com.rousetime.sample.startup.SampleFourthStartup
15 | import com.rousetime.sample.startup.SampleSecondStartup
16 | import com.rousetime.sample.startup.SampleThirdStartup
17 | import com.rousetime.sample.startup.multiple.SampleMultipleFirstStartup
18 | import com.rousetime.sample.startup.multiple.SampleMultipleSecondStartup
19 | import com.rousetime.sample.startup.multiple.SampleMultipleThirdStartup
20 |
21 | /**
22 | * Created by idisfkj on 2020/7/24.
23 | * Email: idisfkj@gmail.com.
24 | */
25 | class SampleApplication : Application() {
26 |
27 | companion object {
28 | const val TAG = "SampleApplication"
29 |
30 | // only in order to test on MainActivity.
31 | val costTimesLiveData = MutableLiveData>()
32 | }
33 |
34 | override fun onCreate() {
35 | super.onCreate()
36 |
37 | // val config = StartupConfig.Builder()
38 | // .setLoggerLevel(LoggerLevel.DEBUG)
39 | // .setAwaitTimeout(12000L)
40 | // .setListener(object : StartupListener {
41 | // override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List) {
42 | // // can to do cost time statistics.
43 | // costTimesLiveData.value = costTimesModels
44 | // Log.d("StartupTrack", "onCompleted: ${costTimesModels.size}")
45 | // }
46 | // })
47 | // .build()
48 | //
49 | // StartupManager.Builder()
50 | // .setConfig(config)
51 | // .addStartup(SampleFirstStartup())
52 | // .addStartup(SampleSecondStartup())
53 | // .addStartup(SampleThirdStartup())
54 | // .addStartup(SampleFourthStartup())
55 | // .addStartup(SampleMultipleFirstStartup())
56 | // .addStartup(SampleMultipleSecondStartup())
57 | // .addStartup(SampleMultipleThirdStartup())
58 | // .build(this)
59 | // .start()
60 | // .await()
61 |
62 | if (StartupCacheManager.instance.hadInitialized(SampleSecondStartup::class.java)) {
63 | Log.d(
64 | TAG,
65 | "SampleSecondStartup had initialized, result => ${StartupCacheManager.instance.obtainInitializedResult(SampleSecondStartup::class.java)}"
66 | )
67 | }
68 |
69 | if (StartupCacheManager.instance.hadInitialized(SampleFourthStartup::class.java)) {
70 | Log.d(
71 | TAG,
72 | "SampleFourthStartup had initialized, result => ${StartupCacheManager.instance.obtainInitializedResult(SampleFourthStartup::class.java)}"
73 | )
74 | } else {
75 | Log.e(TAG, "SampleFourthStartup not initialized.")
76 | }
77 |
78 | // call multiple process init.
79 | val uri = Uri.parse("content://com.rousetime.sample.android_startup.multiple")
80 | contentResolver.query(uri, null, null, null, null)?.apply { close() }
81 | }
82 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/StartupInitializer.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import android.content.ComponentName
4 | import android.content.Context
5 | import android.content.pm.PackageManager
6 | import androidx.core.os.TraceCompat
7 | import com.rousetime.android_startup.execption.StartupException
8 | import com.rousetime.android_startup.extensions.getUniqueKey
9 | import com.rousetime.android_startup.manager.StartupCacheManager
10 | import com.rousetime.android_startup.model.StartupProviderStore
11 | import com.rousetime.android_startup.provider.StartupProviderConfig
12 |
13 | /**
14 | * Created by idisfkj on 2020/8/7.
15 | * Email: idisfkj@gmail.com.
16 | */
17 | class StartupInitializer {
18 |
19 | companion object {
20 | @JvmStatic
21 | val instance by lazy { StartupInitializer() }
22 | }
23 |
24 | internal fun discoverAndInitialize(context: Context, providerName: String): StartupProviderStore {
25 |
26 | TraceCompat.beginSection(StartupInitializer::class.java.simpleName)
27 |
28 | val result = mutableListOf>()
29 | val initialize = mutableListOf()
30 | val initialized = mutableListOf()
31 | var config: StartupProviderConfig? = null
32 | try {
33 | val provider = ComponentName(context.packageName, providerName)
34 | val providerInfo = context.packageManager.getProviderInfo(provider, PackageManager.GET_META_DATA)
35 | val startup = context.getString(R.string.android_startup)
36 | val providerConfig = context.getString(R.string.android_startup_provider_config)
37 | providerInfo.metaData?.let { metaData ->
38 | metaData.keySet().forEach { key ->
39 | val value = metaData[key]
40 | val clazz = Class.forName(key)
41 | if (startup == value) {
42 | if (AndroidStartup::class.java.isAssignableFrom(clazz)) {
43 | doInitialize((clazz.getDeclaredConstructor().newInstance() as AndroidStartup<*>), result, initialize, initialized)
44 | }
45 | } else if (providerConfig == value) {
46 | if (StartupProviderConfig::class.java.isAssignableFrom(clazz)) {
47 | config = clazz.getDeclaredConstructor().newInstance() as? StartupProviderConfig
48 | // save initialized config
49 | StartupCacheManager.instance.saveConfig(config?.getConfig())
50 | }
51 | }
52 | }
53 | }
54 | } catch (t: Throwable) {
55 | throw StartupException(t)
56 | }
57 |
58 | TraceCompat.endSection()
59 |
60 | return StartupProviderStore(result, config)
61 | }
62 |
63 | private fun doInitialize(
64 | startup: AndroidStartup<*>,
65 | result: MutableList>,
66 | initialize: MutableList,
67 | initialized: MutableList
68 | ) {
69 | try {
70 | val uniqueKey = startup::class.java.getUniqueKey()
71 | if (initialize.contains(uniqueKey)) {
72 | throw IllegalStateException("have circle dependencies.")
73 | }
74 | if (!initialized.contains(uniqueKey)) {
75 | initialize.add(uniqueKey)
76 | result.add(startup)
77 | if (startup.dependenciesByName().isNullOrEmpty()) {
78 | startup.dependencies()?.forEach {
79 | doInitialize(it.getDeclaredConstructor().newInstance() as AndroidStartup<*>, result, initialize, initialized)
80 | }
81 | } else {
82 | startup.dependenciesByName()?.forEach {
83 | val clazz = Class.forName(it)
84 | doInitialize(clazz.getDeclaredConstructor().newInstance() as AndroidStartup<*>, result, initialize, initialized)
85 | }
86 | }
87 | initialize.remove(uniqueKey)
88 | initialized.add(uniqueKey)
89 | }
90 | } catch (t: Throwable) {
91 | throw StartupException(t)
92 | }
93 | }
94 |
95 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/MultipleProcessService.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample
2 |
3 | import android.app.Service
4 | import android.content.Intent
5 | import android.os.Handler
6 | import android.os.IBinder
7 | import android.os.Looper
8 | import android.util.Log
9 | import com.rousetime.android_startup.AndroidStartup
10 | import com.rousetime.android_startup.StartupListener
11 | import com.rousetime.android_startup.StartupManager
12 | import com.rousetime.android_startup.manager.StartupCacheManager
13 | import com.rousetime.android_startup.model.CostTimesModel
14 | import com.rousetime.android_startup.model.LoggerLevel
15 | import com.rousetime.android_startup.model.StartupConfig
16 | import com.rousetime.sample.startup.multiple.SampleMultipleFifthStartup
17 | import com.rousetime.sample.startup.multiple.SampleMultipleFourthStartup
18 | import com.rousetime.sample.startup.multiple.SampleMultipleSixthStartup
19 | import com.rousetime.sample.startup.multiple.SampleMultipleThirdStartup
20 |
21 | /**
22 | * Created by idisfkj on 2020/9/15.
23 | * Email: idisfkj@gmail.com.
24 | */
25 | class MultipleProcessService : Service() {
26 |
27 | private var mServiceListener: IServiceListenerInterface? = null
28 |
29 | private var mBinder = object : IMultipleProcessServiceInterface.Stub() {
30 |
31 | override fun clear() {
32 | StartupCacheManager.instance.clear()
33 | }
34 |
35 | override fun initStartup() {
36 | // must be start main thread.
37 | Handler(Looper.getMainLooper()).post {
38 | val list = mutableListOf>()
39 | list.add(SampleMultipleThirdStartup())
40 | list.add(SampleMultipleFourthStartup())
41 | list.add(SampleMultipleFifthStartup())
42 | list.add(SampleMultipleSixthStartup())
43 | val config = StartupConfig.Builder()
44 | .setLoggerLevel(LoggerLevel.DEBUG)
45 | .setAwaitTimeout(12000L)
46 | .setListener(object : StartupListener {
47 | override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List) {
48 | // can to do cost time statistics.
49 | mServiceListener?.onCompleted(buildString {
50 | append("Startup Completed: ")
51 | append("\n")
52 | append("\n")
53 | costTimesModels.forEach {
54 | append("\n")
55 | append("Startup Name: ${it.name}")
56 | append("\n")
57 | append("CallOnMainThread: ${it.callOnMainThread}")
58 | append("\n")
59 | append("WaitOnMainThread: ${it.waitOnMainThread}")
60 | append("\n")
61 | append("Cost times: ${it.endTime - it.startTime} ms")
62 | append("\n")
63 | }
64 | if (costTimesModels.isEmpty()) {
65 | append("result form cache.")
66 | append("\n")
67 | list.forEach {
68 | append("\n")
69 | append("${it::class.java.simpleName}: ${StartupCacheManager.instance.obtainInitializedResult(it::class.java)}")
70 | append("\n")
71 | }
72 | }
73 | }, totalMainThreadCostTime)
74 | Log.d("StartupTrack", "onCompleted: ${costTimesModels.size}")
75 | }
76 | })
77 | .build()
78 |
79 | StartupManager.Builder()
80 | .setConfig(config)
81 | .addAllStartup(list)
82 | .build(this@MultipleProcessService)
83 | .start()
84 | .await()
85 | }
86 | }
87 |
88 | override fun addServiceListener(serviceListener: IServiceListenerInterface?) {
89 | mServiceListener = serviceListener
90 | }
91 |
92 | }
93 |
94 | override fun onBind(intent: Intent?): IBinder? {
95 | return mBinder
96 | }
97 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_more.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
20 |
32 |
33 |
45 |
46 |
58 |
59 |
71 |
72 |
84 |
85 |
97 |
98 |
110 |
111 |
--------------------------------------------------------------------------------
/android-startup/maven-center.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 | apply plugin: 'signing'
3 |
4 | task androidSourcesJar(type: Jar) {
5 | classifier = 'sources'
6 | from android.sourceSets.main.java.source
7 |
8 | exclude "**/R.class"
9 | exclude "**/BuildConfig.class"
10 | }
11 |
12 | ext {
13 | PUBLISH_GROUP_ID = 'io.github.idisfkj'
14 | PUBLISH_ARTIFACT_ID = 'android-startup'
15 | PUBLISH_VERSION = '1.1.0'
16 | }
17 |
18 | ext["signing.keyId"] = ''
19 | ext["signing.password"] = ''
20 | ext["signing.secretKeyRingFile"] = ''
21 | ext["ossrhUsername"] = ''
22 | ext["ossrhPassword"] = ''
23 |
24 | File secretPropsFile = project.rootProject.file('local.properties')
25 | if (secretPropsFile.exists()) {
26 | println "Found secret props file, loading props"
27 | Properties p = new Properties()
28 | p.load(new FileInputStream(secretPropsFile))
29 | p.each { name, value ->
30 | ext[name] = value
31 | }
32 | } else {
33 | println "No props file, loading env vars"
34 | }
35 | publishing {
36 | publications {
37 | release(MavenPublication) {
38 | // The coordinates of the library, being set from variables that
39 | // we'll set up in a moment
40 | groupId PUBLISH_GROUP_ID
41 | artifactId PUBLISH_ARTIFACT_ID
42 | version PUBLISH_VERSION
43 |
44 | // Two artifacts, the `aar` and the sources
45 | artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
46 | artifact androidSourcesJar
47 |
48 | // Self-explanatory metadata for the most part
49 | pom {
50 | name = PUBLISH_ARTIFACT_ID
51 | description = '🔥The Android Startup library provides a straightforward, performant way to initialize components at the application startup. Both library developers and app developers can use Android Startup to streamline startup sequences and explicitly set the order of initialization.'
52 | // If your project has a dedicated site, use its URL here
53 | url = 'https://github.com/idisfkj/android-startup'
54 | licenses {
55 | license {
56 | //协议类型,一般默认Apache License2.0的话不用改:
57 | name = 'The Apache License, Version 2.0'
58 | url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
59 | }
60 | }
61 | developers {
62 | developer {
63 | id = 'Rouse'
64 | name = 'Rouse'
65 | email = 'idisfkj@gmail.com'
66 | }
67 | }
68 | // Version control info, if you're using GitHub, follow the format as seen here
69 | scm {
70 | //修改成你的Git地址:
71 | connection = 'https://github.com/idisfkj/android-startup'
72 | developerConnection = 'https://github.com/idisfkj/android-startup.git'
73 | //分支地址:
74 | url = 'https://github.com/idisfkj/android-startup/tree/master'
75 | }
76 | // A slightly hacky fix so that your POM will include any transitive dependencies
77 | // that your library builds upon
78 | withXml {
79 | def dependenciesNode = asNode().appendNode('dependencies')
80 |
81 | project.configurations.implementation.allDependencies.each {
82 | if (it.group != null && it.name != "unspecified" && it.version != null) {
83 | def dependencyNode = dependenciesNode.appendNode('dependency')
84 | dependencyNode.appendNode('groupId', it.group)
85 | dependencyNode.appendNode('artifactId', it.name)
86 | dependencyNode.appendNode('version', it.version)
87 | }
88 | }
89 | }
90 | }
91 | }
92 | }
93 | repositories {
94 | // The repository to publish to, Sonatype/MavenCentral
95 | maven {
96 | // This is an arbitrary name, you may also use "mavencentral" or
97 | // any other name that's descriptive for you
98 | name = "mavencentral"
99 |
100 | def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
101 | def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
102 | // You only need this if you want to publish snapshots, otherwise just set the URL
103 | // to the release repo directly
104 | url = PUBLISH_VERSION.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
105 |
106 | // The username and password we've fetched earlier
107 | credentials {
108 | username ossrhUsername
109 | password ossrhPassword
110 | }
111 | }
112 | }
113 | }
114 | signing {
115 | sign publishing.publications
116 | }
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/sort/TopologySort.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup.sort
2 |
3 | import androidx.core.os.TraceCompat
4 | import com.rousetime.android_startup.Startup
5 | import com.rousetime.android_startup.execption.StartupException
6 | import com.rousetime.android_startup.extensions.getUniqueKey
7 | import com.rousetime.android_startup.model.StartupSortStore
8 | import com.rousetime.android_startup.utils.StartupLogUtils
9 | import java.util.*
10 |
11 | /**
12 | * Created by idisfkj on 2020/7/24.
13 | * Email: idisfkj@gmail.com.
14 | */
15 | internal object TopologySort {
16 |
17 | fun sort(startupList: List>): StartupSortStore {
18 | TraceCompat.beginSection(TopologySort::class.java.simpleName)
19 |
20 | val mainResult = mutableListOf>()
21 | val ioResult = mutableListOf>()
22 | val temp = mutableListOf>()
23 | val startupMap = hashMapOf>()
24 | val zeroDeque = ArrayDeque()
25 | val startupChildrenMap = hashMapOf>()
26 | val inDegreeMap = hashMapOf()
27 |
28 | startupList.forEach {
29 | val uniqueKey = it::class.java.getUniqueKey()
30 | if (!startupMap.containsKey(uniqueKey)) {
31 | startupMap[uniqueKey] = it
32 | // save in-degree
33 | inDegreeMap[uniqueKey] = it.getDependenciesCount()
34 | if (it.dependenciesByName().isNullOrEmpty() && it.dependencies().isNullOrEmpty()) {
35 | zeroDeque.offer(uniqueKey)
36 | } else if (it.dependenciesByName().isNullOrEmpty()) {
37 | // add key parent, value list children
38 | it.dependencies()?.forEach { parent ->
39 | val parentUniqueKey = parent.getUniqueKey()
40 | if (startupChildrenMap[parentUniqueKey] == null) {
41 | startupChildrenMap[parentUniqueKey] = arrayListOf()
42 | }
43 | startupChildrenMap[parentUniqueKey]?.add(uniqueKey)
44 | }
45 | } else {
46 | // add key parent, value list children
47 | it.dependenciesByName()?.forEach { parent ->
48 | val parentUniqueKey = parent.getUniqueKey()
49 | if (startupChildrenMap[parentUniqueKey] == null) {
50 | startupChildrenMap[parentUniqueKey] = arrayListOf()
51 | }
52 | startupChildrenMap[parentUniqueKey]?.add(uniqueKey)
53 | }
54 | }
55 | } else {
56 | throw StartupException("$it multiple add.")
57 | }
58 | }
59 |
60 | while (!zeroDeque.isEmpty()) {
61 | zeroDeque.poll()?.let {
62 | startupMap[it]?.let { androidStartup ->
63 | temp.add(androidStartup)
64 | // add zero in-degree to result list
65 | if (androidStartup.callCreateOnMainThread()) {
66 | mainResult.add(androidStartup)
67 | } else {
68 | ioResult.add(androidStartup)
69 | }
70 | }
71 | startupChildrenMap[it]?.forEach { children ->
72 | inDegreeMap[children] = inDegreeMap[children]?.minus(1) ?: 0
73 | // add zero in-degree to deque
74 | if (inDegreeMap[children] == 0) {
75 | zeroDeque.offer(children)
76 | }
77 | }
78 | }
79 | }
80 |
81 | if (mainResult.size + ioResult.size != startupList.size) {
82 | throw StartupException("lack of dependencies or have circle dependencies.")
83 | }
84 |
85 | val result = mutableListOf>().apply {
86 | addAll(ioResult)
87 | addAll(mainResult)
88 | }
89 | printResult(temp)
90 |
91 | TraceCompat.endSection()
92 |
93 | return StartupSortStore(
94 | result,
95 | startupMap,
96 | startupChildrenMap
97 | )
98 | }
99 |
100 | private fun printResult(result: List>) {
101 | val printBuilder = buildString {
102 | append("TopologySort result: ")
103 | append("\n")
104 | append("|================================================================")
105 | result.forEachIndexed { index, it ->
106 | append("\n")
107 | append("| order | [${index + 1}] ")
108 | append("\n")
109 | append("|----------------------------------------------------------------")
110 | append("\n")
111 | append("| Startup | ${it::class.java.simpleName}")
112 | append("\n")
113 | append("|----------------------------------------------------------------")
114 | append("\n")
115 | append("| Dependencies size | ${it.getDependenciesCount()}")
116 | append("\n")
117 | append("|----------------------------------------------------------------")
118 | append("\n")
119 | append("| callCreateOnMainThread | ${it.callCreateOnMainThread()}")
120 | append("\n")
121 | append("|----------------------------------------------------------------")
122 | append("\n")
123 | append("| waitOnMainThread | ${it.waitOnMainThread()}")
124 | append("\n")
125 | append("|================================================================")
126 | }
127 | }
128 | StartupLogUtils.d { printBuilder }
129 | }
130 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/android-startup/src/main/java/com/rousetime/android_startup/StartupManager.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.android_startup
2 |
3 | import android.content.Context
4 | import android.os.Looper
5 | import android.text.TextUtils
6 | import androidx.core.os.TraceCompat
7 | import com.rousetime.android_startup.annotation.MultipleProcess
8 | import com.rousetime.android_startup.dispatcher.StartupManagerDispatcher
9 | import com.rousetime.android_startup.execption.StartupException
10 | import com.rousetime.android_startup.manager.StartupCacheManager
11 | import com.rousetime.android_startup.model.LoggerLevel
12 | import com.rousetime.android_startup.model.StartupConfig
13 | import com.rousetime.android_startup.model.StartupSortStore
14 | import com.rousetime.android_startup.sort.TopologySort
15 | import com.rousetime.android_startup.utils.ProcessUtils
16 | import com.rousetime.android_startup.utils.StartupCostTimesUtils
17 | import com.rousetime.android_startup.utils.StartupLogUtils
18 | import java.util.concurrent.CountDownLatch
19 | import java.util.concurrent.TimeUnit
20 | import java.util.concurrent.atomic.AtomicInteger
21 |
22 | /**
23 | * Created by idisfkj on 2020/7/24.
24 | * Email : idisfkj@gmail.com.
25 | */
26 | class StartupManager private constructor(
27 | private val context: Context,
28 | private val startupList: List>,
29 | private val needAwaitCount: AtomicInteger,
30 | private val config: StartupConfig
31 | ) {
32 |
33 | private var mAwaitCountDownLatch: CountDownLatch? = null
34 |
35 | companion object {
36 | const val AWAIT_TIMEOUT = 10000L
37 | }
38 |
39 | init {
40 | // save initialized config
41 | StartupCacheManager.instance.saveConfig(config)
42 | StartupLogUtils.level = config.loggerLevel
43 | }
44 |
45 | fun start() = apply {
46 | if (Looper.getMainLooper() != Looper.myLooper()) {
47 | throw StartupException("start method must be call in MainThread.")
48 | }
49 |
50 | if (mAwaitCountDownLatch != null) {
51 | throw StartupException("start method repeated call.")
52 | }
53 | mAwaitCountDownLatch = CountDownLatch(needAwaitCount.get())
54 |
55 | if (startupList.isNullOrEmpty()) {
56 | StartupLogUtils.e { "startupList is empty in the current process." }
57 | return@apply
58 | }
59 |
60 | TraceCompat.beginSection(StartupManager::class.java.simpleName)
61 | StartupCostTimesUtils.startTime = System.nanoTime()
62 |
63 | TopologySort.sort(startupList).run {
64 | mDefaultManagerDispatcher.prepare()
65 | execute(this)
66 | }
67 |
68 | if (needAwaitCount.get() <= 0) {
69 | StartupCostTimesUtils.endTime = System.nanoTime()
70 | TraceCompat.endSection()
71 | }
72 | }
73 |
74 | private fun execute(sortStore: StartupSortStore) {
75 | sortStore.result.forEach { mDefaultManagerDispatcher.dispatch(it, sortStore) }
76 | }
77 |
78 | /**
79 | * Startup dispatcher
80 | */
81 | private val mDefaultManagerDispatcher by lazy {
82 | StartupManagerDispatcher(context, needAwaitCount, mAwaitCountDownLatch, startupList.size, config.listener)
83 | }
84 |
85 | /**
86 | * to await startup completed
87 | * block main thread.
88 | */
89 | fun await() {
90 | if (mAwaitCountDownLatch == null) {
91 | throw StartupException("must be call start method before call await method.")
92 | }
93 |
94 | val count = needAwaitCount.get()
95 | try {
96 | mAwaitCountDownLatch?.await(config.awaitTimeout, TimeUnit.MILLISECONDS)
97 | } catch (e: InterruptedException) {
98 | e.printStackTrace()
99 | }
100 |
101 | if (count > 0) {
102 | StartupCostTimesUtils.endTime = System.nanoTime()
103 | TraceCompat.endSection()
104 | }
105 | }
106 |
107 | class Builder {
108 | private var mStartupList = mutableListOf>()
109 | private var mNeedAwaitCount = AtomicInteger()
110 | private var mLoggerLevel = LoggerLevel.NONE
111 | private var mAwaitTimeout = AWAIT_TIMEOUT
112 | private var mConfig: StartupConfig? = null
113 |
114 | fun addStartup(startup: AndroidStartup<*>) = apply {
115 | mStartupList.add(startup)
116 | }
117 |
118 | fun addAllStartup(list: List>) = apply {
119 | list.forEach {
120 | addStartup(it)
121 | }
122 | }
123 |
124 | fun setConfig(config: StartupConfig?) = apply {
125 | mConfig = config
126 | }
127 |
128 | @Deprecated("Use setConfig() instead.")
129 | fun setLoggerLevel(level: LoggerLevel) = apply {
130 | mLoggerLevel = level
131 | }
132 |
133 | @Deprecated("Use setConfig() instead.")
134 | fun setAwaitTimeout(timeoutMilliSeconds: Long) = apply {
135 | mAwaitTimeout = timeoutMilliSeconds
136 | }
137 |
138 | fun build(context: Context): StartupManager {
139 | val realStartupList = mutableListOf>()
140 | mStartupList.forEach {
141 | val process = it::class.java.getAnnotation(MultipleProcess::class.java)?.process ?: arrayOf()
142 | if (process.isNullOrEmpty() || ProcessUtils.isMultipleProcess(context, process)) {
143 | realStartupList.add(it)
144 | if (it.waitOnMainThread() && !it.callCreateOnMainThread()) {
145 | mNeedAwaitCount.incrementAndGet()
146 | }
147 | }
148 | }
149 |
150 | return StartupManager(
151 | context,
152 | realStartupList,
153 | mNeedAwaitCount,
154 | mConfig ?: StartupConfig.Builder()
155 | .setLoggerLevel(mLoggerLevel)
156 | .setAwaitTimeout(mAwaitTimeout)
157 | .build()
158 | )
159 | }
160 | }
161 |
162 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rousetime.sample
2 |
3 | import android.app.Service
4 | import android.content.ComponentName
5 | import android.content.Intent
6 | import android.content.ServiceConnection
7 | import android.os.Bundle
8 | import android.os.Handler
9 | import android.os.IBinder
10 | import android.os.Looper
11 | import android.util.Log
12 | import android.view.View
13 | import androidx.appcompat.app.AppCompatActivity
14 | import com.rousetime.android_startup.AndroidStartup
15 | import com.rousetime.android_startup.StartupListener
16 | import com.rousetime.android_startup.StartupManager
17 | import com.rousetime.android_startup.manager.StartupCacheManager
18 | import com.rousetime.android_startup.model.CostTimesModel
19 | import com.rousetime.android_startup.model.LoggerLevel
20 | import com.rousetime.android_startup.model.StartupConfig
21 | import com.rousetime.sample.startup.*
22 | import com.rousetime.sample.startup.priority.SamplePriorityFirstStartup
23 | import com.rousetime.sample.startup.priority.SamplePrioritySecondStartup
24 | import com.rousetime.sample.startup.priority.SamplePriorityThirdStartup
25 | import kotlinx.android.synthetic.main.activity_common.*
26 |
27 | /**
28 | * Created by idisfkj on 2020/8/13.
29 | * Email: idisfkj@gmail.com.
30 | */
31 | class SampleCommonActivity : AppCompatActivity() {
32 |
33 | private var mMultipleProcessService: IMultipleProcessServiceInterface? = null
34 |
35 | private val mMultipleProcessServiceConnection by lazy {
36 | object : ServiceConnection {
37 | override fun onServiceDisconnected(name: ComponentName?) {}
38 |
39 | override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
40 | mMultipleProcessService = IMultipleProcessServiceInterface.Stub.asInterface(service)
41 | mMultipleProcessService?.addServiceListener(object : IServiceListenerInterface.Stub() {
42 | override fun onCompleted(result: String?, totalMainThreadCostTime: Long) {
43 | result?.let {
44 | Handler(Looper.getMainLooper()).post {
45 | showResult(result)
46 | }
47 | }
48 | }
49 | })
50 | mMultipleProcessService?.initStartup()
51 | }
52 | }
53 | }
54 |
55 | override fun onCreate(savedInstanceState: Bundle?) {
56 | super.onCreate(savedInstanceState)
57 | setContentView(R.layout.activity_common)
58 |
59 | start()
60 | }
61 |
62 | fun onClick(view: View) {
63 | if (R.id.clear == view.id) {
64 | mMultipleProcessService?.clear()
65 | StartupCacheManager.instance.clear()
66 | content.text = getString(R.string.clear_cache_success)
67 | } else if (R.id.get == view.id) {
68 | unbindService()
69 | start()
70 | }
71 | }
72 |
73 | private fun start() {
74 | // show initialize tips
75 | content.text = getString(R.string.sample_startup_not_initialized)
76 |
77 | val list = mutableListOf>()
78 | when (intent.getIntExtra("id", -1)) {
79 | R.id.sync_and_sync -> {
80 | list.add(SampleSyncOneStartup())
81 | list.add(SampleSyncTwoStartup())
82 | }
83 | R.id.sync_and_async -> {
84 | list.add(SampleSyncThreeStartup())
85 | list.add(SampleAsyncOneStartup())
86 | }
87 | R.id.async_and_sync -> {
88 | list.add(SampleAsyncTwoStartup())
89 | list.add(SampleSyncFourStartup())
90 | }
91 | R.id.async_and_async -> {
92 | list.add(SampleAsyncFiveStartup())
93 | list.add(SampleAsyncThreeStartup())
94 | }
95 | R.id.async_and_async_await_main_thread -> {
96 | list.add(SampleAsyncSixStartup())
97 | list.add(SampleAsyncFourStartup())
98 | }
99 | R.id.manual_dispatch -> {
100 | list.add(SampleManualDispatchStartup())
101 | list.add(SampleAsyncSevenStartup())
102 | list.add(SampleSyncFiveStartup())
103 | }
104 | R.id.thread_priority -> {
105 | list.add(SamplePriorityThirdStartup())
106 | list.add(SamplePrioritySecondStartup())
107 | list.add(SamplePriorityFirstStartup())
108 | }
109 | R.id.multiply_process -> {
110 | bindService(Intent(this, MultipleProcessService::class.java), mMultipleProcessServiceConnection, Service.BIND_AUTO_CREATE)
111 | }
112 | }
113 | val config = StartupConfig.Builder()
114 | .setLoggerLevel(LoggerLevel.DEBUG)
115 | .setAwaitTimeout(12000L)
116 | .setListener(object : StartupListener {
117 | override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List) {
118 | // can to do cost time statistics.
119 | showResult(buildString {
120 | append("Startup Completed: ")
121 | append("\n")
122 | append("\n")
123 | costTimesModels.forEach {
124 | append("\n")
125 | append("Startup Name: ${it.name}")
126 | append("\n")
127 | append("CallOnMainThread: ${it.callOnMainThread}")
128 | append("\n")
129 | append("WaitOnMainThread: ${it.waitOnMainThread}")
130 | append("\n")
131 | append("Cost times: ${it.endTime - it.startTime} ms")
132 | append("\n")
133 | }
134 | if (costTimesModels.isEmpty()) {
135 | append("result form cache.")
136 | append("\n")
137 | list.forEach {
138 | append("\n")
139 | append("${it::class.java.simpleName}: ${StartupCacheManager.instance.obtainInitializedResult(it::class.java)}")
140 | append("\n")
141 | }
142 | }
143 | })
144 | Log.d("StartupTrack", "onCompleted: ${costTimesModels.size}")
145 | }
146 | })
147 | .build()
148 |
149 | // because some scenarios startup block on main thread.
150 | // in order to show initialize tips,to delay a frame times.
151 | Handler().postDelayed({
152 | StartupManager.Builder()
153 | .setConfig(config)
154 | .addAllStartup(list)
155 | .build(this)
156 | .start()
157 | .await()
158 | }, 16)
159 | }
160 |
161 | private fun showResult(result: String) {
162 | content.text = result
163 | }
164 |
165 | private fun unbindService() {
166 | mMultipleProcessService?.let {
167 | unbindService(mMultipleProcessServiceConnection)
168 | }
169 | }
170 |
171 | override fun onDestroy() {
172 | unbindService()
173 | super.onDestroy()
174 | }
175 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README-ch.md:
--------------------------------------------------------------------------------
1 | 中文|[English](README.md)
2 |
3 | # android-startup
4 | [](https://idisfkj.github.io/archives/)
5 | [](https://www.android.com/)
6 | [](https://android-arsenal.com/api?level=15)
7 | [](https://kotlinlang.org/)
8 | [](https://github.com/idisfkj/android-startup/releases)
9 | []()
10 | [](https://www.apache.org/licenses/LICENSE-2.0)
11 |
12 | `android-startup`提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用`android-startup`来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系。
13 | 与此同时`android-startup`支持**同步与异步等待**,并通过有向无环图[拓扑排序](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/sort/TopologySort.kt)的方式来保证内部依赖组件的初始化顺序。
14 |
15 | 下面是一张与google的[App Startup](https://developer.android.com/topic/libraries/app-startup)功能对比的表格。
16 |
17 | |指标|App Startup|Android Startup|
18 | |:---:|:------:| :------:|
19 | |手动配置| ✅ | ✅ |
20 | |自动配置| ✅ | ✅ |
21 | |依赖支持| ✅ | ✅ |
22 | |闭环处理| ✅ | ✅ |
23 | |线程控制| ❌ | ✅ |
24 | |异步等待| ❌ | ✅ |
25 | |依赖回调| ❌ | ✅ |
26 | |手动通知| ❌ | ✅ |
27 | |拓扑优化| ❌ | ✅ |
28 | |耗时统计| ❌ | ✅ |
29 | |线程优先级| ❌ | ✅ |
30 | |多进程| ❌ | ✅ |
31 |
32 | > 开源不易,希望朋友小手一抖,右上角来个star,感谢🙏
33 |
34 | ## 相关文章
35 |
36 | [我为何弃用Jetpack的App Startup?](https://juejin.im/post/6859500445669752846)
37 |
38 | [Android Startup实现分析](https://juejin.im/post/6871006041262260237)
39 |
40 | [Android Startup最新进展](https://mp.weixin.qq.com/s?__biz=MzIzNTc5NDY4Nw==&mid=2247484784&idx=1&sn=435833fade53cfbe62bf5b3e98e60d59&chksm=e8e0fce0df9775f65748debdfc1b8f5013970d92c7e76ea2c7436240438c278fb62bfbbc6cf3&token=630713225&lang=zh_CN#rd)
41 |
42 | ## 添加依赖
43 | 将下面的依赖添加到`build.gradle`文件中:
44 |
45 | ```
46 | repositories {
47 | mavenCentral()
48 | }
49 |
50 | dependencies {
51 | implementation 'io.github.idisfkj:android-startup:1.1.0'
52 | }
53 | ```
54 |
55 | > 依赖版本的更新信息: [Release](https://github.com/idisfkj/android-startup/releases)
56 |
57 | ## 快速使用
58 |
59 | 
60 |
61 | android-startup提供了两种使用方式,在使用之前需要先定义初始化的组件。
62 |
63 | ### 定义初始化的组件
64 | 每一个初始化的组件都需要实现[AndroidStartup](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/AndroidStartup.kt)抽象类,它实现了`Startup`接口,它主要有以下四个抽象方法:
65 |
66 | * `callCreateOnMainThread(): Boolean`用来控制`create()`方法调时所在的线程,返回true代表在主线程执行。
67 |
68 | * `waitOnMainThread(): Boolean`用来控制当前初始化的组件是否需要在主线程进行等待其完成。如果返回true,将在主线程等待,并且阻塞主线程。
69 |
70 | * `create(): T?`组件初始化方法,执行需要处理的初始化逻辑,支持返回一个`T`类型的实例。
71 |
72 | * `dependenciesByName(): List?`返回`String`类型的`list`集合。用来表示当前组件在执行之前需要依赖的组件。
73 |
74 | 例如,下面定义一个`SampleFirstStartup`类来实现`AndroidStartup`抽象类:
75 |
76 | ```
77 | class SampleFirstStartup : AndroidStartup() {
78 |
79 | override fun callCreateOnMainThread(): Boolean = true
80 |
81 | override fun waitOnMainThread(): Boolean = false
82 |
83 | override fun create(context: Context): String? {
84 | // todo something
85 | return this.javaClass.simpleName
86 | }
87 |
88 | override fun dependenciesByName(): List? {
89 | return null
90 | }
91 |
92 | }
93 | ```
94 | 因为`SampleFirstStartup`在执行之前不需要依赖其它组件,所以它的`dependenciesByName()`方法可以返回空,同时它会在主线程中执行。
95 |
96 | > 注意:️虽然`waitOnMainThread()`返回了`false`,但由于它是在主线程中执行,而主线程默认是阻塞的,所以`callCreateOnMainThread()`返回`true`时,该方法设置将失效。
97 |
98 | 假设你还需要定义`SampleSecondStartup`,它依赖于`SampleFirstStartup`。这意味着在执行`SampleSecondStartup`之前`SampleFirstStartup`必须先执行完毕。
99 |
100 | ```
101 | class SampleSecondStartup : AndroidStartup() {
102 |
103 | override fun callCreateOnMainThread(): Boolean = false
104 |
105 | override fun waitOnMainThread(): Boolean = true
106 |
107 | override fun create(context: Context): Boolean {
108 | // 模仿执行耗时
109 | Thread.sleep(5000)
110 | return true
111 | }
112 |
113 | override fun dependenciesByName(): List {
114 | return listOf("com.rousetime.sample.startup.SampleFirstStartup")
115 | }
116 |
117 | }
118 | ```
119 | 在`dependenciesByName()`方法中返回了`com.rousetime.sample.startup.SampleFirstStartup`,所以它能保证`SampleFirstStartup`优先执行完毕。
120 | 它会在子线程中执行,但由于`waitOnMainThread()`返回了`true`,所以主线程会阻塞等待直到它执行完毕。
121 |
122 | 例如,你还定义了[SampleThirdStartup](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/startup/SampleThirdStartup.kt)与[SampleFourthStartup](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/startup/SampleFourthStartup.kt)
123 |
124 | ### Manifest中自动配置
125 | 第一种初始化方法是在Manifest中进行自动配置。
126 |
127 | 在Android Startup中提供了`StartupProvider`类,它是一个特殊的content provider,提供自动识别在manifest中配置的初始化组件。
128 | 为了让其能够自动识别,需要在`StartupProvider`中定义``标签。其中的`name`为定义的组件类,`value`的值对应为`android.startup`。
129 |
130 | ```
131 |
135 |
136 |
139 |
140 |
141 | ```
142 | 你不需要将`SampleFirstStartup`、`SampleSecondStartup`与`SampleThirdStartup`添加到``标签中。这是因为在`SampleFourthStartup`中,它的`dependenciesByName()`中依赖了这些组件。`StartupProvider`会自动识别已经声明的组件中依赖的其它组件。
143 |
144 | ### Application中手动配置
145 | 第二种初始化方法是在Application进行手动配置。
146 |
147 | 手动初始化需要使用到`StartupManager.Builder()`。
148 |
149 | 例如,如下代码使用`StartupManager.Builder()`进行初始化配置。
150 |
151 | ```
152 | class SampleApplication : Application() {
153 |
154 | override fun onCreate() {
155 | super.onCreate()
156 | StartupManager.Builder()
157 | .addStartup(SampleFirstStartup())
158 | .addStartup(SampleSecondStartup())
159 | .addStartup(SampleThirdStartup())
160 | .addStartup(SampleFourthStartup())
161 | .build(this)
162 | .start()
163 | .await()
164 | }
165 | }
166 | ```
167 | 完整的示例代码,你可以通过查看[app](https://github.com/idisfkj/android-startup/tree/master/app)获取。
168 |
169 | 运行示例代码,控制台将会产生如下日志:
170 |
171 | 1. 排序优化之后的初始化顺序
172 |
173 | ```
174 | *****/com.rousetime.sample D/StartupTrack: TopologySort result:
175 | |================================================================
176 | | order | [1]
177 | |----------------------------------------------------------------
178 | | Startup | SampleFirstStartup
179 | |----------------------------------------------------------------
180 | | Dependencies size | 0
181 | |----------------------------------------------------------------
182 | | callCreateOnMainThread | true
183 | |----------------------------------------------------------------
184 | | waitOnMainThread | false
185 | |================================================================
186 | | order | [2]
187 | |----------------------------------------------------------------
188 | | Startup | SampleSecondStartup
189 | |----------------------------------------------------------------
190 | | Dependencies size | 1
191 | |----------------------------------------------------------------
192 | | callCreateOnMainThread | false
193 | |----------------------------------------------------------------
194 | | waitOnMainThread | true
195 | |================================================================
196 | | order | [3]
197 | |----------------------------------------------------------------
198 | | Startup | SampleThirdStartup
199 | |----------------------------------------------------------------
200 | | Dependencies size | 2
201 | |----------------------------------------------------------------
202 | | callCreateOnMainThread | false
203 | |----------------------------------------------------------------
204 | | waitOnMainThread | false
205 | |================================================================
206 | | order | [4]
207 | |----------------------------------------------------------------
208 | | Startup | SampleFourthStartup
209 | |----------------------------------------------------------------
210 | | Dependencies size | 3
211 | |----------------------------------------------------------------
212 | | callCreateOnMainThread | false
213 | |----------------------------------------------------------------
214 | | waitOnMainThread | false
215 | |================================================================
216 | ```
217 |
218 | 2. 各组件初始化所消耗的时间
219 |
220 | ```
221 | *****/com.rousetime.sample D/StartupTrack: startup cost times detail:
222 | |=================================================================
223 | | Startup Name | SampleFirstStartup
224 | | ----------------------- | --------------------------------------
225 | | Call On Main Thread | true
226 | | ----------------------- | --------------------------------------
227 | | Wait On Main Thread | false
228 | | ----------------------- | --------------------------------------
229 | | Cost Times | 0 ms
230 | |=================================================================
231 | | Startup Name | SampleSecondStartup
232 | | ----------------------- | --------------------------------------
233 | | Call On Main Thread | false
234 | | ----------------------- | --------------------------------------
235 | | Wait On Main Thread | true
236 | | ----------------------- | --------------------------------------
237 | | Cost Times | 5001 ms
238 | |=================================================================
239 | | Startup Name | SampleThirdStartup
240 | | ----------------------- | --------------------------------------
241 | | Call On Main Thread | false
242 | | ----------------------- | --------------------------------------
243 | | Wait On Main Thread | false
244 | | ----------------------- | --------------------------------------
245 | | Cost Times | 3007 ms
246 | |=================================================================
247 | | Startup Name | SampleFourthStartup
248 | | ----------------------- | --------------------------------------
249 | | Call On Main Thread | false
250 | | ----------------------- | --------------------------------------
251 | | Wait On Main Thread | false
252 | | ----------------------- | --------------------------------------
253 | | Cost Times | 102 ms
254 | |=================================================================
255 | | Total Main Thread Times | 5008 ms
256 | |=================================================================
257 | ```
258 |
259 | ## 更多
260 |
261 | ### 可选配置
262 |
263 | * [LoggerLevel](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/model/LoggerLevel.kt): 控制Android Startup中的日志输出,可选值包括`LoggerLevel.NONE`, `LoggerLevel.ERROR` and `LoggerLevel.DEBUG`。
264 |
265 | * [AwaitTimeout](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/model/StartupConfig.kt): 控制Android Startup中主线程的超时等待时间,即阻塞的最长时间。
266 |
267 | * [StartupListener](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/StartupListener.kt): Android Startup监听器,所有组件初始化完成之后该监听器会被调用。
268 |
269 | * [OpenStatistic](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/model/StartupConfig.kt): 控制Android Startup是否开启对各个任务的耗时统计。
270 |
271 | #### Manifest中配置
272 | 使用这些配置,你需要定义一个类去实现`StartupProviderConfig`接口,并且实现它的对应方法。
273 |
274 | ```
275 | class SampleStartupProviderConfig : StartupProviderConfig {
276 |
277 | override fun getConfig(): StartupConfig =
278 | StartupConfig.Builder()
279 | .setLoggerLevel(LoggerLevel.DEBUG) // default LoggerLevel.NONE
280 | .setAwaitTimeout(12000L) // default 10000L
281 | .setOpenStatistics(true) // default true
282 | .setListener(object : StartupListener {
283 | override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List) {
284 | // can to do cost time statistics.
285 | }
286 | })
287 | .build()
288 | }
289 | ```
290 | 与此同时,你还需要在manifest中进行配置`StartupProviderConfig`。
291 |
292 | ```
293 |
297 |
298 |
301 |
302 |
303 | ```
304 | 经过上面的配置,`StartupProvider`会自动解析`SampleStartupProviderConfig`。
305 |
306 | #### Application中配置
307 | 在Application需要借助`StartupManager.Builder()`进行配置。
308 |
309 | ```
310 | override fun onCreate() {
311 | super.onCreate()
312 |
313 | val config = StartupConfig.Builder()
314 | .setLoggerLevel(LoggerLevel.DEBUG)
315 | .setAwaitTimeout(12000L)
316 | .setListener(object : StartupListener {
317 | override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List) {
318 | // can to do cost time statistics.
319 | }
320 | })
321 | .build()
322 |
323 | StartupManager.Builder()
324 | .setConfig(config)
325 | ...
326 | .build(this)
327 | .start()
328 | .await()
329 | }
330 | ```
331 |
332 | ### [AndroidStartup](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/AndroidStartup.kt)
333 |
334 | * `createExecutor(): Executor`: 如果定义的组件没有运行在主线程,那么可以通过该方法进行控制运行的子线程。
335 |
336 | * `onDependenciesCompleted(startup: Startup<*>, result: Any?)`: 该方法会在每一个依赖执行完毕之后进行回调。
337 |
338 | * `manualDispatch(): Boolean`: 返回`true`时,代表需要手动去通知依赖自身的子组件; 需要配合`onDispatch()`来使用。
339 |
340 | * `onDispatch()`: 配合`manualDispatch()`使用,通知依赖自身的子组件,开始执行子组件的初始化逻辑。
341 |
342 | ### [StartupCacheManager](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/manager/StartupCacheManager.kt)
343 |
344 | * `hadInitialized(zClass: Class>)`: 检验对应的组件是否已经初始化完成。
345 |
346 | * `obtainInitializedResult(zClass: Class>): T?`: 获取对应已经初始化的组件所返回的结果。
347 |
348 | * `remove(zClass: Class>)`: 清除对应组件的初始化缓存结果。
349 |
350 | * `clear()`: 清除所有组件初始化的缓存结果。
351 |
352 | ### [Annotation](https://github.com/idisfkj/android-startup/tree/master/android-startup/src/main/java/com/rousetime/android_startup/annotation)
353 |
354 | * ThreadPriority: 设置`Startup`初始化的线程优先级。
355 |
356 | * MultipleProcess: 设置`Startup`初始化时所在的进程。
357 |
358 | ## 示例
359 |
360 | * [Sync And Sync](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): 同步与同步依赖的场景
361 |
362 | * [Sync And Async](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): 同步与异步依赖的场景
363 |
364 | * [Async And Sync](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): 异步与同步依赖的场景
365 |
366 | * [Async And Async](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): 异步与异步依赖的场景
367 |
368 | * [Async And Async Await Main Thread](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): 异步与异步依赖在主线程等候的场景
369 |
370 | * [Manual Dispatch](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): 手动通知依赖完成的场景
371 |
372 | * [Thread Priority](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): 改变线程优先级的场景
373 |
374 | * [Multiple Processes](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): 多进程初始化的场景
375 |
376 | ## 实战测试
377 | [AwesomeGithub](https://github.com/idisfkj/AwesomeGithub)中使用了`Android Startup`,优化配置的初始化时间与组件化开发的配置注入时机,使用前与使用后时间对比:
378 |
379 | |状态|启动页面|消耗时间|
380 | |---|------| ------|
381 | |使用前|WelcomeActivity|420ms|
382 | |使用后|WelcomeActivity|333ms|
383 |
384 | ## 联系我
385 | 微信搜索公众号【Android补给站】或者扫描下方二维码
386 |
387 | 
388 |
389 | QQ交流群
390 |
391 |
392 |
393 | ## License
394 | 请查看[LICENSE](https://github.com/idisfkj/android-startup/blob/master/LICENSE)。
395 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | English|[中文](README-ch.md)
2 |
3 | # android-startup
4 | [](https://idisfkj.github.io/archives/)
5 | [](https://www.android.com/)
6 | [](https://android-arsenal.com/api?level=15)
7 | [](https://kotlinlang.org/)
8 | [](https://github.com/idisfkj/android-startup/releases)
9 | []()
10 | [](https://www.apache.org/licenses/LICENSE-2.0)
11 |
12 | The `android-startup` library provides a straightforward, performant way to initialize components at application startup. Both library developers and app developers can use `android-startup` to streamline startup sequences and explicitly set the order of initialization.
13 |
14 | At the same time, the `android-startup` support **async await and sync await**. And [topological ordering](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/sort/TopologySort.kt) is used to ensure the initialization order of dependent components.
15 |
16 | Here is a piece of with Google [App Startup](https://developer.android.com/topic/libraries/app-startup) feature comparison table.
17 |
18 | |indicator|App Startup|Android Startup|
19 | |:---:|:------:| :------:|
20 | |Manually Config| ✅ | ✅ |
21 | |Automatic Config| ✅ | ✅ |
22 | |Support Dependencies| ✅ | ✅ |
23 | |Handle Circle| ✅ | ✅ |
24 | |Thread Of Control| ❌ | ✅ |
25 | |Async Await| ❌ | ✅ |
26 | |Callback Dependencies| ❌ | ✅ |
27 | |Manual Notify| ❌ | ✅ |
28 | |Topology Optimization| ❌ | ✅ |
29 | |Time Cost Statistics| ❌ | ✅ |
30 | |Thread Priority| ❌ | ✅ |
31 | |Multiple Processes| ❌ | ✅ |
32 |
33 | > Open source is not easy, I hope friends shake hands, a star in the upper right corner, thank you🙏
34 |
35 | ## Related Articles
36 |
37 | [Why I abandoned the Jetpack App Startup?](https://medium.com/@idisfkj/why-i-abandoned-the-jetpack-app-startup-9963bd8865ef)
38 |
39 | [Android Startup Analysis](https://medium.com/@idisfkj/android-startup-analysis-8ce7560f3672)
40 |
41 | ## Setup
42 | Add the following dependency to your `build.gradle` file:
43 |
44 | ```
45 | repositories {
46 | mavenCentral()
47 | }
48 |
49 | dependencies {
50 | implementation 'io.github.idisfkj:android-startup:1.1.0'
51 | }
52 | ```
53 |
54 | > Versions update information: [Release](https://github.com/idisfkj/android-startup/releases)
55 |
56 | ## Quick Usage
57 |
58 | 
59 |
60 | There are tow ways of using android-startup in your project,need to be initialized before using android-startup.
61 |
62 | ### Define Initialize components
63 | You define each component initializer by creating a class that implements the [AndroidStartup](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/AndroidStartup.kt) abstract.
64 | This abstract implements the `Startup` interface. And this abstract defines four important methods:
65 |
66 | * The `callCreateOnMainThread(): Boolean`method,which control the `create()`method is in the main thread calls.Othrewise in the other thread.
67 |
68 | * The `waitOnMainThread(): Boolean`method,which control the current component should call waiting in the main thread.If returns true, will block the main thread.
69 |
70 | * The `create(): T?`method,which contains all of the necessary operations to initialize the component and returns an instance of `T`
71 |
72 | * The `dependenciesByName(): List?`method,which returns a list of type `String` that the initializer depends on.
73 |
74 | For example, Define a `SampleFirstStartup` class that implements `AndroidStartup`:
75 |
76 | ```
77 | class SampleFirstStartup : AndroidStartup() {
78 |
79 | override fun callCreateOnMainThread(): Boolean = true
80 |
81 | override fun waitOnMainThread(): Boolean = false
82 |
83 | override fun create(context: Context): String? {
84 | // todo something
85 | return this.javaClass.simpleName
86 | }
87 |
88 | override fun dependenciesByName(): List? {
89 | return null
90 | }
91 |
92 | }
93 | ```
94 | The `dependenciesByName()` method returns an null list because `SampleFirstStartup ` does not depend on any other libraries.
95 |
96 | Suppose that your app also depends on a library called `SampleSecondStartup`, which in turn depends on `SampleFirstStartup`. This dependency means that you need to make sure that Android Startup initializes `SampleFirstStartup ` first.
97 |
98 | ```
99 | class SampleSecondStartup : AndroidStartup() {
100 |
101 | override fun callCreateOnMainThread(): Boolean = false
102 |
103 | override fun waitOnMainThread(): Boolean = true
104 |
105 | override fun create(context: Context): Boolean {
106 | // Simulation execution time.
107 | Thread.sleep(5000)
108 | return true
109 | }
110 |
111 | override fun dependenciesByName(): List {
112 | return listOf("com.rousetime.sample.startup.SampleFirstStartup")
113 | }
114 |
115 | }
116 | ```
117 | Because you include `com.rousetime.sample.startup.SampleFirstStartup` in the `dependenciesByName()` method, Android Startup initializes `SampleFirstStartup` before `SampleSecondStartup`.
118 |
119 | For example, you also define a [SampleThirdStartup](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/startup/SampleThirdStartup.kt) and a [SampleFourthStartup](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/startup/SampleFourthStartup.kt)
120 |
121 | ### Automatic initialization in manifest
122 | The first one is automatic initializes startup in manifest.
123 |
124 | Android Startup includes a special content provider called `StartupProvider` that it uses to discover and call your component startup.
125 | In order for it to automatically identify, need in `StartupProvider` defined in the `` label.The `name` as defined by the component class, `value` values corresponding to the `android.startup`.
126 |
127 | ```
128 |
132 |
133 |
136 |
137 |
138 | ```
139 | You don't need to add a `` entry for `SampleFirstStartup`, `SampleSecondStartup` and `SampleThirdStartup`, because them are a dependency of `SampleFourthStartup`. This means that if `SampleFourthStartup` is discoverable, then are also.
140 |
141 | ### Manually initialization in application
142 | The second one is manually initializes startup in application.
143 |
144 | Consider again the example,to make sure Android Startup can initializes,you can use `StartupManager.Builder()` directly in order to manually initialize components.
145 |
146 | For example, the following code calls `StartupManager.Builder()` and manually initializes them:
147 |
148 | ```
149 | class SampleApplication : Application() {
150 |
151 | override fun onCreate() {
152 | super.onCreate()
153 | StartupManager.Builder()
154 | .addStartup(SampleFirstStartup())
155 | .addStartup(SampleSecondStartup())
156 | .addStartup(SampleThirdStartup())
157 | .addStartup(SampleFourthStartup())
158 | .build(this)
159 | .start()
160 | .await()
161 | }
162 | }
163 | ```
164 | You can check out the sample [app](https://github.com/idisfkj/android-startup/tree/master/app) for more code information.
165 |
166 | Run the example code, the console will produce the log as follows:
167 |
168 | 1. After the initialization sequence sorting optimization
169 |
170 | ```
171 | *****/com.rousetime.sample D/StartupTrack: TopologySort result:
172 | |================================================================
173 | | order | [1]
174 | |----------------------------------------------------------------
175 | | Startup | SampleFirstStartup
176 | |----------------------------------------------------------------
177 | | Dependencies size | 0
178 | |----------------------------------------------------------------
179 | | callCreateOnMainThread | true
180 | |----------------------------------------------------------------
181 | | waitOnMainThread | false
182 | |================================================================
183 | | order | [2]
184 | |----------------------------------------------------------------
185 | | Startup | SampleSecondStartup
186 | |----------------------------------------------------------------
187 | | Dependencies size | 1
188 | |----------------------------------------------------------------
189 | | callCreateOnMainThread | false
190 | |----------------------------------------------------------------
191 | | waitOnMainThread | true
192 | |================================================================
193 | | order | [3]
194 | |----------------------------------------------------------------
195 | | Startup | SampleThirdStartup
196 | |----------------------------------------------------------------
197 | | Dependencies size | 2
198 | |----------------------------------------------------------------
199 | | callCreateOnMainThread | false
200 | |----------------------------------------------------------------
201 | | waitOnMainThread | false
202 | |================================================================
203 | | order | [4]
204 | |----------------------------------------------------------------
205 | | Startup | SampleFourthStartup
206 | |----------------------------------------------------------------
207 | | Dependencies size | 3
208 | |----------------------------------------------------------------
209 | | callCreateOnMainThread | false
210 | |----------------------------------------------------------------
211 | | waitOnMainThread | false
212 | |================================================================
213 | ```
214 |
215 | 2. Consumed components initialization times
216 |
217 | ```
218 | *****/com.rousetime.sample D/StartupTrack: startup cost times detail:
219 | |=================================================================
220 | | Startup Name | SampleFirstStartup
221 | | ----------------------- | --------------------------------------
222 | | Call On Main Thread | true
223 | | ----------------------- | --------------------------------------
224 | | Wait On Main Thread | false
225 | | ----------------------- | --------------------------------------
226 | | Cost Times | 0 ms
227 | |=================================================================
228 | | Startup Name | SampleSecondStartup
229 | | ----------------------- | --------------------------------------
230 | | Call On Main Thread | false
231 | | ----------------------- | --------------------------------------
232 | | Wait On Main Thread | true
233 | | ----------------------- | --------------------------------------
234 | | Cost Times | 5001 ms
235 | |=================================================================
236 | | Startup Name | SampleThirdStartup
237 | | ----------------------- | --------------------------------------
238 | | Call On Main Thread | false
239 | | ----------------------- | --------------------------------------
240 | | Wait On Main Thread | false
241 | | ----------------------- | --------------------------------------
242 | | Cost Times | 3007 ms
243 | |=================================================================
244 | | Startup Name | SampleFourthStartup
245 | | ----------------------- | --------------------------------------
246 | | Call On Main Thread | false
247 | | ----------------------- | --------------------------------------
248 | | Wait On Main Thread | false
249 | | ----------------------- | --------------------------------------
250 | | Cost Times | 102 ms
251 | |=================================================================
252 | | Total Main Thread Times | 5008 ms
253 | |=================================================================
254 | ```
255 |
256 |
257 | ## More
258 |
259 | ### Optional Config
260 |
261 | * [LoggerLevel](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/model/LoggerLevel.kt): Control Android Startup log level, include `LoggerLevel.NONE`, `LoggerLevel.ERROR` and `LoggerLevel.DEBUG`.
262 |
263 | * [AwaitTimeout](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/model/StartupConfig.kt): Control Android Startup timeout of await on main thread.
264 |
265 | * [StartupListener](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/StartupListener.kt): Android Startup listener, all the component initialization completes the listener will be called.
266 |
267 | * [OpenStatistic](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/model/StartupConfig.kt): Control the elapsed time statistics for each Android Startup task.
268 |
269 | #### config in manifest
270 | To use these config, you must define a class than implements the `StartupProviderConfig` interface:
271 |
272 | ```
273 | class SampleStartupProviderConfig : StartupProviderConfig {
274 |
275 | override fun getConfig(): StartupConfig =
276 | StartupConfig.Builder()
277 | .setLoggerLevel(LoggerLevel.DEBUG) // default LoggerLevel.NONE
278 | .setAwaitTimeout(12000L) // default 10000L
279 | .setOpenStatistics(true) // default true
280 | .setListener(object : StartupListener {
281 | override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List) {
282 | // can to do cost time statistics.
283 | }
284 | })
285 | .build()
286 | }
287 | ```
288 | At the same time, you need add `StartupProviderConfig` to manifest file:
289 |
290 | ```
291 |
295 |
296 |
299 |
300 |
301 | ```
302 | `StartupProvider` that it uses to discover and call `SampleStartupProviderConfig`.
303 |
304 | #### config in application
305 | To use these config,you need use `StartupManager.Builder()` in application.
306 |
307 | ```
308 | override fun onCreate() {
309 | super.onCreate()
310 |
311 | val config = StartupConfig.Builder()
312 | .setLoggerLevel(LoggerLevel.DEBUG)
313 | .setAwaitTimeout(12000L)
314 | .setListener(object : StartupListener {
315 | override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List) {
316 | // can to do cost time statistics.
317 | }
318 | })
319 | .build()
320 |
321 | StartupManager.Builder()
322 | .setConfig(config)
323 | ...
324 | .build(this)
325 | .start()
326 | .await()
327 | }
328 | ```
329 |
330 | ### [AndroidStartup](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/AndroidStartup.kt)
331 |
332 | * `createExecutor(): Executor`: If the startup not create on main thread, them the startup will run in the executor.
333 |
334 | * `onDependenciesCompleted(startup: Startup<*>, result: Any?)`: This method is called whenever there is a dependency completion.
335 |
336 | * `manualDispatch(): Boolean`: Returns true that manual to dispatch. but must be call `onDispatch()`, in order to notify children that dependencies startup completed.
337 |
338 | * `onDispatch()`: Start to dispatch when `manualDispatch()` return true.
339 |
340 | ### [StartupCacheManager](https://github.com/idisfkj/android-startup/blob/master/android-startup/src/main/java/com/rousetime/android_startup/manager/StartupCacheManager.kt)
341 |
342 | * `hadInitialized(zClass: Class>)`: Check whether the corresponding component initialization has been completed.
343 |
344 | * `obtainInitializedResult(zClass: Class>): T?`: Obtain corresponding components of has been initialized the returned results.
345 |
346 | * `remove(zClass: Class>)`: To get rid of the corresponding component initialization cache the results.
347 |
348 | * `clear()`: Remove all the component initialization cache the results.
349 |
350 | ### [Annotation](https://github.com/idisfkj/android-startup/tree/master/android-startup/src/main/java/com/rousetime/android_startup/annotation)
351 |
352 | * ThreadPriority: Set `Startup` to initialize thread priority.
353 |
354 | * MultipleProcess: The process on which `Startup` is initialized.
355 |
356 | ## Sample
357 |
358 | * [Sync And Sync](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): Synchronization and synchronization depend on the scene.
359 |
360 | * [Sync And Async](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): Synchronous and asynchronous depend on the scene.
361 |
362 | * [Async And Sync](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): Asynchronous and synchronous depend on the scene.
363 |
364 | * [Async And Async](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): Asynchronous and asynchronous depend on the scene.
365 |
366 | * [Async And Async Await Main Thread](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): Asynchronous with asynchronous rely on wait in the main thread.
367 |
368 | * [Manual Dispatch](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): Manually inform rely on the complete scene.
369 |
370 | * [Thread Priority](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): Scenarios for changing thread priorities.
371 |
372 | * [Multiple Processes](https://github.com/idisfkj/android-startup/blob/master/app/src/main/java/com/rousetime/sample/SampleCommonActivity.kt): Multi-process initialization scenarios.
373 |
374 | ## License
375 | Please see [LICENSE](https://github.com/idisfkj/android-startup/blob/master/LICENSE)
376 |
--------------------------------------------------------------------------------