如何使用 AsyncTask 更新 RecyclerView 项目

Posted

技术标签:

【中文标题】如何使用 AsyncTask 更新 RecyclerView 项目【英文标题】:How to update the RecyclerView Items Using AsyncTask 【发布时间】:2019-05-31 17:27:41 【问题描述】:

我正在尝试从SQLite 数据库中的数据更新recyclerview 项目,而这些数据又会不时使用从 API 获取的数据进行更新。

我有一个TabLayout,其中包含三个fragments

现在在一个片段中,我以recyclerView 的形式展示了用户玩的游戏。 recyclerView 的每一项都包含用户名、他的高分和编号。击败他的高分的其他用户。

现在在片段中,我调用 API 来更新 AsyncTask 内的数据库,然后再次使用 RecyclerView 适配器类中的同一数据库检索另一个 AsyncTask 中的数据。使用TimerTask 和处理程序定期调用AsyncTask

 @TargetApi(Build.VERSION_CODES.CUPCAKE)
    @RequiresApi(api = Build.VERSION_CODES.CUPCAKE)
    public class PerformBackgroundTask extends AsyncTask<String, Void, String>

        @Override
        protected String doInBackground(String... gameID) 

            try

                SQLiteDatabase challengeDatabase = mContext.openOrCreateDatabase("Challenge", Context.MODE_PRIVATE, null);

                challengeDatabase.execSQL("CREATE TABLE IF NOT EXISTS DISPLAY_MESSAGE (gameID VARCHAR, highestScorer VARCHAR, count INT)");
                Cursor d = challengeDatabase.rawQuery("SELECT * FROM DISPLAY_MESSAGE WHERE gameID", null);

                int gameCode = d.getColumnIndex("gameID");
                int highestScorer = d.getColumnIndex("highestScorer");
                int count = d.getColumnIndex("count");

                d.moveToFirst();

                while(d!=null&&d.getCount()>0)
                    if((d.getString(gameCode)).equals(gameID))

                        int k = Integer.parseInt(d.getString(count));

                        if(k>0)
                        
                            return d.getString(highestScorer) + " and " + k + " others have beaten your highscore.";
                        else if(k==0)
                            return d.getString(highestScorer) + " has beaten your highscore.";
                        
                        else
                            return "Yay! you have beaten all your opponents!";
                        
                    

                    d.moveToNext();
                

                challengeDatabase.close();
            catch (Exception e)
                e.printStackTrace();
            
            return null;
        
    


    public GridAdapterChallenge(Context c, List<CardContainer> listCards, Vector<Object> cardDetails, String sname) 
        //selectedCards = new HashSet<Integer>();
        inflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mContext = c;
        this.listCards = listCards;
        this.sname = sname;
        cards = cardDetails;
        //notifyDataSetChanged();

    

    public class ViewHolder extends RecyclerView.ViewHolder 

        private int viewType;
        private ImageView imageView;
        private Button increaseScore, challengeFriend;
        private TextView challengeDetails, points, name, challengeCode;
        private ImageView nativeAdIcon;
        private TextView nativeAdTitle;
        private TextView nativeAdBody;
        private MediaView nativeAdMedia;
        private TextView nativeAdSocialContext;
        private Button nativeAdCallToAction;
        private ProgressBar detailProgress;


        public ViewHolder(@NonNull View itemView, int viewType) 
            super(itemView);

            view = itemView;
            this.viewType = viewType;
            if (viewType == 1) 
                this.imageView = (ImageView) itemView.findViewById(R.id.thumb);
                this.name = (TextView) itemView.findViewById(R.id.gameName);
                this.challengeCode = itemView.findViewById(R.id.challengeCode);
                this.points = itemView.findViewById(R.id.points);
                this.challengeDetails = itemView.findViewById(R.id.challengeDetail);
                this.increaseScore = itemView.findViewById(R.id.increaseScore);
                this.challengeFriend = itemView.findViewById(R.id.challengeFriend);
                this.challengeDetails = itemView.findViewById(R.id.challengeDetail);
                this.detailProgress = itemView.findViewById(R.id.detailProgressBar);
             else 
                this.nativeAdIcon = (ImageView) itemView.findViewById(R.id.native_ad_icon);
                this.nativeAdTitle = (TextView) itemView.findViewById(R.id.native_ad_title);
                this.nativeAdBody = (TextView) itemView.findViewById(R.id.native_ad_body);
                this.nativeAdMedia = (MediaView) itemView.findViewById(R.id.native_ad_media);
                this.nativeAdSocialContext = (TextView) itemView.findViewById(R.id.native_ad_social_context);
                this.nativeAdCallToAction = (Button) itemView.findViewById(R.id.native_ad_call_to_action);
            
        
    

    public void updateDetails(final TextView challengeDetails, final String gameId) 
        final Handler handler;
        handler = new Handler();
        Timer timer = new Timer();
        doAsynchronousTask = new TimerTask() 
            @Override
            public void run() 
                handler.post(new Runnable() 
                    public void run() 
                        try 
                            PerformBackgroundTask performBackgroundTask = new PerformBackgroundTask();
                            challengeDetails.setText((CharSequence) performBackgroundTask.execute(gameId));

                         catch (Exception e) 
                        
                    
                );
            
        ;
        timer.schedule(doAsynchronousTask, 4000, 5000); //execute in every 1000 ms
    

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder viewHolder, final int i) 
        if (viewHolder.viewType == 0) 
            if (adList != null && adList.size() > (i+1)/5 - 1 && adList.get((i+1)/5 - 1) != null) 
                NativeAd ad = adList.get((i+1)/5 - 1);
                viewHolder.nativeAdSocialContext.setText(ad.getAdSocialContext());
                viewHolder.nativeAdCallToAction.setText(ad.getAdCallToAction());
                viewHolder.nativeAdCallToAction.setVisibility(View.VISIBLE);
                viewHolder.nativeAdTitle.setText(ad.getAdHeadline());
                viewHolder.nativeAdBody.setText(ad.getAdBodyText());
                ad.registerViewForInteraction(viewHolder.itemView, viewHolder.nativeAdMedia, viewHolder.nativeAdIcon, Arrays.asList(viewHolder.nativeAdCallToAction, viewHolder.nativeAdMedia));
                NativeAd.Image adCoverImage = ad.getAdCoverImage();
                int bannerWidth = adCoverImage.getWidth();
                int bannerHeight = adCoverImage.getHeight();
                DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
                int mediaWidth = viewHolder.itemView.getWidth() > 0 ? viewHolder.itemView.getWidth() : metrics.widthPixels;
                viewHolder.nativeAdMedia.setLayoutParams(new LinearLayout.LayoutParams(mediaWidth, Math.min(
                        (int) (((double) mediaWidth / (double) bannerWidth) * bannerHeight), metrics.heightPixels / 3)));
                ad.registerViewForInteraction(viewHolder.itemView, viewHolder.nativeAdMedia, Arrays.asList(viewHolder.nativeAdCallToAction, viewHolder.nativeAdMedia));
                ad.registerViewForInteraction(viewHolder.itemView, viewHolder.nativeAdMedia);
            
         else

            viewHolder.name.setText(((CardContainer)cards.get(i)).name);
            challengeCode = getChallengeCode(i);
            viewHolder.challengeCode.setText(challengeCode);
            DisplayMetrics displayMetrics = new DisplayMetrics();
            ((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

            if(getHighScore(((CardContainer)cards.get(i)).gameId)>0)
                PerformBackgroundTask performBackgroundTask = new PerformBackgroundTask();
                viewHolder.challengeDetails.setText(performBackgroundTask.execute(gameId).toString());
                //updateDetails(viewHolder.challengeDetails, ((CardContainer)cards.get(i)).gameId);
                
            else
                viewHolder.challengeDetails.setText("Press Increase Score To Play the Game");
            
            viewHolder.detailProgress.setVisibility(View.GONE);


            int width = displayMetrics.widthPixels;
            try 
                final String uri = ((CardContainer) cards.get(i)).imageurl;
                if (uri != null) 
                    Picasso.get().load(uri)
                            .placeholder(R.mipmap.place)
                            .error(R.mipmap.place)
                            .resize(width / 2, width / 2)
                            .centerCrop()
                            .into(viewHolder.imageView);

                
            
            catch (Exception e)
                final String uri = null;
            

            viewHolder.points.setText(String.valueOf(getHighScore(((CardContainer)cards.get(i)).gameId)));

            viewHolder.increaseScore.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View arg0) 
                    if (((CardContainer) cards.get(i)).link.contains("play.google.com")) 
                        openAppRating(mContext,((CardContainer) cards.get(i)).link.split("id=", 2)[1]);
                    
                    else
                        name = ((CardContainer) cards.get(i)).name;
                        link = ((CardContainer) cards.get(i)).link;
                        imageurl = ((CardContainer) cards.get(i)).imageurl;
                        type = ((CardContainer) cards.get(i)).type;
                        packageName = ((CardContainer) cards.get(i)).packageName;
                        gameId = ((CardContainer)cards.get(i)).gameId;

                        challengeNames.add(name);

                        updateDatabase(name, gameId, Config.uid+"@"+gameId, 0, viewHolder.points);

                        //String gameName, String gameId, String mychCode, int myHighScore
                    
                
            );
            viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() 
                String name = ((CardContainer) cards.get(i)).name;
                String link = ((CardContainer) cards.get(i)).link;
                String imageurl = ((CardContainer) cards.get(i)).imageurl;
                String type = ((CardContainer) cards.get(i)).type;
                String packageName = ((CardContainer) cards.get(i)).packageName;
                Bitmap bitmap = null;
                Integer xyz = android.os.Build.VERSION.SDK_INT;
                @Override
                public boolean onLongClick(View view) 

                    bitmap = ((BitmapDrawable) viewHolder.imageView.getDrawable()).getBitmap();
                    if (sname.equals("fav")) 
                        //    Toast.makeText(mContext, "in fav", Toast.LENGTH_SHORT).show();
                        final Dialog dialog = new Dialog(mContext);
                        dialog.setContentView(R.layout.custom_dialog);
                        TextView dialogText=dialog.findViewById(R.id.txt_dia);
                        dialogText.setText("Do you want to remove "+name+" from your favorites?");
                        dialog.show();
                        Button yes_button = (Button) dialog.findViewById(R.id.btn_yes);
                        yes_button.setOnClickListener(new View.OnClickListener() 
                            @Override
                            public void onClick(View v) 
                                favf = new FavFunction(mContext);
                                int resultFav=favf.removeFromFavorite(link);
                                if (resultFav>=0)
                                    cards.remove(resultFav);

                                    notifyDataSetChanged();
                                
                                dialog.dismiss();
                            
                        );
                        Button no_button = (Button) dialog.findViewById(R.id.btn_no);
                        no_button.setOnClickListener(new View.OnClickListener() 
                            @Override
                            public void onClick(View v) 
                                dialog.dismiss();
                            
                        );
                        return true;
                    
                    else
                        final Dialog dialog = new Dialog(mContext);
                        dialog.setContentView(R.layout.custom_dialog);
                        TextView dialogText=dialog.findViewById(R.id.txt_dia);
                        dialogText.setText("Adding "+name+" to favorites... Do you also want to create it's homescreen shortcut?");
                        dialog.show();
                        Button yes_button = (Button) dialog.findViewById(R.id.btn_yes);
                        yes_button.setOnClickListener(new View.OnClickListener() 
                            @Override
                            public void onClick(View v) 
                                favf = new FavFunction(mContext);
                                Boolean favstatus=favf.addToFavorite(imageurl, link ,name,type,packageName);
                                favf.addShortcut(name, link , imageurl,type,packageName,bitmap);
                                if(favstatus)
                                    ((Activity) mContext).recreate();
                                
                                dialog.dismiss();
                            
                        );
                        Button no_button = (Button) dialog.findViewById(R.id.btn_no);
                        no_button.setOnClickListener(new View.OnClickListener() 
                            @Override
                            public void onClick(View v) 
                                favf = new FavFunction(mContext);
                                Boolean favstatus=favf.addToFavorite(imageurl, link, name,type,packageName);
                                if(favstatus)
                                    ((Activity) mContext).recreate();
                                
                                dialog.dismiss();
                            
                        );
                        return true;
                    
                
            );

            viewHolder.challengeFriend.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View view) 
                    Intent sendIntent = new Intent();
                    sendIntent.setAction(Intent.ACTION_SEND);
                    sendIntent.putExtra(Intent.EXTRA_TEXT, "Hey! I challenge you to beat my highscore " + getHighScore(gameId) + " in this awesome game. Enter my challenge code " + challengeCode + " in the \"Challenges\" Tab of 101 Game Store App. You can download the app from bit.ly/101gamestore");
                    sendIntent.setType("text/plain");
                    mContext.startActivity(sendIntent);
                
            );
        
    

