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

如何从 recyclerview 和 sqlite 中删除项目? [复制]

使用SQLite的搜索结果更新RecyclerView