gpt4 book ai didi

android - 为什么AndroidTestCase.getContext().getApplicationContext() 返回null?

转载 作者:IT老高 更新时间:2023-10-28 23:12:47 40 4
gpt4 key购买 nike

2012 年 2 月 13 日更新:接受了答案,解释说这种行为是一个错误,并指出它似乎在比 v 1.6 更好的模拟器上消失了,这对我们大多数人来说都不是问题。解决方法是简单地循环/休眠,直到 getContext().getApplicationContext() 返回非空值。结束更新

根据 android.app.Application javadoc,我定义了一个单例(称为数据库),我的所有 Activity 都可以访问它以获取状态和持久数据,并且 Database.getDatabase(Context) 通过 Context.getApplicationContext() 获取应用程序上下文。当 Activity 将自身传递给 getDatabase(Context) 时,此设置就像宣传的那样工作,但是当我从 AndroidTestCase 运行单元测试时,getApplicationContext() 调用通常返回 null,尽管测试时间越长,它返回非 null 的频率越高值(value)。

以下代码在 AndroidTestCase 中重现了 null —— 演示不需要单例。

首先,为了记录应用实例化消息,我在被测应用中定义了 MyApp 并将其添加到 list 中。

public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Log.i("MYAPP", "this=" + this);
Log.i("MYAPP", "getAppCtx()=" + getApplicationContext());
}
}

接下来,我定义了一个测试用例报告 AndroidTestCase.getContext() 4 次,由一些 sleep 和 getSharedPreferences() 调用分隔:

public class DatabaseTest extends AndroidTestCase {
public void test_exploreContext() {
exploreContexts("XPLORE1");
getContext().getSharedPreferences("foo", Context.MODE_PRIVATE);
exploreContexts("XPLORE2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exploreContexts("XPLORE3");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exploreContexts("XPLORE4");
}
public void exploreContexts(String tag) {
Context testContext = getContext();
Log.i(tag, "testCtx=" + testContext +
" pkg=" + testContext.getApplicationInfo().packageName);
Log.i(tag, "testContext.getAppCtx()=" + testContext.getApplicationContext());
try {
Context appContext = testContext.createPackageContext("com.foo.android", 0);
ApplicationInfo appInfo = appContext.getApplicationInfo();
Log.i(tag, "appContext=" + appContext +
" pkg=" + appContext.getApplicationInfo().packageName);
Log.i(tag, "appContext.getAppCtx()=" + appContext.getApplicationContext());
} catch (NameNotFoundException e) {
Log.i(tag, "Can't get app context.");
}
}
}

这是生成的 logCat 的一部分(SDK11 WinXP 上的 1.6 模拟器,通过 Eclipse):

INFO/TestRunner(465): started: test_exploreContext(test.foo.android.DatabaseTest)
INFO/XPLORE1(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE1(465): testContext.getAppCtx()=null
INFO/XPLORE1(465): appContext=android.app.ApplicationContext@437801e8 pkg=com.foo.android
INFO/XPLORE1(465): appContext.getAppCtx()=null
INFO/XPLORE2(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE2(465): testContext.getAppCtx()=null
INFO/XPLORE2(465): appContext=android.app.ApplicationContext@43782820 pkg=com.foo.android
INFO/XPLORE2(465): appContext.getAppCtx()=null
INFO/MYAPP(465): this=com.foo.android.MyApplication@43783830
INFO/MYAPP(465): getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE3(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE3(465): testContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE3(465): appContext=android.app.ApplicationContext@43784768 pkg=com.foo.android
INFO/XPLORE3(465): appContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE4(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE4(465): testContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE4(465): appContext=android.app.ApplicationContext@43785778 pkg=com.foo.android
INFO/XPLORE4(465): appContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/TestRunner(465): finished: test_exploreContext(test.foo.android.DatabaseTest)

请注意,getApplicationContext() 有一段时间返回 null,然后开始返回 MyApp 的一个实例。我无法在此测试的不同运行中获得完全相同的结果(这就是我最终进行 4 次迭代、休眠以及调用 getSharedPreferences() 以尝试使应用程序存在的方式)。

上面的 LogCat 消息 block 似乎最相关,但是单个测试的单个运行的整个 LogCat 很有趣。 Android 启动了 4 个 AndroidRuntimes;上面的 block 是从 4 开始的。有趣的是,第三个运行时显示的消息表明它在进程 ID 447 中实例化了一个不同的 MyApp 实例:

INFO/TestRunner(447): started: test_exploreContext(test.foo.android.DatabaseTest)
INFO/MYAPP(447): this=com.foo.android.MyApplication@437809b0
INFO/MYAPP(447): getAppCtx()=com.foo.android.MyApplication@437809b0
INFO/TestRunner(447): finished: test_exploreContext(test.foo.android.DatabaseTest)

我假设 TestRunner(447) 消息来自父测试线程,报告进程 465 中的子进程。不过,问题是:为什么 Android 让 AndroidTestCase 在其上下文正确连接到应用程序实例之前运行?

解决方法:如果我调用 getContext().getSharedPreferences("anyname", Context.MODE_PRIVATE).edit().clear ().commit(); 首先,所以我会这样做。

顺便说一句:如果答案是“这是一个 Android 错误,你为什么不提交它;哎呀,你为什么不修复它?”那么我愿意两者都做。我还没有迈出成为错误提交者或贡献者的一步——也许现在是个好时机。

最佳答案

Instrumentation 在与主应用线程不同的线程中运行,因此它可以在不阻塞或中断(或被阻塞)主线程的情况下执行。如果需要与主线程同步,例如:Instrumentation.waitForIdleSync()

特别是,Application 对象以及所有其他顶级类(如 Activity)都由主线程初始化。您的检测线程在初始化的同时运行。如果您正在接触这些对象中的任何一个并且没有实现您自己的线程安全措施,您可能应该在主线程上运行这样的代码,例如通过:Instrumentation.runOnMainSync(java.lang.Runnable)

关于android - 为什么AndroidTestCase.getContext().getApplicationContext() 返回null?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6516441/

40 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com