我的代码运行良好,但每次人们打开硬币活动时,从 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,我收到此错误:
但是如果我把它改成 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
答案 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
:
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 为完整代码。