Поиск по этому блогу

среда, 13 февраля 2013 г.

Соединяемся с сервером и обрабатываем полученный json

Не думаю что я буду оригинальным в этой теме так как в интернете полным полно примеров которые должны вроде бы как показать как правильно работать, но у меня как у криворукого говнокодера примеры почему-то запускаются только с сайта stackowerflow, поэтому я решил сделать так называемую заметку на будущее если понадобится работать еще с json и с post / get запросами, всегда будет готовый пример / код под рукой. 
И так начнем, для начала нам нужен сервер который нам будет возвращать что либо, для этого нам понадобитсяwamp server который будет эмулировать наш сервер на компьютере, что бы не покупать хостинг за большие деньги, и конечно же нам нужно его настроить, настройка очень простая, запусукаем инсталятор, ждем пока проинсталируется и запускаем наш сервер, дальше по пути где вы установили открываем папку wamp/www/ и закидываем туда login.php к которому мы будем обращаться. 

Так как мы пишем приложение с работой с удаленным сервером я напишу небольшой скрипт который будет при правильном логине и пароле выдавать нам json строку которую позже мы будем парсить на телефоне.

Открываем в блокноте наш login.php и пишем вот этот код:

login.php
<?php // серверная часть вывода json

$login = $_POST['login'];
$pass = $_POST['pass'];
if($login == "user" & $pass == "pass") {
?>      {
"data":[
{
"firstName":"John",
"lastName":"Doe"
},
{
"firstName":"Anna",
"lastName":"Smith"
},
{
"firstName":"Peter",
"lastName":"Jones"
}
]
}<?php }
?> 


Тут все просто, при получении данных постом сервер их обрабатывает и сравнивает с теми данными которые у него есть, то есть user и pass, если они сходятся значит все отлично и можно отдавать json.

Теперь закидывайте этот файл в wamp/www/ и можно начинать писать клиентскую часть приложения. Создайте андроид проект и начнем.

Первая активность у нас будет посылать post запрос на сервер с логином и паролем, вторая активность принимает ответ от сервера и обрабатываем его для вывода в листвью.

MainActivity
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import org.apache.http.NameValuePair;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity {

        public EditText login;
        public EditText pass;
        private ProgressDialog dialog;
        private InputStream is;
        SecondActivity url;

        @Override
        public void onCreate(Bundle savedInstanceState) {

                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                Button btn = (Button) findViewById(R.id.button1);
                login = (EditText) findViewById(R.id.editText1);
                pass = (EditText) findViewById(R.id.editText2);
                btn.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View v) {
                                //тут указываем куда будем конектится, для примера я привел удаленных хост если у вас не получилось освоить wamp (:
                                new RequestTask().execute("http://myhomepage.hol.es/login.php");
                        }
                });
        }

        class RequestTask extends AsyncTask<String, String, String> {

                @Override
                protected String doInBackground(String... params) {

                        try {
                                //создаем запрос на сервер
                                DefaultHttpClient hc = new DefaultHttpClient();
                                ResponseHandler<String> res = new BasicResponseHandler();
                                //он у нас будет посылать post запрос
                                HttpPost postMethod = new HttpPost(params[0]);
                                //будем передавать два параметра
                                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
                                //передаем параметры из наших текстбоксов
                                //лоигн
                                nameValuePairs.add(new BasicNameValuePair("login", login.getText().toString()));
                                //пароль
                                nameValuePairs.add(new BasicNameValuePair("pass", pass.getText().toString()));
                                //собераем их вместе и посылаем на сервер
                                postMethod.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                                //получаем ответ от сервера
                                String response = hc.execute(postMethod, res);
                                //посылаем на вторую активность полученные параметры
                                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                                //то что куда мы будем передавать и что, putExtra(куда, что);
                                intent.putExtra(SecondActivity.JsonURL, response.toString());
                                startActivity(intent);
                        } catch (Exception e) {
                                System.out.println("Exp=" + e);
                        }
                        return null;
                }

                @Override
                protected void onPostExecute(String result) {

                        dialog.dismiss();
                        super.onPostExecute(result);
                }

                @Override
                protected void onPreExecute() {

                        dialog = new ProgressDialog(MainActivity.this);
                        dialog.setMessage("Загружаюсь...");
                        dialog.setIndeterminate(true);
                        dialog.setCancelable(true);
                        dialog.show();
                        super.onPreExecute();
                }
        }
}


Комментарии описывают весь функционал, остальное впринципе нам не сильно нужно, его я добавил чисто для карсоты.

