21

I am developing a simple application using Google Maps V2 API just to get basics and I am facing this error:

09-09 21:21:41.154: E/AndroidRuntime(3796): FATAL EXCEPTION: Thread-441
09-09 21:21:41.154: E/AndroidRuntime(3796): java.lang.ExceptionInInitializerError
09-09 21:21:41.154: E/AndroidRuntime(3796):  at cz.vongrad.dataSourceLayer.DataMapper.loadLocation(DataMapper.java:111)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at cz.vongrad.dataSourceLayer.DBFacade.loadLocations(DBFacade.java:32)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at cz.vongrad.domainLayer.Controller.loadLocations(Controller.java:94)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at cz.vongrad.locator.MapActivity$1.run(MapActivity.java:199)
09-09 21:21:41.154: E/AndroidRuntime(3796): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
09-09 21:21:41.154: E/AndroidRuntime(3796):  at android.os.Handler.<init>(Handler.java:121)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:607)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:607)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at android.os.AsyncTask.<clinit>(AsyncTask.java:190)
09-09 21:21:41.154: E/AndroidRuntime(3796):  ... 4 more
09-09 21:21:41.164: E/EmbeddedLogger(239): App crashed!

DataMapper:

class LoadLocations extends AsyncTask<String, Void, ArrayList<Friend>>{

        private JSONObject jSONObject;


        @Override
        protected ArrayList<Friend> doInBackground(String... args) {

            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("user_name", args[1]));

            jSONObject = jSONParser.makeHttpRequest(args[0], "POST", params);
            Log.d(TAG, jSONObject.toString());

            ArrayList<Friend> tempFriends = new ArrayList<Friend>();

            try {
                if(jSONObject.getInt("success") == 1){

                    JSONArray jsonArray = jSONObject.getJSONArray("users");


                    for (int i = 0; i < jsonArray.length(); i++) {
                        JSONObject o = jsonArray.getJSONObject(i);

                        tempFriends.add(new Friend(o.getString("user_name"), new LatLng(o.getDouble("loc_y"), o.getDouble("loc_x")), false));
                    }

                }
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return tempFriends;
        }

    }

public ArrayList<Friend> loadLocation(String URL, String userName) throws InterruptedException, ExecutionException {
        return new LoadLocations().execute(URL, userName).get();

    }

MapActivity:

public class MapActivity extends Activity implements LocationSource, LocationListener {
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
}
private Thread loadFriendLocations = new Thread()
    {

        @Override
        public void run() {
            Log.d(TAG, "LastKnownLocation: " + locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER));
            try {
                while(true) {
                    controller.loadLocations(LOAD_LOCATIONS);
                    Log.d(TAG, "Loading new locations");
                    Message msg = uIHandler.obtainMessage();
                    msg.what = ADD_MAP_PIN;
                    uIHandler.sendMessage(msg);

                    sleep(10000);                   
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    };

    final Handler uIHandler = new Handler(){
      @Override
      public void handleMessage(Message msg) {

        if(msg.what==ADD_MAP_PIN){

            for (Friend friend : controller.getFriends()) {

                    addMapPin(friend);


            }
        }
        super.handleMessage(msg);
      }
    };

The weird thing is that I can run this app on my emulator (API 16) and it runs just great but when I try to run it on a physical device (API 15), it gets this error. The AsyncTask is run in separate class, so it might cause this problem. I would appreciate any suggestions. Thanks in advance.

Husky
  • 1,451
  • 4
  • 19
  • 35

3 Answers3

37

I had a similar issue, problem was that I was having series of Thread from Main Thread. Actually Main UI Thread started a service, which ran on a service and subsequently started another thread, which finally had a FileObserver (another thread), I had to communicate to UI thread by popping a toast message. I tried for several hours, and tried below, it worked as a charm.

//Let this be the code in your n'th level thread from main UI thread
Handler h = new Handler(Looper.getMainLooper());
h.post(new Runnable() {
  public void run() {
    Toast.makeText(context, "Your message to main thread", Toast.LENGTH_SHORT).show();
  }
});

The main key here is the getMainLooper() function of Looper class, which will provide you the Looper against the Main UI thread.

darthvading
  • 909
  • 2
  • 11
  • 25
  • 1
    With mine, when I put my code in run method, it does not get executed. What should I do?? – Neri Sep 25 '17 at 08:57
10

You cannot execute an AsyncTask from a background thread. See the "Threading Rules" section of the AsyncTask documentation.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • So do you think that if the thread which I have in MapActivity used handler to execute AsyncTask, it would work? Or its a bad idea and I should do it completely different way...? – Husky Sep 09 '13 at 20:10
  • 3
    @Husky: Frankly, the whole load-friend-locations stuff is kinda scary. That being said, since you're *already* on a background thread, you do not need an `AsyncTask` -- just do the work. – CommonsWare Sep 09 '13 at 20:18
  • Just the point why I want to do that using AsyncTask is that I perform a networking operation which cannot be performed in the main thread so I had to do that in AsyncTask. BTW is there any way how to do that without AsyncTask?? – Husky Sep 09 '13 at 20:40
  • @Husky: I repeat: you already have a background thread. Rather than start an `AsyncTask` from the background thread, *just do the work on the existing background thread*. If for some reason you really want it to be on some other background thread, use a `Thread`. – CommonsWare Sep 09 '13 at 20:43
  • I made several layers and I want to perform loading of data from the server (currently done by AsyncTask) at the lowest layer and then pass the data back using return statement. The question is what would be more efficient, placing the thread that is currently in MapActivity to the lowest layer and perform constant loading from there, which would result in higher coupling according to my structure (keeping reference to Controller which stores all the data from the sever)? When I just do the work using the thread in MapActivity, it throws an error: NetworkOnMainThreadException – Husky Sep 09 '13 at 20:55
4

Do this in your do in background...

runOnUiThread(new Runnable() {
    public void run() 
    {
        if (items.equals("null")) 
        {
            Toast.makeText(getApplicationContext(), "No Event Found",Toast.LENGTH_LONG).show();
        }                   
        else {                       
        }   
    }
});
Blundering Philosopher
  • 6,245
  • 2
  • 43
  • 59