Как-то морозным сибирским вечером под Новый Год я задумался о смысле бытия. Я уже давно понял, что друзья очень важны в нашей жизни. Одно оставалось понять: где их найти — единомышленников? Ведь в нашу пору высокоскоростного Интернета люди переплетены связями так крепко, что часто и не замечаешь то, что они могут находиться на огромных расстояниях от тебя…
Тут я начали приходить на ум различные социальные сети, где люди могут находить друг друга по городу. Однако, мне это казалось тоже слишком обширным, хотелось чего-то более близкого: например, программу поиска людей из «соседнего дома».
И тут на ум пришла гениальная идея: почему бы не создать свою иновационную гео-соцсеть, где люди могли бы находить друг друга на карте по GPS? В качестве опознавательных знаков было принято ввести статусы: человек постит свой умный статус, который отражает его текущее состояние души, а его земляки видят это и жмакают по его статусу, а тут открывается целая история статусов — криков его души…
Я понимал, что наверняка что-то подобное существует, но, судорожно открыв поисковик и оглядев несколько результатов, я пришел к выводу, что таких систем для поиска новых людей не существует. Возможно, я сделал этот вывод поспешно, ведь не терпелось почувствовать себя великим человеком и попробовать свои силы в программировании под Android!
Да, я недавно приобрел аппарат на Android и хотелось опробовать программирование под него. В качестве языка программирования для сервера и клиента была выбрана Java. Сразу хочу сказать, что не было задачи написать какую-то высокопроизводительную систему, код которой можно использовать для настоящей социальной сети, хотелось просто создать некий «прототип» и получить немного опыта.
С самого Нового Года я начал писать сервер, попутно придумывая свой протокол (все было на голых сокетах). Одновременно я перебрасывался на клиент, изучая Android API. И, спустя несколько дней, когда много россиян было пьяно алкоголем, я был пьян своим кодом. Я понял, что мне нужен напарник — человек с опытом для координации моих действий. И тут на помощь ко мне пришел специалист из соседнего сибирского города – ForNeVeR (наверняка его знают юзеры jabber-конференций), который значительно помог в развитии проекта, в основном он занимался сервером, а именно связью с СУБД (MySQL).
Теперь, думаю, пора рассказать о технической составляющей. Как уже было сказано: все зиждется на голых сокетах, т.е не используются какие-либо сетевые библиотеки, в основном это было сделано для простоты.
Начнем, пожалуй, с сервера. Здесь главный класс SimpleServer максимально прост: бесконечный цикл, который акцептит новые сокеты от клиентов и запускает их на обработку в новый поток, тем самым обеспечивая асинхронность.
За новый поток отвечает класс SocketHandler, в котором и происходят главные события. Именно в нем хранятся объекты для связи с базой данных: userDAO, statusDAO, coordinateDAO (далее будет разъяснено, но, думаю, умный читатель уже понял о чем речь). Сразу хочу сказать, что вначале мы делали долгоживущий сокет, т.е когда он открыт все время подключения клиента к серверу. Однако, мы столкнулись с небольшими проблемами. Хотелось, чтобы клиент получал новую информацию пассивно, т.е чтобы они приходили сами, а не клиент постоянно опрашивал сервер. Вдобавок к этому держать постоянно открытыми сокеты оказалось не слишком хорошо. После небольшого обдумывания мы решили воспользоваться Google Cloud Messaging для push-обновлений а сокет сделать короткоживущим для одного запроса с аутентификацией. То есть в начале каждого запроса передается авторизация (логин+пароль) и команда. Команды бывают следующего типа: залогиниться, зарегистрироваться, (после успешного выполнения юзер помечается в базе как онлайн), обновить статус, обновить позицию (да, эти команды пришлось разъединить – ведь, локация обновляется довольно часто, а статус по желанию юзера и для получения истории, нужно получить данные именно из таблицы со статусами), получить все онлайн-статусы юзеров, выйти из сети ну и несколько других… После выполнения команд «обновить статус или позицию» происходит отправка сообщения в GCM с указанием адресатов – онлайн юзеров сей программы, которые берутся из базы, а гугл уже рассылает push-сообщения на девайсы. После выполнения команды сокет закрывается.
Описывать классы userDAO, statusDAO, coordinateDAO, которые отвечают за, соответственно, три таблицы базы данных: юзеров, статусов и координат думаю смысла нет. Единственно стоит отметить, что все они связаны. Статусы ссылаются на координаты, а юзеры – на статусы по user id.

