美文网首页
Android 性能优化 05---App启动优化

Android 性能优化 05---App启动优化

作者: 沪漂意哥哥 | 来源:发表于2022-04-02 16:22 被阅读0次

    Android启动速度优化01

    一. 启动框架

    其实启动框架就是一个任务调度系统,是手淘启动的“大管家”。
    管家要做的事情就是把它们的关系梳理得明明白白,有条不紊,合理安排位置、调度时间,同时提升硬件资源的利用率。

    二. 启动框架的思路

    总结下来无非就是两点:

    • 如何保证时序
    • 如何让任务合理的并发执行

    三. 框架的整体实现流程

    • 实现任务的拓扑排序.
    • 同步执行排好序的任务
    • 加入异步操作有效利用CPU资源
    • 框架的初始化问题

    有向无环图[拓扑排序]

    https://gitee.com/luisliuyi/android-optimize-network01.git
    

    四. 任务关系图

    image.png

    五. 入度表

    image.png

    六. 任务依赖表

    image.png

    七. Startup.java任务的基本封装

    public interface Dispatcher {
    
        /**
         * 返回是否在主线程执行
         */
        boolean callCreateOnMainThread();
    
        /**
         * 让每个任务都可以指定自己执行在哪个线程沲
         *
         */
        Executor executor();
    
        /**
         * 指定线程的优先级
         */
        int getThreadPriority();
    
    
        /**
         * 等待
         */
        void toWait();
    
        /**
         * 有父任务执行完毕
         * 计数器-1
         */
        void toNotify();
    
        /**
         * 是否需要主线程等待该任务执行完成
         * callCreateOnMainThread()方法返回false才有意义
         */
        boolean waitOnMainThread();
    }
    
    
    public interface Startup<T> extends Dispatcher {
    
        /**
         * 给程序员写任务逻辑使用
         */
        T create(Context context);
    
        /**
         * 本任务依赖哪些任务
         *
         */
        List<Class<? extends Startup<?>>> dependencies();
    
        /**
         * 入度数
         */
        int getDependenciesCount();
    }
    
    
    public abstract class AndroidStartup<T> implements Startup<T> {
    
        //入度数初始化
        private CountDownLatch mWaitCountDown = new CountDownLatch(getDependenciesCount());
    
        @Override
        public List<Class<? extends Startup<?>>> dependencies() {
            return null;
        }
    
        @Override
        public int getDependenciesCount() {
            List<Class<? extends Startup<?>>> dependencies = dependencies();
            return dependencies == null ? 0 : dependencies.size();
        }
    
        @Override
        public void toWait() {
            try {
                mWaitCountDown.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void toNotify() {
            mWaitCountDown.countDown();
        }
    
        /**
         * 默认使用io线程沲
         */
        @Override
        public Executor executor() {
            return ExecutorManager.ioExecutor;
        }
    
        /**
         * 默认最高优先级
         */
        @Override
        public int getThreadPriority() {
            return Process.THREAD_PRIORITY_DEFAULT;
        }
    
        /**
         * 默认在子线程执行
         */
        @Override
        public boolean callCreateOnMainThread() {
            return false;
        }
    
        /**
         * 默认不需要主线程等待自己执行完成
         */
        @Override
        public boolean waitOnMainThread() {
            return false;
        }
    }
    
    
    public class Task1 extends AndroidStartup<Void> {
    
        @Nullable
        @Override
        public Void create(Context context) {
            LogUtils.log(" Task1:学习Java基础");
            SystemClock.sleep(3_000);
            LogUtils.log(" Task1:掌握Java基础");
            return null;
        }
    
        //执行此任务需要依赖哪些任务
        @Override
        public List<Class<? extends Startup<?>>> dependencies() {
            return super.dependencies();
        }
    
    }
    
    public class Task2 extends AndroidStartup<Void> {
        static List<Class<? extends Startup<?>>> depends;
    
        static {
            depends = new ArrayList<>();
            depends.add(Task1.class);
        }
    
        @Override
        public Void create(Context context) {
            LogUtils.log(" Task2:学习Socket");
            SystemClock.sleep(800);
            LogUtils.log(" Task2:掌握Socket");
            return null;
        }
    
        @Override
        public List<Class<? extends Startup<?>>> dependencies() {
            return depends;
        }
    }
    
    
    public class Task3 extends AndroidStartup<Void> {
    
        static List<Class<? extends Startup<?>>> depends;
    
        static {
            depends = new ArrayList<>();
            depends.add(Task1.class);
        }
    
        @Override
        public Void create(Context context) {
            LogUtils.log(" Task3:学习设计模式");
            SystemClock.sleep(2_000);
            LogUtils.log(" Task3:掌握设计模式");
            return null;
        }
    
        //    执行此任务需要依赖哪些任务
        @Override
        public List<Class<? extends Startup<?>>> dependencies() {
            return depends;
        }
    }
    
    
    public class Task4 extends AndroidStartup<Void> {
    
        static List<Class<? extends Startup<?>>> depends;
    
        static {
            depends = new ArrayList<>();
            depends.add(Task2.class);
        }
    
        @Override
        public Void create(Context context) {
            LogUtils.log( " Task4:学习Http");
            SystemClock.sleep(1_000);
            LogUtils.log( " Task4:掌握Http");
            return null;
        }
    
        @Override
        public List<Class<? extends Startup<?>>> dependencies() {
            return depends;
        }
    }
    
    public class Task5 extends AndroidStartup<Void> {
        static List<Class<? extends Startup<?>>> depends;
    
        static {
            depends = new ArrayList<>();
            depends.add(Task3.class);
            depends.add(Task4.class);
        }
        @Override
        public Void create(Context context) {
            String t = Looper.myLooper() == Looper.getMainLooper()
                    ? "主线程: " : "子线程: ";
            LogUtils.log(t + " Task5:学习OkHttp");
            SystemClock.sleep(500);
            LogUtils.log(t + " Task5:掌握OkHttp");
            return null;
        }
    
        //执行此任务需要依赖哪些任务
        @Override
        public List<Class<? extends Startup<?>>> dependencies() {
            return depends;
        }
    }
    
    

    八. TopologySort.java对任务进行拓扑排序

    public class TopologySort {
        public static StartupSortStore sort(List<? extends Startup<?>> startupList) {
            //入度表
            Map<Class<? extends Startup>, Integer> inDegreeMap = new HashMap<>();
    
            //0度表
            Deque<Class<? extends Startup>> zeroDeque = new ArrayDeque<>();
    
            //任务表
            Map<Class<? extends Startup>, Startup<?>> startupMap = new HashMap<>();
    
            //任务依赖表
            Map<Class<? extends Startup>, List<Class<? extends Startup>>> startupChildrenMap = new HashMap<>();
    
            //1.找出图中入度数为0的节点   填表的过程
            for (Startup<?> startup : startupList) {
                startupMap.put(startup.getClass(), startup);
                //记录每个任务的入度数(依赖的任务数)
                int dependenciesCount = startup.getDependenciesCount();
                inDegreeMap.put(startup.getClass(), dependenciesCount);
                //记录入度数(依赖的任务数)为0的任务
                if (dependenciesCount == 0) {
                    zeroDeque.offer(startup.getClass());
                } else {
                    //遍历本任务的依赖任务,生成任务依赖表
                    for (Class<? extends Startup<?>> parent : startup.dependencies()) {
                        List<Class<? extends Startup>> children = startupChildrenMap.get(parent);
                        if (children == null) {
                            children = new ArrayList<>();
                            //记录这个父任务的所有子任务
                            startupChildrenMap.put(parent, children);
                        }
                        children.add(startup.getClass());
                    }
                }
            }
    
            //2.删除图中入度为0的这些顶点,并更新全图,,最后完成排序
            List<Startup<?>> result = new ArrayList<>();
            //处理入度为0的任务
            while (!zeroDeque.isEmpty()) {
                Class<? extends Startup> cls = zeroDeque.poll();
                Startup<?> startup = startupMap.get(cls);
                result.add(startup);
    
                //删除此入度为0的任务
                if (startupChildrenMap.containsKey(cls)) {
                    List<Class<? extends Startup>> childStartup = startupChildrenMap.get(cls);
                    for (Class<? extends Startup> childCls : childStartup) {
                        Integer num = inDegreeMap.get(childCls);
                        inDegreeMap.put(childCls, num - 1);
                        if (num - 1 == 0) {
                            zeroDeque.offer(childCls);
                        }
                    }
                }
            }
            return new StartupSortStore(result, startupMap, startupChildrenMap);
        }
    }
    

    九. StartupSortStore.java用于保存排序结果

    public class StartupSortStore {
        //所有的任务
        List<Startup<?>> result;
        Map<Class<? extends Startup>, Startup<?>> startupMap;
        //当前任务的子任务
        Map<Class<? extends Startup>, List<Class<? extends Startup>>> startupChildrenMap;
    
        public StartupSortStore(List<Startup<?>> result, Map<Class<? extends Startup>, Startup<?>> startupMap, Map<Class<? extends Startup>, List<Class<? extends Startup>>> startupChildrenMap) {
            this.result = result;
            this.startupMap = startupMap;
            this.startupChildrenMap = startupChildrenMap;
        }
    
        public List<Startup<?>> getResult() {
            return result;
        }
    
        public void setResult(List<Startup<?>> result) {
            this.result = result;
        }
    
        public Map<Class<? extends Startup>, Startup<?>> getStartupMap() {
            return startupMap;
        }
    
        public void setStartupMap(Map<Class<? extends Startup>, Startup<?>> startupMap) {
            this.startupMap = startupMap;
        }
    
        public Map<Class<? extends Startup>, List<Class<? extends Startup>>> getStartupChildrenMap() {
            return startupChildrenMap;
        }
    
        public void setStartupChildrenMap(Map<Class<? extends Startup>, List<Class<? extends Startup>>> startupChildrenMap) {
            this.startupChildrenMap = startupChildrenMap;
        }
    }
    

    十. StartupCacheManager.java用于保存任务的执行结果

    /**
     * 缓存执行完成的任务的结果
     */
    public class StartupCacheManager {
    
        //用于缓存每一个任务的执行结果    Result用来给ConcurrentHashMap防null
        private ConcurrentHashMap<Class<? extends Startup>, Result> mInitializedComponents =
                new ConcurrentHashMap();
    
        private static StartupCacheManager mInstance;
    
        private StartupCacheManager() {
    
        }
    
        public static StartupCacheManager getInstance() {
            if (mInstance == null) {
                synchronized (StartupCacheManager.class) {
                    if (mInstance == null) {
                        mInstance = new StartupCacheManager();
                    }
                }
            }
            return mInstance;
        }
    
        /**
         * save result of initialized component.
         */
        public void saveInitializedComponent(Class<? extends Startup> zClass, Result result) {
            mInitializedComponents.put(zClass, result);
        }
    
        /**
         * check initialized.
         */
        public boolean hadInitialized(Class<? extends Startup> zClass) {
            return mInitializedComponents.containsKey(zClass);
        }
    
        public <T> Result<T> obtainInitializedResult(Class<? extends Startup<T>> zClass) {
            return mInitializedComponents.get(zClass);
        }
    
    
        public void remove(Class<? extends Startup> zClass) {
            mInitializedComponents.remove(zClass);
        }
    
        public void clear() {
            mInitializedComponents.clear();
        }
    }
    
    

    十一. StartupManager.java执行所有已经排好序的任务

    public class StartupManager {
        private Context context;
        private List<Startup<?>> startupList;
        private StartupSortStore startupSortStore;
        private CountDownLatch awaitCountDownLatch;
    
        public StartupManager(Context context, List<Startup<?>> startupList, CountDownLatch awaitCountDownLatch) {
            this.context = context;
            this.startupList = startupList;
            this.awaitCountDownLatch = awaitCountDownLatch;
        }
    
        public StartupManager start() {
            if (Looper.myLooper() != Looper.getMainLooper()) {
                throw new RuntimeException("请在主线程调用!");
            }
            startupSortStore = TopologySort.sort(startupList);
            for (Startup<?> startup : startupSortStore.getResult()) {
                StartupRunnable startupRunnable = new StartupRunnable(context, startup, this);
                if (startup.callCreateOnMainThread()) {
                    startupRunnable.run();
                } else {
                    startup.executor().execute(startupRunnable);
                }
            }
            return this;
        }
    
        public void notifyChildren(Startup<?> startup) {
            if (!startup.callCreateOnMainThread() &&
                    startup.waitOnMainThread()) {
                awaitCountDownLatch.countDown();
            }
    
            //获得已经完成的当前任务的所有子任务
            if (startupSortStore
                    .getStartupChildrenMap().containsKey(startup.getClass())) {
                List<Class<? extends Startup>> childStartupCls = startupSortStore
                        .getStartupChildrenMap().get(startup.getClass());
                for (Class<? extends Startup> cls : childStartupCls) {
                    //通知子任务 startup父任务已完成
                    Startup<?> childStartup = startupSortStore.getStartupMap().get(cls);
                    childStartup.toNotify();
                }
            }
        }
    
        public void await() {
            try {
                awaitCountDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        //基于分阶段处理任务,不能用单例
        public static class Builder {
            private List<Startup<?>> startupList = new ArrayList<>();
    
            public Builder addStartup(Startup<?> startup) {
                startupList.add(startup);
                return this;
            }
    
            public Builder addAllStartup(List<Startup<?>> startups) {
                startupList.addAll(startups);
                return this;
            }
    
            public StartupManager build(Context context) {
                //记录有多少个在子线程执行,又需要主线程等待的任务
                AtomicInteger needAwaitCount = new AtomicInteger();
                for (Startup<?> startup : startupList) {
                    //记录有多少个在子线程执行,又需要主线程等待的任务
                    if (!startup.callCreateOnMainThread() &&
                            startup.waitOnMainThread()) {
                        needAwaitCount.incrementAndGet();//i++
                    }
                }
                //根据任务数新建一个闭锁
                CountDownLatch awaitCountDownLatch = new CountDownLatch(needAwaitCount.get());
                return new StartupManager(context, startupList, awaitCountDownLatch);
            }
        }
    
    }
    
    

    十二. ExecutorManager进行线程沲的管理

    public class ExecutorManager {
        public static ThreadPoolExecutor cpuExecutor;
        public static ExecutorService ioExecutor;
        public static Executor mainExecutor;
    
        //获得CPU核心数
        private static int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        private static int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 5));
        private static int MAX_POOL_SIZE = CORE_POOL_SIZE;
        private static long KEEP_ALIVE_TIME = 5L;
    
        static {
            cpuExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE,
                    KEEP_ALIVE_TIME, TimeUnit.SECONDS,
                    new LinkedBlockingDeque<Runnable>(),
                    Executors.defaultThreadFactory());
            cpuExecutor.allowCoreThreadTimeOut(true);
    
            ioExecutor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
    
            mainExecutor = new Executor() {
                Handler handler = new Handler(Looper.getMainLooper());
                @Override
                public void execute(Runnable command) {
                    handler.post(command);
                }
            };
        }
    }
    
    

    十三. 框架的初始化问题

     public class MyApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
            new StartupManager.Builder()
                    .addStartup(new Task2())
                    .addStartup(new Task4())
                    .addStartup(new Task5())
                    .addStartup(new Task3())
                    .addStartup(new Task1())
                    .build(this)
                    .start().await();
        }
    }
    

    可用方案
    APT,字节码插桩,利用ContentProvider
    面试题LeakCanary 为什么不需要在Application中手动初始化?

    <provider
                android:name=".startup.provider.StartupProvider"
                android:authorities="${applicationId}.android_startup"
                android:exported="false">
                <meta-data
                    android:name="com.luisliuyi.demo.optimize.startup.tasks.Task5"
                    android:value="android.startup" />
            </provider>
    
    public class StartupProvider extends ContentProvider {
        @Override
        public boolean onCreate() {
            try {
                List<Startup<?>> startups = StartupInitializer
                        .discoverAndInitializ(getContext(), getClass().getName());
                new StartupManager.Builder()
                        .addAllStartup(startups)
                        .build(getContext())
                        .start()
                        .await();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;
        }
    
        @Nullable
        @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
            return null;
        }
    
        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            return null;
        }
    
        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
            return null;
        }
    
        @Override
        public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
            return 0;
        }
    
        @Override
        public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
            return 0;
        }
    }
    
    
    public class StartupInitializer {
        public static String META_VALUE = "android.startup";
        public static List<Startup<?>> discoverAndInitializ(Context context,
                                                            String providerName) throws Exception {
            //用来保存有向无环图  任务的结构图
            Map<Class<? extends Startup>, Startup<?>> startups = new HashMap<>();
    
            //获得manifest contentProvider中的meta-data
            ComponentName provider = new ComponentName(context, providerName);
            ProviderInfo providerInfo = context.getPackageManager().getProviderInfo(provider,
                    PackageManager.GET_META_DATA);
    
            //得到manifest中配置的任务
            for (String key : providerInfo.metaData.keySet()) {
                String value = providerInfo.metaData.getString(key);
                if (TextUtils.equals(META_VALUE, value)) {
                    Class<?> clazz = Class.forName(key);//"com.example.appstartup_step4.tasks.Task5"
                    if (Startup.class.isAssignableFrom(clazz)) {//clazz是Startup的子类就返回true
                        doInitialize((Startup<?>) clazz.newInstance(), startups);
                    }
                }
            }
            List<Startup<?>> result = new ArrayList<>(startups.values());
            return result;
        }
    
        private static void doInitialize(Startup<?> startup,
                                         Map<Class<? extends Startup>, Startup<?>> startups) throws Exception {
            //避免重复 不能使用List
            startups.put(startup.getClass(), startup);
            if (startup.getDependenciesCount() != 0) {
                //遍历父任务
                for (Class<? extends Startup<?>> dependency : startup.dependencies()) {
                    doInitialize(dependency.newInstance(), startups);
                }
            }
        }
    }
    

    十四. 运行结果

    image.png

    十五. 代码地址

    https://gitee.com/luisliuyi/android-optimize-startup.git
    

    十六. 推荐中小项目可用框架

    https://github.com/idisfkj/android-startup/blob/master/README-ch.md
    

    Android启动速度优化01

    启动流程

    ①点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起 startActivity请求;
    ②system_server进程接收到请求后,向zygote进程发送创建进程的请求;
    ③Zygote进程fork出新的子进程,即App进程;
    ④App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
    ⑤system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC 向App进程发送scheduleLaunchActivity请求;
    ⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主 线程发送LAUNCH_ACTIVITY消息;
    ⑦主线程在收到Message后,通过反射机制创建目标Activity,并回调 Activity.onCreate()等方法。
    ⑧到此,App便正式启动,开始进入Activity生命周期,执行完 onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。

    启动状态

    应用有三种启动状态,每种状态都会影响应用向用户显示所需的时间:冷启动、温启动与热启动。

    • 冷启动
      冷启动是指应用从头开始启动:系统进程在冷启动后才创建应用进程。发生冷启动的情况包括应用自设备 启动后或系统终止应用后首次启动。

    • 热启动
      在热启动中,系统的所有工作就是将 Activity 带到前台。只要应用的所有 Activity 仍驻留在内存中, 应用就不必重复执行对象初始化、布局加载和绘制。

    • 温启动
      温启动包含了在冷启动期间发生的部分操作;同时,它的开销要比热启动高。有许多潜在状态可视为温启 动。例如:
      1.用户在退出应用后又重新启动应用。进程可能未被销毁,继续运行,但应用需要执行 onCreate() 从 头开始重新创建 Activity
      2.系统将应用从内存中释放,然后用户又重新启动它。进程和 Activity 需要重启,但传递到 onCreate() 的已保存的实例savedInstanceState对于完成此任务有一定助益

    启动耗时统计

    • 系统日志统计
      在 Android 4.4(API 级别 19)及更高版本中,logcat 包含一个输出行,其中包含名为 Displayed 的值。 此值代表从启动进程到在屏幕上完成对应 Activity 的绘制所用的时间

    • adb命令统计

      adb shell am start-S-W [packageName]/[activityName image.png

    adb命令启动应用,一般会输入三个值:ThisTime、TotalTime与WaitTime。
    1.WaitTime:包括前一个应用Activitypause的时间和新应用启动的时间;
    2.ThisTime:表示一连串启动Activity的最后一个Activity的启动耗时;
    3.TotalTime:表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activitypause
    的耗时。

    CPUProfile

    image.png

    StrictMode

    StrictMode是一个开发人员工具,它可以检测出我们可能无意中做的事情,并将它们提请我们注意,以便我 们能够修复它们。 StrictMode最常用于捕获应用程序主线程上的意外磁盘或网络访问。帮助我们让磁盘和网络操作远离主线程, 可以使应用程序更加平滑、响应更快

    启动黑白屏

    当系统加载并启动 App 时,需要耗费相应的时间,这样会造成用户会感觉到当点击 App 图标时会有 “延迟” 现象,
    为了解决这一问题,Google 的做法是在 App 创建的过程中,先展示一个空白页面,让用户体会到点击图标之后立
    马就有响应。
    如果你的application或activity启动的过程太慢,导致系统的BackgroundWindow没有及时被替换,就会出现启动
    时白屏或黑屏的情况(取决于Theme主题是Dark还是Light)。

    消除启动时的黑/白屏问题,大部分App都采用自己在Theme中设置背景图的方式来解决。

    <style name="AppTheme.Launcher">
     <item name="android:windowBackground">@drawable/bg</item> 
    </style>
     <activity android:name=".activity.SplashActivity" android:screenOrientation="portrait" android:theme="@style/AppTheme.Launcher">
     <intent-filter>
     <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> 
    </intent-filter> 
    </activity>
    

    然后在Activity的onCreate方法,把Activity设置回原来的主题。

    @Override protected void onCreate(Bundle savedInstanceState) { 
    //替换为原来的主题,在onCreate之前调用 setTheme(R.style.AppTheme); 
    super.onCreate(savedInstanceState); 
    }
    

    这么做,只是提高启动的用户体验。并不能做到真正的加快启动速度。

    启动优化相关

    • 合理的使用异步初始化、延迟初始化、懒加载机制。
    • 启动过程避免耗时操作,如数据库 I/O操作不要放在主线程执行。
    • 类加载优化:提前异步执行类加载。
    • 合理使用IdleHandler进行延迟初始化。
    • 简化布局

    相关文章

      网友评论

          本文标题:Android 性能优化 05---App启动优化

          本文链接:https://www.haomeiwen.com/subject/gwrtsrtx.html