java.lang.IllegalStateException: System services not available to Activities before onCreate() when creating custom-adapter

306 Views Asked by At

I am getting an error which says:

java.lang.IllegalStateException: System services not available to Activities before onCreate()

although every action I do are after onCreate().

I have a custom adapter which has 4 buttons. When one of the button is clicked, it ultimately calls the updateList() function which is supposed to update the list with the new details.

Here's the code: MainActivity.java

public class MainActivity extends Activity {
    static int accountsCount = 0;
    static FileWorker fileWorker;

    static File directory;
    ListView ledgerListView;
    TextView noAccountsTextView;
    TextView accountHierarchy;
    EditText accountName;
    EditText accountLimit;

    AlertDialog.Builder accountDialogBuilderChild;
    AlertDialog accountDialogChild;

    static ArrayList<AccountsView> ledgerList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        boolean introDone = preferences.getBoolean("intro_done", false);
        boolean howToDone = preferences.getBoolean("howto_done", false);
        boolean prefFilesCreated = preferences.getBoolean("files_created", false);

       if(!introDone || !howToDone) {
            Intent introActivity = new Intent(this, IntroActivity.class);
            startActivity(introActivity);
        }

        noAccountsTextView = findViewById(R.id.noAccountsTextView);
        ledgerListView = findViewById(R.id.mainListView);

        fileWorker = new FileWorker();
        directory = getFilesDir();

        if(!prefFilesCreated) {
            boolean filesCreated = fileWorker.createFiles(directory);
            if(filesCreated) {
                SharedPreferences.Editor prefEditor = preferences.edit();
                prefEditor.putBoolean("files_created", true);
                prefEditor.apply();
            }
        }
        accountsCount = fileWorker.countAccounts(directory);
        setMainActivityView();
    }

    public void addChildAccountDialog(Context context, int pos) {
        String hierarchy = ledgerList.get(pos).getAccountName();
        String renewalType = ledgerList.get(pos).getRenewalType();
        accountDialogBuilderChild = new AlertDialog.Builder(context);
        accountDialogBuilderChild.setTitle(R.string.add_child_account_dialog_title);
        accountDialogBuilderChild.setPositiveButton("Ok",
                (dialogInterface, i) -> addChildAccount(hierarchy, renewalType));
        accountDialogBuilderChild.setNegativeButton("Cancel",
                (dialogInterface, i) -> dialogInterface.cancel());
        accountDialogBuilderChild.setView(R.layout.dialog_add_child_account);
        accountDialogChild = accountDialogBuilderChild.create();
        accountDialogChild.show();

        accountHierarchy = accountDialogChild.findViewById(R.id.accountHierarchyValueTV);
        accountHierarchy.setText(hierarchy);
        accountName = accountDialogChild.findViewById(R.id.accountNameDialogET);
        accountLimit = accountDialogChild.findViewById(R.id.accountLimitDialogET);
    }

    private void addChildAccount(String hierarchy, String renewalType) {
        String accName = hierarchy.concat(formatAccountName(accountName
                .getText().toString()));
        double accLimit = Double.parseDouble(accountLimit.getText().toString());
        fileWorker.addChildAccount(directory,
                accName,
                renewalType,
                accLimit);
        setMainActivityView();
    }

    private void setMainActivityView() {
        accountsCount = fileWorker.countAccounts(directory);
        if(accountsCount <= 0) {
            ledgerListView.setVisibility(View.GONE);
            noAccountsTextView.setVisibility(View.VISIBLE);
        } else {
            updateList();
            ledgerListView.setVisibility(View.VISIBLE);
            noAccountsTextView.setVisibility(View.GONE);
        }
    }

    public void updateList() {
        fileWorker.sortAccounts(directory);
        ledgerList = fileWorker.getAccountsList(directory);
        AccountsViewAdapter ledgerAdapter = new
                AccountsViewAdapter(this, ledgerList);
        ledgerListView.setAdapter(ledgerAdapter);
    }

    public String formatAccountName(String accName) {
        accName = accName.trim().toLowerCase();
        accName = accName.replace(' ', '_');
        accName = accName.replace('/', '_');
        if(accName.charAt(0) != '/') {
            accName = "/".concat(accName);
        }
        return accName;
    }

    public void onBackPressed() {
        new AlertDialog.Builder(this)
                .setTitle("Exit")
                .setMessage("Are you sure?")
                .setPositiveButton("Yes", (dialog, which) -> this.finishAffinity())
                .setNegativeButton("No", null)
                .show();
    }
}

