SQLite 的 Recyclerview 第一次显示空白页面
Posted
技术标签:
【中文标题】SQLite 的 Recyclerview 第一次显示空白页面【英文标题】:Recyclerview from SQLite displays empty page on first time 【发布时间】:2017-05-23 08:14:11 【问题描述】:我正在尝试从异步任务中填充 recyclerview
。在异步任务中,数据通过 API 调用获取并推送到 SQLite 表,然后从 SQLite 表填充列表。首次加载时列表无法显示。但是当我关闭应用程序并重新打开应用程序时,列表会填充。这是需要正确解决的一般问题还是我缺少其他东西?
public class KingsActivity extends AppCompatActivity
RecyclerView mRecyclerView;
RecyclerView.Adapter mAdapter;
RecyclerView.LayoutManager mLayoutManager;
List<King> kingList=new ArrayList<King>();
@Override
protected void onCreate(Bundle savedInstanceState)
Log.d("method_track","onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//To avoid the load on the main thread
new DownloadDataAsync().execute(this);
public class DownloadDataAsync extends AsyncTask<Context, Integer, Context>
@Override
protected Context doInBackground(Context... ctx)
downloadData(ctx[0]);
return ctx[0];
@Override
protected void onProgressUpdate(Integer... progress)
@Override
protected void onPostExecute(Context result)
Log.d("method_track","PostExecute");
Cursor kingsRows=DatabaseHelper.getInstance(result.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_KINGS);
kingsRows.moveToFirst();
while(kingsRows.moveToNext())
String kingName=kingsRows.getString(kingsRows.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_NAME));
int battleCount=kingsRows.getInt(kingsRows.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT));
int rating=kingsRows.getInt(kingsRows.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_RATING));
kingList.add(new King(kingName,rating,battleCount));
kingsRows.close();
mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
mLayoutManager = new LinearLayoutManager(result);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new KingsAdapterRC(kingList,result);
mRecyclerView.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
public void downloadData(final Context ctx)
// Get a RequestQueue
RequestQueue queue = HttpRequestHandler.getInstance(ctx.getApplicationContext()).
getRequestQueue();
String url ="http://starlord.hackerearth.com/gotjson";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>()
@Override
public void onResponse(String response)
// Display the first 500 characters of the response string.
//Log.d("result_check",response.substring(0,500));
loadToDb(response,ctx);
populateList(ctx);
calculateRating(ctx);
logRatings(ctx);
//mTextView.setText("Response is: "+ response.substring(0,500));
, new Response.ErrorListener()
@Override
public void onErrorResponse(VolleyError error)
Log.d("result_check","Error");
//mTextView.setText("That didn't work!");
);
// Add a request to RequestQueue.
HttpRequestHandler.getInstance(ctx).addToRequestQueue(stringRequest);
protected void loadToDb(String jsonResponse,Context ctx)
DatabaseHelper.getInstance(ctx.getApplicationContext()).truncate(DatabaseHelper.TABLE_WESTEROS_DATA);
try
JSONArray jsonArray = new JSONArray(jsonResponse);
for(int i=0;i<jsonArray.length();i++)
JSONObject jsonObj = (JSONObject)jsonArray.get(i);
Iterator<String> iter = jsonObj.keys();
HashMap<String,String> fieldVales=new HashMap<String, String>();
while (iter.hasNext())
String key = iter.next();
try
Object value = jsonObj.get(key);
fieldVales.put(key,value.toString());
catch (JSONException e)
// Something went wrong!
Log.e("loadTodb",e.toString());
DatabaseHelper.getInstance(ctx.getApplicationContext()).insert(DatabaseHelper.TABLE_WESTEROS_DATA,fieldVales);
catch (Throwable t)
//Log.e("My App", "Could not parse malformed JSON: \"" + json + "\"");
protected void populateList(Context ctx)
List<King> kingList=new ArrayList<King>();
String[] kings;
int kingsCount=0;
String sql="SELECT DISTINCT "+DatabaseHelper.KEY_WESTEROS_ATTACKER_KING+ " FROM "+ DatabaseHelper.TABLE_WESTEROS_DATA
+" WHERE "+ DatabaseHelper.KEY_WESTEROS_ATTACKER_KING +" <> ''";
Cursor rows=DatabaseHelper.getInstance(ctx.getApplicationContext()).getReadableDatabase().rawQuery(sql, null);
kingsCount+=rows.getCount();
String sql2="SELECT DISTINCT "+DatabaseHelper.KEY_WESTEROS_DEFENDER_KING + " FROM "+ DatabaseHelper.TABLE_WESTEROS_DATA
+ " WHERE "+ DatabaseHelper.KEY_WESTEROS_DEFENDER_KING + " NOT IN ( "+sql+ " ) AND "
+ DatabaseHelper.KEY_WESTEROS_DEFENDER_KING +" <> ''";
Cursor rows2=DatabaseHelper.getInstance(ctx.getApplicationContext()).getReadableDatabase().rawQuery(sql2, null);
kingsCount+=rows2.getCount();
kings=new String[kingsCount];
int i=0;
if (rows.moveToFirst())
while (!rows.isAfterLast())
//your code to implement
kings[i]=rows.getString(rows.getColumnIndex(DatabaseHelper.KEY_WESTEROS_ATTACKER_KING));
i++;
rows.moveToNext();
rows.close();
if (rows2.moveToFirst())
while (!rows2.isAfterLast())
//your code to implement
kings[i]=rows2.getString(rows2.getColumnIndex(DatabaseHelper.KEY_WESTEROS_DEFENDER_KING));
i++;
rows2.moveToNext();
rows2.close();
DatabaseHelper.getInstance(ctx.getApplicationContext()).truncate(DatabaseHelper.TABLE_WESTEROS_KINGS);
for(i=0;i<kingsCount;i++)
HashMap<String,String> fieldValues=new HashMap<String, String>();
fieldValues.put(DatabaseHelper.KEY_WESTEROS_KINGS_NAME,kings[i]);
fieldValues.put(DatabaseHelper.KEY_WESTEROS_KINGS_RATING,"400");
fieldValues.put(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT,"0");
DatabaseHelper.getInstance(ctx.getApplicationContext()).insert(DatabaseHelper.TABLE_WESTEROS_KINGS,fieldValues);
protected void calculateRating(Context ctx)
Cursor battles_cur=DatabaseHelper.getInstance(ctx.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_DATA);
if(battles_cur.moveToFirst())
while(!battles_cur.isAfterLast())
String attackingKing=battles_cur.getString(battles_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_ATTACKER_KING));
String defendingKing=battles_cur.getString(battles_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_DEFENDER_KING));
if(!attackingKing.equals("") && !defendingKing.equals(""))
HashMap<String,String> whereConDfk=new HashMap<String,String>();
whereConDfk.put(DatabaseHelper.KEY_WESTEROS_KINGS_NAME,defendingKing);
Cursor cursor1=DatabaseHelper.getInstance(ctx.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_KINGS,whereConDfk);
double defKing_rating;
double defKing_battleCount;
if(cursor1.moveToFirst())
defKing_rating=cursor1.getDouble(cursor1.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_RATING));
defKing_battleCount=cursor1.getDouble(cursor1.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT));
cursor1.close();
HashMap<String,String> whereConAtk=new HashMap<String,String>();
whereConAtk.put(DatabaseHelper.KEY_WESTEROS_KINGS_NAME,attackingKing);
Cursor cursor2=DatabaseHelper.getInstance(ctx.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_KINGS,whereConAtk);
Double atkKing_rating;
Double atkKing_battleCount;
if(cursor2.moveToFirst())
atkKing_rating=cursor2.getDouble(cursor2.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_RATING));
atkKing_battleCount=cursor2.getDouble(cursor2.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT));
cursor2.close();
atkKing_battleCount++;
defKing_battleCount++;
Double defKing_rating_tr=Math.pow(10,(defKing_rating/400));
Double atkKing_rating_tr=Math.pow(10,(atkKing_rating/400));
Double defKing_rating_ex=defKing_rating_tr/(defKing_rating_tr+atkKing_rating_tr);
Double atkKing_rating_ex=atkKing_rating_tr/(defKing_rating_tr+atkKing_rating_tr);
String attackerStatus=battles_cur.getString(battles_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_ATTACKER_OUTCOME));
Double atkKing_rating_new=atkKing_rating;
Double defKing_rating_new=defKing_rating;
if(attackerStatus.equals("win"))
atkKing_rating_new=atkKing_rating+(32*(1-atkKing_rating_ex));
defKing_rating_new=defKing_rating+(32*(0-defKing_rating_ex));
else if(attackerStatus.equals("loss"))
atkKing_rating_new=atkKing_rating+(32*(0-atkKing_rating_ex));
defKing_rating_new=defKing_rating+(32*(1-defKing_rating_ex));
else if(attackerStatus.equals("draw"))
atkKing_rating_new=atkKing_rating+(32*(0.5-atkKing_rating_ex));
defKing_rating_new=defKing_rating+(32*(0.5-defKing_rating_ex));
String update_atkKing_ratingQuery="UPDATE "+ DatabaseHelper.TABLE_WESTEROS_KINGS + " SET "
+ DatabaseHelper.KEY_WESTEROS_KINGS_RATING+" = "+atkKing_rating_new+", "
+ DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT+" = "+atkKing_battleCount
+ " WHERE "+ DatabaseHelper.KEY_WESTEROS_KINGS_NAME +" =\""+attackingKing+"\"";
String update_defKing_ratingQuery="UPDATE "+ DatabaseHelper.TABLE_WESTEROS_KINGS + " SET "
+ DatabaseHelper.KEY_WESTEROS_KINGS_RATING+" = "+defKing_rating_new+", "
+ DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT+" = "+defKing_battleCount
+ " WHERE "+ DatabaseHelper.KEY_WESTEROS_KINGS_NAME +" =\""+defendingKing+"\"";
DatabaseHelper.getInstance(ctx.getApplicationContext()).getWritableDatabase().execSQL(update_atkKing_ratingQuery);
DatabaseHelper.getInstance(ctx.getApplicationContext()).getWritableDatabase().execSQL(update_defKing_ratingQuery);
battles_cur.moveToNext();
protected void logRatings(Context ctx)
Log.d("method_track","logratings");
Cursor kings_cur=DatabaseHelper.getInstance(ctx.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_KINGS);
try
while (kings_cur.moveToNext())
String name=kings_cur.getString(kings_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_NAME));
String rating=kings_cur.getString(kings_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_RATING));
String battleCount=kings_cur.getString(kings_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT));
//Log.d("method_track", "logratings");
Log.d("rating_inspect",name+" - "+rating+" - "+battleCount);
finally
kings_cur.close();
【问题讨论】:
调试你的应用程序..它会第一次告诉你到底发生了什么......尝试删除 mAdapter.notifyDataSetChanged(); 不要在 onPostExecute 中初始化您的回收器视图,然后在调用异步任务之前执行此操作,并在从异步任务(即 onPostExecute 内部)获取列表后通过调用 notifyDataSetChange 更新适配器列表 【参考方案1】:这里:
downloadData(ctx[0]);
downloadData
方法引起的问题。
在downloadData
方法中使用StringRequest
从服务器获取数据。 StringRequest
处理工作线程上的所有请求并使用Response.Listener
返回结果。
doInBackground
也完成了工作线程的所有工作。所以doInBackground
方法的工作线程只是执行downloadData
方法而不等待从StringRequest
得到响应。
使用StringRequest
时无需使用额外的线程。只需删除 AsyncTask
并仅使用 StringRequest
即可使其正常工作。
【讨论】:
当我在没有异步任务的情况下使用时,我收到此消息I/Choreographer: Skipped 68 frames! The application may be doing too much work on its main thread.
,这是由于方法calculateRating
@DharanBro:是的,这是可能的,因为您也在 onResponse
方法中执行其他操作。所以只需在onResponse
中使用`AsyncTask` 来做其他操作
@DharanBro:将您的代码安排为 StringRequest 请求 -> onResponse -> 运行 AsyncTask
以在 doInBackground
中调用 loadToDb(response,ctx);
方法并从 onPostExecute
调用 populateList(ctx);calculateRating(ctx);logRatings(ctx);
这就是我对代码所做的混乱。现在它完美地工作了。它让我失去了我的夜晚。谢谢【参考方案2】:
你不应该从doInBackground
调用volley StringRequest
,因为async task
立即执行post execute
方法,您的列表将是空的,因此它不会显示任何数据。只需使用string request
并在获得响应后将其保存在您的“数据库”中并从那里获取(您可以直接从database
保存和获取数据或使用异步任务)。
不要只使用StringRequest
而不使用任何async task
并对其进行测试。
【讨论】:
以上是关于SQLite 的 Recyclerview 第一次显示空白页面的主要内容,如果未能解决你的问题,请参考以下文章
如何在基于 SQLite 的 recyclerview 中实现 searchview? - 安卓
使用 SQLite 的搜索结果更新 RecyclerView
如何使用光标和循环显示来自 sqlite 的片段的 recyclerview
使用从 SQLite 概括的数据填充 RecyclerView/ListView