Simple client server on Android (Internet messenger)
- From the sandbox
- Tutorial
Important. Everything written below does not represent any value for professionals, but it can serve as a useful example for beginner Android developers! In the code I tried to comment and log all actions.
Go. Many mobile applications (and not only) use the client-server architecture. The general scheme, I think, is understandable.

We pay attention to each element and note:
It doesn't matter how any of these elements are implemented, they are all present anyway. Let's implement a primitive server and an Android client working with it. As an example, we will use any popular mobile Internet messenger (Viber, ICQ), and the application will be called “Internet chat”.
The interaction scheme is as follows:

A client installed on device A sends a message to a client installed on device B. And vice versa. The server plays the role of a link between device A and B ... C, D ... etc. It also plays the role of a “drive" of messages, for their recovery, in case of deletion on one of the client devices.
To store messages we use SQL databases both on the server and on client devices (in principle, all the work of Internet messenger clients comes down to constant synchronization of the local and remote databases with messages). Additionally, our online chat will be able to start with the launch of the device and work in the background. Interaction will occur through HTTP requests and JSON responses.
To implement a “server”, we need to register on any hosting that allows us to work with SQL and PHP.
Create an empty SQL database, create a table in it.
The structure is as follows:
In the following two files, it is necessary to change the variables containing the data for accessing the database to your own ones that you received when registering your “server”.
The structure of requests for api:
Examples:
Now the structure of the Android application:

In the background, FoneService.java is running, which, in a separate thread, makes a request to the server every 15 seconds. If the server response contains new messages, FoneService.java writes them to the local database and sends a ChatActivity.java message about the need to update the ListView, with messages. ChatActivity.java (if it is open at that moment) receives a message and updates the contents of the ListView from the local database.
A new message from ChatActivity.java is sent immediately to the server, bypassing FoneService.java. At the same time, our message is NOT written to the local database! There it will appear only after receiving it back as a server response. I used this implementation in connection with the important nuance of the work of any Internet chat - the mandatory grouping of messages by time. If you do not use time grouping, the message sequence will be disrupted. Considering that client applications simply physically cannot be synchronized with an accuracy of milliseconds, and may even work in different time zones, it will be most logical to use server time. So we do.
Creating a new message, we send a request to the server: the name of the author of the message, the name of the recipient of the message, the message text. Getting this record back, in the form of a server response, we get what we sent + the fourth parameter: the time the server received the message.
In MainActivity.java, for clarity, I added the ability to delete messages from the local database - this is equivalent to a clean installation of the application (in this case, FoneService will send a request to the server to receive all messages of the selected chat). It is also possible to send a request to delete all messages from the database located on the server.
Activity Code:
Markup:
And all together (+ apk):
github.com/andreidanilevich/temp_chat
P.S .:
1. If you downloaded apk from the link above, first check the availability of my “server” at l29340eb.bget.ru/showBD.php .
2. The code was written for a long time, of course, not everything is beautiful and according to the canons, maybe not all exceptions are processed and there are errors. This is a draft. Everything worked for me on real devices and on an emulator.
3. If someone comes in handy - I will be glad. For criticism - thanks, for constructive messages - thanks twice as much. I will try to answer the questions.
Go. Many mobile applications (and not only) use the client-server architecture. The general scheme, I think, is understandable.

We pay attention to each element and note:
- server - is a certain program running on a remote computer and that implements the “communication” functionality with client applications (listens to requests, recognizes the transferred parameters and values, correctly answers them);
- client - in our case, a program on a mobile device that can generate a request that is understandable to the server and read the received response;
- interaction interface - a certain format and method of transmitting / receiving requests / responses by both parties.
It doesn't matter how any of these elements are implemented, they are all present anyway. Let's implement a primitive server and an Android client working with it. As an example, we will use any popular mobile Internet messenger (Viber, ICQ), and the application will be called “Internet chat”.
The interaction scheme is as follows:

