草庐IT

Android Jetpack架构组件(十)— StartUp

独自闯天涯的码农 2023-03-28 原文

一、StartUp简介

应用程序启动库提供了一种在应用程序启动时初始化组件的简单、高效的方法。库开发人员和应用程序开发人员都可以使用StartUp来简化启动序列并显式设置初始化顺序。
StartUp允许您定义共享单个内容提供程序的组件初始化程序,而不是为每个需要初始化的组件定义单独的content provider。这可以显著缩短应用程序启动时间。

简单的说就是通过一个公共的content provider来集中管理需要初始化的组件,从而提高应用的启动速度。

二、StartUp使用

1、在build.gradle中添加所需依赖

dependencies {
    implementation "androidx.startup:startup-runtime:1.0.0"
}

2、创建初始化入口

每一个需要初始化的组件我们需要创建一个class去实现Initializer<T>接口

class AModuleInitializer : Initializer<AModule> {
    override fun create(context: Context): ASdk {
        Log.i("xjm","AModuleInitializer create()方法执行" )
        return AModule.getInstance()
    }
    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        Log.i("xjm","AModuleInitializer dependencies()方法执行" )
        return mutableListOf()
    }
}

只需实现两个方法:

  • create ()方法
    包含初始化组件所需的所有操作,并返回T的实例。
  • dependencies()方法
    返回的是一个Initializer<T>的list,这个集合当中包含了当前的Initializer<T>所依赖的其他的Initializer<T>,由此可见该方法的作用是让我们可以控制在程序启动时的组件的初始化顺序。(即需要依赖的Initializer初始化后,才会创建自己)

两个方法执行顺序为dependencies() -> create ()

3、启动StartUp

StartUp为我们提供了两种方式来启动,一种是自动启动,一种是手动调用启动。

1.自动启动

我们只需要在AndroidManifest中对InitializationProvider添加对应声明:

  • 声明其原理中真正使用的ContentProvider类androidx.startup.InitializationProvider
  • authorities要携带${applicationId}来避免与其他App产生冲突
  • 要在这个provider下声明meta-data;
    Startup会去找自己Provider下声明的第一个meta-data所对应的类,来对他进行初始化,如果他声明了他有依赖项,那么他的依赖项也会被初始化。顺序寻找meta-data对应的类并依次初始化。

声明ContentProvider类其基本原理就是依靠ContentProvider的onCreate会在应用初始化时被自动调用;

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.alanmyapplication. CModuleInitializer"
        android:value="androidx.startup" />
    <meta-data
        android:name="com.alan.myapplication.DModuleInitializer"
        android:value="androidx.startup"
        tools:node="remove"/>
</provider>
meta-data标签:
  • name:是Initializer的路径
  • value:必须为androidx.startup才可以被收集到,后续的触发是按照在Manifest中声明的顺序进行的。
依赖链声明
  • 若存在依赖关系C -> B -> A,则只需要声明C即可,因为A和B可以通过C的dependencies()方法链式调用进行初始化。A,B,C的执行顺序为:
C的dependencies() -> 
B的dependencies() -> 
A的dependencies() -> 
A的create () -> 
B的create () -> 
C的create () -> 
  • 若A,B,C之间不存在依赖关系的话,则需要对每一个对应的Initializer进行声明。而A,B,C的执行顺序则与我们的声明顺序保持一致。

需要注意的是:
会使用Map mInitialized来确保每个类只会被初始化一次,而通过临时的initializing确保在依赖关系中不允许循环依赖

2.手动调用启动
(1)、声明中关闭自动初始化

tools:node="remove"这个标签的作用是为了防止在其他引用的三方库中有对相同组件的一个初始化,保证该组件的自动初始化真正的被关闭。

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
     <meta-data
        android:name="com.alan.myapplication.DModuleInitializer"
        android:value="androidx.startup"
        tools:node="remove"/>
</provider>

