Giter Site home page Giter Site logo

idisfkj / android-startup Goto Github PK

View Code? Open in Web Editor NEW
1.5K 20.0 148.0 498 KB

🔥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.

Home Page: https://rousetime.com

License: Apache License 2.0

Kotlin 99.29% AIDL 0.71%
android startup kotlin android-startup manifest async thread multi-process

android-startup's Introduction

English|中文

android-startup

Author Platform API Language Release Code Size License

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.

At the same time, the android-startup support async await and sync await. And topological ordering is used to ensure the initialization order of dependent components.

Here is a piece of with Google App Startup feature comparison table.

indicator App Startup Android Startup
Manually Config
Automatic Config
Support Dependencies
Handle Circle
Thread Of Control
Async Await
Callback Dependencies
Manual Notify
Topology Optimization
Time Cost Statistics
Thread Priority
Multiple Processes

Open source is not easy, I hope friends shake hands, a star in the upper right corner, thank you🙏

Related Articles

Why I abandoned the Jetpack App Startup?

Android Startup Analysis

Setup

Add the following dependency to your build.gradle file:

repositories {
    mavenCentral()
}

dependencies {
    implementation 'io.github.idisfkj:android-startup:1.1.0'
}

Versions update information: Release

Quick Usage

There are tow ways of using android-startup in your project,need to be initialized before using android-startup.

Define Initialize components

You define each component initializer by creating a class that implements the AndroidStartup abstract. This abstract implements the Startup<T> interface. And this abstract defines four important methods:

  • The callCreateOnMainThread(): Booleanmethod,which control the create()method is in the main thread calls.Othrewise in the other thread.

  • The waitOnMainThread(): Booleanmethod,which control the current component should call waiting in the main thread.If returns true, will block the main thread.

  • The create(): T?method,which contains all of the necessary operations to initialize the component and returns an instance of T

  • The dependenciesByName(): List<String>?method,which returns a list of type String that the initializer depends on.

For example, Define a SampleFirstStartup class that implements AndroidStartup<String>:

class SampleFirstStartup : AndroidStartup<String>() {

    override fun callCreateOnMainThread(): Boolean = true

    override fun waitOnMainThread(): Boolean = false

    override fun create(context: Context): String? {
        // todo something
        return this.javaClass.simpleName
    }

    override fun dependenciesByName(): List<String>? {
        return null
    }

}

The dependenciesByName() method returns an null list because SampleFirstStartup does not depend on any other libraries.

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.

class SampleSecondStartup : AndroidStartup<Boolean>() {

    override fun callCreateOnMainThread(): Boolean = false

    override fun waitOnMainThread(): Boolean = true

    override fun create(context: Context): Boolean {
        // Simulation execution time.
        Thread.sleep(5000)
        return true
    }

    override fun dependenciesByName(): List<String> {
        return listOf("com.rousetime.sample.startup.SampleFirstStartup")
    }

}

Because you include com.rousetime.sample.startup.SampleFirstStartup in the dependenciesByName() method, Android Startup initializes SampleFirstStartup before SampleSecondStartup.

For example, you also define a SampleThirdStartup and a SampleFourthStartup

Automatic initialization in manifest

The first one is automatic initializes startup in manifest.

Android Startup includes a special content provider called StartupProvider that it uses to discover and call your component startup. In order for it to automatically identify, need in StartupProvider defined in the <meta-data> label.The name as defined by the component class, value values corresponding to the android.startup.

<provider
    android:name="com.rousetime.android_startup.provider.StartupProvider"
    android:authorities="${applicationId}.android_startup"
    android:exported="false">

    <meta-data
        android:name="com.rousetime.sample.startup.SampleFourthStartup"
        android:value="android.startup" />

</provider>

You don't need to add a <meta-data> entry for SampleFirstStartup, SampleSecondStartup and SampleThirdStartup, because them are a dependency of SampleFourthStartup. This means that if SampleFourthStartup is discoverable, then are also.

Manually initialization in application

The second one is manually initializes startup in application.

Consider again the example,to make sure Android Startup can initializes,you can use StartupManager.Builder() directly in order to manually initialize components.

For example, the following code calls StartupManager.Builder() and manually initializes them:

class SampleApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        StartupManager.Builder()
            .addStartup(SampleFirstStartup())
            .addStartup(SampleSecondStartup())
            .addStartup(SampleThirdStartup())
            .addStartup(SampleFourthStartup())
            .build(this)
            .start()
            .await()
    }
}