A client installed on device A sends a message to a client installed on device B. And vice versa. The server plays the role of a link between device A and B ... C, D ... etc. It also plays the role of a “drive" of messages, for their recovery, in case of deletion on one of the client devices.
To store messages we use SQL databases both on the server and on client devices (in principle, all the work of Internet messenger clients comes down to constant synchronization of the local and remote databases with messages). Additionally, our online chat will be able to start with the launch of the device and work in the background. Interaction will occur through HTTP requests and JSON responses.
It is more logical if synchronization occurs through a port / socket, this simplifies the task on the one hand (you do not need to send HTTP requests to check for new messages cyclically, just check the status of the listening socket), but on the other hand, it complicates the creation of the server side of the application.
Make server
To implement a “server”, we need to register on any hosting that allows us to work with SQL and PHP.
Create an empty SQL database, create a table in it.
Chat table.
CREATETABLE`chat` (
`_id`int(11) NOTNULL AUTO_INCREMENT,
`author`textCHARACTERSET utf8 COLLATE utf8_unicode_ci NOTNULL,
`client`textCHARACTERSET utf8 COLLATE utf8_unicode_ci NOTNULL,
`data`bigint(20) NOTNULL,
`text`textCHARACTERSET utf8 COLLATE utf8_unicode_ci NOTNULL,
PRIMARY KEY (`_id`)
)
The structure is as follows:
- author - the author of the message;
- client - message recipient;
- data - time and date of receipt of the message on the server;
- text is a message.
In the following two files, it is necessary to change the variables containing the data for accessing the database to your own ones that you received when registering your “server”.
$mysql_host = "localhost"; // sql сервер, может быть локальным или внешним. например mysql5.000webhost.com
$mysql_user = "l29340eb_chat"; // пользователь
$mysql_password = "123456789"; // пароль
$mysql_database = "l29340eb_chat"; // имя базы данных на сервере SQL
The chat.php file is our api that implements the structure of server-friendly requests.
<?php// сохранить в utf-8 !// ---------------------------------------------------------- эти значения задавались при создании БД на сервере
$mysql_host = "localhost"; // sql сервер
$mysql_user = "l29340eb_chat"; // пользователь
$mysql_password = "123456789"; // пароль
$mysql_database = "l29340eb_chat"; // имя базы данных chat// ---------------------------------------------------------- проверяем переданные в строке запроса параметры// например ...chat.php?action=select//-----------------------------------------------------------// переменная action может быть:// select - формируем содержимое таблицы chat в JSON и отправляем назад// insert - встваляем новую строку в таблицу chat, так же нужны 4 параметра : автор/получатель/время создания/сообщение// ВАЖНО время создания мы не передаем в параметрах, его берем текущее на сервере// delete - удаляет ВСЕ записи из таблицы chat - пусть будет для быстрой очистки// ------------------------------------------- получим переданный actionif (isset($_GET["action"])) {
$action = $_GET['action'];
}
// ------------------------------------------- если action=insert тогда получим еще author|client|textif (isset($_GET["author"])) {
$author = $_GET['author'];
}
if (isset($_GET["client"])) {
$client = $_GET['client'];
}
if (isset($_GET["text"])) {
$text = $_GET['text'];
}
// ------------------------------------------- если action=select тогда получим еще data - от после какого времени передавать ответif (isset($_GET["data"])) {
$data = $_GET['data'];
}
mysql_connect($mysql_host, $mysql_user, $mysql_password); // коннект к серверу SQL
mysql_select_db($mysql_database); // коннект к БД на сервере
mysql_set_charset('utf8'); // кодировка// ------------------------------------------------------------ обрабатываем запрос если он былif($action == select){ // если действие SELECTif($data == null){
// выберем из таблицы chat ВСЕ данные что есть и вернем их в JSON
$q=mysql_query("SELECT * FROM chat");
}else{
// выберем из таблицы chat ВСЕ данные ПОЗНЕЕ ОПРЕДЕЛЕННОГО ВРЕМЕНИ и вернем их в JSON
$q=mysql_query("SELECT * FROM chat WHERE data > $data");
}
while($e=mysql_fetch_assoc($q))
$output[]=$e;
print(json_encode($output));
}
if($action == insert && $author != null && $client != null && $text != null){ // если действие INSERT и есть все что нужно// время = время сервера а не клиента !
$current_time = round(microtime(1) * 1000);
// пример передачи скрипту данных:// chat.php?action=insert&author=author&client=client&text=text// вставим строку с переданными параметрами
mysql_query("INSERT INTO `chat`(`author`,`client`,`data`,`text`) VALUES ('$author','$client','$current_time','$text')");
}
if($action == delete){ // если действие DELETE// полностью обнулим таблицу записей
mysql_query("TRUNCATE TABLE `chat`");
}
mysql_close();
?>
The structure of requests for api:
- required action attribute - can be equal to select (the server will respond with a list of records from its database), insert (the server will add a new record to its database), delete (the server will clear its database)
- if action = insert, we will need to pass additional parameters: author (who wrote the message), client (to whom the message is addressed), text (message)
- action = select may contain an additional parameter data; in this case, the server response does not contain all messages from the database, but only those with a creation time later than that transmitted
Examples:
- chat.php? action = delete - delete all entries on the server
- chat.php? action = insert & author = Jon & client = Smith & text = Hello - will add a new entry on the server: author Jon, recipient Smith, content Hello
- chat.php? action = select & data = 151351333 - will return all records received after the transmitted time in long format
The showBD.php file is an optional script for displaying the contents of the database in a browser.
<?php// сохранить utf-8 !// -------------------------------------------------------------------------- логины пароли
$mysql_host = "localhost"; // sql сервер
$mysql_user = "l29340eb_chat"; // пользователь
$mysql_password = "123456789"; // пароль
$mysql_database = "l29340eb_chat"; // имя базы данных chat// -------------------------------------------------------------------------- если база недоступнаif (!mysql_connect($mysql_host, $mysql_user, $mysql_password)){
echo"<h2>База недоступна!</h2>";
exit;
}else{
// -------------------------------------------------------------------------- если база доступнаecho"<h2>База доступна!</h2>";
mysql_select_db($mysql_database);
mysql_set_charset('utf8');
// -------------------------------------------------------------------------- выведем JSON
$q=mysql_query("SELECT * FROM chat");
echo"<h3>Json ответ:</h3>";
// Выводим jsonwhile($e=mysql_fetch_assoc($q))
$output[]=$e;
print(json_encode($output));
// -------------------------------------------------------------------------- выведем таблицу
$q=mysql_query("SELECT * FROM chat");
echo"<h3>Табличный вид:</h3>";
echo"<table border=\"1\" width=\"100%\" bgcolor=\"#999999\">";
echo"<tr><td>_id</td><td>author</td>";
echo"<td>client</td><td>data</td><td>text</td></tr>";
for ($c=0; $c<mysql_num_rows($q); $c++){
$f = mysql_fetch_array($q);
echo"<tr><td>$f[_id]</td><td>$f[author]</td><td>$f[client]</td><td>$f[data]</td><td>$f[text]</td></tr>";
}
echo"</tr></table>";
}
mysql_close();
// -------------------------------------------------------------------------- разорвем соединение с БД?>
Client part
Now the structure of the Android application:

