【问题标题】:Android Room Query Using AsyncTask in Repository Null Pointer Exception在存储库空指针异常中使用 AsyncTask 进行 Android 房间查询
【发布时间】:2019-03-25 22:15:08
【问题描述】:

我正在尝试使用 id 查询保存在房间本地数据库中的一些数据。我从一个序列化对象接收到 id 并将其传递给我的视图模型类,该类调用存储库以使用 dao 执行查询。

asynctask 用于使事情异步并从 db 返回请求的值。我已经创建了一个接口并在异步任务中对其进行了初始化,因此我可以从 db 返回结果,但它因 NPE 而失败。

在活动中,我调用了存储库并传递了序列化数据,例如:

   public class ViewSessionActivity extends AppCompatActivity implements View.OnClickListener{

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

    private Toolbar toolbar;
    private Sessions sessions; // serialized object from adapter

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_session);

        init();
        setSupportActionBar(toolbar);

    }

    private void init() {

        // skipped fvid calls

        sessions = (Sessions) getIntent().getSerializableExtra(Constants.SESSION_DETAIL_KEY);
        Log.d(TAG, "Prog id:\t" + sessions.prog_sessionId);

        //error occurs here which leads to async task call in repository
SessionsRepository.getRepository(getApplication()).getSessionByID(sessions.prog_sessionId); 

    }

