Closed shiraeeshi closed 8 years ago
I've found this answer useful: http://stackoverflow.com/a/4878259 I think that a map of weak references is a good solution.
I ended up starting TopicsActivity as usual with "startActivity" to speed up getting back to MainActivity, so we got some hybrid of "with-global" and "no-global" approaches.
Some thoughts without digging in the current code. My first approach was to use serialization (parcelable), but with big objects packing and upacking still can be slow, so I switched to passing the unique ID of the object that resides in a global space. The global space is a singleton pattern (reusing application singleton mechanism), because we do not need different copies of data (need access to a shared resource from multiple parts). For now it's just like a cached in memory file (and it's smaller than a good quality photo).
As for DB, I modified the table with new compound IDs. The format is topic_test.number_question.number, I was thinking about pre-cooked by python database and at start the app would copy it to the internal storage. But you was faster =)
It's the first time I hear about python database in android, it would be interesting to see how it works.
By pre-cooked I mean to create binary file in res/raw (when you build project) and than copy it by the app in the internal memory (so there is no overhead at start). Python uses sqlite3 as well.
I use python script to import questions from google spreadsheet. And there is a task exportQuestions in the build.gradle
I misunderstood that part (I was a little surprised about python on android, lol).
doIt -> push commit button
5494-5494/doit.study.droid E/AndroidRuntime: FATAL EXCEPTION: main
Process: doit.study.droid, PID: 5494
android.database.sqlite.SQLiteException: no such table: stats (code 1): , while compiling: UPDATE stats SET right_count = 0 WHERE question_id = 8
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:887)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:498)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)
at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1674)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1605)
at doit.study.droid.sqlite.helper.DatabaseHelper.addStats(DatabaseHelper.java:370)
at doit.study.droid.QuestionsActivity$1.onAnswer(QuestionsActivity.java:42)
at doit.study.droid.QuestionFragment.onClick(QuestionFragment.java:246)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
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)
push setTopic
12-16 22:58:05.040 8085-8085/doit.study.droid E/AndroidRuntime: FATAL EXCEPTION: main
Process: doit.study.droid, PID: 8085
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:275)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
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.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:270)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
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: android.database.sqlite.SQLiteException: no such table: stats (code 1): , while compiling: SELECT tag_id, COUNT(*) as count, SUM(score) as learned FROM questions_tags qt JOIN (SELECT question_id,CASE WHEN right_count < 3 THEN 0 ELSE 1 END score FROM stats) s ON qt.question_id=s.question_id GROUP BY qt.tag_id
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:887)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:498)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1316)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1255)
at doit.study.droid.sqlite.helper.DatabaseHelper.getTagStats(DatabaseHelper.java:305)
at doit.study.droid.QuizData.getTagStats(QuizData.java:121)
at doit.study.droid.MainActivity.setTopicButton(MainActivity.java:39)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:270)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
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)
The meaning of those error messages is that an app haven't created the 'stats' table. The reason is that previous schema haven't had this table. DatabaseHelper checks the existence of a file named 'imported': if this file exists, then there's no need to perform schema creation. The solution is to clear the app's data so that 'imported' file doesn't exist anymore.
Can you share your thoughts about this branch, what prevents you from accepting this pull request?
You wrote lots of good code, just need some discussion and polishing. I pushed the version with pre-cooked db.
Some ideas about the app architecture (maybe I'm wrong, so need your thoughts). We need serializable compound key to identify questions and statistics. By this key we get data from global space (which query from DB and caches info, sometimes drops cache). We have a read only questions table (a good candidate to be created at the build stage) and a writable statistics table.
There is a questionId column in question table, it uniquely identifies the question, and I think we can identify statistics by this id too.
There is a questionId
If we want to have predefined test sets, we need a compound primary key. My suggestion is to use topic_num_id, test_num_id, question_num_id.
Even if we have predefined test sets, questionId uniquely identifies a question. Our goal is to identify a question, right?
The compound key will uniquely identifies a question and statistics, check the sketch. The goal is also to have a predefined order in test sets.
I deleted data holder, don't see any use of it. All queries create data locally and return references directly to the caller. Data will be cleaned with the caller. So for now we have a copy for every query. "Truth emerges from the clash of adverse ideas". I may be wrong.
Selecting tags and questions by tags. In order to limit the use of global data, the code is organized as follows:
The reason for all this is to make the code more modular. It's all good, but it's very slow, so I don't know what is the best solution. Need to google it.