android - 使用 Picasso/Glide 在 androidTest 中加载资产图像失败
问题描述
简短的问题
如何使用 Picasso 或 Glide 在 androidTest 中成功加载资产图像?
长解释
在 UI 测试中,我想调用我的应用程序将图像加载到 ImageView 中,并对其应用某些操作(例如裁剪、居中等)。为了执行图像加载、裁剪等,我使用毕加索。在 UI 测试中,我想验证通过 UI 触发加载后,附加到 ImageView 的位图正是我打算使用 Picasso 的,即我正在连接 Picasso 并正确应用 Picasso DSL。
为此,我启动了一个 androidTest,提供要验证的图像作为src/androidTest/resources/assets
. 我将file:///android_asset/$filename
图像文件名注入到我的应用程序中,让毕加索使用AssetManager
来加载图像。
尽管应用程序使用存储在设备/模拟器中的常规图像,但测试无法加载资产图像——AssetManager 报告 FileNotFoundException:
Caused by: java.io.FileNotFoundException: australia.jpg
at android.content.res.AssetManager.nativeOpenAsset(Native Method)
at android.content.res.AssetManager.open(AssetManager.java:744)
at android.content.res.AssetManager.open(AssetManager.java:721)
at com.squareup.picasso.AssetRequestHandler.load(AssetRequestHandler.java:45)
at com.squareup.picasso.BitmapHunter.hunt(BitmapHunter.java:206)
at com.squareup.picasso.RequestCreator.get(RequestCreator.java:396)
顺便说一句,使用 Glide 会导致相同的 FileNotFoundException。
我设置了一个小型仪器测试AssetTest.kt
来分析和展示问题。请点击链接在 GitHub 上完整查看。
@RunWith(AndroidJUnit4::class)
@LargeTest
class AssetTest {
private lateinit var activityScenario: ActivityScenario<TasksActivity>
private val filename = "victoria-priessnitz-KBIui3I44SY-unsplash.jpg"
// NOTE: the tests show the same behavior regardless of whether the activity is launched or not
private val enableActivityUsage = true
@Before
fun launchActivity() {
if (enableActivityUsage) {
activityScenario = ActivityScenario.launch(TasksActivity::class.java)
}
}
@After
fun shutdownActivity() {
if (enableActivityUsage) {
activityScenario.close()
}
}
@Test
fun loadingAssetImageWithInstrumentationContextSucceeds() {
val ctx = InstrumentationRegistry.getInstrumentation().context
val inputStream = ctx.resources.assets.open(filename)
val bytes = inputStream.readBytes()
assertEquals(1903567, bytes.size)
}
@Test(expected = FileNotFoundException::class)
fun loadingAssetImageWithInstrumentationTargetContextFails() {
val targetCtx = InstrumentationRegistry.getInstrumentation().targetContext
val inputStream = targetCtx.resources.assets.open(filename)
inputStream.readBytes()
}
@Test(expected = FileNotFoundException::class)
fun loadingAssetImageWithInstrumentationTargetContextsApplicationContextFails() {
val targetCtx = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
val inputStream = targetCtx.resources.assets.open(filename)
inputStream.readBytes()
}
// NOTE: when not using the test orchestrator to separate tests, this tests may fail
// because the execution shows the same behavior as with targetContext
@Test(expected = NullPointerException::class)
fun picassoFailsWithNullPointerExceptionWhenUsingContext() {
val uri = Uri.parse("file:///android_asset/$filename")
// synchronous image loading cannot be performed in main thread
val context = InstrumentationRegistry.getInstrumentation().context
try {
Picasso.with(context)
.load(uri)
.get()
} catch (e: Exception) {
// sometimes Android Studio does not display callstacks in debugger
println(e.message)
throw e
}
}
@Test(expected = FileNotFoundException::class)
fun picassoFailsWithExceptionInAssetManagerOpenWhenUsingTargetContext() {
val uri = Uri.parse("file:///android_asset/$filename")
val context = InstrumentationRegistry.getInstrumentation().targetContext
try {
Picasso.with(context)
.load(uri)
.get()
} catch (e: Exception) {
// sometimes Android Studio does not display callstacks in debugger
println(e.message)
throw e
}
}
}
我观察到以下情况:
测试
loadingAssetImageWithInstrumentationContextSucceeds
可以加载资产而loadingAssetImageWithInstrumentationTargetContextFails
不能加载资产——无论是否在 ActivityScenario 中运行。我得出结论,资产存储在可从上下文访问的 APK 中,而不是存储在可从 targetContext 访问的 APK 中。不幸的是,我无法查看引用的 APK,因为设备文件资源管理器无法在 /data/app 中打开它们。测试
picassoFailsWithNullPointerExceptionWhenUsingContext
表明 usingInstrumentationRegistry.getInstrumentation().context
会让 Picasso 由于空指针异常而失败。我可以在 Picasso builder internals 中看到 Picasso 没有直接使用提供的上下文,而是调用ContextImpl::getApplicationContext
-- wheremPackageInfo.getApplication
isnull
:
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
测试
picassoFailsWithExceptionInAssetManagerOpenWhenUsingTargetContext
表明 usingInstrumentationRegistry.getInstrumentation().targetContext
不会导致空指针异常,但会导致上面提到的FileNotFoundException
a 。AssetManager
我得出结论,附加到帽子的 APKtargetContext.applicationContext
不包含资产。我对启动活动时创建的所有不同上下文感到困惑。
但回到问题上来:
当然还有其他(更昂贵的)方法来测试我的场景,但我认为应该有一种工作方法可以在仪器测试中使用 Picasso 加载资产。
解决方案
推荐阅读
- c++ - 为什么断点显示我的数组的第二个值是一个很大的数字?额外的学分工作
- python - pm2 在 scripts.sh 上因太多不稳定的重启而崩溃
- c++ - 关于获取事件日志使用 EvtOpenSession (为什么用任何文本填充域参数都可以成功?)
- javascript - 无论如何我可以使用 Yup.js 来验证排序数组吗?
- modelica - 查找我在dymola中构建的模型中的函数用法
- c++ - 我的 sumASCII 函数中的这个偏移量是多少?
- ios - 坚持通过 FetchedResultsController、Diffable Data Sources 和 CollectionView 添加搜索
- php - 图像分页 - 如何在一个查询中获取所有参数
- jasper-reports - JasperReports 和 Microsoft Word - 对齐文本对齐
- php - 您如何为不同的用户角色设置权限/视图/路由?