我想更新号码。 recyclerView 的每个项目中的高分者。然而,正在发生的事情是,一旦TimerTask 启动,UI 就会冻结。正在成功获取数据。任何比我更好的解决方案都将不胜感激。

【问题讨论】:

如果您使用 ROOM 然后公开 LiveData&lt;List&lt;T&gt;&gt; 并将其放入 ViewModel 然后观察它,您不应该在修改后使用第二个 AsyncTask 从数据库中读取新数据从您的 Activity/Fragment 中,然后将新收到的列表传递给 ListAdapter,然后大部分可以删除 @EpicPandaForce 您能否进一步详细说明或为我提供资源。这些术语对我来说似乎很陌生。 见codelabs.developers.google.com/codelabs/android-persistence/#5 @EpicPandaForce 谢谢伙计!非常有用的链接。 【参考方案1】:

要更新您的适配器,您应该在主线程中进行。 AsyncTask 提供 onPostExecute 方法。

不要替换适配器,而是应该替换适配器中的数据。

class MyAsyncTask extends AsyncTask<Void, Void, List<String>> 
    MyAdapter myAdapter;
    ArrayList<String> values = new ArrayList<>();

    public MyAsyncTask(MyAdapter adapter) 
        this.myAdapter = myAdapter;
    

    @Override
    protected List<String> doInBackground(String... params) 
        ArrayList<String> result = new ArrayList<>();
        // long operation, for example: get results from url
        return result;
    

    @Override
    protected void onPostExecute(List<String> list) 
        myAdapter.setNewList(list);
    


