• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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