2

I have the following working flow of my application: main activity has a button which starts the second activity after the click. In the second activity there is a TextView which shows the city which located in the specified geopoint. To find this city I make a request to Geocoder which I make in the background thread.

What I expect: second activity starts (almost) immediately, when background thread finishes the request, ui thread updates the TextView content.

What happens: the second activity gets started only when Geocoder finishes its job. To make it obvious we can turn off the wi-fi and click on the button - five-six seconds of expectation, and right after the message that tells that Geocoder couldn't get the geopoint appears in the log, the second activity launches.

What I'm doing wrong? The relevant code is below, the full sample project is on github.

public class SecondActivity extends Activity implements Handler.Callback {
    private HandlerThread mHandlerThread = new HandlerThread("BackgroundThread");
    private Handler mUIHandler;
    private Handler mBackgroundHandler;

    private TextView mLocationView;

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

        mLocationView = (TextView) findViewById(R.id.location_name);

        mUIHandler = new Handler(getMainLooper(), this);

        mHandlerThread.start();
        mBackgroundHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 0) {
                    final Geocoder geocoder = new Geocoder(SecondActivity.this);

                    try {
                        final List<Address> results = geocoder.getFromLocation(53.539316, 49.396494, 1);

                        if (results != null && !results.isEmpty()) {
                            mUIHandler.dispatchMessage(Message.obtain(mUIHandler, 1, results.get(0)));
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }

    @Override
    protected void onResume() {
        super.onResume();
        mBackgroundHandler.dispatchMessage(Message.obtain(mBackgroundHandler, 0));
    }

    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == 1) {
            mLocationView.setText("I live in " + ((Address) msg.obj).getLocality());
            return true;
        }

        return false;
    }
}
aga
  • 27,954
  • 13
  • 86
  • 121
  • "the second activity gets started only when Geocoder finishes its job" -- you have nothing that starts an activity in the code shown above. Beyond that, I would strongly encourage you to use more standard threading patterns, like `AsyncTask`, than what you are doing here. – CommonsWare Oct 02 '14 at 17:26
  • @CommonsWare I've posted the code of the second activity here, not the main activity. – aga Oct 02 '14 at 17:28
  • @CommonsWare I know I can use `AsyncTask` here and update `TextView` in its `onPostExecute(Result result)` method. what I can't understand is why the code which has to work in parallel with the activity initialization, in fact blocks it. I've written my sample based on the code from the second chapter of "Android Programming: Pushing the Limits". – aga Oct 02 '14 at 17:39
  • "what I can't understand is why the code which has to work in parallel with the activity initialization, in fact blocks it" -- well, that's why we're steering you towards better solutions the problem. You're welcome to poke around with Traceview, logging, and so forth to try to determine where things are going astray. For example, if perhaps the `Geocoder` work is actually happening on the main application thread, despite your attempts to the contrary, Traceview would show that. – CommonsWare Oct 02 '14 at 17:47
  • Apparently somewhere in the code, before the UI is completely loaded, your Geocoder is being executed on the main thread not in the background. Try this and see if it makes any difference: private HandlerThread mHandlerThread = new HandlerThread("BackgroundThread", Process.THREAD_PRIORITY_BACKGROUND); – NightwareSystems Oct 02 '14 at 17:55
  • I know the answer! You don't have a birthday party, that's what u doin' wrong! HBDay and happy coding! Even if it was yesterday :/ – yablokoff Nov 21 '14 at 21:54
  • @yablokoff it was the day before yeasterday, but thanks anyway. :) – aga Nov 22 '14 at 07:54

1 Answers1

1

I agree with CommonsWare, using AsyncTask would make your life easier. Just adjust the parameters and return types as required, or keep the variables in a global scope.

new AsyncTask<Void, Void, Void>()
    {
        @Override
        protected Void doInBackground(Void... params)
        {
            // Your Geolocation operation goes here
            return null;
        }

        @Override
        protected void onPostExecute(Void result)
        {

            super.onPostExecute(result);
            // This is called when your operation is completed
        }
    }.execute();
NightwareSystems
  • 433
  • 5
  • 17
  • I know that I can use `AsyncTask` here, I'm interested in deep understanding of *why the current code doesn't work*. – aga Oct 02 '14 at 17:39