Как получить JSON данные с сервера в Android?
Надо получить одну переменную int с сервера. Не получается, эмулятор вылетает, никакую ошибку не выдаёт. Код вот такой:
private class DownloadDataTask extends AsyncTask < @Override protected String doInBackground(String. strings) < java.net.URL url =` null; HttpURLConnection urlConnection = null; StringBuilder result = new StringBuilder(); try < url = new URL(strings[0]); urlConnection = (HttpURLConnection) url.openConnection(); InputStream inputStream = urlConnection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader reader = new BufferedReader(inputStreamReader); String line = reader.readLine(); while (line != null) < result.append(line); line = reader.readLine(); >return result.toString(); > catch (MalformedURLException e) < e.printStackTrace(); >catch (IOException e) < e.printStackTrace(); >finally < if (urlConnection != null) < urlConnection.disconnect(); >> return null; > @Override protected void onPostExecute(String s) < super.onPostExecute(s); try < JSONObject jsonObject = new JSONObject(s); poluchennyedannye = jsonObject.getInt("data"); >catch (JSONException e) < e.printStackTrace(); Toast.makeText(MainActivity.this, "ВСЕ ПЛОХО. ВЫРУБАЙ ЭТОТ КОД, НИЧЕ НЕ РАБОТАЕТ. ", Toast.LENGTH_SHORT).show(); >> >
Отслеживать
11.7k 3 3 золотых знака 24 24 серебряных знака 33 33 бронзовых знака
задан 25 ноя 2022 в 12:54
AndroidStudioUser AndroidStudioUser
3 1 1 бронзовый знак
Код выглядит что написан лет 10 назад. Может пришло время переписать? Есть куча библиотек в помощь.
25 ноя 2022 в 13:45
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
- Для начала в папке res/xml нужно создать файл: network_security_config.xml со следующим содержимым:
package com.example.android; import androidx.appcompat.app.AppCompatActivity; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class MainActivity extends AppCompatActivity < @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyTask myTask = new MyTask(); myTask.execute(); >private class MyTask extends AsyncTask < StringBuilder builder = new StringBuilder(); String line; @Override protected void onPreExecute() < super.onPreExecute(); Toast.makeText(MainActivity.this, "Подключение к API", Toast.LENGTH_SHORT).show(); >@Override protected Void doInBackground(Void. voids) < try < URL url = new URL("https://jsonplaceholder.typicode.com/posts/1"); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setDoInput(true); httpURLConnection.connect(); if(httpURLConnection.getResponseCode() != HttpURLConnection.HTTP_OK) < throw new Exception(httpURLConnection.getResponseMessage()) >BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream())); while((line = bufferedReader.readLine()) != null) < builder.append(line); >bufferedReader.close(); httpURLConnection.disconnect(); > catch (Exception e) < Log.e("ERROR", e.getMessage()); >return null; > @Override protected void onPostExecute(Void unused) < super.onPostExecute(unused); try < JSONObject buffer = new JSONObject(builder.toString()); Log.d("Тайтл", buffer.getString("title")); >catch (JSONException e) < e.printStackTrace(); >> > >
Как получить данные от сервера java android
На сегодняшний день если не все, то большинство Android-устройств имеют доступ к сети интернет. А большое количество мобильных приложений так или иначе взаимодействуют с средой интернет: загружают файлы, авторизуются и получают информацию с внешних веб-сервисов и т.д. Рассмотрим, как мы можем использовать в своем приложении доступ к сети интернет.
Среди стандартных элементов нам доступен виджет WebView, который может загружать контент с определенного url-адреса. Но этим возможности работы с сетью в Android не ограничиваются. Для получения данных с определенного интернет-ресурса мы можем использовать классы HttpUrlConnection (для протокола HTTP) и HttpsUrlConnection (для протокола HTTPS) из стандартной библиотеки Java.
Итак, создадим новый проект с пустой MainActivity. Первым делом для работы с сетью нам надо установить в файле манифеста AndroidManifest.xml соответствующее разрешение:
В файле activity_main.xml , который представляет разметку для MainActivity, определим следующий код:
Здесь определена кнопка для загрузки данных, а сами данные для примера загружаются одновременно в виде строки в текстовое поле и в элемент WebView. Так как данных может быть очень много, то текстовое поле помещено в элемент ScrollView.
Поскольку загрузка данных может занять некоторое время, то обращение к интернет-ресурсу определим в отдельном потоке и для этого изменим код MainActivity следующим образом:
package com.example.httpapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.webkit.WebView; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import javax.net.ssl.HttpsURLConnection; public class MainActivity extends AppCompatActivity < @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView contentView = findViewById(R.id.content); WebView webView = findViewById(R.id.webView); webView.getSettings().setJavaScriptEnabled(true); Button btnFetch = findViewById(R.id.downloadBtn); btnFetch.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View v) < contentView.setText("Загрузка. "); new Thread(new Runnable() < public void run() < try< String content = getContent("https://stackoverflow.com/"); webView.post(new Runnable() < public void run() < webView.loadDataWithBaseURL("https://stackoverflow.com/",content, "text/html", "UTF-8", "https://stackoverflow.com/"); Toast.makeText(getApplicationContext(), "Данные загружены", Toast.LENGTH_SHORT).show(); >>); contentView.post(new Runnable() < public void run() < contentView.setText(content); >>); > catch (IOException ex) < contentView.post(new Runnable() < public void run() < contentView.setText("Ошибка: " + ex.getMessage()); Toast.makeText(getApplicationContext(), "Ошибка", Toast.LENGTH_SHORT).show(); >>); > > >).start(); > >); > private String getContent(String path) throws IOException < BufferedReader reader=null; InputStream stream = null; HttpsURLConnection connection = null; try < URL url=new URL(path); connection =(HttpsURLConnection)url.openConnection(); connection.setRequestMethod("GET"); connection.setReadTimeout(10000); connection.connect(); stream = connection.getInputStream(); reader= new BufferedReader(new InputStreamReader(stream)); StringBuilder buf=new StringBuilder(); String line; while ((line=reader.readLine()) != null) < buf.append(line).append("\n"); >return(buf.toString()); > finally < if (reader != null) < reader.close(); >if (stream != null) < stream.close(); >if (connection != null) < connection.disconnect(); >> > >
Непосредственно для самой загрузки определен метод getContent() , который будет загружать веб-страницу с помощью класса HttpsURLConnection и возвращать код загруженной страницы в виде строки.
Вначале создается элемент HttpsURLConnection :
URL url=new URL(path); connection =(HttpsURLConnection)url.openConnection(); connection.setRequestMethod("GET"); // установка метода получения данных -GET connection.setReadTimeout(10000); // установка таймаута перед выполнением - 10 000 миллисекунд connection.connect(); // подключаемся к ресурсу
После подключение происходит считывание со входного потока:
stream = connection.getInputStream(); reader= new BufferedReader(new InputStreamReader(stream));
Используя входной поток, мы можем считать его в строку.
Этот метод getContent() затем будет вызываться в обработчике нажатия кнопки:
Button btnFetch = (Button)findViewById(R.id.downloadBtn); btnFetch.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View v) < contentView.setText("Загрузка. "); new Thread(new Runnable() < public void run() < try< String content = getContent("https://stackoverflow.com/");
Поскольку загрузка может занять долгое время, то метод getContent() в отдельном потоке с помощью объектов Thread и Runnable. Для примера в данном случае обращение идет к ресурсу "https://stackoverflow.com/".
Запустим приложение и нажмем на кнопку. И при наличии интернета приложение загрузит гравную страницу с "https://stackoverflow.com/" и отобразит ее в WebView и TextView:
Конечно, данный способ вряд ли подходит для просмотра интернет-страниц, однако таким образом, мы можем получать какие-либо данные (не интернет-страницы) от различных веб-сервисов, например, в формате xml или json (например, различные курсы валют, показатели погоды), используя специальные api, и затем после обработки показывать их пользователю.
Как в Android получить данные с сервера с помощью Retrofit

Retrofit - популярная и удобная библиотека, которая используется, если приложению необходимо работать с сервером. В этой статье я на очень простом примере рассмотрю основы работы с ней.
Предположим, что на каком-то сервере есть данные. И сервер готов нам выдать эти данные. Чаще всего для этого используется формат json. Вот пример таких данных: https://rawgit.com/startandroid/data/master/messages/messages1.json
Давайте используем Retrofit, чтобы получить в приложении данные из этого файла.
Для начала немного теории.
Конфигурирование Retrofit можно разделить на две части: API интерфейс и билдер. Давайте рассмотрим подробно, кто из них за что отвечает.
Представим себе работу по обмену данными приложения с сервером. Как это происходит? Обычно сервер предоставляет нам какое-то API, т.е. набор методов (он же REST). Если, например, это сервер интернет-магазина, то его API может содержать следующие методы:
getProducts - получить список товаров
getProdut - получить детальные данные о продукте
getOrders - получить список заказов
getOrder - получить детальные данные о заказе
createOrder - создать заказ
и т.д.
Это серверные методы и, чтобы их вызывать, нам необходимо выполнять запросы, которые могут выглядеть, например, так:
http://server/api/v1/getProducts
http://server/api/v1/getProduct
http://server/api/v1/getOrders
http://server/api/v1/getOrder
http://server/api/v1/createOrder
и т.д.
Т.е. у сервера есть какой-то базовый URL - http://server/api/v1/. И к нему просто добавляются имена методов (getProducts, getOrders, и т.д.), чтобы получать ссылки для работы с API методами.
Возвращаемся в приложение. Было бы очень удобно, если бы у нас в приложении был класс ServerApi с методами getProducts, getOrders и т.д. И при вызове этих методов происходил бы вызов соответствующих серверных методов. И Retrofit как раз может создать для нас такой класс. От нас потребуется интерфейс, в котором мы распишем необходимые методы.
Вот пример такого интерфейса:
public interface ServerApi < @GET("getProducts") ListgetProducts(); @GET("getProduct") Product getProduct(long productId); @GET("getOrders") List getOrders(); @GET("getOrder") Order getOrder(long orderId); @POST("createOrder") void createOrder(Order order); >
Этот код немного упрощен. В реальном примере будут дополнительные аннотации и обертки. Но общий смысл он вполне передает. Retrofit создаст класс, который реализует этот интерфейс. Внутри этого класса и будут сгенерированы вызовы к серверу.
А мы в этом интерфейсе можем настроить названия методов и некоторые HTTP штуки, такие как передаваемые параметры (Query), заголовки (Header) и т.п.
Рассмотрим описание метода getProducts подробнее:
@GET("getProducts") List getProducts();
Аннотация GET означает, что HTTP-запрос для getProducts должен быть типа GET. А в качестве параметра для этой аннотации нам необходимо указать имя метода на сервере. Обратите внимание, что здесь не фигурирует базовый URL (http://server/api/v1/). Его мы будем указывать позже, в билдере.
List - это тип возвращаемых данных. Retrofit сам сможет сконвертировать json данные в список объектов Product. Подробнее об этом тоже поговорим во время обсуждения билдера.
Ну и имя метода - getProducts(). В этом примере оно совпадает с именем метода на сервере. Но это необязательно. Т.е. вы вполне можете сделать так
@GET("getProducts") List getProductList();
Теперь имена методов различаются. Вы в коде будете вызывать метод getProductList, а на сервере будет вызываться getProducts.
Переходим к практике. Чтобы использовать в вашем проекте Retrofit, в build.gradle файле, в секции dependencies добавляйте зависимости:
compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0'
Первая строка - это непосредственно Retrofit, а вторая - это его Gson-конвертер.
Она вернет нам json данные, которые представляют из себя список сообщений. Каждое сообщение содержит поля id, time, text и иногда image. Эта ссылка, конечно, не API, а просто текстовый файл, но смысл остается тем же, просто вместо имени метода у нас будет имя файла.
Мы делим ссылку на две части:
"https://rawgit.com/startandroid/data/master/messages/" - базовый URL (будет указан в билдере)
"messages1.json" - имя файла (будет указано в интерфейсе, в GET аннотации)
Мы можем создать интерфейс для работы с этой ссылкой. Там будет только один метод, и он будет возвращать List.
Давайте сначала создадим класс Message.
public class Message < private long id; private long time; private String text; private String image; // getters and setters . >
Его поля соответствуют полям в данных в messages1.json.
Теперь создаем интерфейс:
import java.util.List; import retrofit2.Call; import retrofit2.http.GET; public interface MessagesApi < @GET("messages1.json") Call> messages(); >
В аннотации GET указываем, что к базовому URL надо будет добавить "messages1.json", чтобы получилась нужная нам ссылка.
Обратите внимание на Call. Эта обертка нужна для работы Retrofit. В ней мы указываем, какой тип данных ожидаем получить из messages1.json - т.е. List.
Интерфейс создан, теперь переходим к билдеру:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://rawgit.com/startandroid/data/master/messages/") .addConverterFactory(GsonConverterFactory.create()) .build();
В билдере мы указываем базовый URL и добавляем Gson конвертер, чтобы Retrofit сам смог сконвертировать json данные в объекты Message. Это минимум настроек, который от нас требуется для нашего простого примера. Для сложных случаев количество параметров может быть больше.
В итоге у нас есть объект Retrofit, который содержит базовый URL и способность преобразовывать json данные с помощью Gson. Мы передаем ему в метод create класс интерфейса, в котором описывали методы.
MessagesApi messagesApi = retrofit.create(MessagesApi.class);
И получаем реализацию MessagesApi от Retrofit. В этой реализации соединены настройки билдера (базовый URL и Gson конвертер) и описания методов из интерфейса (метод messages для получения файла messages1.json). В итоге Retrofit будет брать базовый URL из билдера, присоединять к нему остаток пути, который мы указываем в GET в интерфейсе, и тем самым получит полную ссылку.
Возможно, возникает вопрос, зачем было разделять функционал на билдер и интерфейс. Я могу привести вам пару примеров из своей практики.
1) У интернет-магазина есть два сервера: рабочий (с реальными данными) и тестовый. На обоих развернуто одинаковое API. И есть приложение, которое общается с сервером. Debug-версия приложения должна работать с тестовым сервером и выводить в лог всю информацию об этом. Release-версия работает с рабочим сервером и ничего не выводит в лог.
Эту разницу между Debug и Release версиями можно реализовать двумя разными билдерами. Просто указываем для них разные базовые URL и разные настройки логирования. А интерфейс в обоих случаях будет использоваться один и тот же.
2) Есть сервер с API. И есть приложение, которое вызывает эти методы. На сервере реализована авторизация. Т.е. приложение должно вызвать метод авторизации, получить токен, и затем использовать этот токен при вызове некоторых API методов, которые связаны с пользователем.
В билдере есть возможность использовать специальный обработчик, который будет добавлять токен ко всем вызовам. Но нам надо добавлять токен не ко всем, а только к некоторым вызовам.
В этом случае можно создать два билдера. Первый не будет использовать токен. Реализацию, полученную от этого билдера используем для работы с методами, не требующими токена. А второй билдер будет добавлять токен ко всем вызовам. Реализацию, полученную от этого билдера используем для всех методов, где нужен токен.
Итак, связка интерфейса и билдера дала нам реализацию MessagesApi. Используем ее для получения данных. Вызываем метод messages()
Call> messages = messagesApi.messages(); messages.enqueue(new Callback>() < @Override public void onResponse(Call> call, Response> response) < log("response " + response.body().size()); >@Override public void onFailure(Call> call, Throwable t) < >>);
Метод log - это просто вывод в лог текста
Вызов метода messages вернет нам Call объект. При выполнении этого кода запрос к серверу еще не был отправлен. Call дает нам возможность отправить запрос синхронно (блокируя текущий поток) или асинхронно (с колбэком). Мы выбираем асинхронный вариант: вызываем метод enqueue и создаем для него Callback. Запрос будет выполнен в отдельном потоке, а результат придет в Callback в main потоке.
Если взаимодействие с сервером пройдет успешно, то мы получим результат в метод onResponse в объекте Response. Чтобы добраться до данных, необходимо вызвать метод body() у Response объекта. Это даст нам список сообщений - List. Выводим в лог количество записей в этом списке.
Запустив этот код, мы получим список из 50 записей. Лог покажет следующее:
Если по каким-то причинам не удалось достучаться до сервера, то вместо onResponse, будет вызван метод onFailure. Добавим вывод в лог ошибки, которую вернет нам Retrofit
@Override public void onFailure(Call> call, Throwable t)
Чтобы эмулировать ошибку, мы можем просто поломать имя сервера в базовом URL, который задается в билдере. Например, добавим букву r к rawgit:
https://rrawgit.com/startandroid/data/master/messages/
Запускаем и видим в логе ошибку.
failure java.net.UnknownHostException: Unable to resolve host "rrawgit.com": No address associated with hostname
Retrofit сообщает, что не нашел сервер rrawgit.com
Если случится какая-то ошибка в процессе работы Retrofit, то она попадет также в onFailure. Например, если сервер вернет некорректные данные, то GsonConverterFactory не сможет их распарсить и выкинет ошибку. А Retrofit отправит ее нам в onFailure.
Есть тип ошибок, который Retrofit умеет обрабатывать без вызова onFailure. Это HTTP-ошибки сервера. Самый простой случай - ошибка 404. Запрос дойдет до сервера, но сервер вместо данных вернет HTTP 404 Not found. Казалось бы - это ошибка и на должна прийти в onFailure. Но нет. onFailure вызывается в случае, когда не удалось достучаться до сервера. А здесь с сервером все ок, он просто вместо данных вернул нам информацию о том, что мы странные и хотим непонятного.
В итоге, ответ сервера мы получим в методе onResponse. И нам надо как-то различать, вернул сервер данные или ошибку. Для этого у объекта Response есть метод isSuccessful.
Перепишем метод onResponse
@Override public void onResponse(Call> call, Response> response) < if (response.isSuccessful()) < log("response " + response.body().size()); >else < log("response code " + response.code()); >>
Если запрос был выполнен успешно (isSuccessfull), то выводим в лог количество полученных записей. Иначе выводим в лог код ошибки.
Снова поменяем базовый URL, чтобы спровоцировать ошибку 404. Добавим лишнюю букву s в слово messages:
https://rawgit.com/startandroid/data/master/messagess/
Запускаем и в логе видим
response code 404
Чтобы получить полный текст ошибки сервера, используйте
response.errorBody().string()
Приведу весь код работы с Retrofit:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://rawgit.com/startandroid/data/master/messages/") .addConverterFactory(GsonConverterFactory.create()) .build(); MessagesApi messagesApi = retrofit.create(MessagesApi.class); Call> messages = messagesApi.messages(); messages.enqueue(new Callback>() < @Override public void onResponse(Call> call, Response> response) < if (response.isSuccessful()) < log("response " + response.body().size()); >else < log("response code " + response.code()); >> @Override public void onFailure(Call> call, Throwable t) < log("failure " + t); >>);
Разумеется, вам не нужно при каждом запросе к серверу использовать билдер и создавать новый объект MessagesApi. Это делается один раз за время работы приложения и далее объект MessagesApi используется везде, где необходима работа с сервером. Для реализации Singleton подойдет Dagger или любая другая реализация этого паттерна.
На этом пока все, чтобы не перегружать вас информацией. Продолжение в следующем посте.
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
Получение данных с сервера Android без нажатий на кнопку, как организовать?
Всем привет. Подскажите пожалуйста, как ( и какой либрой) организовать получение данных с сервера без нажатий на кнопку в андроид приложении. На данный момент есть приложение которое получает данные с сервера по нажатию на кнопку. Хотелось бы от этого избавиться и получать данные например раз в n секунд. Использую OkHttp.
вот пример моего говнокода)
public class MainActivity extends AppCompatActivity < Button button; TextView getTemperature; private OkHttpClient client; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = findViewById(R.id.submitButton); getTemperature = findViewById(R.id.getTemperature); button.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View v) < getWeb(); >>); client = new OkHttpClient(); > private void getWeb() < Request request = new Request.Builder().url("http://192.168.0.23:3000").build(); client.newCall(request).enqueue(new Callback() < @Override public void onFailure(Call call, IOException e) < runOnUiThread(new Runnable() < @Override public void run() < getTemperature.setText("в работе возникла ошибка, скорее всего отсутствует интернет"); >>); > @Override public void onResponse(Call call, final Response response) < runOnUiThread(new Runnable() < @Override public void run() < try< getTemperature.setText(response.body().string()); >catch (IOException ioe) < getTemperature.setText("r"); >> >); > >); > >
- Вопрос задан более трёх лет назад
- 958 просмотров
Комментировать
Решения вопроса 1

Разработчик Android
Создать фоновый таймер для загрузки данных:
AsyncLoaderTimer bgTimer = new AsyncLoaderTimer(); //новое поле класса MainActivity class AsyncLoaderTimer extends AsyncTask /класс фонового таймера @Override protected Void doInBackground(Long. longs) < while (isCancelled()) < try < Thread.sleep(longs[0]); > catch (InterruptedException e) < e.printStackTrace(); >publishProgress(); > return null; > @Override protected void onProgressUpdate(Void. values) < getWeb(); >> @Override protected void onStart() < super.onStart(); bgTimer.execute(2_000L); //интервал миллисекунд >@Override protected void onStop()
То как Вы хотите реализовать обновление данных - не лучшая практика. Вам лучше посмотреть как реализовывать такие клиент-серверные решения.
Ответ написан более трёх лет назад
Комментировать
Нравится 1 Комментировать
