【问题标题】:Accessing DB with different Contexts使用不同的上下文访问数据库
【发布时间】:2016-12-07 14:30:42
【问题描述】:

我正在摆弄 Android 中的 db 访问,以了解事情是如何处理的。

我的 MainActivity.java 文件中有以下代码:

Log.v("test db acc", "start getApplicationContext test");
FileDbHelper dbHelper1 = new FileDbHelper(getApplicationContext());
SQLiteDatabase db1 = dbHelper1.getWritableDatabase();
Log.v("test db acc", "success getApplicationContext test");

Log.v("test db acc", "start getContext test");
FileProvider provider = new FileProvider();
provider.testDbAccess();
Log.v("test db acc", "success getContext test");

这是 provider.testDbAccess() 函数的定义:

FileDbHelper dbHelper2 = new FileDbHelper(getContext());
SQLiteDatabase db2 = dbHelper2.getWritableDatabase();

第一次尝试访问数据库成功,没有任何错误。如果DB不存在就创建,创建db1对象后我可以查询和写入数据。

当我尝试使用Context 获取可写数据库并返回getContext() 时,它只是以NullPointerException 失败。它甚至没有开始创建数据库。即使我删除了getApplicationContext() 测试行的代码,也会出现症状。

这里的问题是,我正在尝试编写代码以从FileProvider 的数据库中获取查询,但我无法从该文件访问getApplicationContext()(它只会引发编译器错误)。

如果我在 MainActivity.java 文件中完成所有过程,我没有错误(我知道这不好,我这样做只是为了测试目的)。

我的问题是:

  1. 我应该如何以及在什么情况下创建我的数据库?
  2. 为什么我不能使用来自FileProvidergetApplicationContext()
  3. 我可以从创建数据库的另一个上下文访问数据库吗?据我所知,SQLite 数据库只是在应用程序自己的文件夹中的 android 文件系统中创建的一个文件。不同的Contexts 有什么区别?
  4. 为什么我不能在FileProvider.java 中创建一个由getContext() 返回的Context 的数据库?

--编辑--

这是错误的logcat:

08-02 16:03:55.628 7614-7614/com.permasse.apps.file.android E/AndroidRuntime: FATAL EXCEPTION: main
            java.lang.RuntimeException: Unable to resume activity {com.permasse.apps.file.android/com.permasse.apps.file.android.MainActivity}: java.lang.NullPointerException
                at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2575)
                at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2603)
                at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2089)
                at android.app.ActivityThread.access$600(ActivityThread.java:130)
                at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
                at android.os.Handler.dispatchMessage(Handler.java:99)
                at android.os.Looper.loop(Looper.java:137)
                at android.app.ActivityThread.main(ActivityThread.java:4745)
                at java.lang.reflect.Method.invokeNative(Native Method)
                at java.lang.reflect.Method.invoke(Method.java:511)
                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
                at dalvik.system.NativeStart.main(Native Method)
             Caused by: java.lang.NullPointerException
                at com.permasse.apps.file.android.FileProvider.testDbAccess(FileProvider.java:120)
                at com.permasse.apps.file.android.MainActivity.onResume(MainActivity.java:32)
                at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1184)
                at android.app.Activity.performResume(Activity.java:5082)
                at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2565)
                at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2603) 
                at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2089) 
                at android.app.ActivityThread.access$600(ActivityThread.java:130) 
                at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195) 
                at android.os.Handler.dispatchMessage(Handler.java:99) 
                at android.os.Looper.loop(Looper.java:137) 
                at android.app.ActivityThread.main(ActivityThread.java:4745) 
                at java.lang.reflect.Method.invokeNative(Native Method) 
                at java.lang.reflect.Method.invoke(Method.java:511) 
                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
                at dalvik.system.NativeStart.main(Native Method) 

这里是 FileProvider.java 的相关部分。原始代码包括查询生成器、uri 匹配器等。我简化了代码以隔离问题。

public class FileProvider extends ContentProvider {

    private FileDbHelper dbHelper;

    @Override
    public boolean onCreate() {
        dbHelper = new FileDbHelper(getContext());
        return true;
    }

    public void testDbAccess() {

        SQLiteDatabase db = dbHelper.getWritableDatabase(); //Line no 120

    }

}

和 MainActivity.java

public class MainActivity extends AppCompatActivity{

    @Override
    protected void onResume() {
        super.onResume();

        FileProvider provider = new FileProvider();

        provider.testDbAccess(); //Line no 32

    }
}

【问题讨论】:

  • 我不确定您要做什么,但是您可以将context 传递给FileProvider...new FileProvider(getApplicationContext()); 并在方法中使用context 而不是getContext(),您可以从继承具有它们的类的类中调用 getContext() 或 getApplicationContext() ,例如Activity 否则您必须传递它
  • 我基本上试图做的是创建一个提供程序,稍后将绑定到加载程序。我喜欢在提供程序类中创建一个数据库对象,它根本不起作用(使用 getContext 上下文)。在具有 getApplicationContext 上下文的 MainActivity 中,相同的代码可以正常工作。这对我来说很奇怪,因为在这个应用程序中,它似乎工作:github.com/udacity/Sunshine-Version-2/blob/sunshine_master/app/… line no: 142
  • 你的FileProvider 上课extends ContentProvider 吗?
  • 是的,确实如此。我什至实现了查询方法。
  • 好的,现在对于空指针异常,您可以发布logcat吗?

标签: android nullpointerexception android-sqlite android-context