На клиенте все тоже предельно просто. Вначале логин или регистрация с указанием e-mail и контактых данных (должен же человек связаться со своим другом). В качестве основного Activity используется MapFragment с гуглокартами. Точнее SupportMapFragment – мы заботились и о девайсах со старым android. Да, и если у аппарата нет GPS, то приложение закрывается. Если же есть, то загружаться все онлайн юзеры, т.е маркеры с их местоположением и статусом и через каждые 30 сек будет происходить отсылка новых координат на сервер, если позиция изменилась хотя бы на 5 метров.
В любой момент юзер может обновить свой текстовый статус, отправив его на сервер. Также можно открыть «страничку» юзера с его прошлыми статусами и контактной информацией. Была мысль сделать показ мини-карты с поинтами, где менялись статусы :). Реализуется это просто, ведь, как уже было сказано, статусы ссылаются на координаты.
Естественно все сделано асинхронно: на каждую задачу будь то логин, или отправка данных на сервер создается Android Task’a
Несколько скриншотов:



Исходный код проекта лежит в открытом доступе на github. Код далеко не идеален, да и текущее состояние проекта можно лишь с натяжкой назвать альфа-версией. Энтузиазма осталось мало, поэтому я и пишу данный пост сейчас в таком сыром виде проекта. Буду рад, если найдутся люди, заинтересовавшиеся этим поделием и готовые привнести свой вклад. Сейчас есть круглосуточный сервер для тестирования. Также можно запустить у себя локально.
Прошу сильно не ругать за код. Автором сей программы и сего поста является 17-летний школьник.
Спасибо за внимание.
Тут я начали приходить на ум различные социальные сети, где люди могут находить друг друга по городу. Однако, мне это казалось тоже слишком обширным, хотелось чего-то более близкого: например, программу поиска людей из «соседнего дома».
И тут на ум пришла гениальная идея: почему бы не создать свою иновационную гео-соцсеть, где люди могли бы находить друг друга на карте по GPS? В качестве опознавательных знаков было принято ввести статусы: человек постит свой умный статус, который отражает его текущее состояние души, а его земляки видят это и жмакают по его статусу, а тут открывается целая история статусов — криков его души…
Я понимал, что наверняка что-то подобное существует, но, судорожно открыв поисковик и оглядев несколько результатов, я пришел к выводу, что таких систем для поиска новых людей не существует. Возможно, я сделал этот вывод поспешно, ведь не терпелось почувствовать себя великим человеком и попробовать свои силы в программировании под Android!
Да, я недавно приобрел аппарат на Android и хотелось опробовать программирование под него. В качестве языка программирования для сервера и клиента была выбрана Java. Сразу хочу сказать, что не было задачи написать какую-то высокопроизводительную систему, код которой можно использовать для настоящей социальной сети, хотелось просто создать некий «прототип» и получить немного опыта.
С самого Нового Года я начал писать сервер, попутно придумывая свой протокол (все было на голых сокетах). Одновременно я перебрасывался на клиент, изучая Android API. И, спустя несколько дней, когда много россиян было пьяно алкоголем, я был пьян своим кодом. Я понял, что мне нужен напарник — человек с опытом для координации моих действий. И тут на помощь ко мне пришел специалист из соседнего сибирского города – ForNeVeR (наверняка его знают юзеры jabber-конференций), который значительно помог в развитии проекта, в основном он занимался сервером, а именно связью с СУБД (MySQL).
Теперь, думаю, пора рассказать о технической составляющей. Как уже было сказано: все зиждется на голых сокетах, т.е не используются какие-либо сетевые библиотеки, в основном это было сделано для простоты.
Начнем, пожалуй, с сервера. Здесь главный класс SimpleServer максимально прост: бесконечный цикл, который акцептит новые сокеты от клиентов и запускает их на обработку в новый поток, тем самым обеспечивая асинхронность.
ServerSocket server = new ServerSocket(port);
ExecutorService pool = Executors.newCachedThreadPool();
while (true) {
Socket sock = server.accept();
if(connect.isClosed()) {
connect = DriverManager.getConnection(connectionString);
userDAO = new UserDAO(connect);
statusDAO = new StatusDAO(connect);
coordinateDAO = new CoordinateDAO(connect);
}
pool.submit(new SocketHandler(sock, userDAO, statusDAO, coordinateDAO));
}
За новый поток отвечает класс SocketHandler, в котором и происходят главные события. Именно в нем хранятся объекты для связи с базой данных: userDAO, statusDAO, coordinateDAO (далее будет разъяснено, но, думаю, умный читатель уже понял о чем речь). Сразу хочу сказать, что вначале мы делали долгоживущий сокет, т.е когда он открыт все время подключения клиента к серверу. Однако, мы столкнулись с небольшими проблемами. Хотелось, чтобы клиент получал новую информацию пассивно, т.е чтобы они приходили сами, а не клиент постоянно опрашивал сервер. Вдобавок к этому держать постоянно открытыми сокеты оказалось не слишком хорошо. После небольшого обдумывания мы решили воспользоваться Google Cloud Messaging для push-обновлений а сокет сделать короткоживущим для одного запроса с аутентификацией. То есть в начале каждого запроса передается авторизация (логин+пароль) и команда. Команды бывают следующего типа: залогиниться, зарегистрироваться, (после успешного выполнения юзер помечается в базе как онлайн), обновить статус, обновить позицию (да, эти команды пришлось разъединить – ведь, локация обновляется довольно часто, а статус по желанию юзера и для получения истории, нужно получить данные именно из таблицы со статусами), получить все онлайн-статусы юзеров, выйти из сети ну и несколько других… После выполнения команд «обновить статус или позицию» происходит отправка сообщения в GCM с указанием адресатов – онлайн юзеров сей программы, которые берутся из базы, а гугл уже рассылает push-сообщения на девайсы. После выполнения команды сокет закрывается.
String user = din.readUTF();
String pass = din.readUTF();
String command = din.readUTF();
loggedUser = userDAO.load(user, pass);
if (command.equals("login")) {
if (loggedUser == null) {
writeError(dout, "invalid login or password");
return;
}
writeLoggedIn(dout);
} else if (command.equals("register")) {
String email = din.readUTF();
String info = din.readUTF();
loggedUser = userDAO.create(user, pass, email, info);
if (loggedUser == null) {
writeError(dout, "cannot register user");
return;
}
writeLoggedIn(dout);
} else if (loggedUser == null) {
writeError(dout, "invalid login or password");
return;
} else if (command.equals("updateStatus")) {
double lat = din.readDouble();
double lng = din.readDouble();
String status = din.readUTF();
if (statusDAO.create(loggedUser, lat, lng, status)) {
writeMessages(dout, "success");
Message msg = new Message.Builder() // создание сообщения для отправки на GCM
.addData("command", "updateStatus")
.addData("user", loggedUser.getLogin())
.addData("lat", String.valueOf(lat))
.addData("lng", String.valueOf(lng))
.addData("status", status)
.build();
List<String> list = userDAO.getOnlineIDs();
if (!list.isEmpty() && sender != null)
sender.send(msg, list, 2);
} else {
writeError(dout, "error updating status");
}
} else if (command.equals("updatePosition")) {
double lat = din.readDouble();
double lng = din.readDouble();
if (coordinateDAO.update(loggedUser, lat, lng)) {
writeMessages(dout, "success");
Message msg = new Message.Builder()
.addData("command", "updatePosition")
.addData("user", loggedUser.getLogin())
.addData("lat", String.valueOf(lat))
.addData("lng", String.valueOf(lng))
.build();
List<String> list = userDAO.getOnlineIDs();
if (!list.isEmpty())
sender.send(msg, list, 2);
} else {
writeError(dout, "Error updating coordinates");
}
}
Описывать классы userDAO, statusDAO, coordinateDAO, которые отвечают за, соответственно, три таблицы базы данных: юзеров, статусов и координат думаю смысла нет. Единственно стоит отметить, что все они связаны. Статусы ссылаются на координаты, а юзеры – на статусы по user id.

