-
Notifications
You must be signed in to change notification settings - Fork 154
/
StartupManager.kt
162 lines (137 loc) 路 5.26 KB
/
StartupManager.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package com.rousetime.android_startup
import android.content.Context
import android.os.Looper
import android.text.TextUtils
import androidx.core.os.TraceCompat
import com.rousetime.android_startup.annotation.MultipleProcess
import com.rousetime.android_startup.dispatcher.StartupManagerDispatcher
import com.rousetime.android_startup.execption.StartupException
import com.rousetime.android_startup.manager.StartupCacheManager
import com.rousetime.android_startup.model.LoggerLevel
import com.rousetime.android_startup.model.StartupConfig
import com.rousetime.android_startup.model.StartupSortStore
import com.rousetime.android_startup.sort.TopologySort
import com.rousetime.android_startup.utils.ProcessUtils
import com.rousetime.android_startup.utils.StartupCostTimesUtils
import com.rousetime.android_startup.utils.StartupLogUtils
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
/**
* Created by idisfkj on 2020/7/24.
* Email : idisfkj@gmail.com.
*/
class StartupManager private constructor(
private val context: Context,
private val startupList: List<AndroidStartup<*>>,
private val needAwaitCount: AtomicInteger,
private val config: StartupConfig
) {
private var mAwaitCountDownLatch: CountDownLatch? = null
companion object {
const val AWAIT_TIMEOUT = 10000L
}
init {
// save initialized config
StartupCacheManager.instance.saveConfig(config)
StartupLogUtils.level = config.loggerLevel
}
fun start() = apply {
if (Looper.getMainLooper() != Looper.myLooper()) {
throw StartupException("start method must be call in MainThread.")
}
if (mAwaitCountDownLatch != null) {
throw StartupException("start method repeated call.")
}
mAwaitCountDownLatch = CountDownLatch(needAwaitCount.get())
if (startupList.isNullOrEmpty()) {
StartupLogUtils.e { "startupList is empty in the current process." }
return@apply
}
TraceCompat.beginSection(StartupManager::class.java.simpleName)
StartupCostTimesUtils.startTime = System.nanoTime()
TopologySort.sort(startupList).run {
mDefaultManagerDispatcher.prepare()
execute(this)
}
if (needAwaitCount.get() <= 0) {
StartupCostTimesUtils.endTime = System.nanoTime()
TraceCompat.endSection()
}
}
private fun execute(sortStore: StartupSortStore) {
sortStore.result.forEach { mDefaultManagerDispatcher.dispatch(it, sortStore) }
}
/**
* Startup dispatcher
*/
private val mDefaultManagerDispatcher by lazy {
StartupManagerDispatcher(context, needAwaitCount, mAwaitCountDownLatch, startupList.size, config.listener)
}
/**
* to await startup completed
* block main thread.
*/
fun await() {
if (mAwaitCountDownLatch == null) {
throw StartupException("must be call start method before call await method.")
}
val count = needAwaitCount.get()
try {
mAwaitCountDownLatch?.await(config.awaitTimeout, TimeUnit.MILLISECONDS)
} catch (e: InterruptedException) {
e.printStackTrace()
}
if (count > 0) {
StartupCostTimesUtils.endTime = System.nanoTime()
TraceCompat.endSection()
}
}
class Builder {
private var mStartupList = mutableListOf<AndroidStartup<*>>()
private var mNeedAwaitCount = AtomicInteger()
private var mLoggerLevel = LoggerLevel.NONE
private var mAwaitTimeout = AWAIT_TIMEOUT
private var mConfig: StartupConfig? = null
fun addStartup(startup: AndroidStartup<*>) = apply {
mStartupList.add(startup)
}
fun addAllStartup(list: List<AndroidStartup<*>>) = apply {
list.forEach {
addStartup(it)
}
}
fun setConfig(config: StartupConfig?) = apply {
mConfig = config
}
@Deprecated("Use setConfig() instead.")
fun setLoggerLevel(level: LoggerLevel) = apply {
mLoggerLevel = level
}
@Deprecated("Use setConfig() instead.")
fun setAwaitTimeout(timeoutMilliSeconds: Long) = apply {
mAwaitTimeout = timeoutMilliSeconds
}
fun build(context: Context): StartupManager {
val realStartupList = mutableListOf<AndroidStartup<*>>()
mStartupList.forEach {
val process = it::class.java.getAnnotation(MultipleProcess::class.java)?.process ?: arrayOf()
if (process.isNullOrEmpty() || ProcessUtils.isMultipleProcess(context, process)) {
realStartupList.add(it)
if (it.waitOnMainThread() && !it.callCreateOnMainThread()) {
mNeedAwaitCount.incrementAndGet()
}
}
}
return StartupManager(
context,
realStartupList,
mNeedAwaitCount,
mConfig ?: StartupConfig.Builder()
.setLoggerLevel(mLoggerLevel)
.setAwaitTimeout(mAwaitTimeout)
.build()
)
}
}
}