最近在研究APP的启动优化,也是发现了Jetpack中的
App Startup库,可以进行SDK的初始化操作,于是便是学习了,特此记录
首先,先是讲解了关于SDK的初始化方式,像我们一般使用到百度地图,或者某些开源库的时候,需要我们进行初始化操作
而他们文档给出的大多数为以下方式:
自定义一个Application,在Application对应的生命周期
OnCreate()方法中进行初始化操作
这样一样,一旦项目接入的SDK过多,Application里的初始化逻辑也是多了起来,且代码层面,每次都要去加代码,也是十分繁琐.
针对代码繁琐的问题,有的开源库作者找到了一种比较取巧的方法,那就是通过四大组件中的ContentProvider进行初始化操作
具体怎么做呢?
首先,创建一个ContentProvider类:
package site.starsone.abdemo
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import com.blankj.utilcode.util.LogUtils
class MyContentProvider: ContentProvider() {
override fun insert(uri: Uri, values: ContentValues?): Uri? {
TODO("Not yet implemented")
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
TODO("Not yet implemented")
}
override fun onCreate(): Boolean {
context?.let {
LogUtils.d("初始化了...")
}
return true
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
TODO("Not yet implemented")
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
TODO("Not yet implemented")
}
override fun getType(uri: Uri): String? {
TODO("Not yet implemented")
}
}
PS:上面,因为我们只是为了初始化,所以只给了onCreate()其他的方法直接留空不写即可
之后,还需要在AndroidManifest.xml文件中进行声明
<application ...>
<provider
android:name=".MyContentProvider"
android:authorities="${applicationId}.myProvider"
android:exported="false" />
</application>
authorities在这里并没有固定的要求,填写什么值都是可以的,但必须保证这个值在整个手机上是唯一的,所以通常会使用${applicationId}作为前缀,以防止和其他应用程序冲突。
运行结果如下所示:

关于运行的流程,如下图所示

一个应用程序的执行顺序是这个样子的。首先调用
Application的attachBaseContext()方法,然后调用
ContentProvider的onCreate()方法,接下来调用Application的onCreate()方法。
我们使用上述方法治好,在提供库给别的开发者用的时候,别的开发者就不用再去多写一步初始化的操作了,简化了其他开发者使用库的操作流程。
上面的方法虽然是比较巧妙,但是ContentProvider会增加许多额外的耗时

一个空白的ContentProvider会多出2ms的加载时间,实际项目复杂多一多,加载速度岂不是直接拖慢?
这有什么解决方法呢?
当然有,官方也是发现了开发者通过ContentProvider来取巧进行初始化的步骤,于是进行了进一步的封装,于是就是今天所讲的App Startup:
它可以将所有用于初始化的ContentProvider合并成一个,从而使App的启动速度变得更快。
话说是不是官方已经变相承认了这种取巧方式哈哈 ?
使用之前,老惯例了,引入依赖
implementation 'androidx.startup:startup-runtime:1.0.0'
这里需要注意: 如果使用最新的1.1.1版本,要求最低的SDK版本(compileSdk)要求为31
关于已发布版本,可以点击此链接进行查看jetpack各库版本
//Java中这个Unit是Void类型
class SdkAInitializer : Initializer<Unit> {
override fun create(context: Context) {
LogUtils.d("Sdk A 初始化")
}
//默认实现方法返回MutableList,将其改为List
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
在AndroidManifest.xml文件中声明:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="site.starsone.abdemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AbDemo">
...
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.site.starsone.abdemo.SdkAInitializer"
android:value="androidx.startup" />
</provider>
</application>
</manifest>
注意点:
tools:node需要application标签上声明tools的命名空间meta-data里的android:name要输入class全类名(因为App Startup本质上也是通过反射来找到你的这个类)效果如下图所示:

我们再添加一个SdkBInitializer的初始化类:
class SdkBInitializer : Initializer<Unit> {
override fun create(context: Context) {
LogUtils.d("Sdk B 初始化")
}
//默认实现方法返回MutableList,将其改为List
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
代码实际上与SdkAInitializer一样,上面基本使用没有扩展讲解,这里先讲解一下
dependencies()方法是用来说明当前的SdkBInitializer是否需要前置的某个依赖初始化完毕才执行
我们添加了SdkBInitializer,和之前一样,也得声明
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="site.starsone.abdemo.SdkBInitializer"
android:value="androidx.startup" />
<meta-data
android:name="site.starsone.abdemo.SdkAInitializer"
android:value="androidx.startup" />
</provider>
这里就声明了A和B两个SDK的初始化类,本来我以为声明的顺序会影响初始化的顺序,实际上并不影响
以下截图就是上述代码运行的效果:

于是,我们现在要将上面的顺序变更为以下:
SDKA的初始化,需要SDKB初始化完之后才能进行,于是我们取修改SdkAInitializer的代码
class SdkAInitializer : Initializer<Unit> {
override fun create(context: Context) {
LogUtils.d("Sdk A 初始化")
}
//默认实现方法返回MutableList,将其改为List
override fun dependencies(): List<Class<out Initializer<*>>> {
//这里回传依赖项回去
return listOf(SdkBInitializer::class.java)
}
}
运行结果:

PS: 如果项目中的只有两个依赖的初始化,且这两个初始化有先后顺序,在
AndroidManifest.xml文件可以只写一个
如上面的例子,由于SDKA是要在SDKB初始化后才初始化,所以只用声明SDKA即可(因为代码里已经返回了对应的List,会被找到,无需声明)
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="site.starsone.abdemo.SdkAInitializer"
android:value="androidx.startup" />
</provider>
上面说明的是自动初始化,考虑到还会有手动初始化的需求
首先,我们得先配置AndroidManifest.xml文件中的属性,取消初始化
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
tools:node="remove"
android:name="site.starsone.abdemo.SdkAInitializer"
android:value="androidx.startup" />
</provider>
tools:node="remove"而不是直接移除这个标签,是为了确保清单合并工具能够移除所有合并文件的这个标签。在meta-data标签中添加
tools:node="remove",是禁止单个组件的自动初始化如果想要禁止所有组件,则是在provider标签上加上属性
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove">
<meta-data
android:name="site.starsone.abdemo.SdkAInitializer"
android:value="androidx.startup" />
</provider>
PS: 实际上测试发现,如果你不想你的组件被自动初始化,直接删除对应的
meta-data标签即可
至于初始化,则是在代码中进行即可,具体的时机由开发者来定,比如说在点击某个按钮之后
只需要传对应初始化的类即可,代码如下:
AppInitializer.getInstance(this).initializeComponent(SdkBInitializer::class.java)
上述代码是在Activity中进行的,所以使用的this
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po