Search code examples
androidlistviewandroid-asynctask

My App crashes when items in Listview overflows


App i am trying to build crashes whenever items in listview overflows. It works fine when items are limited(<19). Please help. Any coding tips are also welcome as i am a newbie.

This is console output:

E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #2
    Process: com.kmb.budget, PID: 5394
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:354)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:9446)
        at android.view.ViewRootImpl.focusableViewAvailable(ViewRootImpl.java:4549)
        at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:928)
        at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:928)
        at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:928)
        at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:928)
        at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:928)
        at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:928)
        at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:928)
        at android.view.View.setFlags(View.java:15709)
        at android.view.View.setFocusableInTouchMode(View.java:10901)
        at android.widget.AdapterView.checkFocus(AdapterView.java:1168)
        at android.widget.ListView.setAdapter(ListView.java:615)
>>>>        at com.kmb.budget.TransactionsActivity.createTransactionList(TransactionsActivity.java:35)
>>>>        at com.kmb.budget.DBClass.doInBackground(DBClass.java:116)
>>>>        at com.kmb.budget.DBClass.doInBackground(DBClass.java:14)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764) 

part of DBClass resposible to handle function. Scroll down. I had marked main lines with ">>>".

class DBClass extends AsyncTask<Void,Void,Integer> {

    private MainDatabase db ;
    private MainActivity mActivity;
    private TransactionsActivity tActivity;
    private CategoryDAO categoryDAO;
    private TransactionDAO transactionDAO;
    private String nm;
    private String tp;
    private String operation;
    private String to;
    private String from;
    private String comment;
    private int amount;
    private Date createDate;
    private Date transactionDate;

    public DBClass(Context context,TransactionsActivity activity,String operation){
        this.db = MainDatabase.getMainDatabase(context);
        this.categoryDAO = db.categoryDAO();
        this.transactionDAO = db.transactionDAO();
        this.tActivity = activity;
        this.operation = operation;
    }

    public DBClass(Context context,MainActivity activity,String operation){
        this.db = MainDatabase.getMainDatabase(context);
        this.categoryDAO = db.categoryDAO();
        this.mActivity = activity;
        this.operation = operation;
    }
    public DBClass(Context context, String nm, String tp){
        this.db = MainDatabase.getMainDatabase(context);
        this.categoryDAO = db.categoryDAO();
        this.nm = nm;
        this.tp = tp;
        this.operation = "CATEGORY";
    }
    public DBClass(Context context, String to, String from, String comment, int amount, Date createDate, Date transactionDate) {
        this.db = MainDatabase.getMainDatabase(context);
        this.transactionDAO = db.transactionDAO();
        this.categoryDAO = db.categoryDAO();
        this.to = to;
        this.from = from;
        this.operation = "TRANSACTION";
        this.comment = comment;
        this.amount = amount;
        this.createDate = createDate;
        this.transactionDate = transactionDate;
    }
    @Override
    protected Integer doInBackground(Void... voids) {
        int st = 0;
        switch(operation){
            case("CATEGORY"):
                CategoryModal category = new CategoryModal();
                category.setCategoryName(nm);
                category.setType(tp);
                categoryDAO.insertCategory(category);
                Log.i("Category created",nm);
                long a = categoryDAO.getCategoryId(nm);
                Log.i("Ccategory id in db",Long.toString(a));
                List<String> categ= categoryDAO.getAllCategoryNames();
                for(String cm : categ){
                    Log.i("category name" , cm);
                }
                break;
            case("TRANSACTION"):
                TransactionModal transaction = new TransactionModal();
                transaction.setToId(categoryDAO.getCategoryId(to));
                transaction.setFromId(categoryDAO.getCategoryId(from));
                transaction.setComment(comment);
                transaction.setAmount(amount);
                transaction.setTransactionDate(transactionDate);
                transaction.setCreateDate(createDate);
                transactionDAO.insert(transaction);
                Log.e("transaction",transaction.getComment());
                break;
            case("GET_CATEGORIES"):
                try {
                    mActivity.setList(categoryDAO.getAllCategoryNames());
                }catch (Exception e){
                    e.printStackTrace();
                    mActivity.setList(null);
                }

                Log.i("category call","done");
                break;
            case("GET_TRANSACTIONS"):
>>>                List<TransactionModal> tmlist = transactionDAO.getAllTransactions();
>>>                List<Transaction> list = new ArrayList<>();
>>>                int i = 1;
>>>                List<CategoryModal> cm = categoryDAO.getAllCategories();
>>>                for(TransactionModal tm : tmlist){
>>>                    Long fromId = tm.getFromId();
>>>                    Long toId = tm.getToId();
>>>                    Transaction t = new Transaction(Integer.toString(i),categoryDAO.getCategoryName(fromId),categoryDAO.getCategoryName(toId),tm.getComment(),tm.getTransactionDate().toString(),Integer.toString(tm.getAmount()));
>>>                    CategoryModal cmc = categoryDAO.getCategoryById(tm.getFromId());
>>>                    String to = cmc.getCategoryName();
>>>                    list.add(t);
>>>                    i++;
>>>                }
>>>                tActivity.createTransactionList(list);

                break;
            case("convertOperation"):
                break;
        }
        return st;
    }



}

