简体   繁体   中英

How to fill spinner with data from Room without blocking UI thread

I have pre-populated an Android Room database with the names of sports teams in a table. I want to populate a spinner on the activity below with those names. I need to run several similar tasks as well and cannot use the main thread for this. How can I do this using asynctask or executor?

This data is not live and will not change, but will be called to populate several different widgets.

`//Activity`

    public class RegisterPlayers extends AppCompatActivity {
    public static PlayerRegistryDatabase playerRegistryDatabase;
`@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register_players);
createTeamsSpinner();
}
    private Spinner createTeamsSpinner() {
        Spinner spinner = findViewById(R.id.rank1);
        List<Teams> teamName = PlayerRegistryDatabase.getInstance(this).playerRegistryDao().getAllTeams();
        List<String> teamNameStrings = new LinkedList<>();
        for (int i = 0; i < teamName.size(); i++) {
            teamNameStrings.add(teamName.get(i).getTeamName());
        }
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_dropdown_item_1line,
                teamNameStrings);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
        return spinner;
    }
}
`

`//Teams Class`    


    @Entity
`public class Teams {private String conferenceName;`

    @PrimaryKey@NonNull
    private String teamName;

    public Teams(String conferenceName, String teamName) {
        this.conferenceName = conferenceName;
        this.teamName = teamName;
    }

    public String getConferenceName() {
        return conferenceName;
    }

    public void setConferenceName(String conferenceName) {
        this.conferenceName = conferenceName;
    }

    public String getTeamName() {
        return teamName;
    }

    public void setTeamName(String teamName) {
        this.teamName = teamName;
    }

    public static Teams[] populateData() {
        return new Teams[] {
                new Teams("W", "Panthers"),
                new Teams("W", "Eagles"),
                new Teams("E", "Bulldogs"),
                new Teams("E", "Cheetahs"),
        };
    }`

`//Dao`

`@Dao
public interface PlayerRegistryDao {`

    @Insert
    void insertAll(Teams... teamName);

    @Query("SELECT * FROM Teams")
    List<Teams> getAllTeams();
`}`

`//Database`

    @Database(entities = {PlayerRegistry.class, Teams.class}, version = 1, exportSchema = false)
`public abstract class PlayerRegistryDatabase extends RoomDatabase {`

    private static PlayerRegistryDatabase buildDatabase(final Context context) {
        return Room.databaseBuilder(context,
                PlayerRegistryDatabase.class,
                "teams")
                .addCallback(new Callback() {
                    @Override
                    //@NonNull error, tried android support option
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
                        super.onCreate(db);
                        Executors.newSingleThreadScheduledExecutor().execute(new Runnable() {
                            @Override
                            public void run() {
                                getInstance(context).playerRegistryDao().insertAll(Teams.populateData());
                            }
                        });
                    }
                })
                .build();
    }

I expect the activity to load with the spinner populated with the team names from the Teams table, but the app crashes before displaying the activity with an error related to queries not being permitted to run on the main thread.

According to the android documentation you have to use a repository to populate your ui.

Here is my code with a sample spinner populate from a simple room entity.

My entity.

@Entity(tableName = Category.TABLE_NAME)
public class Category implements Parcelable {

    //constructor setters and getters
    protected Category(Parcel in) {
        id = in.readLong();
        name = in.readString();
    }

    public static final Creator<Category> CREATOR = new Creator<Category>() {
        @Override
        public Category createFromParcel(Parcel in) {
            return new Category(in);
        }

        @Override
        public Category[] newArray(int size) {
            return new Category[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(id);
        dest.writeString(name);
    }

    /** The name of the Cheese table. */
    public static final String TABLE_NAME = "category";

    /** The name of the ID column. */
    public static final String COLUMN_ID = BaseColumns._ID;

    /** The name of the name column. */
    public static final String COLUMN_NAME = "name";

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(index = true, name = COLUMN_ID)
    private long id;

    @ColumnInfo(name = COLUMN_NAME)
    private String name;

    public Category(){
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

My dao, takes care to return LiveData.

@Dao
public interface CategoryDao {

    @Query("SELECT COUNT(*) FROM " + Category.TABLE_NAME)
    LiveData<Integer> count();

    @Insert
    long insert(Category category);

    @Query("SELECT * FROM " +Category.TABLE_NAME)
    LiveData<List<Category>> selectAll();



    @Query("DELETE FROM " + Category.TABLE_NAME + " WHERE " + Category.COLUMN_ID + " = :id")
    int deleteById(long id);

    @Update
    int update(Category item);

    @Delete
    public void delete(Category item);


}

The repository, according to android best practices.

public class CategoryRepository {

    private CategoryDao mDao;
    private LiveData<List<Category>> allItems;

    public CategoryRepository(Application application) {
        AppDatabase db = AppDatabase.getInstance(application);
        mDao = db.categoryDao();
        allItems = mDao.selectAll();
    }

    public LiveData<List<Category>> getAll() {
        return allItems;
    }

    public void insert(Category item) {
        new InsertAsyncTask(mDao).execute(item);
    }

    public void update(Category item) {
        new UpdateAsyncTask(mDao).execute(item);
    }

    public void delete(Category item) {
        new DeleteAsyncTask(mDao).execute(item);
    }


    private static class InsertAsyncTask extends AsyncTask<Category, Void, Void> {
        private CategoryDao asyncTaskDao;

        InsertAsyncTask(CategoryDao dao) {
            asyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(final Category... params) {
            asyncTaskDao.insert(params[0]);
            return null;
        }
    }

    private static class UpdateAsyncTask extends AsyncTask<Category, Void, Void> {
        private CategoryDao asyncTaskDao;

        UpdateAsyncTask(CategoryDao dao) {
            asyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(final Category... params) {
            asyncTaskDao.update(params[0]);
            return null;
        }
    }

    private static class DeleteAsyncTask extends AsyncTask<Category, Void, Void> {

        private CategoryDao asyncTaskDao;

        DeleteAsyncTask(CategoryDao dao) {
            asyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(final Category... params) {
            asyncTaskDao.delete(params[0]);
            return null;
        }
    }

}

Populate code from repository. Here we are, this is the last step and you got it !

private void loadSpinnerData() {
        // database handler
        CategoryRepository categoryRepository = new CategoryRepository(getActivity().getApplication());

        // Creating adapter for spinner
        final ArrayAdapter<Category> dataAdapter = new ArrayAdapter<Category>(getActivity(),
                android.R.layout.simple_spinner_item, new ArrayList<Category>(0));

        // Drop down layout style - list view with radio button
        dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

        // attaching data adapter to spinner
        editCategorySpinner.setAdapter(dataAdapter);

        categoryRepository.getAll().observe(this, new Observer<List<Category>>() {

            @Override
            public void onChanged(@Nullable final List<Category> items) {
                // tri de la liste
                Collections.sort(items, new Comparator<Category>() {
                    public int compare(Category obj1, Category obj2) {
                        // ## Ascending order
                        return obj1.getName().compareToIgnoreCase(obj2.getName());
                    }
                });
                // ajout dans la list et refresh
                dataAdapter.addAll(items);
                dataAdapter.notifyDataSetChanged();
            }
        });

    }

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