You can check out the sample app for more code information.

Run the example code, the console will produce the log as follows:

  1. After the initialization sequence sorting optimization
*****/com.rousetime.sample D/StartupTrack: TopologySort result:
    |================================================================
    |         order          |    [1]
    |----------------------------------------------------------------
    |        Startup         |    SampleFirstStartup
    |----------------------------------------------------------------
    |   Dependencies size    |    0
    |----------------------------------------------------------------
    | callCreateOnMainThread |    true
    |----------------------------------------------------------------
    |    waitOnMainThread    |    false
    |================================================================
    |         order          |    [2]
    |----------------------------------------------------------------
    |        Startup         |    SampleSecondStartup
    |----------------------------------------------------------------
    |   Dependencies size    |    1
    |----------------------------------------------------------------
    | callCreateOnMainThread |    false
    |----------------------------------------------------------------
    |    waitOnMainThread    |    true
    |================================================================
    |         order          |    [3]
    |----------------------------------------------------------------
    |        Startup         |    SampleThirdStartup
    |----------------------------------------------------------------
    |   Dependencies size    |    2
    |----------------------------------------------------------------
    | callCreateOnMainThread |    false
    |----------------------------------------------------------------
    |    waitOnMainThread    |    false
    |================================================================
    |         order          |    [4]
    |----------------------------------------------------------------
    |        Startup         |    SampleFourthStartup
    |----------------------------------------------------------------
    |   Dependencies size    |    3
    |----------------------------------------------------------------
    | callCreateOnMainThread |    false
    |----------------------------------------------------------------
    |    waitOnMainThread    |    false
    |================================================================
  1. Consumed components initialization times
*****/com.rousetime.sample D/StartupTrack: startup cost times detail:
    |=================================================================
    |      Startup Name       |   SampleFirstStartup
    | ----------------------- | --------------------------------------
    |   Call On Main Thread   |   true
    | ----------------------- | --------------------------------------
    |   Wait On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |       Cost Times        |   0 ms
    |=================================================================
    |      Startup Name       |   SampleSecondStartup
    | ----------------------- | --------------------------------------
    |   Call On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |   Wait On Main Thread   |   true
    | ----------------------- | --------------------------------------
    |       Cost Times        |   5001 ms
    |=================================================================
    |      Startup Name       |   SampleThirdStartup
    | ----------------------- | --------------------------------------
    |   Call On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |   Wait On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |       Cost Times        |   3007 ms
    |=================================================================
    |      Startup Name       |   SampleFourthStartup
    | ----------------------- | --------------------------------------
    |   Call On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |   Wait On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |       Cost Times        |   102 ms
    |=================================================================
    | Total Main Thread Times |   5008 ms
    |=================================================================

More

Optional Config

  • LoggerLevel: Control Android Startup log level, include LoggerLevel.NONE, LoggerLevel.ERROR and LoggerLevel.DEBUG.

  • AwaitTimeout: Control Android Startup timeout of await on main thread.

  • StartupListener: Android Startup listener, all the component initialization completes the listener will be called.

  • OpenStatistic: Control the elapsed time statistics for each Android Startup task.

config in manifest

To use these config, you must define a class than implements the StartupProviderConfig interface:

class SampleStartupProviderConfig : StartupProviderConfig {

    override fun getConfig(): StartupConfig =
        StartupConfig.Builder()
            .setLoggerLevel(LoggerLevel.DEBUG) // default LoggerLevel.NONE
            .setAwaitTimeout(12000L) // default 10000L
            .setOpenStatistics(true) // default true
            .setListener(object : StartupListener {
                override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List<CostTimesModel>) {
                    // can to do cost time statistics.
                }
            })
            .build()
}

At the same time, you need add StartupProviderConfig to manifest file:

<provider
    android:name="com.rousetime.android_startup.provider.StartupProvider"
    android:authorities="${applicationId}.android_startup"
    android:exported="false">

    <meta-data
        android:name="com.rousetime.sample.startup.SampleStartupProviderConfig"
        android:value="android.startup.provider.config" />

</provider>

StartupProvider that it uses to discover and call SampleStartupProviderConfig.

config in application

To use these config,you need use StartupManager.Builder() in application.

