Threaded HTTP-Request mit Android API22

Nach Stunden des Java-Frustes hab' ich es endlich geschafft, via App einen HTTP-Request abzusenden, also mit dem Internet zu kommunizieren.

Es ist immer schön, wenn offizieller Beispielcode nicht funktioniert, das bin ich jedoch von gewissen Institutionen schon gewohnt 😸, daher hier meine Vorgehensweise:

Erstmal hier der offizielle Code von Google: URLConnection

URL url = new URL("ftp://mirror.csclub.uwaterloo.ca/index.html");
URLConnection urlConnection = url.openConnection();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
try {
  readStream(in);
 finally {
  in.close();
 }
}

Das funktioniert aus 2 (4) Gründen nicht:

  • die readStream Methode muss man sich noch besorgen, z.B. hier
  • Unhandled Exception

Nachdem ich das gefixt hatte, geht immer noch nichts:
W/System.err﹕ at android.app.ActivityThread.main(ActivityThread.java:5293)
Ab Android API soundso darf im UI-Thread, demnach in der MainActivity, kein blockierender Code, wie z.B. eine HTTP-Anfrage, ausgeführt werden.
Dazu muss ein neuer Thread her:

package net.dckg.javasucks;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;


public class MainActivity extends ActionBarActivity {

    protected String result;


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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void OnClickFunction(View view) {
        EditText et;
        et = (EditText) findViewById(R.id.editText3);
        new Thread(new Runnable() {
            public void run() {
                URL url;
                try {
                    url = new URL("http://ipv4.icanhazip.com");
                    try {
                        URLConnection urlConnection = url.openConnection();
                        InputStream in = new BufferedInputStream(urlConnection.getInputStream());
                        result = readStream(in);
                        in.close();
                    } catch (Exception e) {
                        result = "Exception: UrlConnection failed";
                        e.printStackTrace();
                    }
                } catch (MalformedURLException e) {
                    result = "Exception: MalformedURLException";
                }
            }
        }).start();

        et.setText(result);
    }

    private String readStream(InputStream is) throws IOException {
        StringBuilder sb = new StringBuilder();
        BufferedReader r = new BufferedReader(new InputStreamReader(is), 1000);
        for (String line = r.readLine(); line != null; line = r.readLine()) {
            sb.append(line);
        }
        is.close();
        return sb.toString();
    }

}


Der Thread, welcher den HTTP-Request ausführt, befindet sich in der Main Klasse in einer Funktion, welche als Onclick-Event für einen Button gesetzt wurde.
Sollen die Ergebnisse des HTTP-Requests vom Thread in die Activity zurückgegeben werden, muss die entsprechende Variable vorher in der selben Klasse definiert sein, am Besten als protected. Schlussendlich wird result in einen editText geschrieben, auf welchen der Thread zum Beispiel keinen Zugriff hätte, da die Variable result nicht global in der Klasse definiert ist.

Eine letzte Einstellung fehlt noch, nämlich die Internet-Berechtigung, zu setzen im AndroidManifest.xml
 <uses-permission android:name="android.permission.INTERNET"></uses-permission>

Ein ziemlicher Aufwand, wenn ich das mit
file_get_contents("url")   //PHP
vergleiche...
Muss da mal eine Funktion schreiben, die als Einzeiler benutzbar ist.