1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.example.tracing.demo
17
18 import android.app.Activity
19 import android.content.Intent
20 import android.os.Handler
21 import android.os.HandlerThread
22 import android.os.Process
23 import android.os.Trace
24 import androidx.core.app.AppComponentFactory
25 import com.example.tracing.demo.experiments.BasicTracingTutorial
26 import com.example.tracing.demo.experiments.CancellableSharedFlow
27 import com.example.tracing.demo.experiments.CollectFlow
28 import com.example.tracing.demo.experiments.CombineDeferred
29 import com.example.tracing.demo.experiments.Experiment
30 import com.example.tracing.demo.experiments.FlowTracingTutorial
31 import com.example.tracing.demo.experiments.LaunchNested
32 import com.example.tracing.demo.experiments.LaunchSequentially
33 import com.example.tracing.demo.experiments.LaunchStressTest
34 import com.example.tracing.demo.experiments.LeakySharedFlow
35 import com.example.tracing.demo.experiments.SharedFlowUsage
36 import dagger.Component
37 import dagger.Module
38 import dagger.Provides
39 import javax.inject.Qualifier
40 import javax.inject.Singleton
41 import kotlin.annotation.AnnotationRetention.RUNTIME
42 import kotlinx.coroutines.CoroutineDispatcher
43 import kotlinx.coroutines.DelicateCoroutinesApi
44 import kotlinx.coroutines.android.asCoroutineDispatcher
45 import kotlinx.coroutines.newFixedThreadPoolContext
46
47 @Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread0
48
49 @Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread1
50
51 @Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread2
52
53 @Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread3
54
55 @Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread4
56
57 @Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedPool
58
59 // Initialize threads in the top-level to force their creation in a specific order:
60 internal val delayHandler = startThreadWithLooper("delay-thread")
61 private val thread0 = startThreadWithLooper("Thread:0")
62 private val thread1 = startThreadWithLooper("Thread:1")
63 private val thread2 = startThreadWithLooper("Thread:2")
64 private val thread3 = startThreadWithLooper("Thread:3")
65 private val thread4 = startThreadWithLooper("Thread:4")
66 @OptIn(DelicateCoroutinesApi::class)
67 private val fixedThreadPool = newFixedThreadPoolContext(4, "ThreadPool")
68
69 @Module
70 class ConcurrencyModule {
71 @Provides
72 @Singleton
73 @FixedThread0
provideDispatcher0null74 fun provideDispatcher0(): CoroutineDispatcher = thread0.asCoroutineDispatcher()
75
76 @Provides
77 @Singleton
78 @FixedThread1
79 fun provideDispatcher1(): CoroutineDispatcher = thread1.asCoroutineDispatcher()
80
81 @Provides
82 @Singleton
83 @FixedThread2
84 fun provideDispatcher2(): CoroutineDispatcher = thread2.asCoroutineDispatcher()
85
86 @Provides
87 @Singleton
88 @FixedThread3
89 fun provideDispatcher3(): CoroutineDispatcher = thread3.asCoroutineDispatcher()
90
91 @Provides
92 @Singleton
93 @FixedThread4
94 fun provideDispatcher4(): CoroutineDispatcher = thread4.asCoroutineDispatcher()
95
96 @Provides
97 @Singleton
98 @FixedPool
99 fun provideFixedThreadPoolDispatcher(): CoroutineDispatcher = fixedThreadPool
100 }
101
102 @Module
103 class ExperimentModule {
104 @Provides
105 @Singleton
106 fun provideExperimentList(
107 basicTracingTutorial: BasicTracingTutorial,
108 flowTracingTutorial: FlowTracingTutorial,
109 launchSequentially: LaunchSequentially,
110 launchNested: LaunchNested,
111 launchStressTest: LaunchStressTest,
112 combineDeferred: CombineDeferred,
113 sharedFlowUsage: SharedFlowUsage,
114 cancellableSharedFlow: CancellableSharedFlow,
115 collectFlow: CollectFlow,
116 leakySharedFlow: LeakySharedFlow,
117 ): List<Experiment> =
118 listOf(
119 basicTracingTutorial,
120 flowTracingTutorial,
121 launchSequentially,
122 launchNested,
123 launchStressTest,
124 combineDeferred,
125 sharedFlowUsage,
126 cancellableSharedFlow,
127 collectFlow,
128 leakySharedFlow,
129 )
130 }
131
132 @Singleton
133 @Component(modules = [ConcurrencyModule::class, ExperimentModule::class])
134 interface ApplicationComponent {
135 /** Returns [Experiment]s that should be used with the application. */
getExperimentListnull136 @Singleton fun getExperimentList(): List<Experiment>
137
138 @Singleton @FixedThread0 fun getExperimentDefaultCoroutineDispatcher(): CoroutineDispatcher
139 }
140
141 class MainAppComponentFactory : AppComponentFactory() {
142
143 init {
144 Trace.registerWithPerfetto()
145 }
146
147 private val appComponent: ApplicationComponent = DaggerApplicationComponent.create()
148
149 override fun instantiateActivityCompat(
150 cl: ClassLoader,
151 className: String,
152 intent: Intent?,
153 ): Activity {
154 val activityClass = cl.loadClass(className)
155 return if (activityClass == MainActivity::class.java) {
156 MainActivity(appComponent)
157 } else {
158 super.instantiateActivityCompat(cl, className, intent)
159 }
160 }
161 }
162
startThreadWithLoopernull163 private fun startThreadWithLooper(name: String): Handler {
164 val thread = HandlerThread(name, Process.THREAD_PRIORITY_FOREGROUND)
165 thread.start()
166 val looper = thread.looper
167 looper.setTraceTag(Trace.TRACE_TAG_APP)
168 return Handler.createAsync(looper)
169 }
170