override fun onCreate() {
    super.onCreate()

    val config = StartupConfig.Builder()
        .setLoggerLevel(LoggerLevel.DEBUG)
        .setAwaitTimeout(12000L)
        .setListener(object : StartupListener {
            override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List<CostTimesModel>) {
                // can to do cost time statistics.
            }
        })
        .build()

    StartupManager.Builder()
        .setConfig(config)
        ...
        .build(this)
        .start()
        .await()
}
  • createExecutor(): Executor: If the startup not create on main thread, them the startup will run in the executor.

  • onDependenciesCompleted(startup: Startup<*>, result: Any?): This method is called whenever there is a dependency completion.

  • manualDispatch(): Boolean: Returns true that manual to dispatch. but must be call onDispatch(), in order to notify children that dependencies startup completed.

  • onDispatch(): Start to dispatch when manualDispatch() return true.

  • hadInitialized(zClass: Class<out Startup<*>>): Check whether the corresponding component initialization has been completed.

  • obtainInitializedResult(zClass: Class<out Startup<*>>): T?: Obtain corresponding components of has been initialized the returned results.

  • remove(zClass: Class<out Startup<*>>): To get rid of the corresponding component initialization cache the results.

  • clear(): Remove all the component initialization cache the results.

  • ThreadPriority: Set Startup to initialize thread priority.

  • MultipleProcess: The process on which Startup is initialized.

Sample

License

Please see LICENSE

android-startup's People

Contributors

idisfkj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-startup's Issues

在AndroidManifest中配置的StartupProviderConfig一定会被打进主dex包吗?

大佬好,我有一个疑问,就是项目比较庞大的时候,分成了很多个模块,每个模块里面都会有如下的配置:

<provider
    android:name="com.rousetime.android_startup.provider.StartupProvider"
    android:authorities="${applicationId}.android_startup"
    android:exported="false">
    <meta-data
        android:name="com.rousetime.sample.startup.SampleStartupProviderConfigX"
        android:value="android.startup.provider.config" />
</provider>

因为65535的问题,class文件会被分到多个dex包,那我打包的时候,meta-data里配置的类会不会不在主dex包内?
如果出现这种情况,app还能正常启动吗?

异常问题

使用了这个库之后, bugly收到了很多异常信息..
#56515 SIGABRT #86512 SIGABRT 很多类似的问题

#00 pc 0001afb8 /system/lib/libc.so (abort+100) [armeabi-v8]
#1 pc 0035b311 /system/lib/libart.so (art::Runtime::Abort(char const*)+392) [armeabi-v8]
.....
com.xxxx.constants.InitTool.initPush(InitTool.java:69)
com.xxxx..common.startup.InitPushStartup.create(InitPushStartup.java:28)
com.xxxx..common.startup.InitPushStartup.create(InitPushStartup.java:17)

代码:
public Boolean create(@NotNull Context context) {
InitTool.initPush(applictaion); ---->> Line 28
return null;
}

create方法

初始化是没有返回对象的时候,怎么处理,比如抽象对象
初始化需要的值是application的时候,怎么处理,比如 ARouter.init

延迟初始化的小疑问

能否支持延迟初始化?

如果支持:
假设 A 延迟初始化, B 依赖了 A ,( A, B 都必须运行在主线程)
那么 B 会等到 A 延迟初始化完毕 ,B 才会执行吗?

StartupCostTimesUtils LinkedHashMap crashes

Hi,
StartupCostTimesUtils uses LinkedHashMap for storing CostTimesModel, but StartupCostTimesUtils#recordStart is called from different threads. In some cases there were crashes.

Can you please add the ability to disable statistics for release builds?

Thanks!

Crashes:

Fatal Exception: java.lang.ArrayIndexOutOfBoundsException: length=16; index=28
       at java.util.LinkedHashMap.addNewEntry(LinkedHashMap.java:197)
       at java.util.HashMap.put(HashMap.java:403)
       at com.rousetime.android_startup.utils.StartupCostTimesUtils.recordStart(StartupCostTimesUtils.java:24)
       at com.rousetime.android_startup.run.StartupRunnable.run(StartupRunnable.java:31)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
       at java.lang.Thread.run(Thread.java:818)

For Android 7 (maybe a bug in android implementation of LinkedHashMap):

Fatal Exception: java.lang.NullPointerException: Attempt to read from field 'int java.util.HashMap$HashMapEntry.hash' on a null object reference
       at java.util.LinkedHashMap.transfer(LinkedHashMap.java:273)
       at java.util.HashMap.resize(HashMap.java:512)
       at java.util.HashMap.addEntry(HashMap.java:808)
       at java.util.LinkedHashMap.addEntry(LinkedHashMap.java:464)
       at java.util.HashMap.put(HashMap.java:436)
       at com.rousetime.android_startup.utils.StartupCostTimesUtils.recordStart(StartupCostTimesUtils.java:24)
       at com.rousetime.android_startup.run.StartupRunnable.run(StartupRunnable.java:31)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
       at java.lang.Thread.run(Thread.java:762)

