OkHTTP

OkHTTP este un proiect open-source, dezvoltat sub licență Apache, punând la dispoziția utilizatorilor o bibliotecă Java pentru accesarea de resurse prin intermediul protocolului HTTP.

Pentru ca metodele din API-ul OkHTTP Components să poată fi utilizate într-o aplicație Android este necesar să se specifice catre sistemul de build, în fișierul build.gradle din app:

...
dependencies {
    ... 
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
}
...

De asemenea, in android manifest trebuie sa punem permisiunea de network

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission
        android:name="android.permission.INTERNET"/>
    ...

Pentru a evita blocarea thread-ului main, oricare din apelurile catre API-ul de HTTP va trebui executat pe un thread secundar. Va reamintim o varianta cu un Thread si un handler pentru comunicare.



public class MainActivity extends AppCompatActivity {

    // Handler to communicate with UI thread
    private Handler mainHandler = new Handler(Looper.getMainLooper());
    private TextView resultTextView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        resultTextView = findViewById(R.id.resultTextView);
        
        // Start HTTP request when a button is clicked
        Button fetchButton = findViewById(R.id.fetchButton);
        // When the button is pressed, make the HTTP request
        fetchButton.setOnClickListener(v -> makeHttpRequest());
    }
    
    private void makeHttpRequest() {
        // We create a new thread, and inside we will run the code for the HTTP API.
        new Thread(() -> {
            try {
                // make request
                mainHandler.post(() -> {
                    // Here we can run code on the main thread, and use variables
                    // defined in the activity such as restulTextView
                    resultTextView.setText(result);
                });
                
            } catch (Exception e) {
                // Handle error on main thread
                mainHandler.post(() -> {
                    resultTextView.setText("Error: " + e.getMessage());
                });
            }
        }).start();
    }
}



class MainActivity : AppCompatActivity() {

    // Handler to communicate with UI thread
    private val mainHandler = Handler(Looper.getMainLooper())
    private lateinit var resultTextView: TextView
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        resultTextView = findViewById(R.id.resultTextView)
        
        // Start HTTP request when a button is clicked
        val fetchButton = findViewById<Button>(R.id.fetchButton)
        // When the button is pressed, make the HTTP request
        fetchButton.setOnClickListener { makeHttpRequest() }
    }
    
    private fun makeHttpRequest() {
        // We create a new thread, and inside we will run the code for the HTTP API.
        Thread {
            try {
                // make request
                mainHandler.post {
                    // Here we can run code on the main thread, and use variables
                    // defined in the activity such as restulTextView
                    resultTextView.text = result
                }
                
            } catch (e: Exception) {
                // Handle error on main thread
                mainHandler.post {
                    resultTextView.text = "Error: ${e.message}"
                }
            }
        }.start()
    }
}

GET. Urmatorul exemplu prezinta o cere simpla de tip GET.



OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
    .url("https://wi-fi.cs.pub.ro/expr/expr_get.php?operation=times&t1=9&t2=2")
    .build();

try (Response response = client.newCall(request).execute()) {
  // Se verifică dacă cererea a avut succes (ex: cod de status 200 OK)
  if (response.isSuccessful() && response.body() != null) {
    // Se extrage conținutul răspunsului sub formă de text (String)
    String content = response.body().string();
    /* aici se poate procesa răspunsul obținut */
    Log.i(Constants.TAG, content);
  } else {
    // Se gestionează cazul de eroare (ex: 404 Not Found, 500 Server Error)
    Log.e(Constants.TAG, "Cererea nu a avut succes. Cod: " + response.code());
  }
} catch (IOException e) {
  Log.e(Constants.TAG, "Cererea de rețea a eșuat: " + e.getMessage());
  if (Constants.DEBUG) {
    e.printStackTrace();
  }
}



val client = OkHttpClient()
val request = Request.Builder()
    .url("https://wi-fi.cs.pub.ro/expr/expr_get.php?operation=times&t1=9&t2=2")
    .build()

try {
    client.newCall(request).execute().use { response ->
        // Se verifică dacă cererea a avut succes (ex: cod de status 200 OK)
        if (response.isSuccessful && response.body != null) {
            // Se extrage conținutul răspunsului sub formă de text (String)
            val content = response.body!!.string()
            /* aici se poate procesa răspunsul obținut */
            Log.i(Constants.TAG, content)
        } else {
            // Se gestionează cazul de eroare (ex: 404 Not Found, 500 Server Error)
            Log.e(Constants.TAG, "Cererea nu a avut succes. Cod: ${response.code}")
        }
    }
} catch (e: IOException) {
    Log.e(Constants.TAG, "Cererea de rețea a eșuat: ${e.message}")
    if (Constants.DEBUG) {
        e.printStackTrace()
    }
}

În situația în care se dorește transmiterea de parametri către serverul web, aceștia trebuie incluși în URL (în clar), fără a se depăși limita de 2048 de caractere și folosind numai caractere ASCII:



Request request = new Request.Builder()
    .url("http://www.server.com?attribute1=value1&...&attributen=valuen")
    .build();



