首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么AndroidTestCase.getContext().getApplicationContext()返回null?

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

Stack Overflow用户
提问于 2011-06-29 06:15:33
回答 2查看 11K关注 0票数 21

更新2/13/2012:接受了一个答案,解释了这个行为是一个错误,并注意到它似乎在模拟器上消失得比v1.6好,这使得它对我们大多数人来说是一个不成问题的问题。解决方法是循环/休眠,直到getContext().getApplicationContext()返回非空。结束更新

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

下面的代码在AndroidTestCase中再现null --演示不需要单例。

首先,为了记录应用程序实例化消息,在正在测试的应用程序中,我定义了MyApp并将其添加到清单中。

代码语言:javascript
运行
复制
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("MYAPP", "this=" + this);
        Log.i("MYAPP", "getAppCtx()=" + getApplicationContext());
    }
}

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

代码语言:javascript
运行
复制
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个仿真器):

代码语言:javascript
运行
复制
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消息块似乎是最相关的,但是单个测试的单个运行的整个LogCat很有趣。安卓启动了4 AndroidRuntimes,上面的部分是从第4位开始的。有趣的是,第三个运行时显示的消息表明它在进程ID 447中实例化了一个不同的MyApp实例:

代码语言:javascript
运行
复制
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中的子进程。不过,问题是:为什么安卓让AndroidTestCase在上下文正确连接到应用程序实例之前运行?

解决方案:如果我首先调用getContext().getSharedPreferences("anyname", Context.MODE_PRIVATE).edit().clear().commit();,那么我的一个测试在大多数情况下似乎都避免了无效,所以我将这样做。

BTW:如果答案是“这是一个安卓bug,你为什么不把它归档;见鬼,你为什么不修复它?”那我愿意两者兼得。我还没有采取的步骤,成为一个错误的申报者或贡献者--也许这是一个好时机。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-11-29 08:57:58

工具运行在一个独立于主应用程序线程的线程中,这样它就可以在不阻塞或中断(或被主线程阻塞)的情况下执行。如果需要与主线程同步,请使用例如:Instrumentation.waitForIdleSync()

特别是,Application对象以及所有其他顶级类(如Activity )都由主线程初始化。您的工具线程正在运行,而这些线程正在初始化。如果您正在接触这些对象中的任何一个,并且没有实现您自己的线程安全度量,那么您可能应该在主线程上运行这样的代码,比如via:Instrumentation.runOnMainSync(java.lang.Runnable)

票数 12
EN

Stack Overflow用户

发布于 2013-11-27 08:27:01

正如问题和Dianne的回答(@hackbod)中所提到的,该工具运行在一个单独的线程上。AndroidTestCase要么有实现缺陷(缺少同步),要么没有正确记录。不幸的是,无法从这个特定的测试用例类调用Instrumentation.waitForIdleSync(),因为它无法访问该测试用例。

此子类可用于添加轮询getApplicationContext()的同步,直到返回非空值为止:

代码语言:javascript
运行
复制
public class MyAndroidTestCase extends AndroidTestCase {

    @Override
    public void setContext(Context context) {
        super.setContext(context);

        long endTime = SystemClock.elapsedRealtime() + TimeUnit.SECONDS.toMillis(2);

        while (null == context.getApplicationContext()) {

            if (SystemClock.elapsedRealtime() >= endTime) {
                fail();
            }

            SystemClock.sleep(16);
        }
    }
}

轮询和睡眠时间是基于经验的,必要时可以进行调整。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6516441

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档