this is transaction activity

27    public void createTransactionList(List<Transaction> list){
28        Transaction header = new Transaction("SR","From","To","Comment","Date","Amount");
29        List<Transaction> temp = new ArrayList<>();
30        temp.add(header);
31        temp.addAll(list);
32        ListView transactionListView = findViewById(R.id.transactions_listView);
33        List<Transaction> transactionsList = temp;
34        TransactionListAdapter tLAdapter = new TransactionListAdapter(this,R.layout.transaction_list_adapter,transactionsList);
35        transactionListView.setAdapter(tLAdapter);
36    }

this is conent_transaction.xml which contains listView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".TransactionsActivity"
    tools:showIn="@layout/activity_transactions">

    <ListView
        android:id="@+id/transactions_listView"
        android:layout_width="match_parent"
        android:scrollbars="vertical"
        android:layout_height="match_parent"
        tools:layout_editor_absoluteX="1dp"
        tools:layout_editor_absoluteY="1dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

Edit 1: Transaction list adapter

class TransactionListAdapter extends ArrayAdapter<Transaction> {


    private Context context;
    private int resource;


    /**
     * Default constructor for transaction adapter
     * @param context
     * @param resource
     * @param objects
     */
    public TransactionListAdapter(@NonNull Context context, int resource, @NonNull List<Transaction> objects) {
        super(context, resource, objects);
        this.context = context;
        this.resource = resource;
    }




    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

        String sr = getItem(position).getSr();
        String from = getItem(position).getFrom();
        String to = getItem(position).getTo();
        String comment = getItem(position).getComment();
        String date = getItem(position).getDate();
        String amount = getItem(position).getAmount();

        Transaction transaction = getItem(position);

        LayoutInflater inflater = LayoutInflater.from(context);
        convertView = inflater.inflate(resource,parent,false);
        TextView tvSr = convertView.findViewById(R.id.tr_list_sr);
        TextView tvTo = convertView.findViewById(R.id.tr_list_to);
        TextView tvFrom = convertView.findViewById(R.id.tr_list_from);
        TextView tvComment = convertView.findViewById(R.id.tr_list_comment);
        TextView tvDate = convertView.findViewById(R.id.tr_list_td);
        TextView tvAmount = convertView.findViewById(R.id.tr_list_amount);
        tvSr.setText(sr);
        tvTo.setText(to);
        tvFrom.setText(from);
        tvComment.setText(comment);
        tvAmount.setText(amount);
        tvDate.setText(date);

        return convertView;
    }
}

overflows: increase beyond a certain number which can be viewed on screen


Solution

  • You try to update View from inside AsyncTask's doInBackground(), which is not permissible. Update your DBClass like below:

    class DBClass extends AsyncTask<Void,Void,List<?>> {
    
        ....
    
        @Override
        protected List<?> doInBackground(Void... voids) {
    
            List mList = null;
    
            switch(operation) {
    
                ....
    
                case("GET_CATEGORIES"):
                    try {
                        mList = categoryDAO.getAllCategoryNames();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    break;
                case("GET_TRANSACTIONS"):
                    ....
    
                    mList = list;
                    break;
            }
    
            return mList;
        }
    
        @Override
        protected void onPostExecute(List<?> list) {
            switch(operation) {
                case("GET_CATEGORIES"):
                    mActivity.setList(list);
                    break;
                case("GET_TRANSACTIONS"):
                    tActivity.createTransactionList(list);
                    break;
            }
        }
    }