想问下大家这个库有用在大日活的生产环境吗?

我们的启动任务比较复杂,await函数爆出大量的anr问题,大家有遇到过这种情况吗?

sun.misc.Unsafe.park(Unsafe.java)
java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:230)
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1063)
java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(AbstractQueuedSynchronizer.java:1358)
java.util.concurrent.CountDownLatch.await(CountDownLatch.java:278)

在自己的组件化项目试用了下

app module依赖了module1、module2
1、app module中写了AppStartUp,通过app module的manifest配置了provider和AppStartup
2、module1和module2分别有自己的module1StartUp和module2StartUp,并且都在在各自的Manifest中声明了和
3、运行app,manifest的meta-data会合并,但是startup的执行顺序并不是合并后的那个顺序

所以manifest中声明多个StartUp的时候,这几个StartUp是不会按照声明顺序执行的吧?

manualDispatch的问题

大佬,我运行了demo中的SampleManualDispatchStartup代码。发现SampleAsyncSevenStartup的onDependenciesCompleted在SampleManualDispatchStartup的onDispatch()方法前就已经获取到了。。这个逻辑感觉很奇怪啊,被依赖的组件还没有手动调用onDispatch,其他组件就已经获取到result了,这个result没啥用。我现在使用场景就是“请求权限的组件”被“初始化统计库的组件”依赖,“请求权限的组件”会通过回调的方式把结果赋值给result,发现“初始化统计库的组件”的onDependenciesCompleted方法在“请求权限的组件”的回调之前就已经运行了。

Remove Logging from library

Hi, for better performance of your library will be better to delete all of logging code, because it spends time and allocates a lot of objects, and also it uses reflection api for getting class name and etc, usually this does lazily putting arguments and formatted/concatenated on demand.
LoggerLevel settings nohow to helps here, because message will creates even with LoggerLevel.NONE
for example:

StartupLogUtils.d("${startup::class.java.simpleName} being dispatching, onMainThread ${startup.callCreateOnMainThread()}.")

and somewhere I saw buildString with huge tables)

LeakCanary报的内存泄露

LeakCanary报告

2021-10-10 09:47:06.510 6343-6343/com.aiwu.myapplication D/LeakCanary: ​
    ┬───
    │ GC Root: Local variable in native code
    │
    ├─ android.os.HandlerThread instance
    │    Leaking: NO (PathClassLoader↓ is not leaking)
    │    Thread name: 'LeakCanary-Heap-Dump'
    │    ↓ Thread.contextClassLoader
    ├─ dalvik.system.PathClassLoader instance
    │    Leaking: NO (StartupCacheManager↓ is not leaking and A ClassLoader is never leaking)
    │    ↓ ClassLoader.runtimeInternalObjects
    ├─ java.lang.Object[] array
    │    Leaking: NO (StartupCacheManager↓ is not leaking)
    │    ↓ Object[].[1131]
    ├─ com.rousetime.android_startup.manager.StartupCacheManager class
    │    Leaking: NO (a class is never leaking)
    │    ↓ static StartupCacheManager.instance$delegate
    │                                 ~~~~~~~~~~~~~~~~~
    ├─ kotlin.SynchronizedLazyImpl instance
    │    Leaking: UNKNOWN
    │    Retaining 78.3 kB in 1199 objects
    │    ↓ SynchronizedLazyImpl._value
    │                           ~~~~~~
    ├─ com.rousetime.android_startup.manager.StartupCacheManager instance
    │    Leaking: UNKNOWN
    │    Retaining 78.3 kB in 1198 objects
    │    ↓ StartupCacheManager.initializedConfig
    │                          ~~~~~~~~~~~~~~~~~
    ├─ com.rousetime.android_startup.model.StartupConfig instance
    │    Leaking: UNKNOWN
    │    Retaining 78.1 kB in 1193 objects
    │    ↓ StartupConfig.listener
    │                    ~~~~~~~~
    ├─ com.aiwu.myapplication.SplashActivity$task$1 instance
    │    Leaking: UNKNOWN
    │    Retaining 78.1 kB in 1192 objects
    │    Anonymous class implementing com.rousetime.android_startup.StartupListener
    │    this$0 instance of com.aiwu.myapplication.SplashActivity with mDestroyed = true
    │    ↓ SplashActivity$task$1.this$0
    │                            ~~~~~~
    ╰→ com.aiwu.myapplication.SplashActivity instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.aiwu.myapplication.SplashActivity received
    ​     Activity#onDestroy() callback and Activity#mDestroyed is true)
    ​     Retaining 78.0 kB in 1191 objects
    ​     key = 2490bb45-30d7-4fcc-a5b8-0c6c25215341
    ​     watchDurationMillis = 11702
    ​     retainedDurationMillis = 6700
    ​     mApplication instance of android.app.Application
    ​     mBase instance of androidx.appcompat.view.ContextThemeWrapper
    
    METADATA
    
    Build.VERSION.SDK_INT: 30
    Build.MANUFACTURER: Google
    LeakCanary version: 2.7
    App process name: com.aiwu.myapplication
    Stats: LruCache[maxSize=3000,hits=2278,misses=59102,hitRate=3%]
    RandomAccess[bytes=2891587,reads=59102,travel=23161836688,range=17719770,size=23455318]
    Heap dump reason: user request
    Analysis duration: 9374 ms