Запрос мы выполняем в AsyncTask для меньшей загружености телефона, и что бы он не зависал при обработке запроса.

Главное окно


Ах да, как я забыл про разметочку нашей активити, вот она родимая ниже:

main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:text="Залогиниться" />

    <EditText
        android:id="@+id/editText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/button1"
        android:layout_alignParentLeft="true"
        android:text="pass"
        android:ems="10" >

        <requestFocus />
    </EditText>

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/editText2"
        android:layout_alignParentLeft="true"
        android:text="Пароль" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/textView2"
        android:layout_alignParentLeft="true"
        android:text="user"
        android:ems="10" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/editText1"
        android:layout_alignParentLeft="true"
        android:text="Логин" />
</RelativeLayout>


Воот, теперь нам нужно создать вторую активность которая будет принимать наш json и парсить его, для вывода в листвью. Там так же не сильно много кода, он простой и понятный, конечно же присутствуют комментарии.

SecondActivity
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
public class SecondActivity extends Activity {

        public static String JsonURL;
        private static ArrayList<HashMap<String, Object>> myBooks;
        private static final String FIRST = "firstname";
        private static final String LAST = "lastname";
        public ListView listView;

        /** @param result */
        public void JSONURL(String result) {

                try {
                        //создали читателя json объектов и отдали ему строку - result
                        JSONObject json = new JSONObject(result);
                        //дальше находим вход в наш json им является ключевое слово data
                        JSONArray urls = json.getJSONArray("data");
                        //проходим циклом по всем нашим параметрам
                        for (int i = 0; i < urls.length(); i++) {
                                HashMap<String, Object> hm;
                                hm = new HashMap<String, Object>();
                                //читаем что в себе хранит параметр firstname
                                hm.put(FIRST, urls.getJSONObject(i).getString("firstName").toString());
                                //читаем что в себе хранит параметр lastname
                                hm.put(LAST, urls.getJSONObject(i).getString("lastName").toString());
                                myBooks.add(hm);
                                //дальше добавляем полученные параметры в наш адаптер
                                SimpleAdapter adapter = new SimpleAdapter(SecondActivity.this, myBooks, R.layout.list,
                                                new String[] { FIRST, LAST, }, new int[] { R.id.text1, R.id.text2 });
                                //выводим в листвбю
                                listView.setAdapter(adapter);
                                listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
                        }
                } catch (JSONException e) {
                        Log.e("log_tag", "Error parsing data " + e.toString());
                }
        }

        @Override
        public void onCreate(Bundle savedInstanceState) {

                super.onCreate(savedInstanceState);
                setContentView(R.layout.url);
                listView = (ListView) findViewById(R.id.list);
                myBooks = new ArrayList<HashMap<String, Object>>();
                //принимаем параметр который мы послылали в manActivity
                Bundle extras = getIntent().getExtras();
                //превращаем в тип стринг для парсинга
                String json = extras.getString(JsonURL);
                //передаем в метод парсинга
                JSONURL(json);
        }
}


Опять же в комментариях я максимально пытался объяснить как и что делается в этом коде, надеюсь дальнейшего пояснения он не требует.

Ну и осталось только создать разметку для второй активности, первая у нас главная, в ней listView в который все будет размещаться, а вторая это собственно сама разметка listView так как я захотел что бы он выглядел. В общем смотрите сами.

url.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<ListView 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/list"
    />
</LinearLayout>


и сама разметка listview

list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:orientation="horizontal"
  android:layout_height="wrap_content">

  <LinearLayout
  android:layout_width="265dip"
  android:orientation="vertical"
  android:layout_height="wrap_content">
  
  <TextView android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:id="@+id/text1"
  android:textSize="25dip"
  android:text="This is text1"/>
  
  <TextView android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:id="@+id/text2"
  android:text="This is text2"/>
  
  </LinearLayout>
</LinearLayout>




Не забываем так же прописать в AndroidManifest доступ в интернет:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>


И доступ к второй активности:
<activity android:name="my.home.page.SecondActivity"></activity>


Вот теперь запускаем и проверяем как оно работает. Исходники я закинул на гит так что у тех у кого не получилось что либо смотрите их.

Скачать исходники с GitHub & Скачать исходники с Dropbox

