blob: f0fff66f321937e94a7caad3aa808206f37c39e8 [file] [log] [blame]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.tracing.demo
import android.app.Activity
import android.content.Intent
import android.os.Handler
import android.os.HandlerThread
import android.os.Process
import android.os.Trace
import androidx.core.app.AppComponentFactory
import com.example.tracing.demo.experiments.BasicTracingTutorial
import com.example.tracing.demo.experiments.CancellableSharedFlow
import com.example.tracing.demo.experiments.CollectFlow
import com.example.tracing.demo.experiments.CombineDeferred
import com.example.tracing.demo.experiments.Experiment
import com.example.tracing.demo.experiments.FlowTracingTutorial
import com.example.tracing.demo.experiments.LaunchNested
import com.example.tracing.demo.experiments.LaunchSequentially
import com.example.tracing.demo.experiments.LaunchStressTest
import com.example.tracing.demo.experiments.LeakySharedFlow
import com.example.tracing.demo.experiments.SharedFlowUsage
import dagger.Component
import dagger.Module
import dagger.Provides
import javax.inject.Qualifier
import javax.inject.Singleton
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.newFixedThreadPoolContext
@Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread0
@Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread1
@Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread2
@Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread3
@Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedThread4
@Qualifier @MustBeDocumented @Retention(RUNTIME) annotation class FixedPool
// Initialize threads in the top-level to force their creation in a specific order:
internal val delayHandler = startThreadWithLooper("delay-thread")
private val thread0 = startThreadWithLooper("Thread:0")
private val thread1 = startThreadWithLooper("Thread:1")
private val thread2 = startThreadWithLooper("Thread:2")
private val thread3 = startThreadWithLooper("Thread:3")
private val thread4 = startThreadWithLooper("Thread:4")
@OptIn(DelicateCoroutinesApi::class)
private val fixedThreadPool = newFixedThreadPoolContext(4, "ThreadPool")
@Module
class ConcurrencyModule {
@Provides
@Singleton
@FixedThread0
fun provideDispatcher0(): CoroutineDispatcher = thread0.asCoroutineDispatcher()
@Provides
@Singleton
@FixedThread1
fun provideDispatcher1(): CoroutineDispatcher = thread1.asCoroutineDispatcher()
@Provides
@Singleton
@FixedThread2
fun provideDispatcher2(): CoroutineDispatcher = thread2.asCoroutineDispatcher()
@Provides
@Singleton
@FixedThread3
fun provideDispatcher3(): CoroutineDispatcher = thread3.asCoroutineDispatcher()
@Provides
@Singleton
@FixedThread4
fun provideDispatcher4(): CoroutineDispatcher = thread4.asCoroutineDispatcher()
@Provides
@Singleton
@FixedPool
fun provideFixedThreadPoolDispatcher(): CoroutineDispatcher = fixedThreadPool
}
@Module
class ExperimentModule {
@Provides
@Singleton
fun provideExperimentList(
basicTracingTutorial: BasicTracingTutorial,
flowTracingTutorial: FlowTracingTutorial,
launchSequentially: LaunchSequentially,
launchNested: LaunchNested,
launchStressTest: LaunchStressTest,
combineDeferred: CombineDeferred,
sharedFlowUsage: SharedFlowUsage,
cancellableSharedFlow: CancellableSharedFlow,
collectFlow: CollectFlow,
leakySharedFlow: LeakySharedFlow,
): List<Experiment> =
listOf(
basicTracingTutorial,
flowTracingTutorial,
launchSequentially,
launchNested,
launchStressTest,
combineDeferred,
sharedFlowUsage,
cancellableSharedFlow,
collectFlow,
leakySharedFlow,
)
}
@Singleton
@Component(modules = [ConcurrencyModule::class, ExperimentModule::class])
interface ApplicationComponent {
/** Returns [Experiment]s that should be used with the application. */
@Singleton fun getExperimentList(): List<Experiment>
@Singleton @FixedThread0 fun getExperimentDefaultCoroutineDispatcher(): CoroutineDispatcher
}
class MainAppComponentFactory : AppComponentFactory() {
init {
Trace.registerWithPerfetto()
}
private val appComponent: ApplicationComponent = DaggerApplicationComponent.create()
override fun instantiateActivityCompat(
cl: ClassLoader,
className: String,
intent: Intent?,
): Activity {
val activityClass = cl.loadClass(className)
return if (activityClass == MainActivity::class.java) {
MainActivity(appComponent)
} else {
super.instantiateActivityCompat(cl, className, intent)
}
}
}
private fun startThreadWithLooper(name: String): Handler {
val thread = HandlerThread(name, Process.THREAD_PRIORITY_FOREGROUND)
thread.start()
val looper = thread.looper
looper.setTraceTag(Trace.TRACE_TAG_APP)
return Handler.createAsync(looper)
}