【解决方案1】:

虽然我不确定为什么会这样,但我解决了这个问题。

在我的FileProvider 类(扩展ContentProvider)中,如果我尝试在除onCreate() 方法之外的其他任何地方使用getContext(),我会得到一个空上下文。这就是为什么在我的testDbAccess() 方法上获取数据库引用失败的原因。

我所做的是在我的FileProvider 类中声明了一个静态FileDbHelper(它扩展了SQLiteOpenHelper)。然后在onCreate() 方法中,我创建了具有适当上下文的FileDbHelper 类。因为它是静态的,所以我现在可以稍后在同一个类的任何地方使用这个对象。现在看起来有点像这样:

public class FileProvider extends ContentProvider {

    // Create static FileDbHelper
    private static FileDbHelper dbHelper;

    @Override
    public boolean onCreate() {

        // We can only access the context from onCreate() function, so we 
        // instantiate it here to use later on. 
        dbHelper = new FileDbHelper(this.getContext().getApplicationContext);
        // Important explanation about context in bottom of the answer!!


        return true;
    }

    // Then I can use it like this: 
    public void testDbAccess() {

        // dbHelper was staticly declared within class and instantiated already
        SQLiteDatabase db = dbHelper.getWritableDatabase(); 

        // Then do whatever you want to do with the code

    }
}

这也回答了我的问题:

我应该如何以及在什么情况下创建我的数据库?

  • 无论您在哪个上下文中创建数据库,只要您能获得适当的上下文,您就可以访问它。

为什么我不能使用 FileProvider 中的getApplicationContext()

  • 事实证明我可以。正确的做法是getContext().getApplicationContext(),但您不必这样做来获取对数据库的引用。

我可以从创建数据库的另一个上下文访问数据库吗?

  • 是的,您可以在getApplicationContext() 中创建数据库并在getContext() 中访问它

为什么我不能在 FileProvider.java 文件中创建带有 getContext() 上下文的数据库?

  • 实际上可以,问题基本上是由于我编写的新测试函数无法访问上下文本身造成的。不知道为什么,但情况就是这样:)

希望以后对其他人有所帮助。

更新

关于静态对象和内存泄漏的重要信息。

m0skit0 警告我有关由持有上下文引用的静态对象引起的内存泄漏。详细信息可以找到here。简而言之,它说

如果您打算保留需要上下文的长寿命对象, 记住应用程序对象。您可以通过拨打电话轻松获取 Context.getApplicationContext() 或 Activity.getApplication()。

所以要小心那个。代码 sn-p 相应更新。这让未来的我免于很多头痛:)

【讨论】:

  • 这看起来是一个不错的解决方案(事实上,我们一直使用这种方法,直到它在我们的脸上爆炸)但不幸的是它是错误的。永远不要持有对Context 的静态引用。这将泄漏Contexts(更多细节here)。
  • 我所持有的静态不是对Context 的引用,而是对扩展SQLiteOpenHelper 类的对象的引用。是的,它获取上下文作为参数,但它本身不是上下文。还是错了吗? -- 对不起,我现在正在阅读您引用的页面。
  • 哦,好吧,那我误会你了。这句话“我用适当的上下文创建了FileDbHelper 类”让我很困惑。请注意,任何还具有对 Context 的内部引用的类都会导致同样的问题。
  • 我不明白Context.getApplicationContext() 是如何工作的。这是一个静态调用。无论如何,当你说“FileProvider 类将是我无论如何都要访问它的唯一点”时,不要相信自己。。这在未来可能不会成立,你可能会忘记这个讨论:)
  • 既然你这么说,挑战接受了,我打赌我永远不会忘记它:) 我会为我未来的自己写一个大而胖的提醒评论:) 谢谢你提供的所有信息。答案现在已根据您的输入进行更新,并准备在... 19 小时内自我接受 :)
【解决方案2】:

即使您已经回答了您的问题,我也想补充一些信息。

getContext()getApplicationContext() 方法可以产生NullPointerException,有时这些类无法定义自己的 Context 是什么,在这种情况下,APP 会崩溃。

我认为将上下文显式传递给ContentProvider 是个好主意。这样您就可以确保您的应用不会崩溃。

例如:

public class FileProvider extends ContentProvider {
Context context = null;
public FileProvider(Context context){
 this.context = context;
}

// Create static FileDbHelper
private static FileDbHelper dbHelper;

@Override
public boolean onCreate() {

    if(this.context != null{
       dbHelper = new FileDbHelper(this.context);

    }else{
     // There is an error, notify the user and do something about it
     return false;
    }

    return true;
}

// Then I can use it like this: 
public void testDbAccess() {

    // dbHelper was staticly declared within class and instantiated already
    SQLiteDatabase db = dbHelper.getWritableDatabase(); 

    // Then do whatever you want to do with the code

}

}

你打电话只是改变 FileProvider provider = new FileProvider();

FileProvider provider = new FileProvider(this); 活动

FileProvider provider = new FileProvider(getContext()); 用于片段(女巫总是有上下文)

对于其他问题:数据库类在哪个上下文中工作没有区别。上下文主要是授予数据库助手访问设备的权限。

【讨论】:

  • 感谢您的意见。如果我不知道如何纠正这个问题,这就是我接下来要做的事情。我坚持纠正这个的原因是我在指南中看到了这个,我知道它有效。我只是想弄清楚我的代码出了什么问题:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-30
  • 1970-01-01
相关资源
最近更新 更多