├── app
├── .gitignore
├── app_start.png
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── chris.png
│ │ │ │ ├── img1.png
│ │ │ │ ├── img2.png
│ │ │ │ ├── img3.png
│ │ │ │ ├── img4.png
│ │ │ │ ├── img5.png
│ │ │ │ ├── img6.png
│ │ │ │ ├── joanna.png
│ │ │ │ ├── splash.png
│ │ │ │ ├── shailen.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── layout
│ │ │ │ ├── activity_droid_cards.xml
│ │ │ │ ├── activity_memoryleak.xml
│ │ │ │ ├── activity_memory.xml
│ │ │ │ ├── testblank.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_img_layout.xml
│ │ │ │ ├── news_item.xml
│ │ │ │ └── news_item_constrainlayout.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── vincent
│ │ │ │ └── appstart
│ │ │ │ ├── tasks
│ │ │ │ ├── delayinittask
│ │ │ │ │ ├── DelayInitTaskB.java
│ │ │ │ │ └── DelayInitTaskA.java
│ │ │ │ ├── InitStethoTask.java
│ │ │ │ ├── InitUmengTask.java
│ │ │ │ └── GetDeviceIdTask.java
│ │ │ │ ├── LaunchTimer.java
│ │ │ │ ├── MyApp.java
│ │ │ │ ├── DensityUtil.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── TestActivity.java
│ │ └── AndroidManifest.xml
│ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── vincent
│ │ │ └── appstart
│ │ │ └── ExampleInstrumentedTest.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── vincent
│ │ └── appstart
│ │ └── ExampleUnitTest.java
├── proguard-rules.pro
└── build.gradle
├── launchstarter
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── org
│ │ │ └── jay
│ │ │ └── launchstarter
│ │ │ ├── TaskCallBack.java
│ │ │ ├── MainTask.java
│ │ │ ├── stat
│ │ │ ├── TaskStatBean.java
│ │ │ └── TaskStat.java
│ │ │ ├── utils
│ │ │ ├── DispatcherLog.java
│ │ │ ├── Utils.java
│ │ │ └── DispatcherExecutor.java
│ │ │ ├── DelayInitDispatcher.java
│ │ │ ├── ITask.java
│ │ │ ├── sort
│ │ │ ├── Graph.java
│ │ │ └── TaskSortUtil.java
│ │ │ ├── DispatchRunnable.java
│ │ │ ├── Task.java
│ │ │ └── TaskDispatcher.java
│ ├── test
│ │ └── java
│ │ │ └── org
│ │ │ └── jay
│ │ │ └── launchstarter
│ │ │ ├── ExampleUnitTest.java
│ │ │ └── TaskDispatcherTest.java
│ └── androidTest
│ │ └── java
│ │ └── org
│ │ └── jay
│ │ └── launchstarter
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/launchstarter/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/app_start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/app_start.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='TaskDispatcherProject'
2 | include ':app', ':launchstarter'
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TaskDispatcherApp
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/launchstarter/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LaunchStarter
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/chris.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/chris.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/img1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/img1.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/img2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/img2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/img3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/img3.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/img4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/img4.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/img5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/img5.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/img6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/img6.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/joanna.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/joanna.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/splash.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/shailen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/shailen.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/launchstarter/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VincentStory/TaskDispatcher/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/TaskCallBack.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | public interface TaskCallBack {
4 |
5 | void call();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/MainTask.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | public abstract class MainTask extends Task {
4 |
5 | @Override
6 | public boolean runOnMainThread() {
7 | return true;
8 | }
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_droid_cards.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jun 10 20:45:15 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .idea
4 | /local.properties
5 | /.idea/caches
6 | /.idea/libraries
7 | /.idea/modules.xml
8 | /.idea/workspace.xml
9 | /.idea/navEditor.xml
10 | /.idea/assetWizardSettings.xml
11 | .DS_Store
12 | /build
13 | /captures
14 | .externalNativeBuild
15 | .cxx
16 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/tasks/delayinittask/DelayInitTaskB.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart.tasks.delayinittask;
2 |
3 | import org.jay.launchstarter.MainTask;
4 |
5 | public class DelayInitTaskB extends MainTask {
6 |
7 | @Override
8 | public void run() {
9 | // 模拟一些操作
10 |
11 | try {
12 | Thread.sleep(200);
13 | } catch (InterruptedException e) {
14 | e.printStackTrace();
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_memoryleak.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/launchstarter/src/test/java/org/jay/launchstarter/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_memory.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/tasks/InitStethoTask.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart.tasks;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | import com.facebook.stetho.Stetho;
7 |
8 | import org.jay.launchstarter.Task;
9 |
10 | /**
11 | * 异步的Task
12 | */
13 | public class InitStethoTask extends Task {
14 |
15 | @Override
16 | public void run() {
17 |
18 | Handler handler = new Handler(Looper.getMainLooper());
19 | Stetho.initializeWithDefaults(mContext);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/testblank.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/tasks/delayinittask/DelayInitTaskA.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart.tasks.delayinittask;
2 |
3 | import android.util.Log;
4 |
5 | import org.jay.launchstarter.MainTask;
6 |
7 | public class DelayInitTaskA extends MainTask {
8 |
9 | @Override
10 | public void run() {
11 | // 模拟一些操作
12 | try {
13 | Thread.sleep(100);
14 | } catch (InterruptedException e) {
15 | e.printStackTrace();
16 | }
17 | Log.i("DelayInitTaskA finished", "");
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/stat/TaskStatBean.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter.stat;
2 |
3 | class TaskStatBean {
4 |
5 | private String situation;
6 | private int count;
7 |
8 | public String getSituation() {
9 | return situation;
10 | }
11 |
12 | public void setSituation(String situation) {
13 | this.situation = situation;
14 | }
15 |
16 | public int getCount() {
17 | return count;
18 | }
19 |
20 | public void setCount(int count) {
21 | this.count = count;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/LaunchTimer.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart;
2 |
3 | import android.util.Log;
4 |
5 | public class LaunchTimer {
6 |
7 | private static long sTime;
8 |
9 | public static void startRecord() {
10 | sTime = System.currentTimeMillis();
11 | }
12 |
13 | public static void endRecord() {
14 | endRecord("");
15 | }
16 |
17 | public static void endRecord(String msg) {
18 | long cost = System.currentTimeMillis() - sTime;
19 | Log.i(msg + "--cost==> " + cost, "");
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/utils/DispatcherLog.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter.utils;
2 |
3 | import android.util.Log;
4 |
5 | public class DispatcherLog {
6 |
7 | private static boolean sDebug = true;
8 |
9 | public static void i(String msg) {
10 | if (!sDebug) {
11 | return;
12 | }
13 | Log.i("TaskDispatcher",msg);
14 | }
15 |
16 | public static boolean isDebug() {
17 | return sDebug;
18 | }
19 |
20 | public static void setDebug(boolean debug) {
21 | sDebug = debug;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/tasks/InitUmengTask.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart.tasks;
2 |
3 | import com.umeng.commonsdk.UMConfigure;
4 |
5 | import org.jay.launchstarter.Task;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * 需要在getDeviceId之后执行
12 | */
13 | public class InitUmengTask extends Task {
14 | @Override
15 | public List> dependsOn() {
16 | List> task = new ArrayList<>();
17 | task.add(GetDeviceIdTask.class);
18 | return task;
19 | }
20 |
21 | @Override
22 | public void run() {
23 | UMConfigure.init(mContext, "58edcfeb310c93091c000be2", "umeng",
24 | UMConfigure.DEVICE_TYPE_PHONE, "1fe6a20054bcef865eeb0991ee84525b");
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/launchstarter/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/tasks/GetDeviceIdTask.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart.tasks;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.telephony.TelephonyManager;
6 |
7 | import org.jay.launchstarter.Task;
8 |
9 | public class GetDeviceIdTask extends Task {
10 | private String mDeviceId;
11 |
12 | @Override
13 | public boolean needWait() {
14 | return super.needWait();
15 | }
16 |
17 | @SuppressLint("MissingPermission")
18 | @Override
19 | public void run() {
20 | // 真正自己的代码
21 | TelephonyManager tManager = (TelephonyManager) mContext.getSystemService(
22 | Context.TELEPHONY_SERVICE);
23 | mDeviceId = tManager.getDeviceId();
24 | // MyApp app = (MyApp) mContext;
25 | // app.setDeviceId(mDeviceId);
26 |
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/launchstarter/src/androidTest/java/org/jay/launchstarter/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("org.jay.launchstarter.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/vincent/appstart/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.vincent.appstart", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_img_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/DelayInitDispatcher.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | import android.os.Looper;
4 | import android.os.MessageQueue;
5 |
6 | import java.util.LinkedList;
7 | import java.util.Queue;
8 |
9 | /**
10 | * 延迟初始化分发器
11 | */
12 | public class DelayInitDispatcher {
13 |
14 | private Queue mDelayTasks = new LinkedList<>();
15 |
16 | /**
17 | * 使用IdleHandler 可以实现在app在空闲时执行任务
18 | */
19 | private MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
20 | @Override
21 | public boolean queueIdle() {
22 | if(mDelayTasks.size()>0){
23 | Task task = mDelayTasks.poll();
24 | new DispatchRunnable(task).run();
25 | }
26 | return !mDelayTasks.isEmpty();
27 | }
28 | };
29 |
30 | public DelayInitDispatcher addTask(Task task){
31 | mDelayTasks.add(task);
32 | return this;
33 | }
34 |
35 | public void start(){
36 | Looper.myQueue().addIdleHandler(mIdleHandler);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/news_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
19 |
20 |
26 |
27 |
28 |
29 |
30 |
37 |
38 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/ITask.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | import android.os.Process;
4 | import androidx.annotation.IntRange;
5 |
6 | import java.util.List;
7 | import java.util.concurrent.Executor;
8 |
9 | public interface ITask {
10 |
11 | /**
12 | * 优先级的范围,可根据Task重要程度及工作量指定;之后根据实际情况决定是否有必要放更大
13 | *
14 | * @return
15 | */
16 | @IntRange(from = Process.THREAD_PRIORITY_FOREGROUND, to = Process.THREAD_PRIORITY_LOWEST)
17 | int priority();
18 |
19 | void run();
20 |
21 | /**
22 | * Task执行所在的线程池,可指定,一般默认
23 | *
24 | * @return
25 | */
26 | Executor runOn();
27 |
28 | /**
29 | * 依赖关系
30 | *
31 | * @return
32 | */
33 | List> dependsOn();
34 |
35 | /**
36 | * 异步线程执行的Task是否需要在被调用await的时候等待,默认不需要
37 | *
38 | * @return
39 | */
40 | boolean needWait();
41 |
42 | /**
43 | * 是否在主线程执行
44 | *
45 | * @return
46 | */
47 | boolean runOnMainThread();
48 |
49 | /**
50 | * 只是在主进程执行
51 | *
52 | * @return
53 | */
54 | boolean onlyInMainProcess();
55 |
56 | /**
57 | * Task主任务执行完成之后需要执行的任务
58 | *
59 | * @return
60 | */
61 | Runnable getTailRunnable();
62 |
63 | void setTaskCallBack(TaskCallBack callBack);
64 |
65 | boolean needCall();
66 | }
67 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/stat/TaskStat.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter.stat;
2 |
3 |
4 | import org.jay.launchstarter.utils.DispatcherLog;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.concurrent.atomic.AtomicInteger;
9 |
10 | public class TaskStat {
11 |
12 | private static volatile String sCurrentSituation = "";
13 | private static List sBeans = new ArrayList<>();
14 | private static AtomicInteger sTaskDoneCount = new AtomicInteger();
15 | private static boolean sOpenLaunchStat = false;// 是否开启统计
16 |
17 | public static String getCurrentSituation() {
18 | return sCurrentSituation;
19 | }
20 |
21 | public static void setCurrentSituation(String currentSituation) {
22 | if (!sOpenLaunchStat) {
23 | return;
24 | }
25 | DispatcherLog.i("currentSituation " + currentSituation);
26 | sCurrentSituation = currentSituation;
27 | setLaunchStat();
28 | }
29 |
30 | public static void markTaskDone() {
31 | sTaskDoneCount.getAndIncrement();
32 | }
33 |
34 | public static void setLaunchStat() {
35 | TaskStatBean bean = new TaskStatBean();
36 | bean.setSituation(sCurrentSituation);
37 | bean.setCount(sTaskDoneCount.get());
38 | sBeans.add(bean);
39 | sTaskDoneCount = new AtomicInteger(0);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/news_item_constrainlayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
27 |
28 |
37 |
38 |
--------------------------------------------------------------------------------
/launchstarter/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.3"
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 19
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | testOptions {
19 | unitTests {
20 | includeAndroidResources = true
21 | }
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 |
36 | implementation 'androidx.appcompat:appcompat:1.4.2'
37 |
38 | // 测试
39 | // Core library
40 | /*androidTestImplementation 'androidx.test:core:1.2.0'
41 |
42 | // AndroidJUnitRunner and JUnit Rules
43 | androidTestImplementation 'androidx.test:runner:1.1.0'
44 | androidTestImplementation 'androidx.test:rules:1.1.0'
45 |
46 | // Assertions
47 | androidTestImplementation 'androidx.test.ext:junit:1.0.0'
48 | androidTestImplementation 'androidx.test.ext:truth:1.0.0'
49 | androidTestImplementation 'com.google.truth:truth:0.42'*/
50 |
51 | testImplementation 'junit:junit:4.12'
52 | testImplementation 'org.robolectric:robolectric:4.3'
53 | testImplementation "org.mockito:mockito-core:3.0.0"
54 | androidTestImplementation "org.mockito:mockito-core:3.0.0"
55 | androidTestImplementation 'androidx.test:runner:1.4.0'
56 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
57 |
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/MyApp.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart;
2 |
3 | import android.app.Application;
4 | import android.os.Debug;
5 |
6 | import com.vincent.appstart.tasks.GetDeviceIdTask;
7 | import com.vincent.appstart.tasks.InitStethoTask;
8 | import com.vincent.appstart.tasks.InitUmengTask;
9 | import com.vincent.appstart.tasks.delayinittask.DelayInitTaskA;
10 | import com.vincent.appstart.tasks.delayinittask.DelayInitTaskB;
11 |
12 | import org.jay.launchstarter.DelayInitDispatcher;
13 | import org.jay.launchstarter.TaskDispatcher;
14 |
15 | /**
16 | * @author wangwenbo
17 | * @date 2020/6/11.
18 | * GitHub:
19 | * email: wangwenbo@innotechx.com
20 | * description:
21 | */
22 | public class MyApp extends Application {
23 | private static Application mApplication;
24 |
25 | public static Application getApplication() {
26 | return mApplication;
27 | }
28 |
29 | @Override
30 | public void onCreate() {
31 | super.onCreate();
32 |
33 | LaunchTimer.startRecord();
34 | mApplication = this;
35 | Debug.startMethodTracing("App");
36 |
37 | TaskDispatcher.init(MyApp.this);
38 |
39 | TaskDispatcher dispatcher = TaskDispatcher.createInstance();
40 | dispatcher
41 | .addTask(new InitStethoTask())
42 | .addTask(new InitUmengTask())
43 | .addTask(new GetDeviceIdTask())
44 | .start();
45 |
46 | dispatcher.await();
47 |
48 |
49 | DelayInitDispatcher delayInitDispatcher = new DelayInitDispatcher();
50 | delayInitDispatcher.addTask(new DelayInitTaskA())
51 | .addTask(new DelayInitTaskB())
52 | .start();
53 |
54 |
55 |
56 | LaunchTimer.endRecord();
57 |
58 | Debug.stopMethodTracing();
59 |
60 | }
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/sort/Graph.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter.sort;
2 |
3 | import java.util.*;
4 |
5 | /**
6 | * 有向无环图的拓扑排序算法
7 | */
8 | public class Graph {
9 |
10 | //顶点数
11 | private int mVerticalCount;
12 |
13 | //邻接表
14 | private List[] mAdj;
15 |
16 | public Graph(int verticalCount) {
17 | this.mVerticalCount = verticalCount;
18 | mAdj = new ArrayList[mVerticalCount];
19 | for (int i = 0; i < mVerticalCount; i++) {
20 | mAdj[i] = new ArrayList();
21 | }
22 | }
23 |
24 | /**
25 | * 添加边
26 | *
27 | * @param u from
28 | * @param v to
29 | */
30 | public void addEdge(int u, int v) {
31 | mAdj[u].add(v);
32 | }
33 |
34 | /**
35 | * 拓扑排序
36 | */
37 | public Vector topologicalSort() {
38 | int indegree[] = new int[mVerticalCount];
39 | for (int i = 0; i < mVerticalCount; i++) {//初始化所有点的入度数量
40 | ArrayList temp = (ArrayList) mAdj[i];
41 | for (int node : temp) {
42 | indegree[node]++;
43 | }
44 | }
45 | Queue queue = new LinkedList();
46 | for (int i = 0; i < mVerticalCount; i++) {//找出所有入度为0的点
47 | if (indegree[i] == 0) {
48 | queue.add(i);
49 | }
50 | }
51 | int cnt = 0;
52 | Vector topOrder = new Vector();
53 | while (!queue.isEmpty()) {
54 | int u = queue.poll();
55 | topOrder.add(u);
56 | for (int node : mAdj[u]) {//找到该点(入度为0)的所有邻接点
57 | if (--indegree[node] == 0) {//把这个点的入度减一,如果入度变成了0,那么添加到入度0的队列里
58 | queue.add(node);
59 | }
60 | }
61 | cnt++;
62 | }
63 | if (cnt != mVerticalCount) {//检查是否有环,理论上拿出来的点的次数和点的数量应该一致,如果不一致,说明有环
64 | throw new IllegalStateException("Exists a cycle in the graph");
65 | }
66 | return topOrder;
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
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 |
42 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/DensityUtil.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * liujingyuan
7 | */
8 | public class DensityUtil {
9 |
10 | // private static int[] deviceWidthHeight = new int[2];
11 | // public static int[] getDeviceInfo(Context context) {
12 | // if ((deviceWidthHeight[0] == 0) && (deviceWidthHeight[1] == 0)) {
13 | // DisplayMetrics metrics = new DisplayMetrics();
14 | // ((Activity) context).getWindowManager().getDefaultDisplay()
15 | // .getMetrics(metrics);
16 | //
17 | // deviceWidthHeight[0] = metrics.widthPixels;
18 | // deviceWidthHeight[1] = metrics.heightPixels;
19 | // }
20 | // return deviceWidthHeight;
21 | // }
22 | /**
23 | *
24 | * @param context 上下文
25 | * @param dpValue dp数值
26 | * @return dp to px
27 | */
28 | public static int dip2px(Context context, float dpValue) {
29 | final float scale = context.getResources().getDisplayMetrics().density;
30 | return (int) (dpValue * scale + 0.5f);
31 |
32 | }
33 | // /**
34 | // * 获取屏幕尺寸
35 | // */
36 | // @SuppressWarnings("deprecation")
37 | // @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
38 | // public static Point getScreenSize(Context context){
39 | // WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
40 | // Display display = windowManager.getDefaultDisplay();
41 | // if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2){
42 | // return new Point(display.getWidth(), display.getHeight());
43 | // }else{
44 | // Point point = new Point();
45 | // display.getSize(point);
46 | // return point;
47 | // }
48 | // }
49 |
50 |
51 |
52 |
53 |
54 | //
55 | // /**
56 | // * 获取屏幕高度
57 | // * @param
58 | // * @return
59 | // */
60 | // public static int getScreenHeight(){
61 | // Point point = getScreenSize(KlCore.getApplicationContext());
62 | // return point.y;
63 | // }
64 |
65 | /**
66 | *
67 | * @param context 上下文
68 | * @param pxValue px的数值
69 | * @return px to dp
70 | */
71 | public static int px2dip(Context context, float pxValue) {
72 | final float scale = context.getResources().getDisplayMetrics().density;
73 | return (int) (pxValue / scale + 0.5f);
74 |
75 | }
76 |
77 | /**
78 | * 将px值转换为sp值,保证文字大小不变
79 | */
80 | public static int px2sp(Context context, float pxValue) {
81 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
82 | return (int) (pxValue / fontScale + 0.5f);
83 | }
84 |
85 | /**
86 | * 将sp值转换为px值,保证文字大小不变
87 | */
88 | public static int sp2px(Context context, float spValue) {
89 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
90 | return (int) (spValue * fontScale + 0.5f);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter.utils;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 | import android.net.ConnectivityManager;
6 | import android.net.NetworkInfo;
7 | import android.text.TextUtils;
8 |
9 | import java.io.BufferedReader;
10 | import java.io.FileInputStream;
11 | import java.io.InputStreamReader;
12 |
13 | public class Utils {
14 |
15 | private static String sCurProcessName = null;
16 |
17 | public static boolean isMainProcess(Context context) {
18 | String processName = getCurProcessName(context);
19 | if (processName != null && processName.contains(":")) {
20 | return false;
21 | }
22 | return (processName != null && processName.equals(context.getPackageName()));
23 | }
24 |
25 | public static String getCurProcessName(Context context) {
26 | String processName = sCurProcessName;
27 | if (!TextUtils.isEmpty(processName)) {
28 | return processName;
29 | }
30 | try {
31 | int pid = android.os.Process.myPid();
32 | ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
33 | for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
34 | if (appProcess.pid == pid) {
35 | sCurProcessName = appProcess.processName;
36 | return sCurProcessName;
37 | }
38 | }
39 | } catch (Exception e) {
40 | e.printStackTrace();
41 | }
42 | sCurProcessName = getCurProcessNameFromProc();
43 | return sCurProcessName;
44 | }
45 |
46 | private static String getCurProcessNameFromProc() {
47 | BufferedReader cmdlineReader = null;
48 | try {
49 | cmdlineReader = new BufferedReader(new InputStreamReader(
50 | new FileInputStream(
51 | "/proc/" + android.os.Process.myPid() + "/cmdline"),
52 | "iso-8859-1"));
53 | int c;
54 | StringBuilder processName = new StringBuilder();
55 | while ((c = cmdlineReader.read()) > 0) {
56 | processName.append((char) c);
57 | }
58 | return processName.toString();
59 | } catch (Throwable e) {
60 | // ignore
61 | } finally {
62 | if (cmdlineReader != null) {
63 | try {
64 | cmdlineReader.close();
65 | } catch (Exception e) {
66 | // ignore
67 | }
68 | }
69 | }
70 | return null;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/DispatchRunnable.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | import android.os.Looper;
4 | import android.os.Process;
5 | import androidx.core.os.TraceCompat;
6 | import org.jay.launchstarter.stat.TaskStat;
7 | import org.jay.launchstarter.utils.DispatcherLog;
8 |
9 | /**
10 | * 任务真正执行的地方
11 | */
12 |
13 | public class DispatchRunnable implements Runnable {
14 |
15 | private Task mTask;
16 | private TaskDispatcher mTaskDispatcher;
17 |
18 | public DispatchRunnable(Task task) {
19 | this.mTask = task;
20 | }
21 |
22 | public DispatchRunnable(Task task, TaskDispatcher dispatcher) {
23 | this.mTask = task;
24 | this.mTaskDispatcher = dispatcher;
25 | }
26 |
27 | @Override
28 | public void run() {
29 | TraceCompat.beginSection(mTask.getClass().getSimpleName());
30 | DispatcherLog.i(mTask.getClass().getSimpleName()
31 | + " begin run" + " Situation " + TaskStat.getCurrentSituation());
32 |
33 | Process.setThreadPriority(mTask.priority());
34 |
35 | long startTime = System.currentTimeMillis();
36 |
37 | mTask.setWaiting(true);
38 | mTask.waitToSatisfy();
39 |
40 | long waitTime = System.currentTimeMillis() - startTime;
41 | startTime = System.currentTimeMillis();
42 |
43 | // 执行Task
44 | mTask.setRunning(true);
45 | mTask.run();
46 |
47 | // 执行Task的尾部任务
48 | Runnable tailRunnable = mTask.getTailRunnable();
49 | if (tailRunnable != null) {
50 | tailRunnable.run();
51 | }
52 |
53 | if (!mTask.needCall() || !mTask.runOnMainThread()) {
54 | printTaskLog(startTime, waitTime);
55 |
56 | TaskStat.markTaskDone();
57 | mTask.setFinished(true);
58 | if (mTaskDispatcher != null) {
59 | mTaskDispatcher.satisfyChildren(mTask);
60 | mTaskDispatcher.markTaskDone(mTask);
61 | }
62 | DispatcherLog.i(mTask.getClass().getSimpleName() + " finish");
63 | }
64 | TraceCompat.endSection();
65 | }
66 |
67 | /**
68 | * 打印出来Task执行的日志
69 | *
70 | * @param startTime
71 | * @param waitTime
72 | */
73 | private void printTaskLog(long startTime, long waitTime) {
74 | long runTime = System.currentTimeMillis() - startTime;
75 | if (DispatcherLog.isDebug()) {
76 | DispatcherLog.i(mTask.getClass().getSimpleName() + " wait " + waitTime + " run "
77 | + runTime + " isMain " + (Looper.getMainLooper() == Looper.myLooper())
78 | + " needWait " + (mTask.needWait() || (Looper.getMainLooper() == Looper.myLooper()))
79 | + " ThreadId " + Thread.currentThread().getId()
80 | + " ThreadName " + Thread.currentThread().getName()
81 | + " Situation " + TaskStat.getCurrentSituation()
82 | );
83 | }
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/test/java/com/vincent/appstart/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import static org.junit.Assert.*;
9 |
10 | /**
11 | * Example local unit test, which will execute on the development machine (host).
12 | *
13 | * @see Testing documentation
14 | */
15 | public class ExampleUnitTest {
16 | ArrayList list = new ArrayList<>();
17 |
18 | @Test
19 | public void addition_isCorrect() {
20 | // assertEquals(4, 2 + 2);
21 |
22 | list.add(0, "aaa");
23 | list.add(1, "bbb");
24 | System.out.println(list.toString());
25 | }
26 |
27 | @Test
28 | public void test() {
29 | int intA[][] = {{1, 2}, {3, 4}, {5, 6}};
30 | find(4, intA);
31 | // System.out.println(intA[0][0]);
32 | // System.out.println(find(7, intA));
33 | // int[][] nn = new int[][]{2, 3, 1, 0, 2, 5, 3},{2, 3, 1, 0, 2, 5, 3};
34 | // System.out.println(duplicate1(nn));
35 | }
36 |
37 | public boolean find(int target, int[][] array) {
38 | int rowCount = array.length;
39 | // System.out.println(rowCount);
40 | int colCount = array[0].length;
41 | // System.out.println(colCount);
42 | int y, x;
43 |
44 | for (x = rowCount - 1, y = 0; x >= 0 && y < colCount; ) {
45 | if (target == array[x][y]) {
46 | System.out.println("true:" + array[x][y]);
47 | return true;
48 | } else if (target > array[x][y]) {
49 | y++;
50 | System.out.println("y==:" + y);
51 | continue;
52 | } else if (target < array[x][y]) {
53 | x--;
54 | System.out.println("x==" + x);
55 | continue;
56 | }
57 | }
58 | System.out.println(false);
59 | return false;
60 | }
61 |
62 |
63 | public int duplicate1(int[] nums) {
64 |
65 | for (int i = 0; i < nums.length; i++) {
66 | for (int j = 1; j < nums.length; j++) {
67 | if (nums[i] == nums[j]) {
68 | return nums[i];
69 | }
70 | }
71 | }
72 |
73 | return 0;
74 | }
75 |
76 | public boolean duplicate(int[] nums, int length, int[] duplication) {
77 | if (nums == null || length <= 0)
78 | return false;
79 | for (int i = 0; i < length; i++) {
80 | while (nums[i] != i) {
81 | if (nums[i] == nums[nums[i]]) {
82 | duplication[0] = nums[i];
83 | return true;
84 | }
85 | swap(nums, i, nums[i]);
86 | }
87 | }
88 | return false;
89 | }
90 |
91 | private void swap(int[] nums, int i, int j) {
92 |
93 | int t = nums[i];
94 | nums[i] = nums[j];
95 | nums[j] = t;
96 | }
97 |
98 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AppStart
2 | android性能优化之启动优化,将启动任务统一管理,使代码更简洁,条理更清晰,支持任务优先级,支持任务同步机制,支持任务空闲加载
3 |
4 | 我们进入app的时候一般会有很多工具需要初始化,也会有一些比较耗时的任务,如果都写在application里面,代码不够美观,任务没有优先级也会增加app
5 | 启动时间,本工程完美解决这些问题,将启动任务拆分成一个个task去处理,还可以给每个task设置执行的优先级,大大提高初始化效率。
6 |
7 | 启动优化项目示例代码
8 | 启动器TaskDispatcher
9 |
10 | ### 核心代码
11 | ```
12 | if (mNeedWaitCount.get() > 0) {
13 | if (mCountDownLatch == null) {
14 | throw new RuntimeException("You have to call start() before call await()");
15 | }
16 | //使用CountDownLatch实现线程阻塞,保证必须执行的任务先执行完再进入主页
17 | mCountDownLatch.await(WAITTIME, TimeUnit.MILLISECONDS);
18 | }
19 |
20 |
21 | // 当前Task依赖的Task数量(需要等待被依赖的Task执行完毕才能执行自己),默认没有依赖
22 | private CountDownLatch mDepends = new CountDownLatch(dependsOn() == null ? 0 : dependsOn().size());
23 |
24 |
25 | /**
26 | * 使用IdleHandler 可以实现在app在空闲时执行任务
27 | */
28 | private MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
29 | @Override
30 | public boolean queueIdle() {
31 | if(mDelayTasks.size()>0){
32 | Task task = mDelayTasks.poll();
33 | new DispatchRunnable(task).run();
34 | }
35 | return !mDelayTasks.isEmpty();
36 | }
37 | };
38 |
39 | ```
40 |
41 |
42 | # 使用方法
43 | ## 添加依赖
44 | Step 1. Add it in your root build.gradle at the end of repositories:
45 | ```
46 | allprojects {
47 | repositories {
48 | ...
49 | maven { url 'https://jitpack.io' }
50 | }
51 | }
52 |
53 | ```
54 | Step 2. Add the dependency
55 |
56 | ```
57 | dependencies {
58 | implementation 'com.github.VincentStory:AppStart:Tag'
59 | }
60 |
61 | ```
62 | 具体使用方式如下
63 | 其中DelayInitDispatcher 为空闲加载任务
64 |
65 | 
66 |
67 | 如果需要任务在Application初始化完成之前完成初始化,需要在子task内重写neetWait()方法,然后返回true。
68 | 示例代码:
69 |
70 | ```
71 |
72 | public class GetDeviceIdTask extends Task {
73 | private String mDeviceId;
74 |
75 | @Override
76 | public boolean needWait() {
77 | return true;
78 | }
79 |
80 | @SuppressLint("MissingPermission")
81 | @Override
82 | public void run() {
83 | // MyApp app = (MyApp) mContext;
84 | // app.setDeviceId(mDeviceId);
85 | }
86 | }
87 |
88 | ```
89 | 真实记录统计时间,使用Feed流的第一项展示作为结束时间点,代码如下:
90 | ```
91 | holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
92 | @Override
93 | public boolean onPreDraw() {
94 | holder.itemView.getViewTreeObserver().removeOnPreDrawListener(this);
95 | LaunchTimer.endRecord("FeedShow");
96 | return true;
97 | }
98 | });
99 | ```
100 |
101 | ```
102 | Copyright 2018 VincentStory
103 |
104 | Licensed under the Apache License, Version 2.0 (the "License");
105 | you may not use this file except in compliance with the License.
106 | You may obtain a copy of the License at
107 |
108 | http://www.apache.org/licenses/LICENSE-2.0
109 |
110 | Unless required by applicable law or agreed to in writing, software
111 | distributed under the License is distributed on an "AS IS" BASIS,
112 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
113 | See the License for the specific language governing permissions and
114 | limitations under the License.
115 |
116 | ```
117 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/utils/DispatcherExecutor.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter.utils;
2 |
3 | import java.util.concurrent.*;
4 | import java.util.concurrent.atomic.AtomicInteger;
5 |
6 | public class DispatcherExecutor {
7 |
8 | /**
9 | * CPU 密集型任务的线程池
10 | */
11 | private static ThreadPoolExecutor sCPUThreadPoolExecutor;
12 |
13 | /**
14 | * IO 密集型任务的线程池
15 | */
16 | private static ExecutorService sIOThreadPoolExecutor;
17 |
18 | /**
19 | * CPU 核数
20 | */
21 | private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
22 |
23 | /**
24 | * 线程池线程数
25 | */
26 | public static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 5));
27 |
28 | /**
29 | * 线程池线程数的最大值
30 | */
31 | private static final int MAXIMUM_POOL_SIZE = CORE_POOL_SIZE;
32 |
33 | private static final int KEEP_ALIVE_SECONDS = 5;
34 |
35 | private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue<>();
36 |
37 | /**
38 | * 线程工厂
39 | */
40 | private static final DefaultThreadFactory sThreadFactory = new DefaultThreadFactory();
41 |
42 | // 一般不会到这里
43 | private static final RejectedExecutionHandler sHandler = new RejectedExecutionHandler() {
44 | @Override
45 | public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
46 | Executors.newCachedThreadPool().execute(r);
47 | }
48 | };
49 |
50 | /**
51 | * 获取CPU线程池
52 | *
53 | * @return
54 | */
55 | public static ThreadPoolExecutor getCPUExecutor() {
56 | return sCPUThreadPoolExecutor;
57 | }
58 |
59 | /**
60 | * 获取IO线程池
61 | *
62 | * @return
63 | */
64 | public static ExecutorService getIOExecutor() {
65 | return sIOThreadPoolExecutor;
66 | }
67 |
68 | /**
69 | * The default thread factory.
70 | */
71 | private static class DefaultThreadFactory implements ThreadFactory {
72 | private static final AtomicInteger poolNumber = new AtomicInteger(1);
73 | private final ThreadGroup group;
74 | private final AtomicInteger threadNumber = new AtomicInteger(1);
75 | private final String namePrefix;
76 |
77 | DefaultThreadFactory() {
78 | SecurityManager s = System.getSecurityManager();
79 | group = (s != null) ? s.getThreadGroup() :
80 | Thread.currentThread().getThreadGroup();
81 | namePrefix = "TaskDispatcherPool-" +
82 | poolNumber.getAndIncrement() +
83 | "-Thread-";
84 | }
85 |
86 | public Thread newThread(Runnable r) {
87 | Thread t = new Thread(group, r,
88 | namePrefix + threadNumber.getAndIncrement(),
89 | 0);
90 | if (t.isDaemon()) {
91 | t.setDaemon(false);
92 | }
93 | if (t.getPriority() != Thread.NORM_PRIORITY) {
94 | t.setPriority(Thread.NORM_PRIORITY);
95 | }
96 | return t;
97 | }
98 | }
99 |
100 | static {
101 | sCPUThreadPoolExecutor = new ThreadPoolExecutor(
102 | CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
103 | sPoolWorkQueue, sThreadFactory, sHandler);
104 | sCPUThreadPoolExecutor.allowCoreThreadTimeOut(true);
105 | sIOThreadPoolExecutor = Executors.newCachedThreadPool(sThreadFactory);
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart;
2 |
3 | import android.animation.ObjectAnimator;
4 | import android.animation.ValueAnimator;
5 | import android.os.Bundle;
6 | import android.os.Debug;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.view.ViewTreeObserver;
12 | import android.view.animation.Animation;
13 | import android.view.animation.ScaleAnimation;
14 | import android.widget.TextView;
15 |
16 | import androidx.annotation.NonNull;
17 | import androidx.appcompat.app.AppCompatActivity;
18 | import androidx.appcompat.widget.AppCompatImageView;
19 | import androidx.recyclerview.widget.LinearLayoutManager;
20 | import androidx.recyclerview.widget.RecyclerView;
21 |
22 | import static android.view.animation.Animation.INFINITE;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | public class MainActivity extends AppCompatActivity {
28 |
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_main);
34 |
35 | List list = new ArrayList<>();
36 | list.add(R.mipmap.img1);
37 | list.add(R.mipmap.img2);
38 | list.add(R.mipmap.img3);
39 | list.add(R.mipmap.img4);
40 | list.add(R.mipmap.img5);
41 | list.add(R.mipmap.img6);
42 |
43 |
44 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
45 | RecyclerView recyclerView = findViewById(R.id.recycleView);
46 | recyclerView.setLayoutManager(linearLayoutManager);
47 | recyclerView.setAdapter(new MyAdapter(list));
48 |
49 |
50 | }
51 |
52 | private class MyAdapter extends RecyclerView.Adapter {
53 | private List list;
54 |
55 | private Boolean hasRecorded = false;
56 |
57 | public MyAdapter(List list) {
58 | this.list = list;
59 | }
60 |
61 | @NonNull
62 | @Override
63 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
64 | View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_img_layout, parent, false);
65 |
66 | return new ViewHolder(itemView);
67 | }
68 |
69 | @Override
70 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
71 | if (position == 0 && !hasRecorded) {
72 | hasRecorded = true;
73 | holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
74 | @Override
75 | public boolean onPreDraw() {
76 | holder.itemView.getViewTreeObserver().removeOnPreDrawListener(this);
77 | LaunchTimer.endRecord("FeedShow");
78 | return true;
79 | }
80 | });
81 | }
82 |
83 |
84 | AppCompatImageView imageView = holder.getBinding().findViewById(R.id.imageView);
85 |
86 | imageView.setImageResource(list.get(position));
87 |
88 |
89 | }
90 |
91 | @Override
92 | public int getItemCount() {
93 | return list.size();
94 | }
95 | }
96 |
97 | class ViewHolder extends RecyclerView.ViewHolder {
98 | private View mBinding;
99 |
100 | ViewHolder(View itemView) {
101 | super(itemView);
102 | mBinding = itemView;
103 | }
104 |
105 | View getBinding() {
106 | return mBinding;
107 | }
108 |
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vincent/appstart/TestActivity.java:
--------------------------------------------------------------------------------
1 | package com.vincent.appstart;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Point;
6 | import android.os.Build;
7 | import android.os.Bundle;
8 | import android.provider.Settings;
9 | import android.util.DisplayMetrics;
10 | import android.util.Log;
11 | import android.view.Display;
12 | import android.view.KeyCharacterMap;
13 | import android.view.KeyEvent;
14 | import android.view.ViewConfiguration;
15 | import android.view.WindowManager;
16 |
17 | import androidx.annotation.Nullable;
18 | import androidx.appcompat.app.AppCompatActivity;
19 |
20 | import java.lang.reflect.Method;
21 |
22 | /**
23 | * @author wangwenbo
24 | * @date 2020/7/24.
25 | * GitHub:
26 | * email: wangwenbo@innotechx.com
27 | * description:
28 | */
29 | public class TestActivity extends AppCompatActivity {
30 | @Override
31 | protected void onCreate(@Nullable Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 |
34 | int aa = getVirtualBarHeight(getBaseContext());
35 | // DensityUtil.px2dip(getBaseContext(),aa);
36 | Log.i("wenbo","---"+DensityUtil.px2dip(getBaseContext(),aa));
37 | }
38 |
39 | public static int getVirtualBarHeight(Context context) {
40 | int vh = 0;
41 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
42 | Display display = windowManager.getDefaultDisplay();
43 | DisplayMetrics dm = new DisplayMetrics();
44 | try {
45 | @SuppressWarnings("rawtypes")
46 | Class c = Class.forName("android.view.Display");
47 | @SuppressWarnings("unchecked")
48 | Method method = c.getMethod("getRealMetrics", DisplayMetrics.class);
49 | method.invoke(display, dm);
50 | vh = dm.heightPixels - display.getHeight();
51 | } catch (Exception e) {
52 | e.printStackTrace();
53 | }
54 |
55 | if (isMIUI()) {
56 | if (isFullScreen(context)) {
57 | vh = 0;
58 | }
59 | } else {
60 | if (!hasDeviceNavigationBar(context)) {
61 | vh = 0;
62 | }
63 | }
64 | return vh;
65 | }
66 |
67 |
68 | /**
69 | * 获取是否有虚拟按键
70 | * 通过判断是否有物理返回键反向判断是否有虚拟按键
71 | *
72 | * @param context
73 | * @return
74 | */
75 | public static boolean hasDeviceNavigationBar(Context context) {
76 |
77 |
78 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
79 | Display display = ((Activity) context).getWindowManager().getDefaultDisplay();
80 | Point size = new Point();
81 | Point realSize = new Point();
82 | display.getSize(size);
83 | display.getRealSize(realSize);
84 | boolean result = realSize.y != size.y;
85 | return realSize.y != size.y;
86 | } else {
87 |
88 | boolean menu = ViewConfiguration.get(((Activity) context)).hasPermanentMenuKey();
89 | boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
90 | if (menu || back) {
91 | return false;
92 | } else {
93 | return true;
94 | }
95 | }
96 | }
97 |
98 | public static boolean isFullScreen(Context context) {
99 | // true 是手势,默认是 false
100 | // https://www.v2ex.com/t/470543
101 | return Settings.Global.getInt(context.getContentResolver(), "force_fsg_nav_bar", 0) != 0;
102 |
103 | }
104 |
105 | public static boolean isMIUI() {
106 | String manufacturer = Build.MANUFACTURER;
107 | // 这个字符串可以自己定义,例如判断华为就填写huawei,魅族就填写meizu
108 | if ("xiaomi".equalsIgnoreCase(manufacturer)) {
109 | return true;
110 | }
111 | return false;
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android-extensions'
3 | apply plugin: 'kotlin-android'
4 |
5 | android {
6 | compileSdkVersion 31
7 | buildToolsVersion "29.0.3"
8 |
9 | defaultConfig {
10 | applicationId "com.vincent.appstart"
11 | minSdkVersion 24
12 | targetSdkVersion 29
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | configurations.all {
17 | resolutionStrategy { force 'androidx.core:core-ktx:1.6.0' }
18 | }
19 |
20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 |
35 | implementation 'androidx.appcompat:appcompat:1.1.0'
36 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
37 | testImplementation 'junit:junit:4.12'
38 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
40 | implementation project(':launchstarter')
41 |
42 | // ------------------
43 | implementation 'com.android.support:appcompat-v7:28.0.0'
44 | implementation 'com.android.support:recyclerview-v7:28.0.0'
45 | implementation 'com.android.support:multidex:1.0.3'
46 | //
47 | implementation 'com.umeng.umsdk:analytics:7.5.4'
48 | implementation 'com.umeng.umsdk:common:1.5.4'
49 |
50 | // implementation 'com.github.VincentStory:AppStart:1.2'
51 |
52 | // implementation 'com.tencent.bugly:crashreport:latest.release' //其中latest.release指代最新Bugly SDK版本号,也可以指定明确的版本号,例如2.1.9
53 | // implementation 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新Bugly NDK版本号,也可以指定明确的版本号,例如3.0
54 | //
55 | // implementation 'com.facebook.fresco:fresco:1.11.0'
56 | // implementation 'com.facebook.fresco:animated-gif:1.11.0'
57 | // implementation 'com.facebook.fresco:animated-webp:1.11.0'
58 | // implementation 'com.facebook.fresco:webpsupport:1.11.0'
59 | // implementation 'com.facebook.fresco:drawee:1.11.0'
60 | //
61 | //
62 | // //3D地图so及jar
63 | // implementation 'com.amap.api:3dmap:latest.integration'
64 | // //定位功能
65 | // implementation 'com.amap.api:location:latest.integration'
66 | // //搜索功能
67 | // implementation 'com.amap.api:search:latest.integration'
68 | //
69 | //
70 | // implementation 'cn.jiguang.sdk:jpush:3.1.7'
71 | // implementation 'cn.jiguang.sdk:jcore:1.2.5'
72 | //
73 | // implementation 'com.taobao.android:weex_sdk:0.18.0'
74 | //
75 | //
76 | implementation 'com.squareup.retrofit2:retrofit:2.4.0'
77 | implementation 'org.ligboy.retrofit2:converter-fastjson-android:2.1.0'
78 | implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'
79 | //
80 | implementation 'com.facebook.stetho:stetho:1.5.0'
81 | implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
82 | //
83 | //
84 | implementation 'org.aspectj:aspectjrt:1.8.+'
85 | //
86 | // implementation 'me.weishu:epic:0.3.6'
87 | //
88 | // implementation 'com.android.support:asynclayoutinflater:28.0.0-alpha1'
89 | //
90 | annotationProcessor 'com.zhangyue.we:x2c-apt:1.1.2'
91 | implementation 'com.zhangyue.we:x2c-lib:1.0.6'
92 | //
93 | // implementation 'com.tencent:mmkv:1.0.17'
94 | //
95 | //
96 | implementation ('com.aliyun.ams:alicloud-android-httpdns:1.1.9@aar') {
97 | exclude module:'alicloud-android-utdid'
98 | transitive true
99 | }
100 | compileOnly 'me.ele:lancet-base:1.0.4'
101 |
102 | implementation 'com.github.markzhai:blockcanary-android:1.5.0'
103 |
104 | implementation 'com.github.anrwatchdog:anrwatchdog:1.3.0'
105 | implementation "androidx.core:core-ktx:+"
106 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
107 |
108 |
109 | }
110 | repositories {
111 | mavenCentral()
112 | }
113 |
--------------------------------------------------------------------------------
/launchstarter/src/test/java/org/jay/launchstarter/TaskDispatcherTest.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | import android.util.Log;
4 | import org.jay.launchstarter.utils.DispatcherLog;
5 | import org.junit.Before;
6 | import org.junit.Rule;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.mockito.MockitoAnnotations;
10 | import org.mockito.junit.MockitoJUnit;
11 | import org.mockito.junit.MockitoRule;
12 | import org.robolectric.RobolectricTestRunner;
13 | import org.robolectric.RuntimeEnvironment;
14 | import org.robolectric.shadows.ShadowLog;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | import static org.mockito.Mockito.mock;
20 |
21 | @RunWith(RobolectricTestRunner.class)
22 | public class TaskDispatcherTest {
23 |
24 | @Rule
25 | public MockitoRule rule = MockitoJUnit.rule();
26 |
27 | private TaskDispatcher taskDispatcher;
28 |
29 | @Before
30 | public void setUp() {
31 | ShadowLog.stream = System.out;
32 | MockitoAnnotations.initMocks(this);
33 |
34 | TaskDispatcher.init(RuntimeEnvironment.application);
35 | taskDispatcher = TaskDispatcher.createInstance();
36 | }
37 |
38 | @Test
39 | public void testStart() {
40 | addTask();
41 | taskDispatcher.start();
42 | }
43 |
44 |
45 | @Test(expected = RuntimeException.class)
46 | public void testAwaitFail() {
47 | addNeedWaitTask();
48 | taskDispatcher.await();
49 | }
50 |
51 | @Test
52 | public void testAwait() {
53 | addNeedWaitTask();
54 | taskDispatcher.start();
55 | taskDispatcher.await();
56 | }
57 |
58 | @Test
59 | public void testExecuteTask() {
60 | Task task = getSimpleTask();
61 | taskDispatcher.executeTask(task);
62 | try {
63 | Thread.sleep(500);
64 | } catch (InterruptedException e) {
65 | e.printStackTrace();
66 | }
67 | assert task.isFinished();
68 |
69 | }
70 |
71 | @Test
72 | public void testDependency() {
73 | final boolean[] orderIsRight = {false};
74 | final TaskA taskA = new TaskA();
75 | Task taskB = new TaskB() {
76 | @Override
77 | public void run() {
78 | if (taskA.isFinished()) {
79 | orderIsRight[0] = true;
80 | }
81 | }
82 | };
83 | taskDispatcher.addTask(taskB);
84 | taskDispatcher.addTask(taskA);
85 | taskDispatcher.start();
86 | try {
87 | Thread.sleep(500);
88 | } catch (InterruptedException e) {
89 | e.printStackTrace();
90 | }
91 |
92 | assert orderIsRight[0];
93 | }
94 |
95 | class TaskA extends Task {
96 |
97 | @Override
98 | public void run() {
99 | DispatcherLog.i("TaskB running");
100 | }
101 | }
102 |
103 | class TaskB extends Task {
104 |
105 | @Override
106 | public List> dependsOn() {
107 | List> list = new ArrayList<>();
108 | list.add(TaskA.class);
109 | return list;
110 | }
111 |
112 | @Override
113 | public void run() {
114 | DispatcherLog.i("TaskB running");
115 | }
116 | }
117 |
118 | private Task getSimpleTask() {
119 | return new Task() {
120 | @Override
121 | public void run() {
122 | System.out.println("run");
123 | }
124 | };
125 | }
126 |
127 | private void addNeedWaitTask() {
128 | taskDispatcher.addTask(new Task() {
129 | @Override
130 | public void run() {
131 | System.out.println("run");
132 | }
133 |
134 | @Override
135 | public boolean needWait() {
136 | return true;
137 | }
138 | });
139 | }
140 |
141 | private void addTask() {
142 | taskDispatcher.addTask(new Task() {
143 | @Override
144 | public void run() {
145 | System.out.println("run");
146 | }
147 | });
148 | }
149 |
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/Task.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.os.Process;
6 | import org.jay.launchstarter.utils.DispatcherExecutor;
7 |
8 | import java.util.List;
9 | import java.util.concurrent.CountDownLatch;
10 | import java.util.concurrent.ExecutorService;
11 |
12 | public abstract class Task implements ITask {
13 |
14 | protected String mTag = getClass().getSimpleName().toString();
15 |
16 | protected Context mContext = TaskDispatcher.getContext();
17 | protected Application mApp = TaskDispatcher.getmApp();
18 |
19 | /**
20 | * 当前进程是否是主进程
21 | */
22 | protected boolean mIsMainProcess = TaskDispatcher.isMainProcess();
23 |
24 | /**
25 | * 是否正在等待
26 | */
27 | private volatile boolean mIsWaiting;
28 |
29 | /**
30 | * 是否正在执行
31 | */
32 | private volatile boolean mIsRunning;
33 |
34 | /**
35 | * Task是否执行完成
36 | */
37 | private volatile boolean mIsFinished;
38 |
39 | /**
40 | * Task是否已经被分发
41 | */
42 | private volatile boolean mIsSend;
43 |
44 | // 当前Task依赖的Task数量(需要等待被依赖的Task执行完毕才能执行自己),默认没有依赖
45 | private CountDownLatch mDepends = new CountDownLatch(dependsOn() == null ? 0 : dependsOn().size());
46 |
47 | /**
48 | * 当前Task等待,让依赖的Task先执行
49 | */
50 | public void waitToSatisfy() {
51 | try {
52 | mDepends.await();
53 | } catch (InterruptedException e) {
54 | e.printStackTrace();
55 | }
56 | }
57 |
58 | /**
59 | * 依赖的Task执行完一个
60 | */
61 | public void satisfy() {
62 | mDepends.countDown();
63 | }
64 |
65 | /**
66 | * 是否需要尽快执行,解决特殊场景的问题:一个Task耗时非常多但是优先级却一般,很有可能开始的时间较晚,
67 | * 导致最后只是在等它,这种可以早开始。
68 | *
69 | * @return
70 | */
71 | public boolean needRunAsSoon() {
72 | return false;
73 | }
74 |
75 | /**
76 | * Task的优先级,运行在主线程则不要去改优先级
77 | *
78 | * @return
79 | */
80 | @Override
81 | public int priority() {
82 | return Process.THREAD_PRIORITY_BACKGROUND;
83 | }
84 |
85 | /**
86 | * Task执行在哪个线程池,默认在IO的线程池;
87 | * CPU 密集型的一定要切换到DispatcherExecutor.getCPUExecutor();
88 | *
89 | * @return
90 | */
91 | @Override
92 | public ExecutorService runOn() {
93 | return DispatcherExecutor.getIOExecutor();
94 | }
95 |
96 | /**
97 | * 异步线程执行的Task是否需要在被调用await的时候等待,默认不需要
98 | *
99 | * @return
100 | */
101 | @Override
102 | public boolean needWait() {
103 | return false;
104 | }
105 |
106 | /**
107 | * 当前Task依赖的Task集合(需要等待被依赖的Task执行完毕才能执行自己),默认没有依赖
108 | *
109 | * @return
110 | */
111 | @Override
112 | public List> dependsOn() {
113 | return null;
114 | }
115 |
116 | @Override
117 | public boolean runOnMainThread() {
118 | return false;
119 | }
120 |
121 | @Override
122 | public Runnable getTailRunnable() {
123 | return null;
124 | }
125 |
126 | @Override
127 | public void setTaskCallBack(TaskCallBack callBack) {}
128 |
129 | @Override
130 | public boolean needCall() {
131 | return false;
132 | }
133 |
134 | /**
135 | * 是否只在主进程,默认是
136 | */
137 | @Override
138 | public boolean onlyInMainProcess() {
139 | return true;
140 | }
141 |
142 | public boolean isRunning() {
143 | return mIsRunning;
144 | }
145 |
146 | public void setRunning(boolean mIsRunning) {
147 | this.mIsRunning = mIsRunning;
148 | }
149 |
150 | public boolean isFinished() {
151 | return mIsFinished;
152 | }
153 |
154 | public void setFinished(boolean finished) {
155 | mIsFinished = finished;
156 | }
157 |
158 | public boolean isSend() {
159 | return mIsSend;
160 | }
161 |
162 | public void setSend(boolean send) {
163 | mIsSend = send;
164 | }
165 |
166 | public boolean isWaiting() {
167 | return mIsWaiting;
168 | }
169 |
170 | public void setWaiting(boolean mIsWaiting) {
171 | this.mIsWaiting = mIsWaiting;
172 | }
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/sort/TaskSortUtil.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter.sort;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.collection.ArraySet;
5 | import org.jay.launchstarter.Task;
6 | import org.jay.launchstarter.utils.DispatcherLog;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.Set;
11 |
12 |
13 | public class TaskSortUtil {
14 |
15 | private static List sNewTasksHigh = new ArrayList<>();// 高优先级的Task
16 |
17 | /**
18 | * 任务的有向无环图的拓扑排序
19 | *
20 | * @return
21 | */
22 | public static synchronized List getSortResult(List originTasks,
23 | List> clsLaunchTasks) {
24 | long makeTime = System.currentTimeMillis();
25 |
26 | Set dependSet = new ArraySet<>();
27 | Graph graph = new Graph(originTasks.size());
28 | for (int i = 0; i < originTasks.size(); i++) {
29 | Task task = originTasks.get(i);
30 | if (task.isSend() || task.dependsOn() == null || task.dependsOn().size() == 0) {
31 | continue;
32 | }
33 | for (Class cls : task.dependsOn()) {
34 | int indexOfDepend = getIndexOfTask(originTasks, clsLaunchTasks, cls);
35 | if (indexOfDepend < 0) {
36 | throw new IllegalStateException(task.getClass().getSimpleName() +
37 | " depends on " + cls.getSimpleName() + " can not be found in task list ");
38 | }
39 | dependSet.add(indexOfDepend);
40 | graph.addEdge(indexOfDepend, i);
41 | }
42 | }
43 | List indexList = graph.topologicalSort();
44 | List newTasksAll = getResultTasks(originTasks, dependSet, indexList);
45 |
46 | DispatcherLog.i("task analyse cost makeTime " + (System.currentTimeMillis() - makeTime));
47 | printAllTaskName(newTasksAll);
48 | return newTasksAll;
49 | }
50 |
51 | @NonNull
52 | private static List getResultTasks(List originTasks,
53 | Set dependSet, List indexList) {
54 | List newTasksAll = new ArrayList<>(originTasks.size());
55 | List newTasksDepended = new ArrayList<>();// 被别人依赖的
56 | List newTasksWithOutDepend = new ArrayList<>();// 没有依赖的
57 | List newTasksRunAsSoon = new ArrayList<>();// 需要提升自己优先级的,先执行(这个先是相对于没有依赖的先)
58 | for (int index : indexList) {
59 | if (dependSet.contains(index)) {
60 | newTasksDepended.add(originTasks.get(index));
61 | } else {
62 | Task task = originTasks.get(index);
63 | if (task.needRunAsSoon()) {
64 | newTasksRunAsSoon.add(task);
65 | } else {
66 | newTasksWithOutDepend.add(task);
67 | }
68 | }
69 | }
70 | // 顺序:被别人依赖的————》需要提升自己优先级的————》需要被等待的————》没有依赖的
71 | sNewTasksHigh.addAll(newTasksDepended);
72 | sNewTasksHigh.addAll(newTasksRunAsSoon);
73 | newTasksAll.addAll(sNewTasksHigh);
74 | newTasksAll.addAll(newTasksWithOutDepend);
75 | return newTasksAll;
76 | }
77 |
78 | private static void printAllTaskName(List newTasksAll) {
79 | if (true) {
80 | return;
81 | }
82 | for (Task task : newTasksAll) {
83 | DispatcherLog.i(task.getClass().getSimpleName());
84 | }
85 | }
86 |
87 | public static List getTasksHigh() {
88 | return sNewTasksHigh;
89 | }
90 |
91 | /**
92 | * 获取任务在任务列表中的下标
93 | */
94 | private static int getIndexOfTask(List originTasks,
95 | List> clsLaunchTasks,
96 | Class cls) {
97 | int index = clsLaunchTasks.indexOf(cls);
98 | if (index >= 0) {
99 | return index;
100 | }
101 |
102 | // 仅仅是保护性代码
103 | final int size = originTasks.size();
104 | for (int i = 0; i < size; i++) {
105 | if (cls.getSimpleName().equals(originTasks.get(i).getClass().getSimpleName())) {
106 | return i;
107 | }
108 | }
109 | return index;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/launchstarter/src/main/java/org/jay/launchstarter/TaskDispatcher.java:
--------------------------------------------------------------------------------
1 | package org.jay.launchstarter;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.os.Looper;
6 | import android.util.Log;
7 |
8 | import androidx.annotation.UiThread;
9 |
10 | import org.jay.launchstarter.sort.TaskSortUtil;
11 | import org.jay.launchstarter.stat.TaskStat;
12 | import org.jay.launchstarter.utils.DispatcherLog;
13 | import org.jay.launchstarter.utils.Utils;
14 |
15 | import java.util.ArrayList;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.concurrent.CountDownLatch;
19 | import java.util.concurrent.Future;
20 | import java.util.concurrent.TimeUnit;
21 | import java.util.concurrent.atomic.AtomicInteger;
22 |
23 | /**
24 | * 启动器调用类
25 | */
26 | public class TaskDispatcher {
27 | private long mStartTime;
28 | private static final int WAITTIME = 10000;
29 | private static Context sContext;
30 | private static Application mApp;
31 | private static boolean sIsMainProcess;
32 | private List mFutures = new ArrayList<>();
33 | private static volatile boolean sHasInit;
34 | private List mAllTasks = new ArrayList<>();
35 | private List> mClsAllTasks = new ArrayList<>();
36 | private volatile List mMainThreadTasks = new ArrayList<>();
37 | private CountDownLatch mCountDownLatch;
38 |
39 | /**
40 | * 需要等待的任务数
41 | */
42 | private AtomicInteger mNeedWaitCount = new AtomicInteger();//
43 |
44 | /**
45 | * 调用了 await 还没结束且需要等待的任务列表
46 | */
47 | private List mNeedWaitTasks = new ArrayList<>();
48 |
49 | /**
50 | * 已经结束的Task
51 | */
52 | private volatile List> mFinishedTasks = new ArrayList<>(100);//
53 |
54 | private HashMap, ArrayList> mDependedHashMap = new HashMap<>();
55 |
56 | /**
57 | * 启动器分析的次数,统计下分析的耗时;
58 | */
59 | private AtomicInteger mAnalyseCount = new AtomicInteger();
60 |
61 | private TaskDispatcher() {
62 | }
63 |
64 | public static void init(Application context) {
65 | if (context != null) {
66 | sContext = context;
67 | mApp = context;
68 | sHasInit = true;
69 | sIsMainProcess = Utils.isMainProcess(sContext);
70 | }
71 | }
72 |
73 | /**
74 | * 注意:每次获取的都是新对象
75 | *
76 | * @return
77 | */
78 | public static TaskDispatcher createInstance() {
79 | if (!sHasInit) {
80 | throw new RuntimeException("must call TaskDispatcher.init first");
81 | }
82 | return new TaskDispatcher();
83 | }
84 |
85 | public TaskDispatcher addTask(Task task) {
86 | if (task != null) {
87 | collectDepends(task);
88 | mAllTasks.add(task);
89 | mClsAllTasks.add(task.getClass());
90 | // 非主线程且需要wait的,主线程不需要CountDownLatch也是同步的
91 | if (ifNeedWait(task)) {
92 | mNeedWaitTasks.add(task);
93 | mNeedWaitCount.getAndIncrement();
94 | }
95 | }
96 | return this;
97 | }
98 |
99 | private void collectDepends(Task task) {
100 | if (task.dependsOn() != null && task.dependsOn().size() > 0) {
101 | for (Class extends Task> cls : task.dependsOn()) {
102 | if (mDependedHashMap.get(cls) == null) {
103 | mDependedHashMap.put(cls, new ArrayList());
104 | }
105 | mDependedHashMap.get(cls).add(task);
106 | if (mFinishedTasks.contains(cls)) {
107 | task.satisfy();
108 | }
109 | }
110 | }
111 | }
112 |
113 | private boolean ifNeedWait(Task task) {
114 | return !task.runOnMainThread() && task.needWait();
115 | }
116 |
117 | @UiThread
118 | public void start() {
119 | mStartTime = System.currentTimeMillis();
120 | if (Looper.getMainLooper() != Looper.myLooper()) {
121 | throw new RuntimeException("must be called from UiThread");
122 | }
123 | if (mAllTasks.size() > 0) {
124 | mAnalyseCount.getAndIncrement();
125 | printDependedMsg();
126 | mAllTasks = TaskSortUtil.getSortResult(mAllTasks, mClsAllTasks);
127 | mCountDownLatch = new CountDownLatch(mNeedWaitCount.get());
128 |
129 | sendAndExecuteAsyncTasks();
130 |
131 | DispatcherLog.i("task analyse cost " + (System.currentTimeMillis() - mStartTime) + " begin main ");
132 | executeTaskMain();
133 | }
134 | DispatcherLog.i("task analyse cost startTime cost " + (System.currentTimeMillis() - mStartTime));
135 | }
136 |
137 | public void cancel() {
138 | for (Future future : mFutures) {
139 | future.cancel(true);
140 | }
141 | }
142 |
143 | private void executeTaskMain() {
144 | mStartTime = System.currentTimeMillis();
145 | for (Task task : mMainThreadTasks) {
146 | long time = System.currentTimeMillis();
147 | new DispatchRunnable(task, this).run();
148 | DispatcherLog.i("real main " + task.getClass().getSimpleName() + " cost " +
149 | (System.currentTimeMillis() - time));
150 | }
151 | DispatcherLog.i("maintask cost " + (System.currentTimeMillis() - mStartTime));
152 | }
153 |
154 | /**
155 | * 发送去并且执行异步任务
156 | */
157 | private void sendAndExecuteAsyncTasks() {
158 | for (Task task : mAllTasks) {
159 | if (task.onlyInMainProcess() && !sIsMainProcess) {
160 | markTaskDone(task);
161 | } else {
162 | sendTaskReal(task);
163 | }
164 | task.setSend(true);
165 | }
166 | }
167 |
168 | /**
169 | * 查看被依赖的信息
170 | */
171 | private void printDependedMsg() {
172 | DispatcherLog.i("needWait size : " + (mNeedWaitCount.get()));
173 | if (false) {
174 | for (Class extends Task> cls : mDependedHashMap.keySet()) {
175 | DispatcherLog.i("cls " + cls.getSimpleName() + " " + mDependedHashMap.get(cls).size());
176 | for (Task task : mDependedHashMap.get(cls)) {
177 | DispatcherLog.i("cls " + task.getClass().getSimpleName());
178 | }
179 | }
180 | }
181 | }
182 |
183 | /**
184 | * 通知Children一个前置任务已完成
185 | */
186 | public void satisfyChildren(Task launchTask) {
187 | ArrayList arrayList = mDependedHashMap.get(launchTask.getClass());
188 | if (arrayList != null && arrayList.size() > 0) {
189 | for (Task task : arrayList) {
190 | task.satisfy();
191 | }
192 | }
193 | }
194 |
195 | public void markTaskDone(Task task) {
196 | if (ifNeedWait(task)) {
197 | mFinishedTasks.add(task.getClass());
198 | mNeedWaitTasks.remove(task);
199 | mCountDownLatch.countDown();
200 | mNeedWaitCount.getAndDecrement();
201 | }
202 | }
203 |
204 | /**
205 | * 发送任务
206 | */
207 | private void sendTaskReal(final Task task) {
208 | if (task.runOnMainThread()) {
209 | mMainThreadTasks.add(task);
210 | if (task.needCall()) {
211 | task.setTaskCallBack(new TaskCallBack() {
212 | @Override
213 | public void call() {
214 | TaskStat.markTaskDone();
215 | task.setFinished(true);
216 | satisfyChildren(task);
217 | markTaskDone(task);
218 | DispatcherLog.i(task.getClass().getSimpleName() + " finish");
219 | Log.i("testLog", "call");
220 | }
221 | });
222 | }
223 | } else {
224 | // 直接发,是否执行取决于具体线程池
225 | Future future = task.runOn().submit(new DispatchRunnable(task, this));
226 | mFutures.add(future);
227 | }
228 | }
229 |
230 | public void executeTask(Task task) {
231 | if (ifNeedWait(task)) {
232 | mNeedWaitCount.getAndIncrement();
233 | }
234 | task.runOn().execute(new DispatchRunnable(task, this));
235 | }
236 |
237 | @UiThread
238 | public void await() {
239 | try {
240 | if (DispatcherLog.isDebug()) {
241 | DispatcherLog.i("still has " + mNeedWaitCount.get());
242 | // for (Task task : mNeedWaitTasks) {
243 | // DispatcherLog.i("needWait: " + task.getClass().getSimpleName());
244 | // }
245 | }
246 |
247 | if (mNeedWaitCount.get() > 0) {
248 | if (mCountDownLatch == null) {
249 | throw new RuntimeException("You have to call start() before call await()");
250 | }
251 | //使用CountDownLatch实现线程阻塞,保证必须执行的任务先执行完再进入主页
252 | mCountDownLatch.await(WAITTIME, TimeUnit.MILLISECONDS);
253 | }
254 | } catch (InterruptedException e) {
255 | }
256 | }
257 |
258 | public static Context getContext() {
259 | return sContext;
260 | }
261 |
262 | public static Application getmApp() {
263 | return mApp;
264 | }
265 |
266 | public static boolean isMainProcess() {
267 | return sIsMainProcess;
268 | }
269 | }
270 |
--------------------------------------------------------------------------------