使用 AndroidAnnotations 和 ORMLite 获取 NullPointerException

Getting NullPointerException with AndroidAnnotations & ORMLite

我正在将 AndroidAnnotations 和 SQLite 与 ORMLite 一起使用,并且正在尝试启动数据库 运行。几天前,我能够创建 table 并进行 Contact 对象的测试插入。 但是,我做了一些更改,然后它停止工作了 - 不幸的是我无法恢复我的更改,现在我被卡住了,无法再工作了。

每当我启动应用程序时,我都会收到此错误:

02-12 23:09:39.931 11766-11766/net.gazeapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: net.gazeapp, PID: 11766
java.lang.RuntimeException: Unable to start activity ComponentInfo{net.gazeapp/net.gazeapp.MainActivity_}: java.lang.NullPointerException: Attempt to invoke virtual method 'int net.gazeapp.data.ContactDao.create(java.lang.Object)' on a null object reference
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
 at android.app.ActivityThread.-wrap11(ActivityThread.java)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
 at android.os.Handler.dispatchMessage(Handler.java:102)
 at android.os.Looper.loop(Looper.java:148)
 at android.app.ActivityThread.main(ActivityThread.java:5417)
 at java.lang.reflect.Method.invoke(Native Method)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int net.gazeapp.data.ContactDao.create(java.lang.Object)' on a null object reference
 at net.gazeapp.service.ContactService.addContact(ContactService.java:55)
 at net.gazeapp.MainActivity.testNewORM(MainActivity.java:171)
 at net.gazeapp.MainActivity.createView(MainActivity.java:148)
 at net.gazeapp.MainActivity_.onViewChanged(MainActivity_.java:111)
 at org.androidannotations.api.view.OnViewChangedNotifier.notifyViewChanged(OnViewChangedNotifier.java:41)
 at net.gazeapp.MainActivity_.setContentView(MainActivity_.java:57)
 at net.gazeapp.MainActivity_.onCreate(MainActivity_.java:45)
 at android.app.Activity.performCreate(Activity.java:6251)
 at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) 
 at android.app.ActivityThread.-wrap11(ActivityThread.java) 
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 
 at android.os.Handler.dispatchMessage(Handler.java:102) 
 at android.os.Looper.loop(Looper.java:148) 
 at android.app.ActivityThread.main(ActivityThread.java:5417) 
 at java.lang.reflect.Method.invoke(Native Method) 
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

这是我的 MainActivity,我在其中进行 ORM 测试(在 testNewORM() 方法中):

@EActivity(R.layout.activity_main_viewpagertab)
@OptionsMenu(R.menu.menu_main)
public class MainActivity extends BaseActivity implements ObservableScrollViewCallbacks {

    private final String TAG = getClass().getSimpleName();

    private int mBaseTranslationY;
    private NavigationAdapter mPagerAdapter;
    private Contact mContact;

    private static String[] tabTitles = null;

    @App
    GazeApplication application;

    @ViewById(R.id.header)
    View mHeaderView;

    @ViewById(R.id.toolbar)
    View mToolbarView;

    @ViewById(R.id.pager)
    ViewPager mPager;

    @ViewById(R.id.fab)
    FloatingActionButton fab;

    @ViewById(R.id.adview)
    MoPubView mAdView;

    @Bean
    ContactService contactService;

    @AfterViews
    void createView() {
        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));

        Tools.readJsonFile(this, "fetishes.json");

        // TAB TITLES: RECENT, ALL, MY MEDIA
        tabTitles = new String[]{getString(R.string.recent), getString(R.string.all), getString(R.string.my_media)};

        ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));

        mPagerAdapter = new NavigationAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);

        SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
        slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);
        slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.colorAccent));
        slidingTabLayout.setDistributeEvenly(true);
        slidingTabLayout.setViewPager(mPager);

        // When the page is selected, other fragments' scrollY should be adjusted
        // according to the toolbar status(shown/hidden)
        slidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int i, float v, int i2) {
            }

            @Override
            public void onPageSelected(int i) {
                propagateToolbarState(toolbarIsShown());
            }

            @Override
            public void onPageScrollStateChanged(int i) {
            }
        });

        propagateToolbarState(toolbarIsShown());

        displayAdBanner();

        // TESTING ORMAPPER
        // TESTING ORMAPPER
        testNewORM();
    }

    void testNewORM() {
        java.util.Date date = new java.util.Date();
        Timestamp timeNow = new Timestamp(date.getTime());

        Timestamp birthdateTimestamp = new Timestamp(date.getTime());
        Date birthdate = new Date();

        try {
            DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            birthdate = dateFormat.parse("04/07/1980");
            long time = birthdate.getTime();
            birthdateTimestamp = new Timestamp(time);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Contact contact = new Contact("Dominik Erbsland");
        contact.setBirthdate(birthdate);

        try {
            mContact = contactService.addContact(contact);
        } catch (ItemNotFoundException | SQLException e) {
            Log.e(TAG, e.getLocalizedMessage());
            e.printStackTrace();
        }
    }

...
}

这里另一个使用了 classes:

@EBean(scope = EBean.Scope.Singleton)
public class ContactService {

    private static final String TAG = ContactService.class.getSimpleName();

    @RootContext
    Context ctx;

    @OrmLiteDao(helper = DatabaseHelper.class)
    ContactDao mContactDao;

    public Contact getContact(int contactId) throws ItemNotFoundException, SQLException {
        Contact contact = mContactDao.queryForId(contactId);
        if (contact == null) {
            Log.e(TAG, "Contact not found in database");
            throw new ItemNotFoundException();
        }

        return contact;
    }

