Android 在 Repository 空指针异常中使用 AsyncTask 进行房间查询

Android Room Query Using AsyncTask in Repository Null Pointer Exception

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

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

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

   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


}

道class:

@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

}

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

    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。有人可以帮助我执行异步房间查询,使用实体字段作为搜索字符串 return 列表中的整个实体。视图模型如何接收数据,我的接口是否必要?谢谢。

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

  1. 查询错误(因为您的 select 查询没问题,我猜您传递了错误的 ID)

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

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

关于房间的更新查询,您可以使用内置的 @Update 注释,它通过主键查找传递的对象并更新所有列,或者使用 @Query 注释更新特定的@Query("UPDATE sessions SET session_name = :newName WHERE prog_sessionId = :id") 之类的列更新会话 ID 为 idsession_name 列。下面是一个示例代码:

@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);
    }
}

希望你收到备忘录。