在存储库中:

    public class SessionsRepository {

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

    private SessionsDAO dao;
    private static SessionsRepository repository = null;

    private LiveData<List<Sessions>> allSessions;
    private MutableLiveData<Boolean> loadingState;

    private Context context;
    private final SportsDatabase database;

    public SessionsRepository(Application application) {
        database = SportsDatabase.getInstance(application);
        dao = database.sessionsDAO();
        allSessions = dao.getAllSessions();
        loadingState = new MutableLiveData<>();
        context = application.getApplicationContext();
    }

    public static SessionsRepository getRepository(Application context){
        if (repository == null){
            repository = new SessionsRepository(context);
        }
        return repository;
    }

    public void fetchSessions() {
        String coachId = new PrefsUtils(context).getCoachId();

        Call<SessionDetails> call = RestClient.getRestInstance().getSessionsService().fetchSessions(coachId);

        call.enqueue(new Callback<SessionDetails>() {
            @Override
            public void onResponse(Call<SessionDetails> call, Response<SessionDetails> response) {
                if (response.isSuccessful()) {
                    loadingState.postValue(false); // remove progress
                    SessionDetails details = response.body();
                    List<Sessions> sessions = details.getSessions();
//                    Log.d(TAG, "N/w sesh size:\t" + sessions.size());
                    saveSessions(sessions);
                }
            }

            @Override
            public void onFailure(Call<SessionDetails> call, Throwable t) {
                loadingState.postValue(false);
                Toast.makeText(context, "Error Fetching Sessions", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void saveSessions(List<Sessions> sessions) {
        new SaveSessionsTask(dao).execute(sessions);
    }

    // this method which calls the async task
    public void getSessionByID(String id){
        new FindSessionTask(dao, context).execute(id);
    }

    public LiveData<List<Sessions>> getSavedSessions() {
        return allSessions;
    }

    public class SaveSessionsTask extends AsyncTask<List<Sessions>, Void, Void> {
        private SessionsDAO dao;

        public SaveSessionsTask(SessionsDAO dao) {
            this.dao = dao;
        }

        @Override
        protected Void doInBackground(List<Sessions>... lists) {
            dao.addSessions(lists[0]);
            return null;
        }
    }

    public class FindSessionTask extends AsyncTask<String, Void, Sessions>{

        private SessionsDAO dao;
        private OnSessionResultCallback callback;

        public FindSessionTask(SessionsDAO dao, Context context) {
            this.dao = dao;
            this.callback = (OnSessionResultCallback) context;
        }

        @Override
        protected Sessions doInBackground(String... strings) {
            Sessions sessions = dao.getSessionById(strings[0]);
            return sessions;
        }

        @Override
        protected void onPostExecute(Sessions sessions) {
            super.onPostExecute(sessions);
            Log.d(SessionsRepository.TAG, "Session name:\t" + sessions.session_name);
        }
    }

    // need help in returning the session found from db in onpostexecute method


}

道类:

@Dao
public interface SessionsDAO {

    @Insert
    void addSessions(List<Sessions> sessions);

    @Query("select * from sessions")
    LiveData<List<Sessions>> getAllSessions();

    @Query("select * from sessions where prog_sessionId = :id")
    Sessions getSessionById(String id); // here

}

和接口回调在后执行方法的异步任务中接收数据:

    public interface OnSessionResultCallback {
       void onSessionFound(Sessions sessions);
    }

logcat 中的错误:

   Caused by: java.lang.ClassCastException: sashi.in.ecosports.extras.App cannot be cast to sashi.in.ecosports.interfaces.OnSessionResultCallback
        at sashi.in.ecosports.rest.db.repositories.SessionsRepository$FindSessionTask.<init>(SessionsRepository.java:110)
        at sashi.in.ecosports.rest.db.repositories.SessionsRepository.getSessionByID(SessionsRepository.java:82)
        at sashi.in.ecosports.ui.activities.ViewSessionActivity.init(ViewSessionActivity.java:63)
        at sashi.in.ecosports.ui.activities.ViewSessionActivity.onCreate(ViewSessionActivity.java:38)
        at android.app.Activity.performCreate(Activity.java:6671)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
....

我希望注销找到的会话名称,但得到了 NPE。有人可以帮助我执行房间查询异步,使用实体字段作为搜索字符串从列表中返回整个实体。视图模型如何接收数据,我的接口是否必要?谢谢。

【问题讨论】:

  • 请附上你的logcat
  • 完成,再次检查。你能分享一下如何使用 mvvm 架构在房间中执行 find-by-id 和 find-by-id-and-update 查询吗?
  • 你说你得到了NullPointerException,但在你附加的logcat中我看到了ClassCastException。你贴对了吗?
  • 哦,对不起。 NPE 在我尝试打印 session.session_name 时出现,但它们是相关的
  • 此行出现错误this.callback = (OnSessionResultCallback) context;context 是应用程序上下文,但它应该是一个回调。

标签: android android-asynctask android-room android-mvvm


【解决方案1】:

由于您在尝试打印 session.session_name 时遇到 NPE,这意味着您返回的会话对象为 null,这可能会发生:

  1. 查询错误(因为您的选择查询很好,我猜您传递的 id 错误)

  2. 当您尚未插入任何具有该 ID 的记录时(在这种情况下,您必须调试插入过程并检查数据库以查看插入是否正确)

要在运行时检查您的数据库内容,您可以轻松使用Stetho

关于带有房间的更新查询,您可以使用内置的@Update 注释,它通过其主键查找传递的对象并更新所有列,或者使用@Query 注释来更新特定列,如@ 987654325@ 更新session_name 列,会话ID 为id。下面是一个示例代码:

@Dao
public interface SessionsDAO {
    @Query("SELECT * FROM sessions")   // Retrieve all columns saved in sessions table
    LiveData<List<Sessions>> getAllSavedSessions();

    @Query("SELECT * FROM sessions WHERE prog_sessionId = :id")   // Retrieve a single session with all columns saved in sessions table
    Sessions findSessionWithId(String id);

    @Query("SELECT session_name FROM sessions WHERE prog_sessionId = :id")   // Retrieve a single session name saved in sessions table
    String getSessionNameWithId(String id);

    @Update   // Update all non-primary columns of a session
    void updateSession(Sessions session);

    @Query("UPDATE sessions SET session_name = :newName")   // Update session_name column of all saved sessions
    void updateAllSessionNamesTo(String newName);

    @Query("UPDATE sessions SET session_name = :newName WHERE prog_sessionId = :id")   // Update session_name column of a session located with its id
    void updateSessionNameTo(String newName, String id);

    @Query("UPDATE sessions SET session_name = :newName AND session_date = :newDate WHERE prog_sessionId = :id")   // Update session_name and session_date column of a session located with its id
    void updateSessionNameAndDateTo(String newName, long newDate, String id);
}

如果您需要在工作线程上运行查询,您可以使用 java 执行器,这是一个检索会话名称的简单示例:

@Dao
public abstract class SessionsDAO {
    void getSessionNameWithId(final String id, final NameLoadedListener onFinishListener) {
        Executors.newSingleThreadExecuter().submit(new Runnable() {
             @Override
             public void run() {
                 String name = getSessionNameWithId(id);
                 onFinishListener.onNameLoaded(name);
             }
        });
    }

    @Query("SELECT session_name FROM sessions WHERE prog_sessionId = :id")   // Retrieve a single session name saved in sessions table
    abstract String getSessionNameWithId(String id);

    public interface NameLoadedListener {
        void onNameLoaded(String name);
    }
}

这是一个通过检索实体并在最后返回更新后的实体来更新会话名称的示例。

@Dao
public abstract class SessionsDAO {
    void updateSessionNameWithId(final String id, final String newName, final SessionUpdateListener onFinishListener) {
        Executors.newSingleThreadExecuter().submit(new Runnable() {
             @Override
             public void run() {
                 updateSessionName(id, newName, onFinishListener);
             }
        });
    }

    @Transaction
    void updateSessionName(String id, String newName, SessionUpdateListener onFinishListener) {
         Sessions session = findSessionWithId(id);
         session.setName(newName);
         updateSession(session);
         onFinishListener.onSessionUpdated(session);
    }

    @Query("SELECT * FROM sessions WHERE prog_sessionId = :id")   // Retrieve a single session with all columns saved in sessions table
    Sessions findSessionWithId(String id);

    @Update   // Update all non-primary columns of a session
    void updateSession(Sessions session);

    public interface SessionUpdateListener {
        void onSessionUpdated(Sessions session);
    }
}

希望你能收到备忘录。

【讨论】:

  • 我已经使用 stetho 可视化了数据库,感谢您的链接,很高兴知道这是一个不错的选择。此外,该 id 是正确的,因为我已在活动的 init() 中将其注销,并且它打印的值与 db 中的值相同。你能分享一个更详细的使用房间的 findById 和 findByIdAndUpdate 查询的通用答案吗?我来自MongoDB背景。谢谢
  • @DarthPlagueris 不客气,请参阅我的更新答案。
  • 是的,我理解这部分,但先生的问题在于它的异步部分,因为查询不能在主线程上运行。你能帮忙解决 findbyid 的异步任务并返回结果吗?谢谢
  • dao 类中的抽象前缀将方法标记为 Room 实现的需要实现的方法,您不必在任何地方实现它们,Room 会为您完成,因为它需要与数据库一起使用.正如我所说,使用抽象类而不是接口可以帮助您拥有一个不需要空间来实现它的方法的主体。在我们的例子中,在工作线程上运行一个抽象(数据库操作)方法迫使我们声明一个方法并在其中使用 Java 执行器并运行抽象 db 方法。可以声明abstract Session get()等所有db操作方法强制Room实现。
  • 经过深思熟虑,我决定继续使用接口作为我的 dao 类。我几乎没有时间切换所有内容,而且结果界面仍然抛出错误。谢谢您,先生,您的时间和精力。我会接受你的回答并希望得到你更多的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-08
  • 2013-11-21
  • 1970-01-01
  • 2016-01-16
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
相关资源
最近更新 更多