class MyAdapter 

    private List<String> list;

    ............

    void setNewList(List<String> list) 
        this.list = list;
        notifyDataSetChanged();
    

    ............

【讨论】:

数据库不包含要在 ItemView 中显示的完整数据。 ItemView 包含两种类型的信息:静态和动态。我需要更新动态的,数据库中只有那么多信息。 在 doInBackground 方法中,您应该从数据库中收集数据并将其映射到适配器数据结构。然后在 onPostExecute 方法中,您应该将数据列表设置为适配器。适配器应检查不等于列表项并调用 notifyItemChanged。【参考方案2】:

看看这里....

 class MyAsyncTask extends AsyncTask<String, Void, String> 
    Context mContext;
    ArrayList<String> values= new ArrayList<String>();

    public MyAsyncTask(Context context) 
        mContext = context;
    

    @Override
    protected String doInBackground(String... params) 
        // do your all database operation here and try to stroe in arraylist
        values.add("Data comes from database");
        return "";

    

    @Override
    protected void onPostExecute(String list) 
     // Do your View update here.
    // All your Recycle View here
    yourRecycleView.setAdapter(new MyAdapter(context,values))
    

注意:- 在 AsynTask 中,在 onPreExecute() 方法中完成所有绑定工作,在 doInBackground() 方法中完成所有数据库或长线程工作(例如从数据库中获取数据,下载文件或上传文件等)并更新您的视图在onPostExecute() 方法中。

