如何从api获取数据并将数据插入Android Studio中的ROOM(数据库)? [英] how to fetch data from api and insert the data to ROOM (database) in Android Studio?

查看:104
本文介绍了如何从api获取数据并将数据插入Android Studio中的ROOM(数据库)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的代码运行良好,但每次人们打开硬币活动时,从 coinmarketcap api(名称、符号名称、价格、成交​​量和市值)加载 500 个硬币大约需要 5 秒.我使用 facebook shimmer 让人们知道某些东西正在加载,但 5 秒已经很多了,因为该应用程序在 1 到 2 秒内加载了更多重的东西,例如,带有图像的 wordpress 数据.但是,这 500 个硬币在 5 秒内并不酷...这是我的代码...

 public void getCoinList() {//帖子= 500apiInterface apiInterfaceCoin5 =APIClientCoin.getClient().create(ApiInterface.class);映射<字符串,字符串>params = new HashMap<>();params.put("limit", posts+"");调用call5 = apiInterfaceCoin5.doGetUserListAll(params);call5.enqueue(new Callback() {@覆盖public void onResponse(Call call, Response response){shimmerFrameLayout.stopShimmer();shimmerFrameLayout.setVisibility(View.GONE);swipeRefreshLayout5.setRefreshing(false);int beforeCoinSize = cryptoList5.size();CryptoList list5 = response.body();cryptoList5.addAll(list5.getData());recyclerView4.setAdapter(adapterCoin5);}@覆盖public void onFailure(Call<CryptoList> call, Throwable t) {//Toast.makeText(CryptoListAllCoinActivity.this, onFailure",Toast.LENGTH_SHORT).show();//Log.d(XXXX", t.getLocalizedMessage());电话.取消();progressBar3.setVisibility(View.GONE);shimmerFrameLayout.setVisibility(View.GONE);swipeRefreshLayout5.setRefreshing(false);}});}@Headers(X-CMC_PRO_API_KEY:隐藏")@GET("/v1/cryptocurrency/listings/latest")调用doGetUserListAll(@QueryMap Map params);

适配器:

//涉及通过holder将数据填充到item中//将数据绑定到每一行的TextView@覆盖public void onBindViewHolder(ViewHolder holder, int位置) {//根据位置获取数据模型基准数据 = mData.get(position);//加载硬币图标//如果链接失效了,那么你必须把它上传到你自己的服务器Glide.with(上下文).load(新的字符串生成器(https://s2.coinmarketcap.com/static/img/coins/64x64/").append(datum.getId()).append(".png").toString()).placeholder(R.drawable.money_icon).into(holder.coin_icon);Glide.with(上下文).load(新的字符串生成器(https://s3.coinmarketcap.com/generated/sparklines/web/7d/usd/").append(datum.getId()).append(".png").toString()).placeholder(R.drawable.line_24).into(holder.sparkline);TextView symbolName = holder.symbolName;symbolName.setText(datum.getSymbol());//根据您的视图和数据模型设置项目视图TextView name = holder.name;name.setText(datum.getName());TextView 价格 = holder.price;TextView priceDetails = holder.priceDetails;TextView marketCap = holder.marketCap;marketCap.setText("$" +formatNumber(datum.getQuote().getUSD().getMarketCap()));ImageView coin_icon = holder.coin_icon;ImageView sparkline = holder.sparkline;if(datum.getQuote().getUSD().getPrice() >= 1) {price.setText("$" +formatNumber(datum.getQuote().getUSD().getPrice()));}别的{price.setText("$" + String.format("%f",datum.getQuote().getUSD().getPrice()));}TextView textView24h = holder.textView24h;textView24h.setText(String.format("%.2f",datum.getQuote().getUSD().getPercentChange24h()) + "%");if(datum.getQuote().getUSD().getPercentChange24h() <0.000).{//红色的textView24h.setTextColor(Color.parseColor("#EA3943"));arrowImage.setImageResource(R.drawable.arrow_down);sparkline.setColorFilter(Color.RED,PorterDuff.Mode.MULTIPLY);sparkline.setImageResource(R.drawable.btc_spike);//changeImageColor(context, sparkline,000);}别的{//绿色textView24h.setTextColor(Color.parseColor("#18C784"));arrowImage.setImageResource(R.drawable.arrow_up);sparkline.setColorFilter(Color.GREEN,PorterDuff.Mode.MULTIPLY);sparkline.setImageResource(R.drawable.btc_spike);}}Glide.with(上下文).load(新的字符串生成器(https://s2.coinmarketcap.com/static/img/coins/64x64/").append(datum.getId()).append(".png").toString()).placeholder(R.drawable.money_icon).into(holder.coin_icon);

更新

现在我已经设置了数据库,但不确定如何将数据插入数据库...例如:我在哪里添加这个:

AppDatabase db =AppDatabase.getDbInstance(this.getApplicationContext());//硬币对象db.coinDao().insertAllCoins();

在 onResponse 里面?以及如何?

2021 年 8 月 5 日更新:

AppDatabase.class

@Database(entities = {Coins.class}, version = 1)公共抽象类 AppDatabase 扩展了 RoomDatabase {公共抽象 CoinDao coinDao();私有静态 AppDatabase 实例;公共静态 AppDatabase getDbInstance(上下文上下文){如果(实例 == 空){实例 = 房间.databaseBuilder(context.getApplicationContext(),AppDatabase.class,DB_COIN").allowMainThreadQueries().建造();}返回实例;}}

币道

@Dao公共接口 CoinDao {@Query(SELECT * FROM coin_table")列表<硬币>得到所有();@插入void insertAllCoins(Coins...coins);@删除无效删除(硬币硬币);@更新void updateUsers(Coins...coins);}

Coins.java

@Entity(tableName = "coin_table")公共类硬币{@PrimaryKey(autoGenerate = true)公共整数 ID;@ColumnInfo(name = "name")公共字符串名称;@ColumnInfo(name = "符号")公共字符串符号;@ColumnInfo(name = "slug")公共字符串弹头;@ColumnInfo(name = "circulating_supply")公共双循环供应;@ColumnInfo(name = "total_supply")公共双总供应;@ColumnInfo(name = "max_supply")公共双最大供应;@ColumnInfo(name = "date_ added")公共字符串日期添加;@ColumnInfo(name = "num_market_pairs")公共整数 numMarketPairs;@ColumnInfo(name = "cmc_rank")公共整数 cmcRank;@ColumnInfo(name = "coin_symbol")公共字符串姓氏;@ColumnInfo(name = "last_updated")公共字符串 lastUpdated;@ColumnInfo(name = "价格")公开双倍价;@ColumnInfo(name = "volume_24h")公共双卷24h;@ColumnInfo(name = "percent_change_1h")公共双percentChange1h;@ColumnInfo(name = "percent_change_24h")公共双percentChange24h;@ColumnInfo(name = "percent_change_7d")公共双percentChange7d;@ColumnInfo(name = "market_cap")公开双倍市值;}

UPDATE 8-6-2021 基于 Muhammad Shuja 的回答对于 CoinRepository,我收到此错误:

但是如果我把它改成 CriptoList 那么它说它需要一个 List 哈哈....知道为什么吗?

请注意,我将 Coins 与 s 一起使用,因为这是类名.

此外,如果我将其更改为 CriptoList,它会说这个

 coinDao.insertAll(response.body());

需要一个列表

我有我的 public getCoinList() 一些关于我目前如何从 api 获取数据的参考.

而且,是的,我想每 1 更新一次数据……就像我想进行 api 调用并每 1 分钟更新一次数据.谢谢sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

解决方案

创建一个 CoinRepository 并在那里实现您的逻辑.如果DB为空或者需要刷新数据,让你的repo从API中查询数据并插入到DB中,然后在需要的时候从DB中获取.

查看此代码实验室以实际了解 Room.

创建Coin实体,保持实体/模型类名称单数,各自表名复数是一个好习惯.

Coin.java

@Entity(tableName = "coins")公共类硬币{@PrimaryKey(autoGenerate = true)公共整数 ID;@ColumnInfo(name = "name")公共字符串名称;@ColumnInfo(name = "符号")公共字符串符号;@ColumnInfo(name = "slug")公共字符串弹头;@ColumnInfo(name = "circulating_supply")公共双循环供应;@ColumnInfo(name = "total_supply")公共双总供应;@ColumnInfo(name = "max_supply")公共双最大供应;@ColumnInfo(name = "date_ added")公共字符串日期添加;@ColumnInfo(name = "num_market_pairs")公共整数 numMarketPairs;@ColumnInfo(name = "cmc_rank")公共整数 cmcRank;@ColumnInfo(name = "coin_symbol")公共字符串姓氏;@ColumnInfo(name = "last_updated")公共字符串 lastUpdated;@ColumnInfo(name = "价格")公开双倍价;@ColumnInfo(name = "volume_24h")公共双卷24h;@ColumnInfo(name = "percent_change_1h")公共双percentChange1h;@ColumnInfo(name = "percent_change_24h")公共双percentChange24h;@ColumnInfo(name = "percent_change_7d")公共双percentChange7d;@ColumnInfo(name = "market_cap")公开双倍市值;}

创建CoinDao接口并在其中添加您的查询方法,我添加了其他方法,如get(id)和update(coin),您可以根据需要使用它们.

CoinDao.java

@Dao公共接口 CoinDao {@Query(SELECT * FROM 硬币")LiveData<List<Coin>>得到所有();@Query("SELECT * FROM coin WHERE id = :id")LiveData<Coin>获取(int id);@插入void insertAll(Listcoin);@更新公共无效更新(硬币硬币);@Query(从硬币中删除")void deleteAll();}

按如下方式创建您的数据库类:

DB.java

@Database(entities = {Coin.class}, version = 1)公共抽象类 DB 扩展 RoomDatabase {private static final String TAG =DB";//数据库信息private static final String databaseName =coin_database";//DAO公共抽象 CoinDao coinDao();//实例私有静态易失性数据库实例;私有静态最终 int NUMBER_OF_THREADS = 4;公共静态最终 ExecutorService 数据库WriteExecutor =Executors.newFixedThreadPool(NUMBER_OF_THREADS);公共静态数据库 getInstance(最终上下文上下文){如果(实例 == 空){同步(DB.class){如果(实例 == 空){INSTANCE = Room.databaseBuilder(context.getApplicationContext(), DB.class, databaseName).建造();Log.d(TAG, 新实例创建...");}}}返回实例;}}

创建 CoinRepository 并在其中添加您的 WebAPI/DB 逻辑.请注意,我已将您的 API 调用移动到 getCoins() 方法中,您需要根据您的逻辑对其进行修改.确保您的呼叫响应返回 List,否则您必须自己从 CryptoList 创建一个 List.>

CoinRepository.java

公共类 CoinRepository {private static final String TAG =回购/硬币";私人最终 CoinDao coinDao;私人最终 LiveData<List<Coin>>硬币;public CoinRepository(应用程序){coinDao = DB.getInstance(application).coinDao();硬币 = coinDao.getAll();Log.d(TAG, 新实例创建...");}/*我添加了布尔标志来检查从 Web API 重新加载数据是否是强制性的.您可以删除此标志并根据您的逻辑进行修改.DataReadyListener 是回调侦听器.当数据准备好时,它的 onDataReady() 方法被触发.*/public void getCoins(boolean reload, DataReadyListener listener) {如果(重新加载){//根据你的逻辑修改这部分apiInterface apiInterfaceCoin5 =APIClientCoin.getClient().create(ApiInterface.class);映射<字符串,字符串>params = new HashMap<>();params.put("limit", posts+"");apiInterfaceCoin5.doGetUserListAll(params).enqueue(new Callback>() {@覆盖public void onResponse(Call> call, Response> response) {未来<?>未来 = DB.databaseWriteExecutor.submit(() -> {coinDao.deleteAll();//如果你想保留以前的数据,请删除它coinDao.insertAll(response.body());Log.d(TAG,数据插入\"硬币\表");});尝试 {未来.get();如果(future.isDone())listener.onDataReady(coins);} catch (ExecutionException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}@覆盖public void onFailure(Call> call, Throwable t) {//这里处理失败}});}别的{listener.onDataReady(coins);}}公共 LiveData<Coin>getCoin(int id) {返回 coinDao.get(id);}公共接口 DataReadyListener {void onDataReady(LiveData>coins);}}

并lastley创建CoinViewModel.你可以完全跳过这一步,直接从CoinRepository查询数据.但是 ViewModel 有自己的优点,请查看 docs详情.

CoinViewModel.java

public class CoinViewModel extends AndroidViewModel {私有 CoinRepository 存储库;公共 CoinViewModel(@NonNull 应用程序){超级(应用程序);存储库 = 新 CoinRepository(应用程序);}public void getCoins(boolean reload, CoinRepository.DataReadyListener 监听器) {repository.getCoins(reload, listener);}公共 LiveData<Coin>getCoin(int id) {返回 repository.getCoin(id);}}

现在在您的 Activity/Fragment 中使用 CoinViewModel,首先对其进行初始化:

用户界面(活动/片段)

私有CoinViewModel coinViewModel;@覆盖protected void onCreate(Bundle savedInstanceState) {....coinViewModel = new ViewModelProvider(this).get(CoinViewModel.class);....}

最后使用您的 ViewModel 查询数据.如果您想从 Web API 重新加载数据,请将第一个参数发送为 true,如果您想重用来自 DB 的现有数据,请保留标志为 false:

coinViewModel.getCoins(true, new CoinRepository.DataReadyListener() {@覆盖public void onDataReady(LiveData>coins) {//在适配器中添加或设置数据//对于 setData() 你需要在适配器中创建 setData() 方法//首先清除现有数据,然后将新数据添加到列表并通知数据集更改.coins.observe(this,coinsList ->coinsAdapter.setData(coinsList));}});

希望能有所帮助.

2021 年 7 月 8 日更新

与 OP 讨论后,我更新了 CoinRepository 代码.现在,它不再依赖布尔标志,而是使用 RxAndroid 在 1 分钟间隔后自动更新数据:

公共类 CoinRepository {private static final String TAG =回购/硬币";私人最终 CoinDao coinDao;私人最终 LiveData<List<Coin>>硬币;public CoinRepository(应用程序){coinDao = DB.getInstance(application).coinDao();硬币 = coinDao.getAll();loadCoinsPeriodically();Log.d(TAG, 新实例创建...");}public LiveData<List<Coin>>获得硬币() {返还硬币;}私人无效loadCoinsPeriodically(){Observable.interval(0, 1, TimeUnit.MINUTES).observeOn(AndroidSchedulers.mainThread()).订阅(aLong ->RetrofitService.getClient().getLatest(500).subscribeOn(AndroidSchedulers.mainThread()).订阅(加密列表 ->DB.databaseWriteExecutor.submit(() -> {coinDao.deleteAll();coinDao.insertAll(cryptoList.getCoins());Log.d(TAG,数据插入\"硬币\表");}),可投掷 ->Log.d(TAG, "API observable error: " + throwable.getMessage())),可投掷 ->Log.d(TAG, "周期性可观察错误:" + throwable.getMessage()));}公共 LiveData<Coin>getCoin(int id) {返回 coinDao.get(id);}}

我为 OP 的用例编写了一个示例应用程序,查看此 Github 存储库 以获取完整代码.

My code is working fine but every time people open the coin activity it takes about 5 seconds to load 500 coins from coinmarketcap api (name, symbolName, price, vol and marketcap). I used a facebook shimmer to let people know that something is loading but 5 seconds is a lot tho because the app is loading more heavy stuff within 1 to 2 seconds, for example, wordpress data with images. But, these 500 coins in 5 seconds is not cool... Here is my code...

    public void getCoinList() {
    
    //posts = 500
    ApiInterface apiInterfaceCoin5 = 
    APIClientCoin.getClient().create(ApiInterface.class);
    Map<String, String> params = new HashMap<>();
    params.put("limit", posts+"");

     Call<CryptoList> call5 = apiInterfaceCoin5.doGetUserListAll(params);
  
    call5.enqueue(new Callback<CryptoList>() {
        @Override
        public void onResponse(Call<CryptoList> call, Response<CryptoList> response) 
    {

            shimmerFrameLayout.stopShimmer();
            shimmerFrameLayout.setVisibility(View.GONE);
            swipeRefreshLayout5.setRefreshing(false);

            int beforeCoinSize = cryptoList5.size();

            CryptoList list5 = response.body();


            cryptoList5.addAll(list5.getData());

            recyclerView4.setAdapter(adapterCoin5);

        }

        @Override
        public void onFailure(Call<CryptoList> call, Throwable t) {
            //Toast.makeText(CryptoListAllCoinActivity.this, "onFailure", 
          Toast.LENGTH_SHORT).show();
          //  Log.d("XXXX", t.getLocalizedMessage());
            call.cancel();
            progressBar3.setVisibility(View.GONE);
            shimmerFrameLayout.setVisibility(View.GONE);
            swipeRefreshLayout5.setRefreshing(false);
        }
      });
   }



@Headers("X-CMC_PRO_API_KEY: HIDDEN")
@GET("/v1/cryptocurrency/listings/latest")
Call<CryptoList> doGetUserListAll(@QueryMap Map<String, String> params);

Adapter:

    // Involves populating data into the item through holder
    // binds the data to the TextView in each row
    @Override
    public void onBindViewHolder(ViewHolder holder, int 
    position) {
    // Get the data model based on position
    Datum datum = mData.get(position);

    //load coin icon
    //if the link doesn't work then you have to upload it into 
    your own server
    Glide.with(context)
            .load(new 



     StringBuilder
     ("https://s2.coinmarketcap.com/static/img/coins/64x64/")

    .append(datum.getId())
    .append(".png").toString())
.placeholder(R.drawable.money_icon).into(holder.coin_icon);
    
    Glide.with(context)
            .load(new 


  StringBuilder
    ("https://s3.coinmarketcap.com/generated/
    sparklines/web/7d/usd/")
                    .append(datum.getId())
                    .append(".png").toString())
            .placeholder(R.drawable.line_24)
            .into(holder.sparkline);


    TextView symbolName = holder.symbolName;
    symbolName.setText(datum.getSymbol());

    // Set item views based on your views and data model
    TextView name = holder.name;
    name.setText(datum.getName());

    TextView price = holder.price;
    TextView priceDetails = holder.priceDetails;

    TextView marketCap = holder.marketCap;
    marketCap.setText("$" + 
   formatNumber(datum.getQuote().getUSD().getMarketCap()));


    ImageView coin_icon = holder.coin_icon;
    ImageView sparkline = holder.sparkline;


    if(datum.getQuote().getUSD().getPrice() >= 1) {
        price.setText("$" + 
 formatNumber(datum.getQuote().getUSD().getPrice()));
    }else{
        price.setText("$" + String.format("%f", 
  datum.getQuote().getUSD().getPrice()));
    }

    TextView textView24h = holder.textView24h;
    textView24h.setText(String.format("%.2f", 
   datum.getQuote().getUSD().getPercentChange24h()) + "%");


    if(datum.getQuote().getUSD().getPercentChange24h() <0.000).{
        //red
        textView24h.setTextColor(Color.parseColor("#EA3943"));
        arrowImage.setImageResource(R.drawable.arrow_down);
        sparkline.setColorFilter(Color.RED, 
   PorterDuff.Mode.MULTIPLY);
        sparkline.setImageResource(R.drawable.btc_spike);

        //changeImageColor(context, sparkline,000);

    }else{
        //green
        textView24h.setTextColor(Color.parseColor("#18C784"));
        arrowImage.setImageResource(R.drawable.arrow_up);
        sparkline.setColorFilter(Color.GREEN, 
 PorterDuff.Mode.MULTIPLY);
        sparkline.setImageResource(R.drawable.btc_spike);

    }


}

    Glide.with(context)
            .load(new 
     StringBuilder
    ("https://s2.coinmarketcap.com/static/img/coins/64x64/")
                    .append(datum.getId())
                    .append(".png").toString())
            .placeholder(R.drawable.money_icon)
            .into(holder.coin_icon);

UPDATE

Now I have the database setup but not sure how to insert data into the database...for example: where do I add this:

AppDatabase db = 
AppDatabase.getDbInstance(this.getApplicationContext());

    //coin object


    db.coinDao().insertAllCoins();

inside the onResponse? and how?

UPDATE 8-5-2021:

AppDatabase.class

@Database(entities = {Coins.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {

public abstract CoinDao coinDao();

private static AppDatabase INSTANCE;

public static AppDatabase getDbInstance(Context context){

    if(INSTANCE == null){

        INSTANCE = Room
                .databaseBuilder(context.getApplicationContext(), 
 AppDatabase.class,"DB_COIN")
                .allowMainThreadQueries()
                .build();

    }

    return INSTANCE;
}


}

CoinDao

@Dao
public interface CoinDao {
    @Query("SELECT * FROM coin_table")
    List<Coins> getAll();

    @Insert
    void insertAllCoins(Coins... coins);

    @Delete
    void delete(Coins coins);

    @Update
    void updateUsers(Coins... coins);
}

Coins.java

@Entity(tableName = "coin_table")
public class Coins {
@PrimaryKey(autoGenerate = true)
public int id;

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

@ColumnInfo(name = "symbol")
public String symbol;

@ColumnInfo(name = "slug")
public String slug;

@ColumnInfo(name = "circulating_supply")
public Double circulatingSupply;

@ColumnInfo(name = "total_supply")
public Double totalSupply;

@ColumnInfo(name = "max_supply")
public Double maxSupply;

@ColumnInfo(name = "date_added")
public String dateAdded;

@ColumnInfo(name = "num_market_pairs")
public Integer numMarketPairs;

@ColumnInfo(name = "cmc_rank")
public Integer cmcRank;

@ColumnInfo(name = "coin_symbol")
public String lastName;

@ColumnInfo(name = "last_updated")
public String lastUpdated;

@ColumnInfo(name = "price")
public Double price;

@ColumnInfo(name = "volume_24h")
public Double volume24h;

@ColumnInfo(name = "percent_change_1h")
public Double percentChange1h;

@ColumnInfo(name = "percent_change_24h")
public Double percentChange24h;

@ColumnInfo(name = "percent_change_7d")
public Double percentChange7d;

@ColumnInfo(name = "market_cap")
public Double marketCap;

}

UPDATE 8-6-2021 Base on Muhammad Shuja answer For the CoinRepository I am getting this error:

But if I change it to CriptoList then it says it require a List lol.... Any idea why?

Please note I use Coins with the s because that is the class name.

also if I change it to CriptoList it says that this

 coinDao.insertAll(response.body());

require a List

I have my public getCoinList() a lil bit up for reference on how I am currently getting the data from the api.

And, yes I want to update the data every 1...like I would like to make an api call and update the data every 1 minute. Thankssssssssss

解决方案

Create a CoinRepository and implement your logic there. If DB is empty or it is required to refresh data, let your repo query data from API and insert it in DB, and then fetch it from DB whenever needed.

Check this codelab to practically understand Room.

Create Coin entity, it's a good practice to keep entity/model classes name singular, and their respective table names plural.

Coin.java

@Entity(tableName = "coins")
public class Coin {
    @PrimaryKey(autoGenerate = true)
    public int id;

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

    @ColumnInfo(name = "symbol")
    public String symbol;

    @ColumnInfo(name = "slug")
    public String slug;

    @ColumnInfo(name = "circulating_supply")
    public Double circulatingSupply;

    @ColumnInfo(name = "total_supply")
    public Double totalSupply;

    @ColumnInfo(name = "max_supply")
    public Double maxSupply;

    @ColumnInfo(name = "date_added")
    public String dateAdded;

    @ColumnInfo(name = "num_market_pairs")
    public Integer numMarketPairs;

    @ColumnInfo(name = "cmc_rank")
    public Integer cmcRank;

    @ColumnInfo(name = "coin_symbol")
    public String lastName;

    @ColumnInfo(name = "last_updated")
    public String lastUpdated;

    @ColumnInfo(name = "price")
    public Double price;

    @ColumnInfo(name = "volume_24h")
    public Double volume24h;

    @ColumnInfo(name = "percent_change_1h")
    public Double percentChange1h;

    @ColumnInfo(name = "percent_change_24h")
    public Double percentChange24h;

    @ColumnInfo(name = "percent_change_7d")
    public Double percentChange7d;

    @ColumnInfo(name = "market_cap")
    public Double marketCap;
}

Create CoinDao interface and add your query methods in it, I've added additional methods like get(id) and update(coin), you can use them if needed.

CoinDao.java

@Dao
public interface CoinDao {
    @Query("SELECT * FROM coins")
    LiveData<List<Coin>> getAll();

    @Query("SELECT * FROM coins WHERE id = :id")
    LiveData<Coin> get(int id);

    @Insert
    void insertAll(List<Coin> coins);

    @Update
    public void update(Coin coin);

    @Query("DELETE FROM coins")
    void deleteAll();
}

Create your database class as follows:

DB.java

@Database(entities = {Coin.class}, version = 1)
public abstract class DB extends RoomDatabase {
    private static final String TAG = "DB";

    // DB INFO
    private static final String databaseName = "coin_database";

    // DAOs
    public abstract CoinDao coinDao();


    // INSTANCE
    private static volatile DB INSTANCE;
    private static final int NUMBER_OF_THREADS = 4;
    public static final ExecutorService databaseWriteExecutor =
            Executors.newFixedThreadPool(NUMBER_OF_THREADS);

    public static DB getInstance(final Context context) {
        if (INSTANCE == null) {
            synchronized (DB.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(), DB.class, databaseName)
                            .build();
                    Log.d(TAG, "New instance created...");
                }
            }
        }
        return INSTANCE;
    }
}

Create CoinRepository and add your WebAPI/DB logic in it. Notice, I've moved your API call in getCoins() method, you need to modify it according to your logic. Make sure your call response returns List<Coin>, or you'll have to create a List<Coin> from CryptoList yourself.

CoinRepository.java

public class CoinRepository {
    private static final String TAG = "Repo/Coin";

    private final CoinDao coinDao;
    private final LiveData<List<Coin>> coins;

    public CoinRepository(Application application) {
        coinDao = DB.getInstance(application).coinDao();
        coins = coinDao.getAll();

        Log.d(TAG, "New instance created...");
    }
    
    /*
        I've added boolean flag to check if data reload from Web API is compulsory.
        You can remove this flag and modify it as per your logic.
        
        DataReadyListener is a callback listener.
        Its onDataReady() method gets fired when data is ready.         
    */
    public void getCoins(boolean reload, DataReadyListener listener) {
        if(reload){
            //Modify this portion as per your logic
            ApiInterface apiInterfaceCoin5 =
                    APIClientCoin.getClient().create(ApiInterface.class);
            Map<String, String> params = new HashMap<>();
            params.put("limit", posts+"");

            apiInterfaceCoin5.doGetUserListAll(params)
                    .enqueue(new Callback<List<Coin>>() {
                        @Override
                        public void onResponse(Call<List<Coin>> call, Response<List<Coin>> response) {
                            Future<?> future = DB.databaseWriteExecutor.submit(() -> {
                                coinDao.deleteAll(); //remove this if you want to keep previous data
                                coinDao.insertAll(response.body());

                                Log.d(TAG, "Data inserted in \"coins\" table");
                            });

                            try {
                                future.get();
                                if (future.isDone())
                                    listener.onDataReady(coins);
                            } catch (ExecutionException e) {
                                e.printStackTrace();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public void onFailure(Call<List<Coin>> call, Throwable t) {
                            //Handle failure here
                        }
                    });
        }
        else{
            listener.onDataReady(coins);
        }
    }

    public LiveData<Coin> getCoin(int id) {
        return coinDao.get(id);
    }

    public interface DataReadyListener {
        void onDataReady(LiveData<List<Coin>> coins);
    }
}

And lastley create CoinViewModel. You can completely skip this step and query data directly from CoinRepository. But ViewModel has its own pros, check docs for details.

CoinViewModel.java

public class CoinViewModel extends AndroidViewModel {
    private CoinRepository repository;

    public CoinViewModel(@NonNull Application application) {
        super(application);
        repository = new CoinRepository(application);
    }

    public void getCoins(boolean reload, CoinRepository.DataReadyListener listener) {
        repository.getCoins(reload, listener);
    }

    public LiveData<Coin> getCoin(int id) {
        return repository.getCoin(id);
    }
}

Now use CoinViewModel in your Activity/Fragment, first initialize it:

UI (Activity/Fragment)

private CoinViewModel coinViewModel;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ....
    coinViewModel = new ViewModelProvider(this).get(CoinViewModel.class);
    ....
}

And finally use your ViewModel to query data. If you want to reload the data from Web API send first parameter as true, if you want to reuse existing data from DB keep flag as false:

coinViewModel.getCoins(true, new CoinRepository.DataReadyListener() {
        @Override
        public void onDataReady(LiveData<List<Coin>> coins) {
            //Add or set data in adapter
            //for setData() you need to create setData() method in adapter
            //which first clears existing data and then adds new data to list and notifies about dataset change.
            coins.observe(this, coinsList -> coinsAdapter.setData(coinsList));
        }
    });

Hopefully, it helps.

Update 07/08/2021

After discussion with OP, I've updated the CoinRepository code. Now, instead of relying on boolean flag, it automatically updates data after 1 minute interval using RxAndroid:

public class CoinRepository {
    private static final String TAG = "Repo/Coin";

    private final CoinDao coinDao;
    private final LiveData<List<Coin>> coins;

    public CoinRepository(Application application) {
        coinDao = DB.getInstance(application).coinDao();
        coins = coinDao.getAll();

        loadCoinsPeriodically();

        Log.d(TAG, "New instance created...");
    }

    public LiveData<List<Coin>> getCoins() {
        return coins;
    }

    private void loadCoinsPeriodically() {
        Observable.interval(0, 1, TimeUnit.MINUTES)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        aLong ->
                                RetrofitService.getClient().getLatest(500)
                                        .subscribeOn(AndroidSchedulers.mainThread())
                                        .subscribe(
                                                cryptoList ->
                                                        DB.databaseWriteExecutor.submit(() -> {
                                                            coinDao.deleteAll();
                                                            coinDao.insertAll(cryptoList.getCoins());

                                                            Log.d(TAG, "Data inserted in \"coins\" table");
                                                        }),
                                                throwable ->
                                                        Log.d(TAG, "API observable error: " + throwable.getMessage())),
                        throwable ->
                                Log.d(TAG, "Periodic observable error: " + throwable.getMessage()));
    }

    public LiveData<Coin> getCoin(int id) {
        return coinDao.get(id);
    }
}

I've written an example app for OP's use case, check this Github repo for complete code.

这篇关于如何从api获取数据并将数据插入Android Studio中的ROOM(数据库)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