<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0">

<channel>

<title>Just Developer Blog, заметки с тегом: разработка под Android</title>
<link>http://bibobo.ru/tags/razrabotka-pod-android/</link>
<description></description>
<generator>E2 (v3254; Aegea)</generator>

<item>
<title>Реализация уведомлений-индикаторов для загружаемых на сервер файлов (Android)</title>
<guid isPermaLink="true">http://bibobo.ru/all/realizaciya-uvedomleniy-indikatorov-dlya-zagruzhaemyh-na-server/</guid>
<link>http://bibobo.ru/all/realizaciya-uvedomleniy-indikatorov-dlya-zagruzhaemyh-na-server/</link>
<comments>http://bibobo.ru/all/realizaciya-uvedomleniy-indikatorov-dlya-zagruzhaemyh-na-server/</comments>
<description>&lt;p&gt;При необходимости загрузить файла на сервер из приложения встает вопрос об индикации процесса загрузки. Если дизайн позволяет то можно отобразить диалог прогресс бара (при этом заблокировать интерфейс пользователя). Но на мой взгляд более интересным вариантом индикации являются уведомления в статус баре системы, которые нельзя смахнуть.&lt;/p&gt;
&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;При реализации такого подхода может возникнуть несколько проблем:&lt;/p&gt;
&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Как создать уведомление для каждого загружаемого файла (если например существует возможность единовременно загружать несколько файлов)&lt;/li&gt;
&lt;li&gt;Как программно отменить уведомление после завершения процесса загрузки на сервер&lt;br /&gt;
&lt;input id="button_android_download_file_notification" type="button" onclick="toggle(cut_android_download_file_notification, button_android_download_file_notification)" value="Читать дальше" hiddenText="Свернуть" visibleText="Читать дальше"&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div id="cut_android_download_file_notification" style="display: none;" displayOld=""&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Для решения этих проблем необходимо знать как система индексирует уведомления, создаваемые приложением. Каждое приложение может создать сколь угодно много уведомлений, но чтобы иметь возможность обновить определенное уведомление, либо отменить его — необходимо присвоить каждому из уведомлений свой уникальный ID. Ниже код реализации создания уведомления при старте загрузки файла на сервер и его отмены, по окончании загрузки:&lt;/p&gt;
&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;//Кеш, хранящий список id активных на данный момент уведомлений
private List&amp;lt;int&amp;gt; _notificationCache = new List&amp;lt;int&amp;gt;();