测试代码

class SplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        task()
    }

    private fun task() {
        StartupManager.Builder()
            .addStartup(TestStartup())
            .setConfig(
                StartupConfig.Builder()
                    .setLoggerLevel(LoggerLevel.DEBUG)
                    .setListener(object : StartupListener {
                        override fun onCompleted(
                            totalMainThreadCostTime: Long,
                            costTimesModels: List<CostTimesModel>
                        ) {
                            goMainAct()
                        }
                    })
                    .build()
            )
            .build(this)
            .start()
            .await()
    }

    private fun goMainAct() {
        startActivity(Intent(this, MainActivity::class.java))
        finish()
    }

    class TestStartup : AndroidStartup<Boolean>() {
        override fun callCreateOnMainThread() = false

        override fun waitOnMainThread() = false

        override fun create(context: Context): Boolean {
            Thread.sleep(1000)
            return true
        }
    }
}
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

CountDownLatch.await 报错

一个偶发的错误 ,最新版本代码,启动时候大约有9个子线程任务是waitOnMainThread为true

signal 7 (SIGBUS), code 0 (SI_USER), fault addr --------
x0 fffffffffffffffc x1 0000000000000080 x2 0000000000000002 x3 0000007fed8acb58
x4 0000000000000000 x5 0000000000000000 x6 0000000000000000 x7 0000000000000000
x8 0000000000000062 x9 000000001dccd25d x10 0000000000000000 x11 0000000000000000
x12 0000000000003c26 x13 000000001f4c2e74 x14 0000007fed8acc30 x15 000000004cec4ec5
x16 0000006f6c00f658 x17 0000007217272540 x18 000000722dcb0000 x19 b4000070e019503c
x20 b4000070e0195010 x21 00000000d09d305d x22 0000000000000000 x23 0000000000000004
x24 000000005c000000 x25 000000722d257000 x26 0000006f6c215000 x27 000000722d257000
x28 0000006f6c216000 x29 0000007fed8acb70
lr 0000006f6be697f4 sp 0000007fed8acb40 pc 0000007217272560 pst 0000000040001000
backtrace:
#00 pc 000000000004f560 /apex/com.android.runtime/lib64/bionic/libc.so (syscall+32) (BuildId: c4ad377c1afe6c3cdcbf318f3b21cd88)
#1 pc 00000000006697f0 /apex/com.android.art/lib64/libart.so (art::Thread::Park(bool, long)+1088) (BuildId: ddb65a0ad7a10ff0118559561179de09)
#2 pc 00000000005a993c /apex/com.android.art/lib64/libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+652) (BuildId: ddb65a0ad7a10ff0118559561179de09)
#3 pc 0000000000094504 /system/framework/arm64/boot.oat (art_jni_trampoline+116) (BuildId: d4244adc141b70c1f3f3c5e03e356ec46a43d525)
#4 pc 000000000020a910 /apex/com.android.art/lib64/libart.so (nterp_helper+5648) (BuildId: ddb65a0ad7a10ff0118559561179de09)
#5 pc 000000000022ba24 /apex/com.android.art/javalib/core-oj.jar (java.util.concurrent.locks.LockSupport.parkNanos+32)
#6 pc 0000000000209334 /apex/com.android.art/lib64/libart.so (nterp_helper+52) (BuildId: ddb65a0ad7a10ff0118559561179de09)
#7 pc 000000000022aafe /apex/com.android.art/javalib/core-oj.jar (java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos+134)
#8 pc 000000000020a254 /apex/com.android.art/lib64/libart.so (nterp_helper+3924) (BuildId: ddb65a0ad7a10ff0118559561179de09)
#9 pc 000000000022aefc /apex/com.android.art/javalib/core-oj.jar (java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos+24)
#10 pc 000000000020a254 /apex/com.android.art/lib64/libart.so (nterp_helper+3924) (BuildId: ddb65a0ad7a10ff0118559561179de09)
#11 pc 000000000020d87e /apex/com.android.art/javalib/core-oj.jar (java.util.concurrent.CountDownLatch.await+14)
#12 pc 000000000020a254 /apex/com.android.art/lib64/libart.so (nterp_helper+3924) (BuildId: ddb65a0ad7a10ff0118559561179de09)
#13 pc 00000000004c922e /system/priv-app//.apk (com.tal.znxx.lib_init_manager.StartupManager.await+46)
#14 pc 000000000020a254 /apex/com.android.art/lib64/libart.so (nterp_helper+3924) (BuildId: ddb65a0ad7a10ff0118559561179de09)
#15 pc 00000000004c855c /system/priv-app//.apk (com.tal.znxx.lib_init_manager.InitManager.doInit+216)

