Android中LiveData+Room+Paging简单实现步骤

本文档详述了如何在Android应用中利用Room数据库、ViewModel与LiveData组件进行高效的数据管理与界面更新。从依赖库的引入到数据库实体类、DAO接口、数据库类的编写,再到ViewModel与分页适配器的实现,最后通过Activity整合所有组件,实现数据的增删查与实时刷新。同时,提供了线程执行工具类,确保后台操作不影响UI响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 添加依赖库

    //for lifecycle and LiveData and ViewModel
    implementation "android.arch.lifecycle:runtime:$archRuntimeVersion"
    implementation "android.arch.lifecycle:extensions:$archExtensionVersion"
    annotationProcessor "android.arch.lifecycle:compiler:$archVersion"

    implementation "android.arch.persistence.room:runtime:$archVersion"
    annotationProcessor "android.arch.persistence.room:compiler:$archVersion"

    // Paging
    implementation "android.arch.paging:runtime:1.0.1"

2. 数据库实体类编写

@Entity()
public class Cheese {

    @PrimaryKey(autoGenerate = true)
    @NonNull
    public int id;

    public String name;

    public Cheese(String name) {
        this.name = name;
    }


}

3. 数据库dao接口编写

@Dao
public interface CheeseDao {

    @Query("SELECT * FROM cheese ORDER BY name COLLATE NOCASE ASC")
    DataSource.Factory<Integer, Cheese> allCheesesByName();

    @Insert
    void insertList(List<Cheese> cheeses);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(Cheese cheese);

    @Delete
    void delete(Cheese cheese);
}

4. 数据库类编写

@Database(entities = {Cheese.class}, version = 1, exportSchema = false)
public abstract class CheeseDb extends RoomDatabase {

    public abstract CheeseDao cheeseDao();

    @VisibleForTesting
    public static final String DATABASE_NAME = "cheese.db";

    private static CheeseDb INSTANCE;

    public static CheeseDb getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (CheeseDb.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            CheeseDb.class, DATABASE_NAME)
                            .fallbackToDestructiveMigration()
                            .addCallback(callback)
                            .build();
                }
            }
        }
        return INSTANCE;
    }

    private static RoomDatabase.Callback callback = new RoomDatabase.Callback() {
        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            super.onOpen(db);
            new PopulateDbAsync(INSTANCE).execute();
        }
    };

    private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {

        private final CheeseDao mDao;

        PopulateDbAsync(CheeseDb db) {
            mDao = db.cheeseDao();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            ArrayList<Cheese> list = new ArrayList<>();
            // 假数据
            for (String name : data) {
                list.add(new Cheese(name));
            }
            mDao.insertList(list);
            return null;
        }
    }

    public static final String[] data = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
            "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
            "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
            "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
            "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
            "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
            "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
            "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
            "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
            "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
            "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
            "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
            "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
            "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
            "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
            "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
            "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
            "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
            "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
            "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
            "Buxton Blue", "Cabecou", "Caboc"};

}

5. ViewModel类编写

public class CheeseViewModel extends AndroidViewModel {

    private final CheeseDao mCheeseDao;
    public final LiveData<PagedList<Cheese>> listLiveData;

    public CheeseViewModel(@NonNull Application application) {
        super(application);
        mCheeseDao = CheeseDb.getDatabase(application).cheeseDao();

        listLiveData = new LivePagedListBuilder<>(mCheeseDao.allCheesesByName(),
                new PagedList.Config.Builder()
                        .setPageSize(30)
                        .setEnablePlaceholders(true)
                        .build())
                .build();

    }

    public void insert(final String text) {
        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                mCheeseDao.insert(new Cheese(text));
            }
        });
    }

    public void remove(final Cheese cheese) {

        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                mCheeseDao.delete(cheese);
            }
        });
    }

}

6. 分页设配器类编写ViewHolder 和 Adapter

public class CheeseViewHolder extends RecyclerView.ViewHolder {

