如何从api获取数据并将数据插入Android Studio中的ROOM(数据库)?

时间:2021-07-31 13:14:40

标签: java android json api

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

facebook shimmer

适配器:

    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);

更新

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

    // 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);

在 onResponse 里面?以及如何?

2021 年 8 月 5 日更新:

AppDatabase.class

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

    //coin object


    db.coinDao().insertAllCoins();

币道

@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;
}


}

Coins.java

@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);
}

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

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

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

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

@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;

}

需要一个列表

我有我的 coinDao.insertAll(response.body()); 供参考,以了解我目前如何从 api 获取数据。

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

2 个答案:

答案 0 :(得分:1)

OkHttp 客户端默认可以并发 64 个请求。您可以使用该值并增加 maxRequests 数字。创建新的 OkHttp 调度程序:

Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(200);

然后,初始化 OkHttp 客户端:

OkHttpClient client = new OkHttpClient.Builder()
        ...
        .dispatcher(dispatcher)
        .build();

最后,将这个 OkHttp 添加到 Retrofit 中:

Retrofit retrofit = new Retrofit.Builder()
        ...
        .client(client)
        .build();

答案 1 :(得分:1)

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

检查 this codelab 以实际了解 Room。

创建 Coin 实体,最好保持实体/模型类名称单数,各自的表名称复数。

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;
}

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

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();
}

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

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;
    }
}

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

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);
    }
}

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

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);
    }
}

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

用户界面(活动/片段)

private CoinViewModel coinViewModel;

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

最后使用您的 ViewModel 查询数据。如果要从 Web API 重新加载数据,请将第一个参数发送为 true,如果要重用来自 DB 保留标志的现有数据为 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));
        }
    });

希望能有所帮助。

2021 年 7 月 8 日更新

与 OP 讨论后,我更新了 CoinRepository 代码。现在,它不再依赖布尔标志,而是使用 RxAndroid:

在 1 分钟间隔后自动更新数据
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);
    }
}

我为 OP 的用例编写了一个示例应用程序,check this Github repo 为完整代码。

相关问题