两种注册方式的context有区别

Manifest.xml方式里的context,并非Application。
使用网易云信初始化报错:
java.lang.RuntimeException: Unable to create service com.netease.nimlib.service.NimService: java.lang.IllegalStateException: SDK should be config on Application#onCreate()!

在 Application 里 onCreate 调用StartupManager.Builder就不会报错。

但是2种方式还是有差别的,第一种本质是在ContentProvider里初始化,第二种其实还在Application里初始化

多进程task,为什么要直接在AndroidStartup中指定?

多进程都需要启动的task要怎么处理

if ((TextUtils.isEmpty(process) && ProcessUtils.isMainProcess(context)) || ProcessUtils.isMultipleProcess(context, process)) { realStartupList.add(it) if (it.waitOnMainThread() && !it.callCreateOnMainThread()) { mNeedAwaitCount.incrementAndGet() } }

manualDispatch的使用问题

最近在使用manualDispatch的时候遇到下面两个问题:
根据文档可知,当manualDispatch=true的时候,我们需要通过onDispatch手动通知下一个任务。

  • 1.框架设计时,每个任务的create方法是先于下面这段代码执行的
    override fun notifyChildren(dependencyParent: Startup<*>, result: Any?, sortStore: StartupSortStore) {
        ....
        sortStore.startupChildrenMap[dependencyParent::class.java.getUniqueKey()]?.forEach {
            sortStore.startupMap[it]?.run {
                onDependenciesCompleted(dependencyParent, result)

                if (dependencyParent.manualDispatch()) {
                    dependencyParent.registerDispatcher(this)
                } else {
                    toNotify()
                }
            }
        }
        ....
    }

问题1:
设置了manualDispatch=true的任务,它可能在create的时候就调用onDispatch,也可能在请求结束时通过子线程的Handler回调onDispatch(这里强调子线程的Handler,因为如果是主线程会导致第2个问题,我们下面再讨论)。
如果是后者异步回调,那么没有问题;如果是前者,那么问题来了,由于在create的时候就调用onDispatch,那么此时上述代码还没执行,也就是说下面这段代码里的mObservers还是空的。

    override fun onDispatch() {
        mObservers.forEach {
            it.toNotify()
        }
    }

因此产生第一个问题:手动调用onDispatch的时机会有问题,如果在create的时候就调用了,那么任务无法继续往下执行了。
解决办法:加多一个变量判断即可

    override fun registerDispatcher(dispatcher: Dispatcher) {
        if (mHasDispatched.get()) {
            dispatcher.toNotify()
        } else {
            mObservers.add(dispatcher)
        }
    }

    override fun onDispatch() {
        mObservers.forEach {
            it.toNotify()
        }
        mHasDispatched.set(true)
    }
  • 2.讲述问题1的时候提到,在请求结束时通过onDispatch进行回调,如果是在主线程会有问题。
    我们假设存在任务A和B,他们都是在主线程执行的,任务B依赖任务A,任务A设置了manualDispatch=true,任务A需要等待异步请求结束,通过主线程Handler回调onDispatch。
    而框架的设计就是,执行了任务A之后,就马上执行任务B,如果此时任务A还没有调用onDispatch,那么任务B就会调用下面的代码
