Search code examples
javajavascriptandroidrunnable

Android java runnable runTimeException


I was having some problem when trying to use the equivalent of setTimeout() which is Runnable Thread in Android Java. So basically I have an Array called pathGeometries which store the data returned from API:

 public void getDirection(Event eventModel, final Context context) {
    String eventX = eventModel.getEventX();
    String eventY = eventModel.getEventY();

    final ArrayList<Geometry> pathGeometries = new ArrayList<Geometry>();
    try {
        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(
                "http://www.onemap.sg/API/services.svc/route/solve?token=qo/s2TnSUmfLz+32CvLC4RMVkzEFYjxqyti1KhByvEacEdMWBpCuSSQ+IFRT84QjGPBCuz/cBom8PfSm3GjEsGc8PkdEEOEr&routeStops=29741.9,40039.6;"
                        + eventX + "," + eventY + "&routemode=CYCLE");
        HttpResponse response = client.execute(request);
        HttpEntity entity = response.getEntity();
        //Code to extract and store to pathGeometries 
        } catch (JSONException e) {
            e.printStackTrace();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    for (int iii = 0; iii < pathGeometries.size(); iii++) {
        final int counter = iii;
         final Handler h = new Handler();
            h.postDelayed(new Runnable()
            {
                private long time = 0;

                public void run()
                {
                    time += 1000;
                    Log.d("TimerExample", "Going for... " + time);
                    h.postDelayed(this, 1000);
                    moveNext(pathGeometries.get(counter).getX(),
                            pathGeometries.get(counter).getY(), 0, context);
                }
            }, 1000);     
    }   
}

And inside the for loop, I am looping each point in pathGeometries, set the point as center of the map by calling moveNext() and postDelayed it for 1 milliseconds before moving to the next record.

public static void moveNext(double coordx, double coordy, int k, Context context){
    Point p = new Point(coordx, coordy);
    EventMain.mMapView.zoomToResolution(p, 1);}

However, I am getting the error message as such:

01-17 16:30:41.547: E/AndroidRuntime(32490): FATAL EXCEPTION: AsyncTask #5
01-17 16:30:41.547: E/AndroidRuntime(32490): java.lang.RuntimeException: An error occured while executing doInBackground()
01-17 16:30:41.547: E/AndroidRuntime(32490):    at android.os.AsyncTask$3.done(AsyncTask.java:278)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at java.lang.Thread.run(Thread.java:856)
01-17 16:30:41.547: E/AndroidRuntime(32490): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
01-17 16:30:41.547: E/AndroidRuntime(32490):    at android.os.Handler.<init>(Handler.java:121)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at android.app.Activity.<init>(Activity.java:735)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at nyp.edu.eneighbourhood.directionRunnable.<init>(directionRunnable.java:15)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at Controller.EventController.getDirection(EventController.java:301)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at AsyncTask.GetEventDirectionAsyncTask.doInBackground(GetEventDirectionAsyncTask.java:34)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at AsyncTask.GetEventDirectionAsyncTask.doInBackground(GetEventDirectionAsyncTask.java:1)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at android.os.AsyncTask$2.call(AsyncTask.java:264)
01-17 16:30:41.547: E/AndroidRuntime(32490):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
01-17 16:30:41.547: E/AndroidRuntime(32490):    ... 5 more

Any ideas how to fix this? Because I not sure if I am doing it correctly. Thanks in advance.

EDIT In my UI Thread:

public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    final Handler handler = new Handler();
}

btnGetDirection.setOnClickListener(new OnClickListener() {
        String direction = "";
        int counter = 1;
        ArrayList<String> directionList = new ArrayList<String>();

        public void onClick(View v) {
                            eventModel.setEventX(String.valueOf(eventModel.getEventX()));
            eventModel.setEventY(String.valueOf(eventModel.getEventY()));
            new GetEventDirectionAsyncTask(
                    new GetEventDirectionAsyncTask.OnRoutineFinished() {
                        public void onFinish() {
                            // Hide the callout and plot user location
                            // marker
                                                // close the progress dialog
                            progressDialog.dismiss();
                        }
                    }, handler).execute(eventModel);
        }
    });

In the GetEventDirectionAsyncTask class:

public class GetEventDirectionAsyncTask extends AsyncTask<Event, Integer, Double> {
EventController eventCtrl = new EventController();
public static ArrayList<String> directionList = new ArrayList<String>();
Context context;
Handler handler;

public interface OnRoutineFinished { // interface
    void onFinish();
}

private OnRoutineFinished mCallbacks;

public GetEventDirectionAsyncTask(OnRoutineFinished callback, Handler handler) { // constructor with
                                                    // interface
    mCallbacks = callback;
    this.handler = handler;
}

public GetEventDirectionAsyncTask() {
} // empty constructor to maintain compatibility


@Override
protected Double doInBackground(Event... params) {
    if (params.length == 1) {
        eventCtrl.getDirection(params[0], context);
        directionList = eventCtrl.getDirectionPath(params[0], context);
    }
    return null;
}

protected void onPostExecute(Double result) {
    if (mCallbacks != null)
        mCallbacks.onFinish(); // call interface on finish
}

protected void onProgressUpdate(Integer... progress) {
}

}

In my Controller class getDirection method:

for (int iii = 0; iii < pathGeometries.size(); iii++) {
        final int counter = iii;
         final Handler h = new Handler();
            h.postDelayed(new Runnable()
            {
                private long time = 0;

                public void run()
                {
                    time += 1000;
                    Log.d("TimerExample", "Going for... " + time);
                    h.postDelayed(this, 1000);
                    moveNext(pathGeometries.get(counter).getX(),
                            pathGeometries.get(counter).getY(), 0, context);
                }
            }, 1000); 

Solution

  • RuntimeException: Can't create handler inside thread that has not...

    Because you are trying to call moveNext from doInBackground Thread instead of UI Thread

    To fix following error:

    First Option: Declare Handler h at class level in Activity and initialize it in onCreate method or in any method which run on UI thread like onPreExecute()

    `Handler handle;
    public GetEventDirectionAsyncTask(OnRoutineFinished callback,
                                      Handler handle){
      this.handle=handle;
      ...
     }`
    

    pass hanlder from Activity as:

    final Hanlder hanlder=new Hanlder();
    btnGetDirection.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
    
               GetEventDirectionAsyncTask obj= new GetEventDirectionAsyncTask(
                        new GetEventDirectionAsyncTask.OnRoutineFinished() {
                            public void onFinish() {
                               ...
                            }
                        },hanlder).execute(eventModel);
            }
        });
    

    Now use handle to call postDelayed from doInBackground

    Second Option: Use runOnUiThread to create and call handler from UI Thread:

      YourActivity.this.runOnUiThread(new Runnable() {
         @Override
         public void run() {
            create and call postDelayed method from Handler here
          }
      });