    public List<Contact> getContacts() throws ItemNotFoundException, SQLException {
        List<Contact> contact = mContactDao.queryForAll();
        if (contact == null) {
            Log.e(TAG, "Contacts not found in database");
            throw new ItemNotFoundException();
        }

        return contact;
    }

    public Contact addContact(Contact contact) throws SQLException {
        int rowsAffected = 0;
        try {
            rowsAffected = mContactDao.create(contact);
        } catch (SQLException e) {
            Log.e(TAG, e.getLocalizedMessage());
            e.printStackTrace();
        }

        Log.d(TAG, "New Contact ID: " + contact.getId());

        return contact;
    }

    public void testOutput() {
        Log.d(TAG, "THIS IS A TEST OUTPUT");
    }

}

这里是我的数据库助手:

public class DatabaseHelper extends OrmLiteSqliteOpenHelper {

    private static final String DATABASE_NAME = "Gaze.db";
    private static final int DATABASE_VERSION = 1;
    private final Context context;

    // the DAO object we use to access the Person table
    private Dao<Contact, Integer> contactDao = null;
    private Dao<MyPreferences, Integer> preferencesDao = null;
    private Dao<SecurityQuestion, Integer> securityQuestionDao = null;

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

    /**
     * This is called when the database is first created. Usually you should call createTable statements here to create
     * the tables that will store your data.
     */
    @Override
    public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
        try {
            Log.i(DatabaseHelper.class.getName(), "onCreate");

            TableUtils.createTable(connectionSource, Contact.class);
        } catch (SQLException e) {
            Log.e(DatabaseHelper.class.getName(), "Can't create database", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * This is called when your application is upgraded and it has a higher version number. This allows you to adjust
     * the various data to match the new version number.
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
        try {
            Log.i(DatabaseHelper.class.getName(), "onUpgrade");
            TableUtils.dropTable(connectionSource, Contact.class, true);
            // after we drop the old databases, we create the new ones
            onCreate(db, connectionSource);
        } catch (SQLException e) {
            Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * Returns the Database Access Object (DAO) for our Person class. It will create it or just give the cached
     * value.
     */
    public Dao<Contact, Integer> getContactDao() throws SQLException {
        if (contactDao == null) {
            contactDao = getDao(Contact.class);
        }
        return contactDao;
    }

    public Dao<MyPreferences, Integer> getPreferencesDao() throws SQLException {
        if (preferencesDao == null) {
            preferencesDao = getDao(MyPreferences.class);
        }
        return preferencesDao;
    }

    public Dao<SecurityQuestion, Integer> getSecurityQuestionDao() throws SQLException {
        if (securityQuestionDao == null) {
            securityQuestionDao = getDao(SecurityQuestion.class);
        }
        return securityQuestionDao;
    }

    /**
     * Close the database connections and clear any cached DAOs.
     */
    @Override
    public void close() {
        super.close();
        contactDao = null;
        preferencesDao = null;
        securityQuestionDao = null;
    }   
}                                                              

和数据class:

@DatabaseTable(tableName = "Contact", daoClass = ContactDao.class)
public class Contact implements Serializable {

    @DatabaseField(generatedId = true, columnName = PersistentObject.ID)
    int id;

    @DatabaseField(index = true)
    String contactName;

    @DatabaseField
    String mainPic;

    @DatabaseField(dataType = DataType.DATE_STRING, format = "yyyy-MM-dd HH:mm:ss.S")
    Date birthdate;

    @DatabaseField
    boolean knowPersonally;

    @DatabaseField(dataType = DataType.DATE_STRING, format = "yyyy-MM-dd HH:mm:ss.S")
    Timestamp created;

    @DatabaseField(dataType = DataType.DATE_STRING, format = "yyyy-MM-dd HH:mm:ss.S")
    Timestamp lastMod;

    public Contact() {
        // needed by ormlite
    }

...
}   

和 ContactDao:

public class ContactDao extends BaseDaoImpl<Contact, Integer> {

    public ContactDao(Class<Contact> dataClass) throws SQLException {
        super(dataClass);
    }

    public ContactDao(ConnectionSource connectionSource, Class<Contact> dataClass) throws SQLException {
        super(connectionSource, dataClass);
    }

    public ContactDao(ConnectionSource connectionSource, DatabaseTableConfig<Contact> tableConfig) throws SQLException {
        super(connectionSource, tableConfig);
    }

    public List<Contact> getContacts() throws SQLException {
        return queryForAll();
    }

}

因此,在 "mContactDao.create(contact);" 的 ContactService class 中,它因异常而崩溃。这是我不明白的部分,因为 ContactService 是用 @EBean 注释的,并且在 MainActivity 中使用“@Bean ContactService contactService;" 并且不应该在那里为空...

感谢您提前提供任何帮助或提示。

问题如下:

该代码试图访问 mContactDao 字段,但它确实是 null,尽管它应该由 AndroidAnnotations 注入。但是该字段无法注入,因为dao创建失败并出现异常。这是由 AndroidAnnotations 记录的,您可以在 LogCat.

中查看

问题的原因在于Contactclass。您正在使用 List<Something> 字段,但 ORMLite 不知道如何保留 java.util.List 对象。您可以使用自定义持久化器,也可以使用外部字段:

Contact.java:

@ForeignCollectionField
private ForeignCollection<Address> adresses;

更新:

应用 ForestCollectionField 更改并再次调试显示另一个问题。 DataType.DATE_STRING 持久器不能与 java.sql.Timestamp class 一起使用。但您可以使用 DataType.TIME_STAMP 代替:

@DatabaseField(dataType = DataType.TIME_STAMP, format = "yyyy-MM-dd HH:mm:ss.S")
Timestamp created;

@DatabaseField(dataType = DataType.TIME_STAMP, format = "yyyy-MM-dd HH:mm:ss.S")
Timestamp lastMod;