internal class StartupRunnable(
    ...
) : Runnable {

    override fun run() {
        ...
        startup.toWait()
            ...
    }
}

由于任务B在主线程执行,这样就会阻塞主线程,而任务A回调onDispatch又需要使用到主线程的handler,这样就会导致死锁:任务A因无法使用主线程而无法结束,任务B因任务A无法结束而阻塞主线程。

总结 想问下大家这个库有用在大日活的生产环境吗? #29 应该就是问题2导致的,就是整个任务无法结束而触发了超时。我看那个issue没有下文了,估计是没找到问题的源头,我们也是花了一天才找到问题。
暂时没有找到问题2的解决方案。
其实使用manualDispatch的场景可能是任务B需要依赖任务A的异步请求结果,但manualDispatch的使用会导致整个任务执行变的不可控,综合起来我觉得应该去掉manualDispatch这个功能,让每个任务的开始和结束变的可控。至于任务B依赖任务A的结果的,我觉得可以通过提高任务A的执行优先级以及对A的结果进行缓存来解决。

不知道大佬们有没有更好的解决办法?

是否支持分组启动任务

一般首次启动app时,有些启动任务需要在用户同意隐私服务条款后才能进行。
所以启动任务需要分为两组,首先启动一组不需要授权的任务。当用户点击授权后,再启动另一组需要授权的任务。
当我使用两组startUpBuilder.build(this).start().await()启动任务时,会出现任务依赖的问题。
请问能否实现分组执行启动任务?

接入项目试了下,

并没有明显提高,原先是点击图标到界面显示4s,接入之后还是4s,提升不是很明显

gradle依赖问题

明明1.0.7是最新版,android studio为什么提示上一个版本是新版呢?
提示如下:A newer version of io.github.idisfkj:android-startup than 1.0.7 is available: 1.0.62

Benefit of Topological Sort in Async Initialization

Itssssss Rousetime!

Thanks for the great library! It's exactly what I'm looking for to perform multithreaded, eager initialization of app startup.

What is the benefit to the topological sort for a series of async dependencies? Assuming we have a set of N-number Android Startups, none of which require main thread, we'd end up with N latched Runnables. It seems it'd just be first-to-finish as each Runnable is unlatched and completed, and the order these Runnables are dispatched to an executor won't matter, assuming we have N-number threads available. I suppose if the underlying thread pool is limited to X-number threads, then the sort guarantees the order of Runnables getting access to the pool.

Any other insight here?

Thanks!

如何传递参数给Startup

大部分的SDK初始化都需要参数,有得需要代码调用有得需要在manifest里设置, manifest里设置的不用修改,通过代码设置的参数应该如何传递给startup呢?

谢谢

关于主线程上有相互依赖的任务的执行问题

有这样的业务场景
模块A需要在application的attachBaseContext中执行,并且需要在主线程执行
模块B需要在application的onCreate中执行,且依赖A,并需在主线程中执行

由于当前的库是在for循环中对所有库进行了线程分配,如果是在主线程执行时,则顺序run所有在主线程执行的模块,这时候,如果模块A执行超时(模块A使用手动触发dispatch,以期望在onCreate时初始化其它模块),则主线程冻结

建议:
在dispatch过程中,所有的模块默认都在线程池中等待竞争条件,wait到竞争条件后真正执行时,如果模块需要在主线程中执行,则通过handler来执行。这样可以解决这个问题

初始化失败问题

您好,我在Android9.0的设备上使用startup遇到初始化失败的问题。后来换用您的这个库,依然有相似的问题:

Android10和Android11是ok的

代码如下:

        <provider
            android:name="com.rousetime.android_startup.provider.StartupProvider"
            android:authorities="${applicationId}.android_startup"
            android:exported="false">

            <meta-data
                android:name="com.example.xlulibrary.ToastLifecycle"
                android:value="android.startup" />

        </provider>

internal object ToastLifecycle : AndroidStartup<Unit>() {

    lateinit var application: Application

    override fun callCreateOnMainThread(): Boolean = false

    override fun create(context: Context) {
        application = context as Application
    }

    override fun dependencies(): List<Class<out Startup<*>>>? = null

    override fun waitOnMainThread(): Boolean = false
}

log如下:

