4

I am following a tutorial from Udemy to make a weather app. This is my first time working with JSON data as well so I'm not sure if that is impacting my situation. Every time I run my app, it opens, however; when I press the button that is supposed to display the weather of the given city, the app crashes.

I researched Looper.prepare() as that was included in the error, but adding the looper did not work (I'm new to coding and I may have done so incorrectly) I also tried adding runOnUiThread (new Runnable) to no avail (again I may have done so incorrectly).

    public class MainActivity extends AppCompatActivity {

    EditText editText;
    TextView resultTextView;

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

        editText = findViewById(R.id.editText);
        resultTextView = findViewById(R.id.resultTextView);

    }

    public void getWeather(View view) {
        try {
            DownloadTask task = new DownloadTask();
            String encodedCityName = 
URLEncoder.encode(editText.getText().toString(), "UTF-8");

            task.execute("http://openweathermap.org/data/2.5/weather?q=" 
+ encodedCityName + "&appid=b1b15e88fa797225412429c1c50c122a1");

            InputMethodManager mgr = (InputMethodManager) 
getSystemService(Context.INPUT_METHOD_SERVICE);
            mgr.hideSoftInputFromWindow(editText.getWindowToken(), 0);
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(getApplicationContext(), "Could not find 
weather :(", Toast.LENGTH_SHORT).show();
        }
    }

    public class DownloadTask extends AsyncTask<String, Void, String> {


        @Override
        protected String doInBackground(String... urls) {
            String result = "";
            URL url;
            HttpURLConnection urlConnection = null;
            try {

                url = new URL(urls[0]);
                urlConnection = (HttpURLConnection) url.openConnection();
                InputStream in = urlConnection.getInputStream();
                InputStreamReader reader = new InputStreamReader(in);
                int data = reader.read();

                while (data != -1) {
                    char current = (char) data;
                    result += current;
                    data = reader.read();
                }

                return result;

            } catch (Exception e) {
                e.printStackTrace();

                Toast.makeText(getApplicationContext(), "Could not find 
weather :(", Toast.LENGTH_SHORT).show();
                return null;
            }
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);

            Looper.prepare();
            try {
                JSONObject jsonObject = new JSONObject(s);

                String weatherInfo = jsonObject.getString("weather");

                Log.i("Weather content", weatherInfo);

                JSONArray arr = new JSONArray(weatherInfo);

                String message = "";

                for (int i = 0; i < arr.length(); i++) {
                    JSONObject jsonPart = arr.getJSONObject(i);

                  String main = jsonPart.getString("main");
                  String description = jsonPart.getString("description");

                    if (!main.equals("") && !description.equals("")) {
                        message += main + ": " + description + "\r\n";
                    }
                }

                if (!message.equals("")) {
                    resultTextView.setText(message);
                } else {
                    Toast.makeText(getApplicationContext(), "Could not 
                    find weather :(", Toast.LENGTH_SHORT).show();
                }

            } catch (Exception e) {

                Toast.makeText(getApplicationContext(), "Could not find 
                weather :(", Toast.LENGTH_SHORT).show();

                e.printStackTrace();
            }
        }
    }
}

Here are the errors that appeared in my Logcat

2019-09-11 13:24:52.431 
7574-7658/com.example.whatstheweather E/AndroidRuntime: FATAL EXCEPTION: 
AsyncTask #1

Process: com.example.whatstheweather, PID: 7574
java.lang.RuntimeException: An error occurred while executing 
doInBackground()
        at android.os.AsyncTask$4.done(AsyncTask.java:399)
        at 
    java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
    at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
    at java.util.concurrent.FutureTask.run(FutureTask.java:271)
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)       atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:919)

Caused by: java.lang.RuntimeException: Can't toast on a thread that has 
not called Looper.prepare()
    at android.widget.Toast$TN.<init>(Toast.java:407)
    at android.widget.Toast.<init>(Toast.java:121)
    at android.widget.Toast.makeText(Toast.java:286)
    at android.widget.Toast.makeText(Toast.java:276)

atcom.example.whatstheweather.MainActivity$DownloadTask. 
doInBackground(MainActivity.java:81)
atcom.example.whatstheweather.MainActivity$DownloadTask. 
doInBackground(MainActivity.java:53)
    at android.os.AsyncTask$3.call(AsyncTask.java:378)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289) 
    atjava.util.concurrent.ThreadPoolExecutor. 
    runWorker(ThreadPoolExecutor.java:1167) 

atjava.util.concurrent.ThreadPoolExecutor$Worker. 
run(ThreadPoolExecutor.java:641) 
    at java.lang.Thread.run(Thread.java:919) 

This following is from the Run tab

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.whatstheweather, PID: 11228
    java.lang.IllegalStateException: Could not execute method for 
    android:onClick
    at 
androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener. 
onClick(AppCompatViewInflater.java:390)
    at android.view.View.performClick(View.java:7140)
    at android.view.View.performClickInternal(View.java:7117)
    at android.view.View.access$3500(View.java:801)
    at android.view.View$PerformClick.run(View.java:27351)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller. 
run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
 Caused by: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Method.invoke(Native Method)
    at 
androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener. 
onClick(AppCompatViewInflater.java:385)
    at android.view.View.performClick(View.java:7140) 
    at android.view.View.performClickInternal(View.java:7117) 
    at android.view.View.access$3500(View.java:801) 
    at android.view.View$PerformClick.run(View.java:27351) 
    at android.os.Handler.handleCallback(Handler.java:883) 
    at android.os.Handler.dispatchMessage(Handler.java:100) 
    at android.os.Looper.loop(Looper.java:214) 
    at android.app.ActivityThread.main(ActivityThread.java:7356) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller. 
run(RuntimeInit.java:492) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 
 Caused by: java.lang.RuntimeException: Only one Looper may be created 
per thread
    at android.os.Looper.prepare(Looper.java:108)
    at android.os.Looper.prepare(Looper.java:103)
    at 
com.example.whatstheweather.MainActivity.getWeather(MainActivity.java:43)
    at java.lang.reflect.Method.invoke(Native Method) 
    at 
androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener. 
onClick(AppCompatViewInflater.java:385) 
    at android.view.View.performClick(View.java:7140) 
    at android.view.View.performClickInternal(View.java:7117) 
    at android.view.View.access$3500(View.java:801) 
    at android.view.View$PerformClick.run(View.java:27351) 
    at android.os.Handler.handleCallback(Handler.java:883) 
    at android.os.Handler.dispatchMessage(Handler.java:100) 
    at android.os.Looper.loop(Looper.java:214) 
    at android.app.ActivityThread.main(ActivityThread.java:7356) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller. 
run(RuntimeInit.java:492) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Primtoes
  • 41
  • 2
  • 5

2 Answers2

5

Your Toast.makeText method should be called on the main thread. You can do it like this:

runOnUiThread(new Runnable() {
    public void run() {
        Toast.makeText(getApplicationContext(), "Could not find weather :(", Toast.LENGTH_SHORT).show();
    }
});
Jose Nuno
  • 598
  • 3
  • 13
1

The problem is because your toast is being created and displayed in doInBackground method. In android user interface actions must be handle in UI thread. The method onPostExecute is called in UI thread but the method doInBackground is called in another thread. To solve your problem remove the Toast from doInBackground method and put the following lines in onPostExecute method:

// If s equals to null an error ocurred in doInBackground
if (s == null) {
    Toast.makeText(getApplicationContext(), "Could not find weather :(",
            Toast.LENGTH_SHORT).show();
}

See: https://developer.android.com/training/multiple-threads/communicate-ui

Dhiogo Boza
  • 310
  • 2
  • 9