如何使用 AsyncTask 更新 RecyclerView 项目?

按照相同的过程在doInBackground() 方法中获取您的数据,并在onPostExecute() 方法中通知您的适配器。在其他 AsynTask 中执行此操作

【讨论】:

当您直接在线程中的适配器中更新数据时,任何人都可以通知数据集已更改。适配器将处于无效状态。在 onPostExecute 中需要从 recyclerView 获取适配器,转换为 MyAdapter 然后使用列表参数调用更新方法。更新方法调用 notifyDataSetChanged 或 itemChanged/inserted/removed。 如果您在 onPostExecute() 方法中执行 adapter.notifyDataSetChanged()。数据不会变吗? onPostExecute()UI thread,我认为在onPostExecute() 方法中更改您的视图或适配器不会给您带来任何问题。 我知道在主线程中调用了 onPostExecute。我只是澄清一下,因为这是一个常见问题。 @SushilKumar 数据库不包含要在 ItemView 中显示的完整数据。 ItemView 包含两种类型的信息:静态和动态。我需要更新动态的,数据库中只有那么多信息。

以上是关于如何使用 AsyncTask 更新 RecyclerView 项目的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AsyncTask 的 onPostExecute 方法更新 Android 中的变量

重新创建活动时如何从 AsyncTask 更新 TextView

如何在 AsyncTask 类方法中更新列表的文本视图?

如何在 AsyncTask 中更新 ArrayList 的内容?

AsyncTask Android 中的致命信号 6 (SIGABRT)

在 Android 中,如何从 AsyncTask 更改 TextView? [复制]