val request = Request.Builder()
    .url("http://www.server.com?attribute1=value1&...&attributen=valuen")
    .build()

POST. Urmatorul exemplu prezinta o cere simpla de tip POST.



OkHttpClient client = new OkHttpClient();

/* 1. Lista de perechi tip (atribut, valoare) care vor contine
     informatiile transmise de client pe baza carora serverul
     va genera continutul, de exemplu (user, eim), (parola, 123)*/  
FormBody formBody = new FormBody.Builder()
    .add("attribute1", "value1")
    // ...
    .add("attributen", "valuen")
    .build();

// 2. Se construiește cererea POST, specificând URL-ul și corpul cererii.
Request request = new Request.Builder()
    .url("http://www.server.com")
    .post(formBody)
    .build();

// 3. Se execută cererea și se procesează răspunsul.
//    Acest apel este SINCRON și trebuie executat pe un fir de execuție secundar (background thread).
try (Response response = client.newCall(request).execute()) {
  if (response.isSuccessful() && response.body() != null) {
    // Se extrage conținutul răspunsului sub formă de text.
    String content = response.body().string();
    // Aici se poate procesa răspunsul (content).
    Log.i(Constants.TAG, content);
  } else {
    // Se gestionează cazul de eroare (ex: 404 Not Found, 500 Server Error).
    Log.e(Constants.TAG, "Cererea nu a avut succes. Cod: " + response.code());
  }
} catch (IOException e) {
  // Se gestionează erorile de rețea (ex: fără conexiune, timeout).
  Log.e(Constants.TAG, "Cererea de rețea a eșuat: " + e.getMessage());
  if (Constants.DEBUG) {
    e.printStackTrace();
  }
}



val client = OkHttpClient()

/* 1. Lista de perechi tip (atribut, valoare) care vor contine
     informatiile transmise de client pe baza carora serverul
     va genera continutul, de exemplu (user, eim), (parola, 123)*/
val formBody = FormBody.Builder()
    .add("attribute1", "value1")
    // ...
    .add("attributen", "valuen")
    .build()

// 2. Se construiește cererea POST, specificând URL-ul și corpul cererii.
val request = Request.Builder()
    .url("http://www.server.com")
    .post(formBody)
    .build()

// 3. Se execută cererea și se procesează răspunsul.
//    Acest apel este SINCRON și trebuie executat pe un fir de execuție secundar (background thread).
try {
    client.newCall(request).execute().use { response ->
        if (response.isSuccessful && response.body != null) {
            // Se extrage conținutul răspunsului sub formă de text.
            val content = response.body!!.string()
            // Aici se poate procesa răspunsul (content).
            Log.i(Constants.TAG, content)
        } else {
            // Se gestionează cazul de eroare (ex: 404 Not Found, 500 Server Error).
            Log.e(Constants.TAG, "Cererea nu a avut succes. Cod: ${response.code}")
        }
    }
} catch (e: IOException) {
    // Se gestionează erorile de rețea (ex: fără conexiune, timeout).
    Log.e(Constants.TAG, "Cererea de rețea a eșuat: ${e.message}")
    if (Constants.DEBUG) {
        e.printStackTrace()
    }
}

Prelucrarea raspunsurilor. Prelucrarea unui răspuns HTTP se poate realiza:

  • prin prelucrarea obiectului de tip Response din OkHttp, utilizând fluxuri de intrare/ieșire:


// Presupunem că 'pageResponse' este un obiect valid de tip okhttp3.Response obținut dintr-un apel de rețea.
StringBuilder result = new StringBuilder();

try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(pageResponse.body().byteStream()))) {

  int currentLineNumber = 0;
  String currentLineContent;

  while ((currentLineContent = bufferedReader.readLine()) != null) {
    currentLineNumber++;
    result.append(currentLineNumber).append(": ").append(currentLineContent).append("\n");
  }

  Log.i(Constants.TAG, result.toString());

} catch (IOException exception) {
  Log.e(Constants.TAG, "A apărut o eroare la citirea răspunsului: " + exception.getMessage());
  if (Constants.DEBUG) {
    exception.printStackTrace();
  }
}



// Presupunem că 'pageResponse' este un obiect valid de tip okhttp3.Response obținut dintr-un apel de rețea.
val result = StringBuilder()

try {
    BufferedReader(InputStreamReader(pageResponse.body()!!.byteStream())).use { bufferedReader ->
        var currentLineNumber = 0
        var currentLineContent: String?

        while (bufferedReader.readLine().also { currentLineContent = it } != null) {
            currentLineNumber++
            result.append("$currentLineNumber: $currentLineContent\n")
        }

        Log.i(Constants.TAG, result.toString())
    }
} catch (exception: IOException) {
    Log.e(Constants.TAG, "A apărut o eroare la citirea răspunsului: ${exception.message}")
    if (Constants.DEBUG) {
        exception.printStackTrace()
    }
}

  • utilizând metoda string() a obiectului ResponseBody pentru a obține direct conținutul ca String
  • mai multe tehnici de rezolvare cu OkHTTP.