【问题标题】:Android: Skipped 41 frames! The application may be doing too much work on its main threadAndroid:跳过了 41 帧!应用程序可能在其主线程上做了太多工作
【发布时间】:2021-01-02 13:07:37
【问题描述】:

我真的很难理解这个话题。下面的代码是我的主要活动,我只是想通过运行一个简单的查询来显示一个配方来测试我的 sqlite 数据库是否正常工作。 目前该应用程序只运行主屏幕,但没有其他工作。我该怎么做才能让它工作? 如果需要,我可以提供更多代码/sn-ps。谢谢 包 com.stu54259.plan2cook;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import com.google.android.material.bottomappbar.BottomAppBar;
import com.synnapps.carouselview.CarouselView;
import com.synnapps.carouselview.ImageListener;

public class MainActivity extends AppCompatActivity {

    CarouselView carouselView;
    int[] sampleImages = {R.drawable.avocado_salad, R.drawable.chicken_tikka_curry, R.drawable.turkey_taco_bowls};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setUpBottomAppBar();

        carouselView = findViewById(R.id.recipeImage);
        carouselView.setPageCount(sampleImages.length);
        carouselView.setImageListener(imageListener);


        findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "Create Recipe Clicked.", Toast.LENGTH_SHORT).show();
            }
        });
        GridView gv = findViewById(R.id.gridview);
        gv.setAdapter(new SetImageAdapter(this));

    }
        ImageListener imageListener = new ImageListener() {
            @Override
            public void setImageForPosition(int position, ImageView imageView) {
                imageView.setImageResource(sampleImages[position]);
            }
        };

    private void setUpBottomAppBar() {
        BottomAppBar bottomAppBar = findViewById(R.id.bar);
        setSupportActionBar(bottomAppBar);
        Log.d("appbar setup", "yes");
        bottomAppBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.recipes:
                        Toast.makeText(MainActivity.this, "Recipes clicked.", Toast.LENGTH_LONG).show();
                        Log.d("Intent", "clicked ");
                        Intent intent = new Intent(MainActivity.this, Recipe.class);
                        startActivity(intent);
                        break;
                    case R.id.shoppingList:
                        Toast.makeText(MainActivity.this, "Shopping List clicked", Toast.LENGTH_SHORT).show();
                        break;

                }
                return false;
            }
        });
    }
   /* @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.bottom_nav_primary, menu);
        return super.onCreateOptionsMenu(menu);
    }*/
}

Logcat

Skipped 41 frames!  The application may be doing too much work on its main thread.

食谱.java

    package com.stu54259.plan2cook;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.google.android.material.tabs.TabLayout;
import com.stu54259.plan2cook.Model.RecipeList;
import com.stu54259.plan2cook.database.DatabaseManager;

import java.util.ArrayList;
import java.util.List;

public class Recipe extends MainActivity {

    TabLayout tabLayout;
    ImageView recipeImage;
    TextView descriptionText, courseText, servingsText, costText, caloriesText, methodText;
    RecyclerView listIngredient;
    SQLiteDatabase db;
    String search_name, selectQuery;
    Cursor c;
    RecyclerViewAdapter adapterRecipe;
    List<RecipeList> itemRecipe = new ArrayList<>();

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recipe);
        //search_name = getIntent().getStringExtra("NAME");
        search_name = "Speedy chicken couscous";
        //recyclerview Recipe
        adapterRecipe = new RecyclerViewAdapter(this, itemRecipe);
        listIngredient = findViewById(R.id.listIngredient);

        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this,
                LinearLayoutManager.VERTICAL, false);
        listIngredient.setLayoutManager(mLayoutManager);
        listIngredient.setItemAnimator(new DefaultItemAnimator());
        listIngredient.setAdapter(adapterRecipe);
        loadRecipe();

    }
    public void loadRecipe() {
        itemRecipe.clear();
        db = (new DatabaseManager(this).getWritableDatabase());
        String selectQuery = "";
        selectQuery = selectQuery + "SELECT A.ingredient_quantity, B.measurement_name, B.ingredient_name, B.description " +
                "FROM TABLE_QUANTITY JOIN TABLE_INGREDIENT ON A.ingredient = B.ingredient_name";

        c = db.rawQuery(selectQuery, new String[]{"%" + search_name + "%"});
        if (c.moveToFirst()) {
            do {
                RecipeList recipeList = new RecipeList();
                recipeList.setId(c.getInt(c.getColumnIndex("COL_ID")));
                recipeList.setIngredient_amount(c.getString(c.getColumnIndex("COL_INGREDIENT_QUANTITY")));
                recipeList.setMeasurement_name(c.getString(c.getColumnIndex("COL_MEASUREMENT_NAME")));
                recipeList.setIngredient_name(c.getString(c.getColumnIndex("COL_INGREDIENT_NAME")));
                recipeList.setDescription(c.getString(c.getColumnIndex("COL_DESCRIPTION")));
                itemRecipe.add(recipeList);
            } while (c.moveToNext());
            c.close();
        }

    }
}

Recyclerview 适配器

package com.stu54259.plan2cook;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;

import com.stu54259.plan2cook.Model.RecipeList;