关闭startup的所有组件的自动初始化,除了在<meta-data>标签上一个个添加之外,还可以统一关闭:

<provider android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup" 
    tools:node="remove"/>
(2)、在代码中进行初始化
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //手动启动初始化
        AppInitializer.getInstance(this).initializeComponent(DModuleInitializer::class.java)
    }
}

三、源代码

AppInitializer类

public final class AppInitializer {
    private static AppInitializer sInstance;

    /**
     * Guards app initialization.
     */
    private static final Object sLock = new Object();

    @NonNull
    final Map<Class<?>, Object> mInitialized;

    @NonNull
    final Context mContext;

    /**
     * Creates an instance of {@link AppInitializer}
     *
     * @param context The application context
     */
    AppInitializer(@NonNull Context context) {
        mContext = context.getApplicationContext();
        mInitialized = new HashMap<>();
    }

    /**
     * @param context The Application {@link Context}
     * @return The instance of {@link AppInitializer} after initialization.
     */
    @NonNull
    @SuppressWarnings("UnusedReturnValue")
    public static AppInitializer getInstance(@NonNull Context context) {
        synchronized (sLock) {
            if (sInstance == null) {
                sInstance = new AppInitializer(context);
            }
            return sInstance;
        }
    }
                ...
}

Map<Class<?>, Object> 用map去存储已经被初始化过的组件。

参考:Jetpack StartUp详解