28 комментариев:

  1. Ув. автор подскажите пожалуйста, ну очень надо как сделать чат по wifi. Заранее спасибо

    ОтветитьУдалить
    Ответы
    1. По правде сказать для написания чата, нужна серьезная серверная часть что бы можно было обрабатывать серьезные запросы, я такое никогда не писал, поэтому ищите сперва сервеную часть, а потом уже пишите клиентскую.

      Удалить
    2. Просто Мария

      Удалить
    3. Ответ классный, сам долго интересовался этим вопросом!

      Удалить
  2. А если в ответ сервер будет отдавать урлы по которым лежат изображения. Как их скачать и отобразить?

    ОтветитьУдалить
    Ответы
    1. Тоже очень интересует вопрос касательно изображений.

      Удалить
    2. http://nostra13android.blogspot.ru/2012/03/4-universal-image-loader-part-1.html

      Удалить
  3. "HashMap hm;
    hm = new HashMap();"

    Лучше так: Map hm = new HashMap();

    ОтветитьУдалить
  4. Уважаемый Глеб.
    Хотел бы заказать у Вас исходняки для живых обой под андройд. Занимаетесь ли Вы таким?

    darkavega01@gmail.com

    ОтветитьУдалить
    Ответы
    1. Так я вроде бы выкладывал статью про создание обоев. Или вам что-то другое нужно?

      Удалить
    2. Наверно несколько другое. Напишите пожалуйста как с вами можно связаться.

      Удалить
    3. dajver4 - skype
      lfqdth5@gmail.com - email

      Удалить
  5. Автор, большое спасибо за статью! Несмотря на то, что одно приложение с json я уже делал, но ваша статья дала намного более качественное понимание.

    ОтветитьУдалить
  6. Ёмаё. Если автор "криворукий говнокодер", в его проявлении самокритики, то я амеба.
    Спасибо за Ваш труд!

    ОтветитьУдалить
    Ответы
    1. Пожалуйста! рад был помочь!

      Удалить
    2. Автор молодца! Присоединяюсь!
      Кстати, это отличная идея - писать заметки со сферическими примерами для себя же самого, чтобы потом не забыть. Память не резиновая, да и винты имеют свойство сыпаться, а на сервере оно как то надежнее:) Плюс и людям полезно!

      Удалить
  7. Уважаемый Глеб, у меня такая проблема:
    С Вашей ссылкой - http://myhomepage.hol.es/login.php - всё работает,
    но когда копирую Ваш PHP код на свой сервер - не работает.
    В чём может быть проблема?
    Спасибо.

    ОтветитьУдалить
    Ответы
    1. Здравствуйте, возможно причина в том что телефон не видит сервера, вы уверены что его видно из интернета? У меня такая проблема была когда я пытался из локалки достать по причине того что роутер не хотел пропускать локалхост в инет. Скрипт я тот же что и на сервере выложил.

      Удалить
  8. public static void (String[] args){
    System.out.println("Need help!!!")
    }
    нужно сделать практически тоже ...софтина пишет лог в память.нужно состряпать отправку лога на сервер.(он уже есть) дабы потом смотреть эти файлы.
    Помоги со скриптом который бы слушал запросы от приложения и сохранял эти файлы в свой же каталог.

    ОтветитьУдалить
    Ответы
    1. Нужно сохранять на удаленном сервере лог или на телефоне?

      Удалить
    2. "софтина пишет лог в память.нужно состряпать отправку лога на сервер"
      отправить на сервер..при чем не только отправить а синкать!

      Удалить
    3. Поспрашивал у знакомых в общем есть такая библиотека которая обеспечивает такой функционал http://dajver.blogspot.com/2013/02/json.html, на сайте есть пример и руководство пользователя

      Удалить
    4. эмм простите на каком сайте?

      Удалить
  9. Этот комментарий был удален автором.

    ОтветитьУдалить
  10. Глеб,статья очень понравилась, но хотелось бы поинтересоваться каким образом можно добавить сюда динамическое обновление данных(например если на сервере изменилась информация) и вывод русских букв в нормальном виде(а то при попытке клиентом получить данные в русской раскладке появляются одни знаки вопроса)
    Заранее спасибо за ответ.

    ОтветитьУдалить
  11. не подскажете, сложно ли в апк файле онлайн игры поменять, чтобы из евро версии она стала обращаться к русерверам ? Просто есть мод онлайн игры под евро сервера, а нужно чтобы работал на ру серверах.

    ОтветитьУдалить
    Ответы
    1. это нужно декомпилировать апк и менять адрес в декомпилированом коде, а потом собрать обратно так что бы оно работало - это довольно сложно. Могу только сказать что капать нужно в сторону apk tool и как с ним работать.

      Удалить