private void UploadFile()
{
    //получим рандомный ID для уведомления, при этом он не должен пересекаться с уже существующими (для этого используем кеш)
    Random r = new Random(System.Environment.TickCount);
    int notificationId = 0;
    while (notificationId == 0 || _notificationCache.Contains(notificationId))
    {
        notificationId = r.Next(1, 1000);
    }
    _notificationCache.Add(notificationId);
    //
                
    Notification.Builder builder = new Notification.Builder(Activity);
    builder.SetSmallIcon(Resource.Drawable.file_32x32)
        //устанавливаем заголовок для уведомления
        .SetContentTitle(filename)
        //в дескрипшне будет храниться наша строка &amp;quot;Загружаю файл на сервер...&amp;quot;
        .SetContentText(Activity.GetString(Resource.String.attachement_uploading))
        .SetAutoCancel(false)
        //запрещаем отмену уведомления пользователем
        .SetOngoing(true)
        //разрешаем уведомлению появиться в статус баре, мигать светодиодом и т.п.
        .SetDefaults(NotificationDefaults.All);

    NotificationManager manager = (NotificationManager)Activity.GetSystemService(Activity.NotificationService);
    manager.Notify(notificationId, builder.Build());

    //////////////////////////////////////////
    // ... код загрузки файла на сервер ... //
    //////////////////////////////////////////

    //отменяем уведомление и удаляем его ID из кеша
    manager.Cancel(notificationId);
    _notificationCache.Remove(notificationId);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
<pubDate>Sun, 20 Dec 2015 12:53:09 +0300</pubDate>
</item>

<item>
<title>Использование DownloadManager для скачивания файлов из сети (Android)</title>
<guid isPermaLink="true">http://bibobo.ru/all/ispolzovanie-downloadmanager-dlya-skachivaniya-faylov-iz-seti-an/</guid>
<link>http://bibobo.ru/all/ispolzovanie-downloadmanager-dlya-skachivaniya-faylov-iz-seti-an/</link>
<comments>http://bibobo.ru/all/ispolzovanie-downloadmanager-dlya-skachivaniya-faylov-iz-seti-an/</comments>
<description>&lt;p&gt;В прошлой статье я рассказывал о том, как скачивать файлы из сети и сохранять их на диск, а так же использовать &lt;i&gt;MediaScanner&lt;/i&gt; для того, чтобы сделать файлы доступными другим приложениям (например галерее, если это фотография или видео).&lt;/p&gt;
&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;Сегодня же хотелось бы рассказать о более интересном и предпочтительном способе скачивания файлов — использовании системного сервиса &lt;i&gt;DownloadManager&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;DownloadManager это такой сервис платформы Android, который предоставляет весь спектр функциональности, связанной с загрузкой файлов. Он сам начнет скачивание, корректно отреагирует на обрыв связи или смену типа сети, запустит скачивание по новой (если был обрыв связи), создаст и отобразит уведомление о загрузке файла, уведомит &lt;i&gt;MediaScanner&lt;/i&gt; о том, что файл загружен и многое другое.&lt;/p&gt;
&lt;p&gt;&lt;input id="button_android_download_save_file2" type="button" onclick="toggle(cut_android_download_save_file2, button_android_download_save_file2)" value="Читать дальше" hiddenText="Свернуть" visibleText="Читать дальше"&gt;&lt;/p&gt;
&lt;div id="cut_android_download_save_file2" style="display: none;" displayOld=""&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Для корректного использования &lt;i&gt;DownloadManager&lt;/i&gt; нам понадобится:&lt;/p&gt;
&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Создать свой класс — наследник &lt;i&gt;BroadcastReceiver&lt;/i&gt;, в котором мы будем реагировать на интенты, отсылаемые &lt;i&gt;DownloadManager&lt;/i&gt;’ом;&lt;/li&gt;
&lt;li&gt;Подготовить URI с ресурсом, который нужно загрузить;&lt;/li&gt;
&lt;li&gt;Создать реквест с нашим URI и другими настройками &lt;i&gt;DownloadManager&lt;/i&gt;;&lt;/li&gt;
&lt;li&gt;Запустить скачивание.&lt;br /&gt;
&lt;/br&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Итак перво наперво — напишем класс-наследник &lt;i&gt;BroadcastReceiver&lt;/i&gt; и реализуем логику, которая покажет Toast, когда наш файл скачается:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Ресивер, который принимает информацию о состоянии загружаемых файлов приложением (задача только одна - отобразить сообщение об успешной загрузке)
/// &amp;lt;/summary&amp;gt;
public class DownloadManagerReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        if (intent.Action == DownloadManager.ActionDownloadComplete)
        {
            //это не совсем правильно с точки зрения производительности, лучше передать ссылку на менеджер извне
            DownloadManager downloadManager = (DownloadManager)context.GetSystemService(Activity.DownloadService);

            long downloadId = intent.GetLongExtra(DownloadManager.ExtraDownloadId, -1);
            if (downloadId == -1)
            {
                return;
            }

            DownloadManager.Query query = new DownloadManager.Query();
            query.SetFilterById(new long[]{ downloadId });

            ICursor cursor = downloadManager.InvokeQuery(query);
            if (cursor != null &amp;amp;&amp;amp; cursor.MoveToFirst())
            {
                if (DownloadStatus.Successful == (DownloadStatus)cursor.GetInt(cursor.GetColumnIndex(DownloadManager.ColumnStatus)))
                {
                    string filename = cursor.GetString(cursor.GetColumnIndex(DownloadManager.ColumnTitle));
                    
                    Toast.makeText(context, string.Format(context.GetString(Resource.String.chat_attachement_succesfully_downloaded), filename), Toast.LENGTH_SHORT);
                }
            }

        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;Далее необходимо создать и зарегистрировать наш ресивер:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;protected override async void OnCreate(Bundle bundle)
{
    //код инициализации активности
    //...
    /////////

    //создаем ресивер для корректной обработки успешного скачивания файла
    _downloadManagerReceiver = new DownloadManagerReceiver();
    RegisterReceiver(_downloadManagerReceiver, new IntentFilter(DownloadManager.ActionDownloadComplete));
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;Последним этапом — необходимо получить ссылку на экземпляр сервиса &lt;i&gt;DownloadService&lt;/i&gt;, настроить менеджер и запустить скачивание:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;private void DownloadFile(string filename){
    DownloadManager downloadManager = (DownloadManager)GetSystemService(Activity.DownloadService);
    //реквест содержит URI файла и дополнительные настройки DownloadManager'а
    DownloadManager.Request request = new DownloadManager.Request(Android.Net.Uri.Parse(&amp;quot;http://наш_url_для_скачивания&amp;quot;));

    //нотификейшн будет виден всегда, пока юзер не отменит его вручную или не кликнет
    request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
    //задаем публичную директорию с загрузками и вторым параметром имя файла + расширение
    request.SetDestinationInExternalPublicDir(Android.OS.Environment.DirectoryDownloads, filename);
    //разрешаем сканить новый файл МедиаСканнеру
    request.AllowScanningByMediaScanner();

    //запускаем скачивание
    downloadManager.Enqueue(request);   
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;Вот и все, после старта скачивания менеджер создать нотификейшн со статусом скачивания, а после позволит открыть файл в приложении по-умолчанию.&lt;/p&gt;
&lt;/div&gt;</description>
<pubDate>Mon, 07 Dec 2015 22:12:40 +0300</pubDate>
</item>

<item>
<title>Сохранение файлов из сети на диск в публичную директорию (Android)</title>
<guid isPermaLink="true">http://bibobo.ru/all/sohranenie-faylov-iz-seti-na-disk-v-publichnuyu-direktoriyu-andr/</guid>
<link>http://bibobo.ru/all/sohranenie-faylov-iz-seti-na-disk-v-publichnuyu-direktoriyu-andr/</link>
<comments>http://bibobo.ru/all/sohranenie-faylov-iz-seti-na-disk-v-publichnuyu-direktoriyu-andr/</comments>
<description>&lt;p&gt;Практически любому приложению, которое позволяет обмениваться контентом необходимо скачивать данные из сети и сохранять их на диск устройства. Существует два распространенных способа это сделать:&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Скачать и сохранить файл вручную;&lt;/li&gt;
&lt;li&gt;Использовать класс &lt;i&gt;DownloadManager&lt;/i&gt; и доверить работу ему.&lt;br /&gt;
&lt;br/&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;При этом скачанные файлы не будут видны приложениям на устройстве до тех пор, пока разработчик самостоятельно не сделает их доступными (либо до следующей перезагрузке устройства).&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;В этой статье мы рассмотрим ручной способ сохранения на диск применительно к Xamarin.Android. Более интересный вариант с использованием &lt;i&gt;DownloadManager&lt;/i&gt; будет рассмотрен в другой статье.&lt;/p&gt;
&lt;p&gt;&lt;input id="button_android_download_save_file1" type="button" onclick="toggle(cut_android_download_save_file1, button_android_download_save_file1)" value="Читать дальше" hiddenText="Свернуть" visibleText="Читать дальше"&gt;&lt;/p&gt;
&lt;div id="cut_android_download_save_file1" style="display: none;" displayOld=""&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Для начала необходимо получить массив байт файла из сети (например по его URL). Для этого можно использовать например простейший &lt;i&gt;WebClient&lt;/i&gt;, написать примерно такой код:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;string url = “url_к_нашему_файлу”;
byte[] dataBytes = new System.Net.WebClient().DownloadData(url);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Далее необходимо сохранить полученный массив байт как файл на диске. Для этого сначала необходимо получить путь до одной из публичных директорий, они могут быть следующими:&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Environment.DirectoryMusic;&lt;/li&gt;
&lt;li&gt;Environment.DirectoryPodcasts;&lt;/li&gt;
&lt;li&gt;Environment.DirectoryRingtones;&lt;/li&gt;
&lt;li&gt;Environment.DirectoryAlarms;&lt;/li&gt;
&lt;li&gt;Environment.DirectoryNotifications;&lt;/li&gt;
&lt;li&gt;Environment.DirectoryPictures;&lt;/li&gt;
&lt;li&gt;Environment.DirectoryMovies;&lt;/li&gt;
&lt;li&gt;Environment.DirectoryDownloads;&lt;/li&gt;
&lt;li&gt;Environment.DirectoryDcim.&lt;br /&gt;
&lt;br/&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Назначение директорий вполне ясно из их названия, будем использовать &lt;i&gt;Environment.DirectoryDownloads&lt;/i&gt;. Напишем следующий код (пояснение в комментариях):&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;//получаем путь к директории с загрузками
string dirPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath;
//если директории нет (такое может быть, да О_о, создадим ее)
if (!Directory.Exists(dirPath))
{
    Directory.CreateDirectory(dirPath);
}

//получаем путь до конечного файла в нашей директории, если он уже есть - удалим его
string filePath = Path.Combine(dirPath, file.Name);
if (File.Exists(filePath))
{
    File.Delete(filePath);
}

//записываем массив байт на диск
File.WriteAllBytes(filePath, fileBytes);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Почти готово, осталось только объявить системе о том, что на диске теперь есть новый файл, доступный всем вокруг. Для этого используется подсистема Android, которая называется &lt;i&gt;MediaScanner&lt;/i&gt;. Существует несколько способов работы с МедиаСканнером, все они обладают разным уровнем гибкости и сложности. Мы воспользуемся самым простым — отправим широковещательный интент системе, МедиаСканнер его поймает и сделает добавит файл в свою базу.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;//объявим МедиаСканеру о том, что появился новый файл
Intent mediaScannerIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScannerIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(filePath)));
SendBroadcast(mediaScannerIntent);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
<pubDate>Wed, 02 Dec 2015 22:06:16 +0300</pubDate>
</item>

<item>
<title>Colorizer — определить цвет</title>
<guid isPermaLink="true">http://bibobo.ru/all/colorizer-opredelit-cvet/</guid>
<link>http://bibobo.ru/all/colorizer-opredelit-cvet/</link>
<comments>http://bibobo.ru/all/colorizer-opredelit-cvet/</comments>
<description>&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="768" data-ratio="0.6"&gt;
&lt;img src="http://bibobo.ru/pictures/Screenshot-(1-.-2015-.-12_16_59).png" width="768" height="1280" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/Screenshot-(1-.-2015-.-12_16_05).png" width="768" height="1280" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/Screenshot-(1-.-2015-.-12_30_52).png" width="768" height="1280" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/Screenshot-(1-.-2015-.-13_15_27).png" width="768" height="1280" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/Screenshot_2015-11-01-12-32-06.png" width="768" height="1280" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/Screenshot_2015-11-01-13-24-34.png" width="768" height="1280" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/Screenshot-(1-.-2015-.-12_45_22).png" width="768" height="1280" alt="" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Ссылка на Play Market: &lt;a href="https://play.google.com/store/apps/details?id=ru.bibobo.colorizer"&gt;Colorizer — определить цвет&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Colorizer — приложение для определения параметров цвета. Просто наведи прицел на объект и Colorizer определит его цвет. После, нажатием одной кнопки можно сделать скриншот и расшарить его, или сохранить в заметки.&lt;/p&gt;
&lt;p&gt;Приложение отображает HEX, RGB и CMYK коды цвета.&lt;/p&gt;
</description>
<pubDate>Tue, 17 Nov 2015 22:47:17 +0300</pubDate>
</item>

<item>
<title>Behance Stats Widget</title>
<guid isPermaLink="true">http://bibobo.ru/all/behance-stats-widget/</guid>
<link>http://bibobo.ru/all/behance-stats-widget/</link>
<comments>http://bibobo.ru/all/behance-stats-widget/</comments>
<description>&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="1400" data-ratio="1.8543046357616"&gt;
&lt;img src="http://bibobo.ru/pictures/1.png" width="1400" height="755" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/2.png" width="1400" height="720" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/3.png" width="1400" height="760" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/4.png" width="1400" height="760" alt="" /&gt;
&lt;img src="http://bibobo.ru/pictures/5.png" width="1400" height="760" alt="" /&gt;
&lt;/div&gt;
&lt;div class="e2-text-caption"&gt;Ссылка на Play Market: &lt;a href="https://play.google.com/store/apps/details?id=ru.bibobo.behancewidget"&gt;Behance Stats Widget&lt;/a&gt;&lt;br/&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Мы разработали абсолютно новый виджет статистики для Behance.&lt;/p&gt;
&lt;p&gt;Он отображает информацию о просмотрах твоих проектов, профиля и лайках, так же ты можешь получать статистику за последние 24 часа.&lt;/p&gt;
&lt;p&gt;Существует два типа виджета — обычный и расширенный.&lt;/p&gt;
&lt;p&gt;В процессе разработки мы постарались сделать виджет простым, быстрым и понятным для пользователя. Так же мы сделали его действительно стабильным и дружелюбным к батарейке твоего смартфона.&lt;/p&gt;
&lt;p&gt;Ты можешь создавать сколько угодно экземпляров виджета, например чтобы смотреть не только свою статистику, но и статистику твоих друзей.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Спасибо дизайнеру Владиславу Михайлову за помощь с визуальной составляющей виджета и тестированием. &lt;a href="https://www.behance.net/vladimikh"&gt;Ссылка на профиль Владислава на Behance&lt;/a&gt;&lt;/p&gt;
</description>
<pubDate>Mon, 16 Nov 2015 23:04:49 +0300</pubDate>
</item>

<item>
<title>Отладка Android-приложений через Wi-Fi</title>
<guid isPermaLink="true">http://bibobo.ru/all/otladka-android-prilozheniy-cherez-wi-fi/</guid>
<link>http://bibobo.ru/all/otladka-android-prilozheniy-cherez-wi-fi/</link>
<comments>http://bibobo.ru/all/otladka-android-prilozheniy-cherez-wi-fi/</comments>
<description>&lt;p&gt;Для отладки своих приложений на живых девайсах разработчики в большинстве своем подключают устройство по USB. Стоит подключить устройство (с разрешенным режимом отладки по USB), его тут же видит утилита ADB и позволяет отлаживать. Но у такого подхода есть два очевидных минуса:&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Устройство постоянно заряжается, что пагубно влияет на аккумулятор&lt;/li&gt;
&lt;li&gt;От повышенного износа страдает USB-разъем на устройстве и в один прекрасный момент может сломаться&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Не все знают, но утилита ADB имеет режим отладки по сети, что позволит нам организовать сей процесс через Wi-Fi и избавиться от проблем, описанных выше.&lt;/p&gt;
&lt;p&gt;&lt;input id="button_android_remote_debug" type="button" onclick="toggle(cut_android_remote_debug, button_android_remote_debug)" value="Читать дальше" hiddenText="Свернуть" visibleText="Читать дальше"&gt;&lt;/p&gt;
&lt;div id="cut_android_remote_debug" style="display: none;" displayOld=""&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Итак все, что нам понадобится это консоль и немного времени. Стоит сразу сказать, что для включения данной фичи устройство все же придется присоединить к компьютеру по USB в первый раз. Можно обойтись и без этого, но тогда необходим root-доступ на девайсе.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Перво наперво необходимо подсоединить девайс к компьютеру и убедиться, что отладки работает (например при помощи Android Studio или Xamarin Studio).&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Далее необходимо не отключая устройство запустить терминал, перейти в директорию, где находится утилита ADB, для моего пользователя команда выглядит так:&lt;br/&gt;
&lt;i&gt;cd /Users/gratytony/Library/Android/sdk/platform-tools&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Далее необходимо перезапустить ADB в режиме прослушивания TCP-порта:&lt;br/&gt;
&lt;i&gt;adb -d tcpip 5555&lt;/i&gt;&lt;br/&gt;
В ответ должно прийти:&lt;br/&gt;
&lt;b&gt;restarting in TCP mode port: 5555&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Если в таком состоянии терминал завис — смело нажимаем &lt;i&gt;Ctrl+C&lt;/i&gt;, по неизвестной причине иногда такое происходит. Можно не переживать — ADB перезапустился корректно.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Далее необходимо узнать IP-адрес девайса, это можно сделать через adb-shell, но есть и более простой способ — зайти в настройки устройства и просмотреть статус Wi-Fi сети, к которой оно подключено — там будет виден IP-адрес устройства.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Последнее, что нужно сделать это подключиться к устройству при помощи команды:&lt;br/&gt;
&lt;i&gt;adb connect IP_АДРЕС_УСТРОЙСТВА:5555&lt;/i&gt;&lt;br/&gt;
В ответ должно прийти:&lt;br/&gt;
&lt;b&gt;connected to IP_АДРЕС_УСТРОЙСТВА:5555&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Все, теперь можно смело отключать устройство от USB и отлаживаться по воздуху.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Для прекращения отладки по USB нужно ввести команду:&lt;br/&gt;
&lt;i&gt;adb -s IP_АДРЕС_УСТРОЙСТВА:5555 usb&lt;/i&gt;&lt;br/&gt;
В ответ должно прийти:&lt;br/&gt;
&lt;b&gt;restarting in USB mode&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Отладка работает хорошо, единственное условие, которое необходимо выполнить — это быстрый Wi-Fi. При маленькой скорости соединения загрузка приложения на устройство и шагание по точкам останова существенно медленнее, чем по кабелю.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;UPDATE:&lt;br/&gt;
Если узнать IP-адрес через интерфейс Android не получается можно проделать следующие действия (при подключенном по USB устройстве). Ввести команду:&lt;br/&gt;
&lt;i&gt;adb shell&lt;/i&gt;&lt;br/&gt;
В ответ откроется консоль на устройстве:&lt;br/&gt;
&lt;b&gt;shell@mako:/ $&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Далее необходимо ввести команду:&lt;br/&gt;
&lt;i&gt;netcfg&lt;/i&gt;&lt;br/&gt;
В ответ появится список интерфейсов на устройстве, нас будет интересовать Wi-Fi, у меня это &lt;b&gt;wlan0&lt;/b&gt;, рядом будет написан соответствующий интерфейс IP-адрес.&lt;br/&gt;
Чтобы выйти из консоли на устройстве используем команду &lt;i&gt;exit&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;/div&gt;</description>
<pubDate>Sat, 31 Oct 2015 11:10:05 +0300</pubDate>
</item>

<item>
<title>Использование Fragment без UI для хранения данных</title>
<guid isPermaLink="true">http://bibobo.ru/all/ispolzovanie-fragment-bez-ui-dlya-hraneniya-dannyh/</guid>
<link>http://bibobo.ru/all/ispolzovanie-fragment-bez-ui-dlya-hraneniya-dannyh/</link>
<comments>http://bibobo.ru/all/ispolzovanie-fragment-bez-ui-dlya-hraneniya-dannyh/</comments>
<description>&lt;p&gt;Fragment — это сущность, позволяющая инкапсулировать часть интерфейса и его логику для переиспользования. Фрагменты были представлены в Android 3.0 и позволили существенно упростить работу по проектированию интерфейса для устройств с различными размерами экранов (так называемая парадигма Responsive Design, когда одно и то же приложение выглядит по разному на устройствах с различной диагональю экрана).&lt;/p&gt;
&lt;p&gt;&lt;input id="button_using_fragment_without_ui" type="button" onclick="toggle(cut_using_fragment_without_ui, button_using_fragment_without_ui)" value="Читать дальше" hiddenText="Свернуть" visibleText="Читать дальше"&gt;&lt;/p&gt;
&lt;div id="cut_using_fragment_without_ui" style="display: none;" displayOld=""&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Фрагменты очень удобны и обладают следующими основными свойствами:&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Очень похожи на Activity и имеют схожий цикл жизни, при этом дополненный несколькими событиями;&lt;/li&gt;
&lt;li&gt;Как и Activity инкапсулируют логику работы с интерфейсом;&lt;/li&gt;
&lt;li&gt;Могут жить только внутри Activity, при этом один и тот же фрагмент может быть использован в различных Activity, что существенно упрощает задачу при проектировании интерфейса для различных устройств.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Фрагменты обладают ещё одним свойством, которое может быть весьма полезно при разработке приложений — фрагмент можно создать вовсе без пользовательского интерфейса и заставить его не умирать при пересоздании родительской Activity. Это свойство может быть очень к месту для хранения источника данных (модели) в таком фрагменте.&lt;/p&gt;
&lt;p&gt;Для многих начинающих разработчиков, пришедших с десктопных платформ, может стать настоящим откровением поведение Android например при изменении ориентации устройства — в таком случае Activity будет уничтожена и создана вновь с лэндскейпным лэйаутом. Соответственно если к Activity привязан какой-либо источник данных — он будет так же пересоздан (или перезагружен, в случае ContentProvider).&lt;/p&gt;
&lt;p&gt;Чтобы этого не произошло можно создать специальный фрагмент без UI и передать в его конструкторе значение true в сеттер setRetainInstance (Java), либо установить значение true свойству RetainInstance (C#). Далее инициализировать и сохранить в нём нашу модель.&lt;/p&gt;
&lt;p&gt;Все, после подобных манипуляций получим постоянный доступ к модели с сохранением её состояния даже при пересоздании родительской Activity.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Ниже небольшой пример кода на C# и Xamarin:&lt;/p&gt;
&lt;pre&gt;&lt;pre class="e2-text-code"&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Фрагмент без интерфейса, будет хранить нашу модель
/// (данный фрагмент будет ретейниться после уничтожения основной вьюхи и наши данные не будут перегенериваться при пересоздании Activity)
/// &amp;lt;/summary&amp;gt;
public class ModelHolderFragment : Android.App.Fragment{
	public static string SearchTag = &amp;quot;MODEL_HOLDER_FRAGMENT&amp;quot;;

	public ViewPagerItem[] DataSourceItems { get; private set;}

	public override void OnCreate (Bundle savedInstanceState)
	{
		base.OnCreate (savedInstanceState);

		DataSourceItems = GenViewPagerItems ();

		//установим флаг, который запретит фрагменту умирать при уничтожении основной вьюхи
		RetainInstance = true;
	}

	/// &amp;lt;summary&amp;gt;
	/// Генерация фейковых данных
	/// &amp;lt;/summary&amp;gt;
	/// &amp;lt;returns&amp;gt;The view pager items.&amp;lt;/returns&amp;gt;
	private ViewPagerItem[] GenViewPagerItems(){
		...
		return items;
	}
}

///Хэндлер события OnCreate родительской Activity - тут создадим экземпляр нашего фрагмента
protected override void OnCreate(Bundle savedInstanceState) {
	base.OnCreate(savedInstanceState);
	SetContentView (Resource.Layout.activity_main);

	//инициализируем фрагмент без интерфейса для хранения модели только при первом создании вьюхи
	if (savedInstanceState == null) {
		FragmentManager.BeginTransaction ().Add ((Android.App.Fragment)new ModelHolderFragment (), ModelHolderFragment.SearchTag).Commit();
		FragmentManager.ExecutePendingTransactions ();
	}
	_model = (ModelHolderFragment)FragmentManager.FindFragmentByTag (ModelHolderFragment.SearchTag);
}&lt;/code&gt;&lt;/pre&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
<pubDate>Fri, 19 Jun 2015 21:01:03 +0300</pubDate>
</item>

<item>
<title>Использование паттерна ViewHolder</title>
<guid isPermaLink="true">http://bibobo.ru/all/ispolzovanie-patterna-viewholder/</guid>
<link>http://bibobo.ru/all/ispolzovanie-patterna-viewholder/</link>
<comments>http://bibobo.ru/all/ispolzovanie-patterna-viewholder/</comments>
<description>&lt;p&gt;С этой статьи я бы хотел начать цикл кратких заметок о разработке под мобильные устройства. Приложения под телефоны и планшеты — мое давнее увлечение. К сожалению плотно заняться которым, в силу различных обстоятельств, я смог только сейчас. В прочем хватит лирики, начнём.
В качестве платформы я выбрал Android, как наиболее массовую и открытую систему. В качестве языка программирования сегодня будем использовать C#, писать будем на Xamarin с использованием Android API.&lt;/p&gt;
&lt;p&gt;&lt;input id="button_using_viewholder" type="button" onclick="toggle(cut_using_viewholder, button_using_viewholder)" value="Читать дальше" hiddenText="Свернуть" visibleText="Читать дальше"&gt;&lt;/p&gt;
&lt;div id="cut_using_viewholder" style="display: none;" displayOld=""&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Практически любое мобильное приложение это некий экран с набором однотипных информационных элементов, для отображения которых обычно используется список или какой-либо грид. Из-за того, что платформа мобильная и работает на устройстве с небольшим объёмом памяти и слабым процессором (хаха! В моем первом компьютере было памяти раз в 15 меньше и процессор существенно слабее, чем сейчас в среднем телефоне) операционная система вынуждена ресурсы экономить. Соответственно если речь идёт о списке (ListView в Android), а если точнее об адаптере, который предоставляет данные для списка (например ArrayAdapter в Android) мы сталкиваемся с одной из особенностей, вызванных необходимостью беречь ресурсы.&lt;/p&gt;
&lt;p&gt;Такая особенность заключается в том, что лист не создаёт ячейки под каждую из записей подключенного источника данных. Он создаёт лишь то количество ячеек, сколько может уместиться на экране в данный момент плюс по одной лишней ячейке сверху и снизу. Такое поведение характерно не только для Android, но и для iOS.&lt;/p&gt;
&lt;p&gt;При этом когда пользователь прокручивает список система не удаляет ячейки, она старается их переиспользовать.&lt;/p&gt;
&lt;p&gt;Когда ячейка пропадает из поля видимости пользователя она передаётся в метод GetView() адаптера, в котором происходит заполнение ячейки данными. В этом методе можно решить, переиспользовать уже готовую ячейку или создать новую.&lt;/p&gt;
&lt;p&gt;Все бы ничего, но если лэйаут ячейки сложный то мы напрямую сталкиваемся с необходимостью проводить поиск элементов в лэйауте (TextView, ImageView и т. п.) для заполнения их актуальными данными. Вроде бы ничего страшного, вызвал &lt;i&gt;FindViewById()&lt;/i&gt; с нужным id элемента, получил его и нет проблем, но выполнение метода &lt;i&gt;FindViewById()&lt;/i&gt; это длительная операция, потому что она тянет за собой траверс по XML-коду лэйаута нашей ячейки. В итоге на сложных лэйаутах имеем просадку по производительности скроллинга списка.&lt;/p&gt;
&lt;p&gt;Чтобы решить данную проблему нам необходимо использовать паттерн &lt;b&gt;ViewHolder&lt;/b&gt;. Кратко его суть заключается в следующем:&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Реализуем класс, который в конструкторе принимает нашу &lt;i&gt;View&lt;/i&gt;, делает траверс и ищет все её элементы, которые нам потребуются для заполнения данных;&lt;/li&gt;
&lt;li&gt;Записываем ссылки на эти элементы в соответствующие поля класса, снабжаем их геттерами, чтобы потом к ним обращаться;&lt;/li&gt;
&lt;li&gt;При создании нашей &lt;i&gt;View&lt;/i&gt; — создаём наш &lt;i&gt;класс-ViewHolder&lt;/i&gt; и записываем ссылку на него в свойстве &lt;i&gt;Tag&lt;/i&gt; у нашей &lt;i&gt;View&lt;/i&gt;;&lt;/li&gt;
&lt;li&gt;Теперь при попытке переиспользовать &lt;i&gt;View&lt;/i&gt; нам не нужно делать повторный поиск элементов в лэйауте, достаточно получить ссылку на &lt;i&gt;ViewHolder&lt;/i&gt; из свойства &lt;i&gt;Tag&lt;/i&gt; и обратиться к соответствующим геттерам холдера;&lt;/li&gt;
&lt;li&gt;PROFIT.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Если делать реализацию на Java сложностей возникнуть не должно, но если использовать C# и Xamarin мы столкнемся с корнями, растущими из Java :-)&lt;/p&gt;
&lt;p&gt;Предположим есть такой класс:&lt;/p&gt;
&lt;pre&gt;&lt;pre class="e2-text-code"&gt;&lt;code&gt;public class MyViewHolder{
public TextView MyTextView {get; private set;}

public MyViewHolder(View view){
...
}
}&lt;/code&gt;&lt;/pre&gt;&lt;/pre&gt;
&lt;p&gt;Если теперь попытаться присвоить ссылку на него свойству &lt;i&gt;Tag&lt;/i&gt; нашей &lt;i&gt;View&lt;/i&gt; у нас ничего не получится. Поскольку &lt;i&gt;Tag&lt;/i&gt; принимает объект типа &lt;i&gt;Java.Lang.Object&lt;/i&gt;, а наш класс наследован от типа &lt;i&gt;Object&lt;/i&gt;. Прямое кастование к нужному типу так же не приведёт к успеху. Кастование сначала к &lt;i&gt;Object&lt;/i&gt;, а потом к &lt;i&gt;Java.Lang.Object&lt;/i&gt; не даст ошибки компиляции, зато упадёт в рантайме.&lt;/p&gt;
&lt;p&gt;Что же делать? &lt;b&gt;Все просто :-) наследовать наш класс от типа &lt;i&gt;Java.Lang.Object&lt;/i&gt; и будет счастье!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Небольшой пример того, как все это работает:&lt;/p&gt;
&lt;pre&gt;&lt;pre class="e2-text-code"&gt;&lt;code&gt;public class TimetableItemAdapter : ArrayAdapter&amp;lt;TimetableItem&amp;gt;
{
	/// &amp;lt;summary&amp;gt;
	/// Класс ViewHolder, нужен что бы исключить множественный траверс по элементам вьюхи, для производительности
	/// &amp;lt;/summary&amp;gt;
	public class TimetableItemViewHolder : Java.Lang.Object{
		public TextView TimeText { get; private set;}
		public TextView DescriptionText { get; private set;}
		public ImageView AcceptButton { get; private set;}

		public TimetableItem BoundedItem { get; set;}

		public TimetableItemViewHolder(View view, TimetableItem item){
			TimeText = view.FindViewById&amp;lt;TextView&amp;gt;(Resource.Id.timetable_time_text);
			DescriptionText = view.FindViewById&amp;lt;TextView&amp;gt;(Resource.Id.timetable_description_text);
			AcceptButton = view.FindViewById&amp;lt;ImageView&amp;gt;(Resource.Id.timetable_accept);

			BoundedItem = item;
		}
	}

	private MainActivity _context;

	public TimetableItemAdapter(Context context, int resource, IList&amp;lt;TimetableItem&amp;gt; objects) : base(context, resource, objects) {
		_context = (MainActivity)context;
	}
			
	public override View GetView(int position, View convertView, ViewGroup parent) {
		View resultView = null;
		TimetableItemViewHolder holder = null;
		TimetableItem item = GetItem(position);

		if(convertView != null){
			resultView = convertView;
			holder = (TimetableItemViewHolder)convertView.Tag;
			holder.BoundedItem = item;
		}else{
			resultView = ((LayoutInflater)_context.GetSystemService(Context.LayoutInflaterService))
				.Inflate(Resource.Layout.timetable_list_item, parent, false);
			holder = new TimetableItemViewHolder(resultView, item);
			resultView.Tag = holder;
		}

		if(item != null &amp;amp;&amp;amp; holder != null){
			holder.TimeText.Text = item.Time;
			holder.DescriptionText.Text = item.Description;
		}

		return resultView;
	}
}&lt;/code&gt;&lt;/pre&gt;&lt;/pre&gt;
&lt;p&gt;Во &lt;i&gt;ViewHolder&lt;/i&gt; не обязательно хранить только ссылки на элементы лэйаута, иногда может потребоваться хранить и какие-либо сопутствующие данные. Например в коде выше во &lt;i&gt;ViewHolder&lt;/i&gt; записывается дополнительно ссылка на элемент из источника данных, для последующего использования где-нибудь. Но в данном случае нужно помнить, что при переиспользовании &lt;i&gt;View&lt;/i&gt; нужно не забыть записать в свойство ссылку на новый элемент источника данных.&lt;/p&gt;
&lt;/div&gt;</description>
<pubDate>Wed, 17 Jun 2015 10:56:43 +0300</pubDate>
</item>


</channel>
</rss>