1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.launcher3.util;
17 
18 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
19 
20 import android.os.HandlerThread;
21 import android.os.Looper;
22 import android.os.Process;
23 
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.LinkedBlockingQueue;
28 import java.util.concurrent.ThreadFactory;
29 import java.util.concurrent.ThreadPoolExecutor;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.atomic.AtomicInteger;
32 
33 /**
34  * Various different executors used in Launcher
35  */
36 public class Executors {
37 
38     private static final int POOL_SIZE =
39             Math.max(Runtime.getRuntime().availableProcessors(), 2);
40     private static final int KEEP_ALIVE = 1;
41 
42     /** Dedicated executor instances for work depending on other packages. */
43     private static final Map<String, LooperExecutor> PACKAGE_EXECUTORS = new ConcurrentHashMap<>();
44 
45     /**
46      * An {@link ThreadPoolExecutor} to be used with async task with no limit on the queue size.
47      */
48     public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
49             POOL_SIZE, POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
50 
51     /**
52      * An {@link LooperExecutor} to be used with async task where order is important.
53      */
54     public static final LooperExecutor ORDERED_BG_EXECUTOR = new LooperExecutor(
55             createAndStartNewLooper("BackgroundExecutor", THREAD_PRIORITY_BACKGROUND));
56 
57     /**
58      * Returns the executor for running tasks on the main thread.
59      */
60     public static final LooperExecutor MAIN_EXECUTOR =
61             new LooperExecutor(Looper.getMainLooper());
62 
63     /**
64      * A background executor for using time sensitive actions where user is waiting for response.
65      */
66     public static final LooperExecutor UI_HELPER_EXECUTOR =
67             new LooperExecutor(
68                     createAndStartNewLooper("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND));
69 
70 
71     /** A background executor to preinflate views. */
72     public static final ExecutorService VIEW_PREINFLATION_EXECUTOR =
73             java.util.concurrent.Executors.newSingleThreadExecutor(
74                     new SimpleThreadFactory(
75                             "preinflate-allapps-icons", THREAD_PRIORITY_BACKGROUND));
76 
77     /**
78      * Utility method to get a started handler thread statically
79      */
createAndStartNewLooper(String name)80     public static Looper createAndStartNewLooper(String name) {
81         return createAndStartNewLooper(name, Process.THREAD_PRIORITY_DEFAULT);
82     }
83 
84     /**
85      * Utility method to get a started handler thread statically with the provided priority
86      */
createAndStartNewLooper(String name, int priority)87     public static Looper createAndStartNewLooper(String name, int priority) {
88         HandlerThread thread = new HandlerThread(name, priority);
89         thread.start();
90         return thread.getLooper();
91     }
92 
93     /**
94      * Executor used for running Launcher model related tasks (eg loading icons or updated db)
95      */
96     public static final LooperExecutor MODEL_EXECUTOR =
97             new LooperExecutor(createAndStartNewLooper("launcher-loader"));
98 
99     /**
100      * Returns and caches a single thread executor for a given package.
101      *
102      * @param packageName Package associated with the executor.
103      */
getPackageExecutor(String packageName)104     public static LooperExecutor getPackageExecutor(String packageName) {
105         return PACKAGE_EXECUTORS.computeIfAbsent(
106                 packageName, p -> new LooperExecutor(
107                         createAndStartNewLooper(p, Process.THREAD_PRIORITY_DEFAULT)));
108     }
109 
110     /**
111      * A simple ThreadFactory to set the thread name and priority when used with executors.
112      */
113     public static class SimpleThreadFactory implements ThreadFactory {
114 
115         private final int mPriority;
116         private final String mNamePrefix;
117 
118         private final AtomicInteger mCount = new AtomicInteger(0);
119 
SimpleThreadFactory(String namePrefix, int priority)120         public SimpleThreadFactory(String namePrefix, int priority) {
121             mNamePrefix = namePrefix;
122             mPriority = priority;
123         }
124 
125         @Override
newThread(Runnable runnable)126         public Thread newThread(Runnable runnable) {
127             Thread t = new Thread(() -> {
128                 Process.setThreadPriority(mPriority);
129                 runnable.run();
130             }, mNamePrefix + mCount.incrementAndGet());
131             return t;
132         }
133     }
134 }
135