21

I get this error "Can't create handler inside thread that has not called Looper.prepare()"

Can you tell me how to fix it?

public class PaymentActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.payment);

    final Button buttonBank = (Button) findViewById(R.id.buttonBank);

    buttonBank.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            progressDialog = ProgressDialog.show(PaymentActivity.this, "",
                    "Redirecting to payment gateway...", true, true);

            new Thread() {
                public void run() {
                    try {
                        startPayment("Bank");
                    } catch (Exception e) {
                        alertDialog.setMessage(e.getMessage());
                        handler.sendEmptyMessage(1);
                        progressDialog.cancel();
                    }
                }
            }.start();
        }

    });

StartPayment Method:

    private void startPayment(String id) {
    Bundle b = getIntent().getExtras();
    final Sail sail = b.getParcelable(Constant.SAIL);

    final Intent bankIntent = new Intent(this, BankActivity.class);

    try {
        Reservation reservation = RestService.createReservation(
                sail.getId(),
                getSharedPreferences(Constant.PREF_NAME_CONTACT, 0));
        bankIntent.putExtra(Constant.RESERVATION, reservation);

        // <workingWithDB> Storing Reservation info in Database
        DBAdapter db = new DBAdapter(this);
        db.open();
        @SuppressWarnings("unused")
        long rowid;
        rowid = db.insertRow(sail.getId(), sail.getFrom(),
                sail.getTo(), sail.getShip(), sail.getDateFrom().getTime(),
                sail.getPrice().toString(), reservation.getId().floatValue());
        db.close();
        // </workingWithDB>

        String html = PaymentService.getRedirectHTML(id, reservation);

        bankIntent.putExtra(Constant.BANK, html);
    } catch (Exception e) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        AlertDialog alertDialog = builder.create();
        alertDialog.setMessage(e.getMessage());
        alertDialog.show();
    }

    startActivity(bankIntent);
}
Sergio
  • 529
  • 3
  • 12
  • 26

6 Answers6

34

You should know that when you try to modify your UI , the only thread who can do that is the UiThread.

So if you want to modify your UI in another thread, try to use the method: Activity.runOnUiThread(new Runnable);

Your code should be like this :

 new Thread() {
    public void run() {  
        YourActivity.this.runOnUiThread(new Runnable(){

             @Override
             public void run(){
                 try {
                      startPayment("Bank");//Edit,integrate this on the runOnUiThread
                 } catch (Exception e) {
                     alertDialog.setMessage(e.getMessage());
                     handler.sendEmptyMessage(1);
                     progressDialog.cancel();
                 } 
            });                
           }
      }
  }.start();
Capella
  • 881
  • 3
  • 19
  • 32
Houcine
  • 24,001
  • 13
  • 56
  • 83
3

I assume you create a Handler in startPayment() method. You can't do that, as handlers can be created on th UI thread only. Just create it in your activity class.

Vladimir Ivanov
  • 42,730
  • 18
  • 77
  • 103
3

Instead of new Thread() line, try giving

this.runOnUiThread(new Runnable() {
Anju
  • 9,379
  • 14
  • 55
  • 94
1

Try

final Handler handlerTimer = new Handler(Looper.getMainLooper());
        handlerTimer.postDelayed(new Runnable() {
            public void run() {
                                ...... 

                              }
                                                 }, time_interval});
1

you cant change any UI in thread you can use runOnUIThread or AsyncTask for more detail about this click here

Niranj Patel
  • 32,980
  • 10
  • 97
  • 133
1

I've found that most thread handling can be replaced by AsyncTasks like this:

public class TestStuff extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Button buttonBank = (Button) findViewById(R.id.button);
        buttonBank.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                new StartPaymentAsyncTask(TestStuff.this).execute((Void []) null);
            }
        });
    }

    private class StartPaymentAsyncTask extends AsyncTask<Void, Void, String> {
        private ProgressDialog dialog;
        private final Context context;

        public StartPaymentAsyncTask(Context context) {
            this.context = context;
        }

        @Override
        protected void onPreExecute() {
            dialog = new ProgressDialog(context);
            // setup your dialog here
            dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            dialog.setMessage(context.getString(R.string.doing_db_work));
            dialog.setCancelable(false);
            dialog.show();
        }

        @Override
        protected String doInBackground(Void... ignored) {
            String returnMessage = null;
            try {
                startPayment("Bank");
            } catch (Exception e) {
                returnMessage = e.getMessage();
            }
            return returnMessage;
        }

        @Override
        protected void onPostExecute(String message) {
            dialog.dismiss();
            if (message != null) {
                // process the error (show alert etc)
                Log.e("StartPaymentAsyncTask", String.format("I received an error: %s", message));
            } else {
                Log.i("StartPaymentAsyncTask", "No problems");
            }
        }
    }

    public void startPayment(String string) throws Exception {
        SystemClock.sleep(2000); // pause for 2 seconds for dialog
        Log.i("PaymentStuff", "I am pretending to do some work");
        throw new Exception("Oh dear, database error");
    }
}

I pass in the Application Context to the Async so it can create dialogs from it.

The advantage of doing it this way is you know exactly which methods are run in your UI and which are in a separate background thread. Your main UI thread isn't delayed, and the separation into small async tasks is quite nice.

The code assumes your startPayment() method does nothing with the UI, and if it does, move it into the onPostExecute of the AsyncTask so it's done in the UI thread.

Mark Fisher
  • 9,838
  • 3
  • 32
  • 38
  • I get this error in "new StartPaymentAsyncTask(this).execute((Void []) null) }" Error - The constructor PaymentActivity.StartPaymentAsyncTask(new View.OnClickListener(){}) is undefined – Sergio Jun 02 '11 at 11:26
  • yes, `this` should be `YourActivity.this`. I've updated the code (I think it should be PaymentActivity) – Mark Fisher Jun 02 '11 at 11:44
  • break point the onClick method, and the async task's constructor to see they are working (or add logging). Did you add all the setup for your progress dialog? I've added example code for it above. – Mark Fisher Jun 02 '11 at 12:10
  • I've amended the code to be a fully working class. If you're using eclipse, all you need to do is add a button to the default android application with the id "button" to get this working. If you comment out the exception thrown at the end, you'll get the "no problems" message – Mark Fisher Jun 02 '11 at 12:29
  • Did you try the entire class, or just plug in the bits you needed to your code? The amended code should show a spinner for 2 seconds (do you see this?), then in the logs you should see "I am pretending to do stuff" from the startPayment() method, then the error message, again in the logs. – Mark Fisher Jun 02 '11 at 13:26
  • odd, I've run this on my phone and an emulator and it works on both of these. are you using eclipse? generate a new project called TestStuff, copy the above over the class, organise imports, create the resource string "R.string.doing_db_work", add a button with id "button" and it should work out the box. – Mark Fisher Jun 02 '11 at 14:43