0

I'm facing a problem when trying to show and hide a dialog during an AsyncTask I have put together for an activity in my Android app.

Here is what I'm currently doing:

public void syncCard(final Tag tag) {
    syncTask = new SyncTask();
    syncTask.execute(tag);
}

private class SyncTask extends AsyncTask<Tag, Void, Void> {
    private final Dialog dialog;
    private boolean mException;

    public SyncTask() {
        // Prepare dialog
        dialog = buildSyncingDialog();
    }

    @Override
    protected void onPreExecute() {
        dialog.show();
    }

    @Override
    protected Void doInBackground(Tag... params) {
        mTagcomm = IsoDep.get(params[0]);
        if (mTagcomm == null) {
            //TODO - Handle communication error with the present card
        }
        mException = false;

        try {
             // Open connection
            mTagcomm.connect();
            lastAts = getAts(mTagcomm);

            mProvider.setmTagCom(mTagcomm);

            EmvParser parser = new EmvParser(mProvider, true);
            mCard = parser.readEmvCard();
            if (mCard != null) {
                mCard.setAtrDescription(extractAtsDescription(lastAts));
            }

        } catch (IOException e) {
            mException = true;
        } finally {
            // close tagcomm
            IOUtils.closeQuietly(mTagcomm);
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
       if (dialog.isShowing()) {
           dialog.dismiss();
       }

        if (!mException) {
            if (mCard != null) {
                showPaymentCardDetails();
            } else if (mCard.isNfcLocked()) {
                //TODO - Show error message informing user the card is locked
            }
        } else {

        }
    }

    private Dialog buildSyncingDialog() {
        String syncingText = String.format( "%s\n%s",
                getString( R.string.syncing ), getString( R.string.do_not_remove_card ) );
        Dialog dialog = new CustomDialog( PaymentCardRegisterActivity.this,
                syncingText, R.layout.layout_syncing );
        dialog.setCancelable( false );

        return dialog;
    }

    @Override
    public void onCancelled() {
        dialog.dismiss();
    }
}

I'm seeing the following error:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

I came across this old answer - Can't create handler inside thread which has not called Looper.prepare()

But I'm still a bit confused as to what I need to do, the last response on that post says about running the task from the UI thread, how would I go about doing this with my code above?

EDIT

My activity class extends NfcActivity as I am using this class to read contactless card details.

My syncCard is being called from another background thread (I believe) like so

@Override
public void onNewIntent(final Intent intent) {
    super.onNewIntent(intent);
    processIntent(intent);
}

private void processIntent(final Intent intent) {
    String action = intent.getAction();

    if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
        Tag tagFromIntent = intent.getParcelableExtra( NfcAdapter.EXTRA_TAG );
        onTagDiscovered(tagFromIntent);
    }
}

@Override
public void onTagDiscovered( final Tag tag ) {
    super.onTagDiscovered(tag);
    try {
        PaymentCardRegisterActivity.this.syncCard(tag);
    } catch ( Exception e) {
        Log.e( PaymentCardRegisterActivity.class.getName(), "Failed to sync card", e );
        // Return to start activity
        finish();
    }
}
Community
  • 1
  • 1
mindparse
  • 6,115
  • 27
  • 90
  • 191
  • 1
    How/where is `syncCard()` called? – codeMagic Mar 15 '16 at 13:25
  • you might be starting the asynctask from some background thread – Ankit Aggarwal Mar 15 '16 at 13:27
  • Ah, ok, I am indeed starting from a background thread. Am I right in thinking the main UI thread comes from calls made in `onCreate` in an Activity class? – mindparse Mar 15 '16 at 13:28
  • @mindparse `onCreate` is always called on main thread. Any calls done from there are running on that same thread unless the calls are dispatched to another thread – 0xDEADC0DE Mar 15 '16 at 13:29
  • Ok, I have updated my question. I have realised after comparing with another activity someone else has written in my project, they have a similar asynctask flow, but theirs is being invoked from within `onCreate`. I could refactor my code to follow suit – mindparse Mar 15 '16 at 13:35

1 Answers1

1

if onTagDiscovered is running on a thread that is not the UI thread, then you should dispatch your call to syncCard to the main thread. If you have a reference to an Activity then you can use runOnUiThread and post a Runnable. This will make sure that the Runnable is executed on main thread. In your code, I think you need to change this

@Override
public void onTagDiscovered( final Tag tag ) {
    super.onTagDiscovered(tag);
    try
    {
            PaymentCardRegisterActivity.this.syncCard(tag);
    }
    catch ( Exception e)
    {
            Log.e( PaymentCardRegisterActivity.class.getName(), "Failed to sync card", e );
            // Return to start activity
            finish();
    }
}

into this

@Override
public void onTagDiscovered( final Tag tag ) {
    super.onTagDiscovered(tag);
    runOnUiThread(new Runnable() {

        public void run() {
            try {
                PaymentCardRegisterActivity.this.syncCard(tag);
            } catch ( Exception e) {
                Log.e( PaymentCardRegisterActivity.class.getName(), "Failed to sync card", e );
            }
        }
    });
}
0xDEADC0DE
  • 2,453
  • 1
  • 17
  • 22