有关Android Jetpack架构组件(十)— StartUp的更多相关文章

  1. ruby - Ruby 和 Ruby on Rails 中的三层架构 - 2

    我是一名决定学习Ruby和RubyonRails的ASP.NETMVC开发人员。我已经有所了解并在RoR上创建了一个网站。在ASP.NETMVC上开发,我一直使用三层架构:数据层、业务层和UI(或表示)层。尝试在RubyonRails应用程序中使用这种方法,我发现没有关于它的信息(或者也许我只是找不到它?)。也许有人可以建议我如何在RubyonRails上创建或使用三层架构?附言我使用ruby​​1.9.3和RubyonRails3.2.3。 最佳答案 我建议在制作RoR应用程序时遵循RubyonRails(RoR)风格。Rails

  2. ruby-on-rails - Rails 和 MQTT : Subscribe to topic in background at server startup? - 2

    我想在服务器启动时在我的Rails应用程序中订阅一个mqtt主题,并保持订阅始终处于事件状态和运行状态。我正在使用这个mqttgem进行mqtt通信:https://github.com/njh/ruby-mqtt这是我现在拥有的:在application.rb中:config.after_initializedomqttSub=BackgroundMQTT.newmqttSub.runend后台MQTT类:classMQTTSubscriberdefrunThread.newdoMQTT::Client.connect(:host=>'localhost',:port=>1883,)

  3. ruby-on-rails - 具有六边形架构和 DCI 模式的框架和数据库适配器 - 2

    我尝试用Ruby设计一个基于Web的应用程序。我开发了一个简单的核心应用程序,在没有框架和数据库的情况下在六边形架构中实现DCI范例。核心六边形中有小六边形和网络,数据库,日志等适配器。每个六边形都在没有数据库和框架的情况下自行运行。在这种方法中,我如何提供与数据库模型和实体类的关系作为独立于数据库的关系。我想在将来将框架从Rails更改为Sinatra或数据库。事实上,我如何在这个核心Hexagon中实现完全隔离的rails和mongodb的数据库适配器或框架适配器。有什么想法吗? 最佳答案 ROM呢?(Ruby对象映射器)。还有

  4. 设计一个亿级高并发系统架构 - 12306火车票核心场景DDD领域建模 - 2

    “架设一个亿级高并发系统,是多数程序员、架构师的工作目标。许多的技术从业人员甚至有时会降薪去寻找这样的机会。但并不是所有人都有机会主导,甚至参与这样一个系统。今天我们用12306火车票购票这样一个业务场景来做DDD领域建模。”开篇要实现软件设计、软件开发在一个统一的思想、统一的节奏下进行,就应该有一个轻量级的框架对开发过程与代码编写做一定的约束。虽然DDD是一个软件开发的方法,而不是具体的技术或框架,但拥有一个轻量级的框架仍然是必要的,为了开发一个支持DDD的框架,首先需要理解DDD的基本概念和核心的组件。一.什么是领域驱动设计(DDD)首先要知道DDD是一种开发理念,核心是维护一个反应领域概

  5. ruby - 写密集型特征的架构 - 2

    我在当前项目中使用由Oracle数据库和memcached支持的RubyonRails。有一个非常常用的功能,它依赖于单个数据库View作为数据源,并且该数据源内部有其他数据库View和表。这是一个虚拟数据库View,能够从一个地方访问所有内容,而不是物化数据库View。大多数情况下,如果用户正在使用他们希望更新的功能,那么让数据保持最新很重要。从这个View获取数据时,我将安全表内部连接到View(安全表不是View本身的一部分),其中包含一些我们用来在更细粒度级别上控制数据访问的字段。例如,安全表有user_id,prop_1,prop_2列,其中prop_1,prop_2是数据库

  6. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  7. ruby - 模块化、基于组件的 Sinatra 应用程序的架构 - 2

    我正在开发一个包含大约10个不同功能组件的Sinatra应用程序。我们希望能够将这些组件混合并匹配到应用程序的单独实例中,完全从config.yaml文件配置,如下所示:components:-route:'/chunky'component_type:FoodListercomponent_settings:food_type:baconmax_items:400-route:'places/paris'component_type:Mappercomponent_settings:latitude:48.85387273165654longitude:2.340087890625-

  8. 【哈士奇赠书活动 - 20期】-〖从程序员到架构师〗 - 2

    文章目录⭐️赠书活动-《从程序员到架构师》⭐️编辑推荐⭐️作者简介⭐️赠书活动→获奖名单⭐️赠书活动-《从程序员到架构师》内容简介:《从程序员到架构师:大数据量、缓存、高并发、微服务、多团队协同等核心场景实战》分为数据持久化层场景实战、缓存层场景实战、基于常见组件的微服务场景实战、微服务进阶场景实战和开发运维场景实战5个部分。基于对十余个架构搭建与改造项目的经验总结,介绍了大数据量、缓存、高并发、微服务、多团队协同等核心场景下的架构设计常见问题及其通用技术方案,包含冷热分离、查询分离、分表分库、秒杀架构、注册发现、熔断、限流、微服务等具体需求下的技术选型、技术原理、技术应用、技术要点等内容,将

  9. ruby - 如何使用( ruby ) Rack 中间件组件设置 cookie? - 2

    我正在为需要有条件地设置cookie的Rails应用编写Rack中间件组件。我目前正在尝试设置cookie。通过谷歌搜索,这似乎应该可行:classRackAppdefinitialize(app)@app=appenddefcall(env)@status,@headers,@response=@app.call(env)@response.set_cookie("foo",{:value=>"bar",:path=>"/",:expires=>Time.now+24*60*60})[@status,@headers,@response]endend它不会给出错误,但也不会设置coo

  10. Ruby 插件架构 - 2

    我想要一个非常基本的小型基础程序示例,它读入两个插件并注册它们。这两个插件以相同的方式以不冲突的方式挂接到基础程序。就此而言,我对任何编程语言的元编程都很陌生,我不确定从哪里开始。 最佳答案 我已经研究这个问题一段时间了。我尝试了很多不同的方法来做这件事,并征求了很多人的建议。我仍然不确定我所拥有的是否是“正确的方法”,但它运作良好并且很容易做到。在我的例子中,我专门查看配置并引入配置插件,但原理是相同的,即使我的术语特定于cnfiguration。在非常基础的层面上,我有一个配置类,里面什么都没有——它是空的。我还有一个Confi

随机推荐