2021-12-03 17:30:32.927 9659-9659/com.example.toastbox E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.toastbox, PID: 9659
    java.lang.RuntimeException: Unable to get provider com.rousetime.android_startup.provider.StartupProvider: com.rousetime.android_startup.execption.StartupException: java.lang.IllegalAccessException: Class java.lang.Class<com.rousetime.android_startup.StartupInitializer> cannot access private  method void com.example.xlulibrary.ToastLifecycle.<init>() of class java.lang.Class<com.example.xlulibrary.ToastLifecycle>
        at android.app.ActivityThread.installProvider(ActivityThread.java:6396)
        at android.app.ActivityThread.installContentProviders(ActivityThread.java:5938)
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5853)
        at android.app.ActivityThread.access$1100(ActivityThread.java:199)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: com.rousetime.android_startup.execption.StartupException: java.lang.IllegalAccessException: Class java.lang.Class<com.rousetime.android_startup.StartupInitializer> cannot access private  method void com.example.xlulibrary.ToastLifecycle.<init>() of class java.lang.Class<com.example.xlulibrary.ToastLifecycle>
        at com.rousetime.android_startup.StartupInitializer.discoverAndInitialize$android_startup_release(StartupInitializer.kt:55)
        at com.rousetime.android_startup.provider.StartupProvider.onCreate(StartupProvider.kt:19)
        at android.content.ContentProvider.attachInfo(ContentProvider.java:1919)
        at android.content.ContentProvider.attachInfo(ContentProvider.java:1894)
        at android.app.ActivityThread.installProvider(ActivityThread.java:6391)
        at android.app.ActivityThread.installContentProviders(ActivityThread.java:5938) 
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5853) 
        at android.app.ActivityThread.access$1100(ActivityThread.java:199) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
     Caused by: java.lang.IllegalAccessException: Class java.lang.Class<com.rousetime.android_startup.StartupInitializer> cannot access private  method void com.example.xlulibrary.ToastLifecycle.<init>() of class java.lang.Class<com.example.xlulibrary.ToastLifecycle>
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at com.rousetime.android_startup.StartupInitializer.discoverAndInitialize$android_startup_release(StartupInitializer.kt:43)
        at com.rousetime.android_startup.provider.StartupProvider.onCreate(StartupProvider.kt:19) 
        at android.content.ContentProvider.attachInfo(ContentProvider.java:1919) 
        at android.content.ContentProvider.attachInfo(ContentProvider.java:1894) 
        at android.app.ActivityThread.installProvider(ActivityThread.java:6391) 
        at android.app.ActivityThread.installContentProviders(ActivityThread.java:5938) 
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5853) 
        at android.app.ActivityThread.access$1100(ActivityThread.java:199) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
2021-12-03 17:30:32.934 9659-9659/com.example.toastbox I/Process: Sending signal. PID: 9659 SIG: 9

希望大佬能看下😄

初始化问题

您好,假如设置了超时等待时间,但是超过了这个等待时间,sdk还没有初始化完成就跳转页面了
这样会不会导致sdk初始化失败啊?

如何实现初始化的暂停与恢复?

现在app都需要用户同意隐私协议之后才能进入app,这样就会出现一种特殊的情况:先初始化基础模块,然后暂停,等待用户同意隐私,之后会继续初始化其它配置好的模块

初始化问题

1、如果异步执行初始化,会不会出现还没初始化完就到首页了,但是首页又需要这个三方库,导致执行失败?
2、初始化时需要传appliction时怎么办?

小建议,dependencies能否不使用class

我是多module项目,各module之间不直接联系。现在只能通过Class.fromName()不是很方便。
如果给每个AndroidStartup加个id,使用dependencies使用id就好了。

项目编译失败

clone工程之后,编译失败

A problem occurred evaluating root project 'android-startup'.
> Could not get unknown property 'Dependencies' for object of type org.gradle.api.internal.initialization.DefaultScriptHandler.

日志太长被截断

项目启动项很多,日志被截断,建议打印的时候做下判断,太长的话,可以分批打印

一点建议,希望能结合provider初始化的优点

显示的初始化,不利于组件解耦和组件单独运行,所以希望能结合provider初始化的优点
1组件的初始化依然继承AndroidStartup,但是把声明信息放到组件自己的AndroidManifest.xml的下的<meta-data里
2android-startup库使用provider初始化自身,然后读取<meta-data的配置初始化各个AndroidStartup
3 dependencies方法返回值使用String,使用绝对地址"com.xxx.xxx.xxStartup"
4或者可以选择使用注解来声明初始化信息,注解处理器在编译时处理,生成相应初始化代码

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.