Search code examples
javaandroid-studioandroid-room

Error while compution live data and IllegalStateException ROOM Database


I need to use two arraylists in my database. Here is the entity class:

@Entity
public class User {

    public int getId() {
        return id;
    }

    @NonNull
    public ArrayList<String> getValues(){
        return values;
    }

    @NonNull
    public ArrayList<String> getDates(){
        return dates;
    }

    @NonNull
    public String getType_counter() {
        return type_counter;
    }

    @NonNull
    public String getWhere_counter() {
        return where_counter;
    }

    @PrimaryKey(autoGenerate = true)
    public int id;

    @ColumnInfo(name = "d")
    @TypeConverters({Converters.class})
    public ArrayList<String> dates;                   // arraylist here

    @ColumnInfo(name = "value")
    @TypeConverters({Converters.class})
    public ArrayList<String> values;                  // arraylist here

    @ColumnInfo(name = "type")
    public String type_counter;

    @ColumnInfo(name = "location")
    public String where_counter;
}

I have created a TypeConverter class as described in this answer:

public class Converters {
    @TypeConverter
    public static ArrayList<String> fromString(String value) {
        Type listType = new TypeToken<ArrayList<String>>() {}.getType();
        return new Gson().fromJson(value, listType);
    }

    @TypeConverter
    public static String fromArrayList(ArrayList<String> list) {
        Gson gson = new Gson();
        return gson.toJson(list);
    }
}

I insert the values in the database this way:

private void saveNewValue(String value, String type, String location){

        // Getting current date
        Date c = Calendar.getInstance().getTime();
        SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy", Locale.getDefault());
        String formattedDate = df.format(c);

        //Saving new User
        userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);

        User user = new User();
        user.values.add(value);
        user.dates.add(formattedDate);
        user.type_counter = type;
        user.where_counter = location;

        userViewModel.insert(user);

        Toast.makeText(this, "Successfully saved!", Toast.LENGTH_SHORT).show();
    }

I've got one activity with RecyclerView which has only type and location and when you click on a specific item you go to another activity where another RecyclerView shows all the values and dates of this specific user (by id).

This is my DAO class:

@Dao
public interface UserDao {

    @Query("SELECT * FROM User ")
    LiveData<List<User>> getAllValues();

    @Insert
    void insertValue(User ... users);

    @Delete
    void deleteValue(User user);

    @Query("SELECT location FROM User")
    LiveData<String[]> getAllLocations();

    @Query("SELECT * from User WHERE id=:id")
    LiveData<User> getUserWithId(int id);

}

This is the error I'm getting:

E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
    Process: org.tensorflow.lite.examples.detection, PID: 14774
    java.lang.RuntimeException: Exception while computing database live data.
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $
        at com.google.gson.Gson.fromJson(Gson.java:944)
        at com.google.gson.Gson.fromJson(Gson.java:897)
        at com.google.gson.Gson.fromJson(Gson.java:846)
        at org.tensorflow.lite.examples.detection.Converters.fromString(Converters.java:15)
        at org.tensorflow.lite.examples.detection.db.UserDao_Impl$3.call(UserDao_Impl.java:126)
        at org.tensorflow.lite.examples.detection.db.UserDao_Impl$3.call(UserDao_Impl.java:109)
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
        at java.lang.Thread.run(Thread.java:764) 
     Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $
        at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:351)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
        at com.google.gson.Gson.fromJson(Gson.java:932)
        at com.google.gson.Gson.fromJson(Gson.java:897) 
        at com.google.gson.Gson.fromJson(Gson.java:846) 
        at org.tensorflow.lite.examples.detection.Converters.fromString(Converters.java:15) 
        at org.tensorflow.lite.examples.detection.db.UserDao_Impl$3.call(UserDao_Impl.java:126) 
        at org.tensorflow.lite.examples.detection.db.UserDao_Impl$3.call(UserDao_Impl.java:109) 
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
        at java.lang.Thread.run(Thread.java:764) 
D/OpenGLRenderer: endAllActiveAnimators on 0x72aa705400 (RippleDrawable) with handle 0x72a70ea8e0

Could somebody please help me out? Thanks in advance.

I can provide extra info if necessary.


Solution

  • Somehow you are storing data that does not contain the enclosing square brackets [ and ] around the list (in columns d and or value).

    For example, the Type converter should convert that value fields/members to be along the lines of ["26-Jul-2022"] or for multiple dates ["26-Jul-2022","25-Jul-2022"].

    The Type Converter fromArrayList does this as it stands. The following based upon your code (first 5 rows) shows, using App Inspection that is the case:-

    enter image description here

    • rows with id's 6 and 7, as indicated by the crosses, lack the enclosing square brackets (see the following for how this data replicates the failure you are encountering).

    However, if the fromArrayList is modified to instead be :-

    @TypeConverter
    public static String fromArrayList(ArrayList<String> list) {
        Log.d("TYPCONVERTER","Type Converter >>fromArrayList<< invoked. ArrayList had " + list.size() + " elements.");
        Gson gson = new Gson();
        return gson.toJson(list).replace("[","").replace("]","");
    }
    
    • i.e. it strips the '[' and ']'
    • see rows 6 and 7

    then running a test extraction, results in the exception that you are encountering (for the data):-

    2022-07-26 10:27:15.328 28959-28959/a.a.so73110961javaroomlisttypeconverters E/AndroidRuntime: FATAL EXCEPTION: main
        Process: a.a.so73110961javaroomlisttypeconverters, PID: 28959
        java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so73110961javaroomlisttypeconverters/a.a.so73110961javaroomlisttypeconverters.MainActivity}: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 2 path $
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
            at android.os.Handler.dispatchMessage(Handler.java:106)
            at android.os.Looper.loop(Looper.java:223)
            at android.app.ActivityThread.main(ActivityThread.java:7656)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
         Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 2 path $
    

    The fix is to ascertain where you are saving a string without the enclosing square brackets (and it is very unlikely the Type Converter)

    Changing to use:-

    @Query("SELECT * FROM User " +
             "WHERE " +
             " substr(value,1,1) = '[' " +
             "AND substr(value,length(value),1) = ']'  " +
             "AND substr(d,1,1) = '[' " +
             "AND substr(d,length(d),1) = ']' " +
             "AND id=:id " +
             ";")
     LiveData<User> getValidUser(int id);
    

    circumvents the failure BUT would return nothing for a User that has the incorrect data stored.

    Using App Inspection you could run the following query to ascertain which rows would cause and issue :-

    SELECT * FROM user WHERE substr(d,1,1) <> '[' OR substr(d,length(d),1) <> ']' OR substr(value,1,1) <> '[' OR substr(value,length(value),1) <> ']';
    

    e.g.

    enter image description here