In the background, FoneService.java is running, which, in a separate thread, makes a request to the server every 15 seconds. If the server response contains new messages, FoneService.java writes them to the local database and sends a ChatActivity.java message about the need to update the ListView, with messages. ChatActivity.java (if it is open at that moment) receives a message and updates the contents of the ListView from the local database.
A new message from ChatActivity.java is sent immediately to the server, bypassing FoneService.java. At the same time, our message is NOT written to the local database! There it will appear only after receiving it back as a server response. I used this implementation in connection with the important nuance of the work of any Internet chat - the mandatory grouping of messages by time. If you do not use time grouping, the message sequence will be disrupted. Considering that client applications simply physically cannot be synchronized with an accuracy of milliseconds, and may even work in different time zones, it will be most logical to use server time. So we do.
Creating a new message, we send a request to the server: the name of the author of the message, the name of the recipient of the message, the message text. Getting this record back, in the form of a server response, we get what we sent + the fourth parameter: the time the server received the message.
In MainActivity.java, for clarity, I added the ability to delete messages from the local database - this is equivalent to a clean installation of the application (in this case, FoneService will send a request to the server to receive all messages of the selected chat). It is also possible to send a request to delete all messages from the database located on the server.
Activity Code:
FoneService.java
package by.andreidanilevich.temp_chat;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.JSONArray;
import org.json.JSONObject;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.util.Log;
publicclassFoneServiceextendsService{
// ИМЯ СЕРВЕРА (url зарегистрированного нами сайта)// например http://l29340eb.bget.ru
String server_name = "http://l29340eb.bget.ru";
SQLiteDatabase chatDBlocal;
HttpURLConnection conn;
Cursor cursor;
Thread thr;
ContentValues new_mess;
Long last_time; // время последней записи в БД, отсекаем по нему что нам// тянуть с сервера, а что уже есть@Overridepublic IBinder onBind(Intent intent){
returnnull;
}
publicvoidonStart(Intent intent, int startId){
Log.i("chat", "+ FoneService - запуск сервиса");
chatDBlocal = openOrCreateDatabase("chatDBlocal.db",
Context.MODE_PRIVATE, null);
chatDBlocal
.execSQL("CREATE TABLE IF NOT EXISTS chat (_id integer primary key autoincrement, author, client, data, text)");
// создадим и покажем notification// это позволит стать сервису "бессмертным"// и будет визуально видно в трее
Intent iN = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pI = PendingIntent.getActivity(getApplicationContext(),
0, iN, PendingIntent.FLAG_CANCEL_CURRENT);
Notification.Builder bI = new Notification.Builder(
getApplicationContext());
bI.setContentIntent(pI)
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(
BitmapFactory.decodeResource(getApplicationContext()
.getResources(), R.drawable.ic_launcher))
.setAutoCancel(true)
.setContentTitle(getResources().getString(R.string.app_name))
.setContentText("работаю...");
Notification notification = bI.build();
startForeground(101, notification);
startLoop();
}
// запуск потока, внутри которого будет происходить// регулярное соединение с сервером для чтения новых// сообщений.// если сообщения найдены - отправим броадкаст для обновления// ListView в ChatActivityprivatevoidstartLoop(){
thr = new Thread(new Runnable() {
// ansver = ответ на запрос// lnk = линк с параметрами
String ansver, lnk;
publicvoidrun(){
while (true) { // стартуем бесконечный цикл// глянем локальную БД на наличие сообщщений чата
cursor = chatDBlocal.rawQuery(
"SELECT * FROM chat ORDER BY data", null);
// если какие-либо сообщения есть - формируем запрос// по которому получим только новые сообщенияif (cursor.moveToLast()) {
last_time = cursor.getLong(cursor
.getColumnIndex("data"));
lnk = server_name + "/chat.php?action=select&data="
+ last_time.toString();
// если сообщений в БД нет - формируем запрос// по которому получим всё
} else {
lnk = server_name + "/chat.php?action=select";
}
cursor.close();
// создаем соединение ---------------------------------->try {
Log.i("chat",
"+ FoneService --------------- ОТКРОЕМ СОЕДИНЕНИЕ");
conn = (HttpURLConnection) new URL(lnk)
.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "Mozilla/5.0");
conn.setDoInput(true);
conn.connect();
} catch (Exception e) {
Log.i("chat", "+ FoneService ошибка: " + e.getMessage());
}
// получаем ответ ---------------------------------->try {
InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(
new InputStreamReader(is, "UTF-8"));
StringBuilder sb = new StringBuilder();
String bfr_st = null;
while ((bfr_st = br.readLine()) != null) {
sb.append(bfr_st);
}
Log.i("chat", "+ FoneService - полный ответ сервера:\n"
+ sb.toString());
// сформируем ответ сервера в string// обрежем в полученном ответе все, что находится за "]"// это необходимо, т.к. json ответ приходит с мусором// и если этот мусор не убрать - будет невалидным
ansver = sb.toString();
ansver = ansver.substring(0, ansver.indexOf("]") + 1);
is.close(); // закроем поток
br.close(); // закроем буфер
} catch (Exception e) {
Log.i("chat", "+ FoneService ошибка: " + e.getMessage());
} finally {
conn.disconnect();
Log.i("chat",
"+ FoneService --------------- ЗАКРОЕМ СОЕДИНЕНИЕ");
}
// запишем ответ в БД ---------------------------------->if (ansver != null && !ansver.trim().equals("")) {
Log.i("chat",
"+ FoneService ---------- ответ содержит JSON:");
try {
// ответ превратим в JSON массив
JSONArray ja = new JSONArray(ansver);
JSONObject jo;
Integer i = 0;
while (i < ja.length()) {
// разберем JSON массив построчно
jo = ja.getJSONObject(i);
Log.i("chat",
"=================>>> "
+ jo.getString("author")
+ " | "
+ jo.getString("client")
+ " | " + jo.getLong("data")
+ " | " + jo.getString("text"));
// создадим новое сообщение
new_mess = new ContentValues();
new_mess.put("author", jo.getString("author"));
new_mess.put("client", jo.getString("client"));
new_mess.put("data", jo.getLong("data"));
new_mess.put("text", jo.getString("text"));
// запишем новое сообщение в БД
chatDBlocal.insert("chat", null, new_mess);
new_mess.clear();
i++;
// отправим броадкаст для ChatActivity// если она открыта - она обновить ListView
sendBroadcast(new Intent(
"by.andreidanilevich.action.UPDATE_ListView"));
}
} catch (Exception e) {
// если ответ сервера не содержит валидный JSON
Log.i("chat",
"+ FoneService ---------- ошибка ответа сервера:\n"
+ e.getMessage());
}
} else {
// если ответ сервера пустой
Log.i("chat",
"+ FoneService ---------- ответ не содержит JSON!");
}
try {
Thread.sleep(15000);
} catch (Exception e) {
Log.i("chat",
"+ FoneService - ошибка процесса: "
+ e.getMessage());
}
}
}
});
thr.setDaemon(true);
thr.start();
}
}
ChatActivity.java
package by.andreidanilevich.temp_chat;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
publicclassChatActivityextendsActivity{
// ИМЯ СЕРВЕРА (url зарегистрированного нами сайта)// например http://l29340eb.bget.ru
String server_name = "http://l29340eb.bget.ru";
ListView lv; // полоса сообщений
EditText et;
Button bt;
SQLiteDatabase chatDBlocal;
String author, client;
INSERTtoChat insert_to_chat; // класс отправляет новое сообщение на сервер
UpdateReceiver upd_res; // класс ждет сообщение от сервиса и получив его -// обновляет ListView@OverrideprotectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
// получим 2 переменные по которым будем отбирать инфу из БД:// author - от чьего имени идет чат// client - с кем чатимся
Intent intent = getIntent();
author = intent.getStringExtra("author");
client = intent.getStringExtra("client");
Log.i("chat", "+ ChatActivity - открыт author = " + author
+ " | client = " + client);
lv = (ListView) findViewById(R.id.lv);
et = (EditText) findViewById(R.id.et);
bt = (Button) findViewById(R.id.bt);
chatDBlocal = openOrCreateDatabase("chatDBlocal.db",
Context.MODE_PRIVATE, null);
chatDBlocal
.execSQL("CREATE TABLE IF NOT EXISTS chat (_id integer primary key autoincrement, author, client, data, text)");
// Создаём и регистрируем широковещательный приёмник
upd_res = new UpdateReceiver();
registerReceiver(upd_res, new IntentFilter(
"by.andreidanilevich.action.UPDATE_ListView"));
create_lv();
}
// обновим lv = заполним нужные позиции в lv информацией из БД@SuppressLint("SimpleDateFormat")
publicvoidcreate_lv(){
Cursor cursor = chatDBlocal.rawQuery(
"SELECT * FROM chat WHERE author = '" + author
+ "' OR author = '" + client + "' ORDER BY data", null);
if (cursor.moveToFirst()) {
// если в базе есть элементы соответствующие// нашим критериям отбора// создадим массив, создадим hashmap и заполним его результатом// cursor
ArrayList<HashMap<String, Object>> mList = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> hm;
do {
// мое сообщение !!!// если автор сообщения = автор// и получатель сообщения = клиентif (cursor.getString(cursor.getColumnIndex("author")).equals(
author)
&& cursor.getString(cursor.getColumnIndex("client"))
.equals(client)) {
hm = new HashMap<>();
hm.put("author", author);
hm.put("client", "");
hm.put("list_client", "");
hm.put("list_client_time", "");
hm.put("list_author",
cursor.getString(cursor.getColumnIndex("text")));
hm.put("list_author_time", new SimpleDateFormat(
"HH:mm - dd.MM.yyyy").format(new Date(cursor
.getLong(cursor.getColumnIndex("data")))));
mList.add(hm);
}
// сообщение мне !!!!!!!// если автор сообщения = клиент// и если получатель сообщения = авторif (cursor.getString(cursor.getColumnIndex("author")).equals(
client)
&& cursor.getString(cursor.getColumnIndex("client"))
.equals(author)) {
hm = new HashMap<>();
hm.put("author", "");
hm.put("client", client);
hm.put("list_author", "");
hm.put("list_author_time", "");
hm.put("list_client",
cursor.getString(cursor.getColumnIndex("text")));
hm.put("list_client_time", new SimpleDateFormat(
"HH:mm - dd.MM.yyyy").format(new Date(cursor
.getLong(cursor.getColumnIndex("data")))));
mList.add(hm);
}
} while (cursor.moveToNext());
// покажем lv
SimpleAdapter adapter = new SimpleAdapter(getApplicationContext(),
mList, R.layout.list, new String[] { "list_author",
"list_author_time", "list_client",
"list_client_time", "author", "client" },
newint[] { R.id.list_author, R.id.list_author_time,
R.id.list_client, R.id.list_client_time,
R.id.author, R.id.client });
lv.setAdapter(adapter);
cursor.close();
}
Log.i("chat",
"+ ChatActivity ======================== обновили поле чата");
}
publicvoidsend(View v){
// запишем наше новое сообщение// вначале проверим на пустотуif (!et.getText().toString().trim().equals("")) {
// кнопку сделаем неактивной
bt.setEnabled(false);
// если чтото есть - действуем!
insert_to_chat = new INSERTtoChat();
insert_to_chat.execute();
} else {
// если ничего нет - нечего и писать
et.setText("");
}
}
// отправим сообщение на серверprivateclassINSERTtoChatextendsAsyncTask<Void, Void, Integer> {
HttpURLConnection conn;
Integer res;
protected Integer doInBackground(Void... params){
try {
// соберем линк для передачи новой строки
String post_url = server_name
+ "/chat.php?action=insert&author="
+ URLEncoder.encode(author, "UTF-8")
+ "&client="
+ URLEncoder.encode(client, "UTF-8")
+ "&text="
+ URLEncoder.encode(et.getText().toString().trim(),
"UTF-8");
Log.i("chat",
"+ ChatActivity - отправляем на сервер новое сообщение: "
+ et.getText().toString().trim());
URL url = new URL(post_url);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(10000); // ждем 10сек
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "Mozilla/5.0");
conn.connect();
res = conn.getResponseCode();
Log.i("chat", "+ ChatActivity - ответ сервера (200 - все ОК): "
+ res.toString());
} catch (Exception e) {
Log.i("chat",
"+ ChatActivity - ошибка соединения: " + e.getMessage());
} finally {
// закроем соединение
conn.disconnect();
}
return res;
}
protectedvoidonPostExecute(Integer result){
try {
if (result == 200) {
Log.i("chat", "+ ChatActivity - сообщение успешно ушло.");
// сбросим набранный текст
et.setText("");
}
} catch (Exception e) {
Log.i("chat", "+ ChatActivity - ошибка передачи сообщения:\n"
+ e.getMessage());
Toast.makeText(getApplicationContext(),
"ошибка передачи сообщения", Toast.LENGTH_SHORT).show();
} finally {
// активируем кнопку
bt.setEnabled(true);
}
}
}
// ресивер приёмник ждет сообщения от FoneService// если сообщение пришло, значит есть новая запись в БД - обновим ListViewpublicclassUpdateReceiverextendsBroadcastReceiver{
@OverridepublicvoidonReceive(Context context, Intent intent){
Log.i("chat",
"+ ChatActivity - ресивер получил сообщение - обновим ListView");
create_lv();
}
}
// выходим из чатаpublicvoidonBackPressed(){
Log.i("chat", "+ ChatActivity - закрыт");
unregisterReceiver(upd_res);
finish();
}
}
MainActivity.java
package by.andreidanilevich.temp_chat;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.Toast;
publicclassMainActivityextendsActivity{
// ИМЯ СЕРВЕРА (url зарегистрированного нами сайта)// например http://l29340eb.bget.ru
String server_name = "http://l29340eb.bget.ru";
Spinner spinner_author, spinner_client;
String author, client;
Button open_chat_btn, open_chat_reverce_btn, delete_server_chat;
@OverrideprotectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.i("chat", "+ MainActivity - запуск приложения");
open_chat_btn = (Button) findViewById(R.id.open_chat_btn);
open_chat_reverce_btn = (Button) findViewById(R.id.open_chat_reverce_btn);
delete_server_chat = (Button) findViewById(R.id.delete_server_chat);
// запустим FoneServicethis.startService(new Intent(this, FoneService.class));
// заполним 2 выпадающих меню для выбора автора и получателя сообщения// 5 мужских и 5 женских имен// установим слушателей
spinner_author = (Spinner) findViewById(R.id.spinner_author);
spinner_client = (Spinner) findViewById(R.id.spinner_client);
spinner_author.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, new String[] { "Петя",
"Вася", "Коля", "Андрей", "Сергей", "Оля", "Лена",
"Света", "Марина", "Наташа" }));
spinner_client.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, new String[] { "Петя",
"Вася", "Коля", "Андрей", "Сергей", "Оля", "Лена",
"Света", "Марина", "Наташа" }));
spinner_client.setSelection(5);
open_chat_btn.setText("Открыть чат: "
+ spinner_author.getSelectedItem().toString() + " > "
+ spinner_client.getSelectedItem().toString());
open_chat_reverce_btn.setText("Открыть чат: "
+ spinner_client.getSelectedItem().toString() + " > "
+ spinner_author.getSelectedItem().toString());
spinner_author
.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
publicvoidonItemSelected(AdapterView<?> parent,
View itemSelected, int selectedItemPosition,
long selectedId){
author = spinner_author.getSelectedItem().toString();
open_chat_btn.setText("Открыть чат: "
+ spinner_author.getSelectedItem().toString()
+ " > "
+ spinner_client.getSelectedItem().toString());
open_chat_reverce_btn.setText("Открыть чат: "
+ spinner_client.getSelectedItem().toString()
+ " > "
+ spinner_author.getSelectedItem().toString());
}
publicvoidonNothingSelected(AdapterView<?> parent){
}
});
spinner_client
.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
publicvoidonItemSelected(AdapterView<?> parent,
View itemSelected, int selectedItemPosition,
long selectedId){
client = spinner_client.getSelectedItem().toString();
open_chat_btn.setText("Открыть чат: "
+ spinner_author.getSelectedItem().toString()
+ " > "
+ spinner_client.getSelectedItem().toString());
open_chat_reverce_btn.setText("Открыть чат: "
+ spinner_client.getSelectedItem().toString()
+ " > "
+ spinner_author.getSelectedItem().toString());
}
publicvoidonNothingSelected(AdapterView<?> parent){
}
});
}
// откроем чат с выбранным автором и получателемpublicvoidopen_chat(View v){
// быстрая проверкаif (author.equals(client)) {
// если автор и получатель одинаковы// чат не открываем
Toast.makeText(this, "author = client !", Toast.LENGTH_SHORT)
.show();
} else {
// откроем нужный чат author > client
Intent intent = new Intent(MainActivity.this, ChatActivity.class);
intent.putExtra("author", author);
intent.putExtra("client", client);
startActivity(intent);
}
}
// откроем чат с выбранным автором и получателем, только наоборотpublicvoidopen_chat_reverce(View v){
// быстрая проверкаif (author.equals(client)) {
// если автор и получатель одинаковы// чат не открываем
Toast.makeText(this, "author = client !", Toast.LENGTH_SHORT)
.show();
} else {
// откроем нужный чат client > author
Intent intent = new Intent(MainActivity.this, ChatActivity.class);
intent.putExtra("author", client);
intent.putExtra("client", author);
startActivity(intent);
}
}
// отправим запрос на сервер о удалении таблицы с чатамиpublicvoiddelete_server_chats(View v){
Log.i("chat", "+ MainActivity - запрос на удаление чата с сервера");
delete_server_chat.setEnabled(false);
delete_server_chat.setText("Запрос отправлен. Ожидайте...");
DELETEfromChat delete_from_chat = new DELETEfromChat();
delete_from_chat.execute();
}
// удалим локальную таблицу чатов// и создадим такуюже новуюpublicvoiddelete_local_chats(View v){
Log.i("chat", "+ MainActivity - удаление чата с этого устройства");
SQLiteDatabase chatDBlocal;
chatDBlocal = openOrCreateDatabase("chatDBlocal.db",
Context.MODE_PRIVATE, null);
chatDBlocal.execSQL("drop table chat");
chatDBlocal
.execSQL("CREATE TABLE IF NOT EXISTS chat (_id integer primary key autoincrement, author, client, data, text)");
Toast.makeText(getApplicationContext(),
"Чат на этом устройстве удален!", Toast.LENGTH_SHORT).show();
}
// отправим запрос на сервер о удалении таблицы с чатами// если он пройдет - таблица будет удалена// если не пройдет (например нет интернета или сервер недоступен)// - покажет сообщениеprivateclassDELETEfromChatextendsAsyncTask<Void, Void, Integer> {
Integer res;
HttpURLConnection conn;
protected Integer doInBackground(Void... params){
try {
URL url = new URL(server_name + "/chat.php?action=delete");
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(10000); // ждем 10сек
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "Mozilla/5.0");
conn.connect();
res = conn.getResponseCode();
Log.i("chat", "+ MainActivity - ответ сервера (200 = ОК): "
+ res.toString());
} catch (Exception e) {
Log.i("chat",
"+ MainActivity - ответ сервера ОШИБКА: "
+ e.getMessage());
} finally {
conn.disconnect();
}
return res;
}
protectedvoidonPostExecute(Integer result){
try {
if (result == 200) {
Toast.makeText(getApplicationContext(),
"Чат на сервере удален!", Toast.LENGTH_SHORT)
.show();
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(),
"Ошибка выполнения запроса.", Toast.LENGTH_SHORT)
.show();
} finally {
// сделаем кнопку активной
delete_server_chat.setEnabled(true);
delete_server_chat.setText("Удалить все чаты на сервере!");
}
}
}
publicvoidonBackPressed(){
Log.i("chat", "+ MainActivity - выход из приложения");
finish();
}
}
AutoRun.java
package by.andreidanilevich.temp_chat;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
publicclassAutoRunextendsBroadcastReceiver{
@OverridepublicvoidonReceive(Context context, Intent intent){
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
// получили boot_completed - запустили FoneService
context.startService(new Intent(context, FoneService.class));
Log.i("chat", "+ AutoRun - отработал");
}
}
}
<b>Манифест:</b>
AndroidManifest
<?xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android"package="by.andreidanilevich.temp_chat"android:versionCode="1"android:versionName="1.0" ><uses-permissionandroid:name="android.permission.INTERNET" /><uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED" /><uses-sdkandroid:minSdkVersion="16"android:targetSdkVersion="22" /><applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name=".MainActivity"android:label="@string/app_name"android:screenOrientation="portrait" ><intent-filter><actionandroid:name="android.intent.action.MAIN" /><categoryandroid:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activityandroid:name=".ChatActivity"android:label="@string/app_name"android:screenOrientation="portrait" ></activity><receiverandroid:name=".AutoRun"android:enabled="true"android:exported="false" ><intent-filter><actionandroid:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver><serviceandroid:name=".FoneService" /></application></manifest>
Markup:
Chat.xml markup
<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#999999" ><ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginBottom="60dp"android:scrollbars="none"android:stackFromBottom="true"android:transcriptMode="alwaysScroll" ></ListView><LinearLayoutandroid:layout_width="match_parent"android:layout_height="60dp"android:layout_alignParentBottom="true"android:background="#ffffff" ><EditTextandroid:id="@+id/et"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="top"android:layout_weight="1"android:ems="10" ></EditText><Buttonandroid:id="@+id/bt"android:layout_width="50dp"android:layout_height="wrap_content"android:layout_gravity="top"android:onClick="send"android:text=">" /></LinearLayout></RelativeLayout>
Markup main.xml
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="${relativePackage}.${activityClass}" ><Spinnerandroid:id="@+id/spinner_author"android:layout_width="match_parent"android:layout_height="wrap_content" /><Spinnerandroid:id="@+id/spinner_client"android:layout_width="match_parent"android:layout_height="wrap_content" /><Buttonandroid:id="@+id/open_chat_btn"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="open_chat" /><Buttonandroid:id="@+id/open_chat_reverce_btn"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="open_chat_reverce" /><Buttonandroid:id="@+id/delete_server_chat"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="50dp"android:onClick="delete_server_chats"android:text="Удалить все чаты на сервере!" /><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="delete_local_chats"android:text="Удалить все чаты на этом устройстве!" /></LinearLayout>
Markup list.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#999999"android:orientation="vertical" ><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp" ><TextViewandroid:id="@+id/author"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="left"android:textColor="#c6c6c6"android:textSize="12sp" /><TextViewandroid:id="@+id/client"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="left"android:textColor="#c6c6c6"android:textSize="12sp" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="5dp"android:layout_marginTop="5dp" ><TextViewandroid:id="@+id/list_author"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="left"android:textColor="#0000ff"android:textSize="14sp"android:textStyle="bold" /><TextViewandroid:id="@+id/list_client"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="left"android:textColor="#ffff00"android:textSize="14sp"android:textStyle="bold" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="5dp" ><TextViewandroid:id="@+id/list_author_time"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="right"android:textColor="#c6c6c6"android:textSize="12sp" /><TextViewandroid:id="@+id/list_client_time"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="right"android:textColor="#c6c6c6"android:textSize="12sp" /></LinearLayout></LinearLayout>
And all together (+ apk):
github.com/andreidanilevich/temp_chat
P.S .:
1. If you downloaded apk from the link above, first check the availability of my “server” at l29340eb.bget.ru/showBD.php .
2. The code was written for a long time, of course, not everything is beautiful and according to the canons, maybe not all exceptions are processed and there are errors. This is a draft. Everything worked for me on real devices and on an emulator.
3. If someone comes in handy - I will be glad. For criticism - thanks, for constructive messages - thanks twice as much. I will try to answer the questions.