На клиенте все тоже предельно просто. Вначале логин или регистрация с указанием e-mail и контактых данных (должен же человек связаться со своим другом). В качестве основного Activity используется MapFragment с гуглокартами. Точнее SupportMapFragment – мы заботились и о девайсах со старым android. Да, и если у аппарата нет GPS, то приложение закрывается. Если же есть, то загружаться все онлайн юзеры, т.е маркеры с их местоположением и статусом и через каждые 30 сек будет происходить отсылка новых координат на сервер, если позиция изменилась хотя бы на 5 метров.
SupportMapFragment mapFragment = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);
mMap = mapFragment.getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
listener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
new SendTask().execute("updatePosition", location.getLatitude(), location.getLongitude()); // новая таска
}
};
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 30000, 5f, listener);
В любой момент юзер может обновить свой текстовый статус, отправив его на сервер. Также можно открыть «страничку» юзера с его прошлыми статусами и контактной информацией. Была мысль сделать показ мини-карты с поинтами, где менялись статусы :). Реализуется это просто, ведь, как уже было сказано, статусы ссылаются на координаты.
Естественно все сделано асинхронно: на каждую задачу будь то логин, или отправка данных на сервер создается Android Task’a
Несколько скриншотов:



Исходный код проекта лежит в открытом доступе на github. Код далеко не идеален, да и текущее состояние проекта можно лишь с натяжкой назвать альфа-версией. Энтузиазма осталось мало, поэтому я и пишу данный пост сейчас в таком сыром виде проекта. Буду рад, если найдутся люди, заинтересовавшиеся этим поделием и готовые привнести свой вклад. Сейчас есть круглосуточный сервер для тестирования. Также можно запустить у себя локально.
Прошу сильно не ругать за код. Автором сей программы и сего поста является 17-летний школьник.
Спасибо за внимание.