import java.util.List;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    private List<RecipeList> itemRecipe;
    private LayoutInflater mInflater;
    private ItemClickListener mClickListener;

    // data is passed into the constructor
    RecyclerViewAdapter(Context context, List<RecipeList> data) {
        this.mInflater = LayoutInflater.from(context);
        this.itemRecipe = data;
    }

    // inflates the row layout from xml when needed
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.fragment_item, parent, false);
        return new ViewHolder(view);
    }

    // binds the data to the TextView in each row
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        holder.myTextView.setText(itemRecipe.get(position).getIngredient_amount());
        holder.myTextView.setText(itemRecipe.get(position).getMeasurement_name());
        holder.myTextView.setText(itemRecipe.get(position).getIngredient_name());
        holder.myTextView.setText(itemRecipe.get(position).getDescription());

    }

    // total number of rows
    @Override
    public int getItemCount() {
        return itemRecipe.size();
    }


    // stores and recycles views as they are scrolled off screen
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        TextView myTextView;

        ViewHolder(View itemView) {
            super(itemView);
            myTextView = itemView.findViewById(R.id.listIngredient);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
        }
    }


    // allows clicks events to be caught
    void setClickListener(ItemClickListener itemClickListener) {
        this.mClickListener = itemClickListener;
    }

    // parent activity will implement this method to respond to click events
    public interface ItemClickListener {
        void onItemClick(View view, int position);
    }
}

【问题讨论】:

  • 跳过的帧只是意味着你的主线程上发生了一些缓慢的事情 - 理想情况下它希望在 16 毫秒内完成所有事情(因此它可以保持每秒 60 帧)但是如果你正在做任何繁重的事情- 就像读取或写入文件,或等待网络请求 - 然后它会阻塞线程并且你最终会在等待时跳过帧。但这也可能归结为模拟器运行不佳。什么实际上不起作用?
  • 据我所知,它甚至没有启动Recipe.java,我在底部的应用程序栏上有吐司消息,永远不会显示。
  • 它会打开食谱活动吗?您会看到第一个活动被替换。此外,您正在创建带有空项目列表的 adapterRecipe 适配器,并在使用食谱填充它之前将其设置在 RecyclerView 上 - 您可以在适配器上调用 notifyDatasetChanged 以使其更新,但通常您d 在创建适配器之前获取数据。如果您的活动正在加载但它是空的,这可能就是原因。 (同样在onBindViewHolder 中,您将文本设置在同一TextView 上四次!)
  • 谢谢。我明白你设置相同的 textview 4 次是什么意思,我试图设置列数据。不知道我做了什么或如何改变这一点。

标签: android multithreading sqlite


【解决方案1】:

您可以尝试在AsyncTask 中进行数据库调用

AsyncTask getRecipesTask = new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... voids) {
            loadRecipe();
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            //TODO update adapter with new items
        }
    };

onCreategetRecipesTask.execute()替换对loadRecipe()的调用

public class Recipe extends MainActivity {

    TabLayout tabLayout;
    ImageView recipeImage;
    TextView descriptionText, courseText, servingsText, costText, caloriesText, methodText;
    RecyclerView listIngredient;
    SQLiteDatabase db;
    String search_name, selectQuery;
    Cursor c;
    RecyclerViewAdapter adapterRecipe;
    List<RecipeList> itemRecipe = new ArrayList<>();

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recipe);
        //search_name = getIntent().getStringExtra("NAME");
        search_name = "Speedy chicken couscous";
        //recyclerview Recipe
        adapterRecipe = new RecyclerViewAdapter(this);
        listIngredient = findViewById(R.id.listIngredient);

        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this,
                LinearLayoutManager.VERTICAL, false);
        listIngredient.setLayoutManager(mLayoutManager);
        listIngredient.setItemAnimator(new DefaultItemAnimator());
        listIngredient.setAdapter(adapterRecipe);
        getRecipesTask.execute();

    }
    public void loadRecipe() {
        itemRecipe.clear();
        db = (new DatabaseManager(this).getWritableDatabase());
        String selectQuery = "";
        selectQuery = selectQuery + "SELECT A.ingredient_quantity, B.measurement_name, B.ingredient_name, B.description " +
                "FROM TABLE_QUANTITY JOIN TABLE_INGREDIENT ON A.ingredient = B.ingredient_name";

        c = db.rawQuery(selectQuery, new String[]{"%" + search_name + "%"});
        if (c.moveToFirst()) {
            do {
                RecipeList recipeList = new RecipeList();
                recipeList.setId(c.getInt(c.getColumnIndex("COL_ID")));
                recipeList.setIngredient_amount(c.getString(c.getColumnIndex("COL_INGREDIENT_QUANTITY")));
                recipeList.setMeasurement_name(c.getString(c.getColumnIndex("COL_MEASUREMENT_NAME")));
                recipeList.setIngredient_name(c.getString(c.getColumnIndex("COL_INGREDIENT_NAME")));
                recipeList.setDescription(c.getString(c.getColumnIndex("COL_DESCRIPTION")));
                itemRecipe.add(recipeList);
            } while (c.moveToNext());
            c.close();
        }

    }

    AsyncTask getRecipesTask = new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... voids) {
            loadRecipe();
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            adapterRecipe.setItems(itemRecipe);
        }
    };
}

我不完全确定您的适配器是什么样子,但尝试这样的事情:

setItems 可能类似于适配器中的东西

public void setItems(List<Recipe> items) {
   this.items = items;
   notifyDataSetChanged();
}

【讨论】:

  • 谢谢,我应该把 loadrecipe 函数放在哪里?我对此很陌生。
  • 您能分享一下您的RecyclerViewAdapter 的样子吗? new RecyclerViewAdapter(this, itemRecipe);
  • 共享也获得在 runtime_flags 中设置的未知位:0x8000
  • 不要太担心随机 logcat 消息!那里有很多垃圾邮件,尤其是在模拟器上。您可以将loadRecipe 函数保留在原处,只是不是从onCreate 调用它,而是在onCreate 中执行AsyncTask。它会关闭并将所有东西加载到后台的列表中,然后在onPostExecute 中您使用它执行某些操作 - Cameron 建议在您的适配器中有一个函数,您可以使用项目列表调用该函数,这会将适配器更新为显示内容
猜你喜欢
  • 1970-01-01
  • 2013-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
相关资源
最近更新 更多