Here's the cutomAdapter code:

public class AccountsViewAdapter extends ArrayAdapter<AccountsView> {

    TextView accountName;
    TextView renewalType;
    TextView limitValue;
    TextView balanceValue;

    Button buttonAddAccount;
    Button buttonEditAccount;
    Button buttonIncreaseBalance;
    Button buttonDecreaseBalance;

    MainActivity mainActivity;

    public AccountsViewAdapter(Context context, ArrayList<AccountsView> arrayList) {
        super(context, 0, arrayList);
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        View currentItemView  = convertView;

        if(currentItemView == null) {
            currentItemView = LayoutInflater.from(getContext())
                    .inflate(R.layout.listview_row, parent, false);
        }

        AccountsView currentAccount = getItem(position);

        assert currentAccount != null;

        accountName = currentItemView.findViewById(R.id.accountNameValueTextView);
        renewalType = currentItemView.findViewById(R.id.textViewRenewalTypeValue);
        limitValue = currentItemView.findViewById(R.id.limitValueTextView);
        balanceValue = currentItemView.findViewById(R.id.balanceValueTextView);

        buttonAddAccount = currentItemView.findViewById(R.id.addAccountButton);
        buttonEditAccount = currentItemView.findViewById(R.id.editAccountButton);
        buttonIncreaseBalance = currentItemView.findViewById(R.id.increaseBalanceButton);
        buttonDecreaseBalance = currentItemView.findViewById(R.id.decreaseBalanceButton);

        accountName.setText(currentAccount.getAccountName());
        renewalType.setText(currentAccount.getRenewalType());
        limitValue.setText(currentAccount.getAmountLimit());
        balanceValue.setText(currentAccount.getBalanceValue());

        mainActivity = new MainActivity();

        buttonAddAccount.setOnClickListener(v -> {
            mainActivity.addChildAccountDialog(buttonAddAccount.getContext(), position);
        });
        return currentItemView;
    }
}

As far as I understood, the error happens when I click on the "buttonAddAccount" button. I tried replacing the context in MainActivity with MainActivity.this, but that didn't help.

Here's the error log:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: org.biotstoiq.seshat, PID: 18232
    java.lang.IllegalStateException: System services not available to Activities before onCreate()
        at android.app.Activity.getSystemService(Activity.java:6715)
        at android.view.LayoutInflater.from(LayoutInflater.java:299)
        at android.widget.ArrayAdapter.<init>(ArrayAdapter.java:216)
        at android.widget.ArrayAdapter.<init>(ArrayAdapter.java:210)
        at android.widget.ArrayAdapter.<init>(ArrayAdapter.java:196)
        at org.biotstoiq.seshat.AccountsViewAdapter.<init>(AccountsViewAdapter.java:28)
        at org.biotstoiq.seshat.MainActivity.updateList(MainActivity.java:179)
        at org.biotstoiq.seshat.MainActivity.setMainActivityView(MainActivity.java:170)
        at org.biotstoiq.seshat.MainActivity.addChildAccount(MainActivity.java:161)
        at org.biotstoiq.seshat.MainActivity.lambda$addChildAccountDialog$2$org-biotstoiq-seshat-MainActivity(MainActivity.java:140)
        at org.biotstoiq.seshat.MainActivity$$ExternalSyntheticLambda2.onClick(Unknown Source:6)
        at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:201)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:230)
        at android.app.ActivityThread.main(ActivityThread.java:7875)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:526)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1034)
I/Process: Sending signal. PID: 18232 SIG: 9

The fileWorker code is not here since I didn't think it's required here.

1

There are 1 best solutions below

0
Gabe Sechan On
  1. Never instantiate an Activity with new ActivityClass. That doesn't properly initialize it. Only the Android framework can properly initialize an Activity, and you do that via an Intent.

  2. Even if you could initialize an Activity like that, it would be wrong. You don't want to call that function on a new instance, you want to call it on the instance you're running in.

  3. You shouldn't have any view or adapter class require a specific Activity anyway. You should use interfaces that pass handlers into the adapter. That method is far easier to test and easier to get right. Even if your code worked it would be next to impossible to unit test.