    private TextView tvName;
    public Cheese cheese;

    public CheeseViewHolder(@NonNull View itemView) {
        super(itemView);
        tvName = itemView.findViewById(R.id.name);
    }

    public void bindTo(Cheese cheese) {
        this.cheese = cheese;
        tvName.setText(cheese.name);
    }
}


public class CheeseAdapter extends PagedListAdapter<Cheese, CheeseViewHolder> {

    private static DiffUtil.ItemCallback<Cheese> ITEM_CALLBACK = new DiffUtil.ItemCallback<Cheese>() {
        @Override
        public boolean areItemsTheSame(@NonNull Cheese oldItem, @NonNull Cheese newItem) {
            return oldItem.id == newItem.id;
        }

        @Override
        public boolean areContentsTheSame(@NonNull Cheese oldItem, @NonNull Cheese newItem) {
            return oldItem.name.equals(newItem.name);
        }
    };

    public CheeseAdapter() {
        super(ITEM_CALLBACK);
    }

    @NonNull
    @Override
    public CheeseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cheese_item, parent, false);
        return new CheeseViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull CheeseViewHolder holder, int position) {
        holder.bindTo(getItem(position));
    }
}

7.  Activity 及页面布局编写

public class MainActivity extends AppCompatActivity {

    private CheeseViewModel viewModel;
    private CheeseAdapter adapter;
    private RecyclerView recyclerView;
    private Button btnAdd;
    private EditText etText;

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

        recyclerView = findViewById(R.id.cheeseList);
        btnAdd = findViewById(R.id.addButton);
        etText = findViewById(R.id.inputText);

        viewModel = ViewModelProviders.of(this).get(CheeseViewModel.class);
        adapter = new CheeseAdapter();

        recyclerView.setAdapter(adapter);

        viewModel.listLiveData.observe(this, new Observer<PagedList<Cheese>>() {
            @Override
            public void onChanged(@Nullable PagedList<Cheese> cheeses) {
                adapter.submitList(cheeses);
            }
        });

        initListener();

        initSwipeToDelete();
        
    }

    private void initSwipeToDelete() {

        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
            @Override
            public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
                return makeMovementFlags(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
            }

            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
                if (viewHolder instanceof CheeseViewHolder) {
                    CheeseViewHolder holder = (CheeseViewHolder) viewHolder;
                    viewModel.remove(holder.cheese);
                }
            }
        });

        itemTouchHelper.attachToRecyclerView(recyclerView);
    }

    private void initListener() {
        btnAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addCheese();
            }
        });

        etText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE) {
                    addCheese();
                    return true;
                }
                return false;
            }
        });

        etText.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
                    addCheese();
                    return true;
                }
                return false;
            }
        });
    }

    private void addCheese() {
        String cheese = etText.getText().toString().trim();
        if (!TextUtils.isEmpty(cheese)) {
            viewModel.insert(cheese);
            etText.setText("");
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/inputText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="@string/add_cheese"
            android:imeOptions="actionDone"
            android:inputType="text"
            android:maxLines="1" />

        <Button
            android:id="@+id/addButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0"
            android:text="@string/add" />
    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/cheeseList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="LinearLayoutManager" />
</LinearLayout>

管理线程执行工具类

public class AppExecutors {

    private final Executor mDiskIO;

    private final Executor mNetworkIO;

    private final Executor mMainThread;

    private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
        this.mDiskIO = diskIO;
        this.mNetworkIO = networkIO;
        this.mMainThread = mainThread;
    }

    public AppExecutors() {
        this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(3),
                new MainThreadExecutor());
    }

    public Executor diskIO() {
        return mDiskIO;
    }

    public Executor networkIO() {
        return mNetworkIO;
    }

    public Executor mainThread() {
        return mMainThread;
    }

    private static class MainThreadExecutor implements Executor {
        private Handler mainThreadHandler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
            mainThreadHandler.post(command);
        }
    }
}

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值