简体   繁体   中英

How to pre-populate Room Database from asset?

I was writing an app with the SQLiteOpenHelper class and decided to re-build it all implementing the MVVM pattern using Room for some extra usability. My first issue is that the Room needs to load the database file from the assets folder using the.createFromAsset("database/data_table.db"). It should copy it to the device and then all processes go from there but it doesn't copy it as required, the developer website doesn't specify more than how to call it. Here is the Database class with explanation of each method for learning purposes, since I am in the learning process.

@Database(entities = MarkerObject.class, version = 1, exportSchema = false)
public abstract class MarkerDatabase extends RoomDatabase {

    /**We create the instance so we wouldn't create multiple instances
     of the database and use the same instance throughout the application
     which is accessed through the static variable **/
    private static MarkerDatabase instance;

    /**
     *This is used to access the Dao,
     * no body is provided because Room takes care of that,
     * abstract classes don't have methods but the room generates it because of the @Dao annotation.
     */
    public abstract MarkerDao markerDao();

    /**
     * This method creates a single instance of the database,
     * then we can call this method from the outside so we have a handle to it.
     * Synchronized means that only one thread at a time can access the database
     * eliminating creating multiple instances.
     */
    public static synchronized MarkerDatabase getInstance(Context context){

        /** we want to instantiate the database
         * only if we don't have an instance, thus the if condition */
        if (instance == null) {
            instance = Room.databaseBuilder(context.getApplicationContext(),
                    MarkerDatabase.class, "locations_table")
                    .createFromAsset("database/locations_table.db")
                    .build();
        }
        return instance;
    }
} 

The DAO

@Dao
public interface MarkerDao {

    @Query("SELECT * FROM locations_table")
    LiveData<List<MarkerObject>> getAllMarkers();

}

The Repository

public class MarkerRepository {
  
    private MarkerDao markerDao;
    private LiveData<List<MarkerObject>> allMarkers;

    public MarkerRepository(Application application){
    
    MarkerDatabase markerDatabase = MarkerDatabase.getInstance(application);
    
    markerDao = markerDatabase.markerDao();
  
    allMarkers = markerDao.getAllMarkers();
}
public LiveData<List<MarkerObject>> getAllMarkers(){return allMarkers; }

}

The ViewModel

public class MarkerViewModel extends AndroidViewModel {
  
    private MarkerRepository repository;
    private LiveData<ArrayList<MarkerObject>> allMarkers;

    /**
     * We use the application as context in the constructor
     * because the ViewModel outlives the activities lifecycle.
     * To avoid memory leaks we use application as context instead of activities or views.
     */
    public MarkerViewModel(@NonNull Application application) {
        super(application);
        
        repository = new MarkerRepository(application);
        allMarkers = repository.getAllMarkers();
    }
    public LiveData<ArrayList<MarkerObject>> getAllMarkers(){
        return allMarkers;
    }
}

The MainActivity

public class MainActivity extends AppCompatActivity {
    private MarkerViewModel markerViewModel;

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

        TextView textView = findViewById(R.id.textView);

        markerViewModel = new ViewModelProvider(this).get(MarkerViewModel.class);
        markerViewModel.getAllMarkers().observe(this, new Observer<List<MarkerObject>>() {
            @Override
            public void onChanged(List<MarkerObject> markerObjects) {
                textView.setText(markerObjects.toString());
            }
        });
    }
}

Now I understand that this might be a stupid question to some and I might be judged for it but I don't have other sources other than the internet to learn from. All help is very well appreciated.

Your code is fine and should work without any problems.

A couple of things to check:

  • First : make sure that location_table.db file exists under app\src\main\assets\database directory

It should copy it to the device and then all processes go from there but it doesn't copy it as required, the developer website doesn't specify more than how to call it

  • Second : createFromAsset() & createFromFile never get executed and create the database file unless you do some transaction or query to the database. Check the accepted answer here for more info.

UPDATE

and the error is java.lang.RuntimeException: Cannot create an instance of class com.example.roomtextfirst.MarkerViewModeL

First I can't see any problems with your new shared code. I just need you to make sure that you have the below dependencies in module gradle file:

implementation 'androidx.fragment:fragment:1.2.2'
implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
implementation 'androidx.lifecycle:lifecycle-service:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0'

And not the deprecated ones:

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-rc01'
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0-rc01'

Also thanks to check the answers here that might guide you as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM