Статьи

Облачные приложения Android с Azure

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

В этой статье мы создадим базовый прототип Android, который позволит нам подключиться к облачному провайдеру Windows Azure и использовать Windows Azure Toolkit для Android (  доступен на GitHub ) для выполнения всех основных облачных операций : загрузки контента в облако хранение, просматривать хранилище, загружать или удалять файлы в облачном хранилище. После того, как эти операции будут реализованы, мы увидим, как включить приложение Android для получения push-уведомлений сервера .

Прежде всего, нам нужно настроить учетную запись хранения в облаке Azure:

Учетная запись хранения имеет несколько вариантов управления данными : мы можем хранить данные в хранилище BLOB-объектов, таблиц или очередей. В этой статье мы будем использовать хранилище BLOB-объектов для работы с изображениями. Учетная запись хранения имеет первичный и вторичный ключ доступа , любой из двух можно использовать для выполнения операций с учетной записью хранения. Любой из этих ключей может быть восстановлен в случае взлома.

1. Предварительные сведения

Во-первых, предпосылки:

Получения стартера документ на странице GitHub Windows Azure Toolkit охватывает процедуру установки все необходимое программного обеспечения в деталях.

Весь этот проект ( Cloid ) находится в свободном доступе на GitHub . Поэтому здесь мы ограничимся представлением наиболее важных разделов кода вместе с соответствующими экранами.

Пользовательский интерфейс состоит из нескольких основных экранов действий, которые появляются из основного экрана (вверху по центру):

Поскольку мы используем технологию не ради самих себя, а в соответствии с нашими потребностями, давайте начнем с определения того, что мы хотим:

public abstract class Storage {

    /** All providers will have accesss to context*/
    protected Context context;

    /** All providers will have accesss to SharedPreferences */
    protected CloudPreferences prefs;

    /** All downloads from providers will be saved on SD card */
    protected String DOWNLOAD_PATH = "/sdcard/DCIM/Camera/";

    /**
     * @throws OperationException
     * */
    public Storage(Context ctx) throws OperationException {
        context = ctx;
        prefs = new CloudPreferences(ctx);
    }

    /**
     * @throws OperationException
     * */
    public abstract void uploadToStorage(String file_path) throws OperationException;

    /**
     * @throws OperationException
     * */
    public abstract void downloadFromStorage(String file_name) throws OperationException;

    /**
     * @throws OperationException
     * */
    public abstract void browseStorage() throws OperationException;

    /**
     * @throws OperationException
     * */
    public abstract void deleteInStorage(String file_name) throws OperationException;

}

Выше приведен договор, который наш поставщик облачных хранилищ будет выполнять. Мы предоставим реализацию MockStorage, которая будет претендовать на выполнение команды для тестирования нашего пользовательского интерфейса (то есть нашего списка прокручиваемых элементов, индикатора выполнения, сообщений об исключениях и т. Д.), Чтобы позже мы могли просто подключить операции хранения Azure. Обратите внимание, что на приведенном выше экране действий мы можем в любое время переключаться между хранилищем Azure и Mock Storage, нажав кнопку «Cloud ON / OFF» на экране настроек, сохранив настройки после этого.

public class MockStorage extends Storage {

    // code here...
    @Override
    public void uploadToStorage(String file_path) throws OperationException {

        doNothingButSleep();
        //throw new OperationException( "Test error message",
        //                          new Throwable("Reason: upload test") );
    }

    // other methods will also do nothing but sleep...

    /***/
    private void doNothingButSleep(){

         try{ Thread.sleep(5000L); }
         catch (InterruptedException iex){ return; }
    }

2. Лазурный инструментарий

Инструментарий поставляется с примером приложения под названием «Simple» и двумя библиотеками jar:

  1. контроль доступа для android.jar в    папке wa-toolkit-android \ library \ accesscontrol \ bin
  2. хранилище Azure для android.jar  в    папке wa-toolkit-android \ library \ storage \ bin 

Здесь мы будем использовать только последнее, так как будем напрямую обращаться к хранилищу BLOB-объектов Azure. Излишне говорить, что это не рекомендуемый способ , поскольку наши учетные данные будут храниться на телефоне. С точки зрения безопасности лучшим подходом было бы получение доступа к хранилищу Azure через веб-службы, размещенные в Azure или других публичных / частных облаках.

Когда инструментарий будет готов к использованию, нам нужно немного подумать о настройках . Для использования хранилища BLOB-объектов Azure требуется только 3 поля: имя учетной записи , ключ доступа и контейнер для наших изображений. Ключ доступа представляет собой довольно длинную строку (88 символов), и набрать его довольно сложно, поэтому один из способов выполнить настройку — настроить файл Android res / values ​​/ strings.xml для установки значений по умолчанию:

...
<!-- AZURE configuration direct -->
<string name="azure_direct_account_name">cloid</string>
<string name="azure_direct_access_key">insert-access-key-here</string>
<string name="azure_container">pictures</string>
...

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

А теперь давайте реализуем класс AzureStorage .

3. Операции с хранилищем BLOB-объектов Azure

3.1. Инициализация хранилища

Конструктор AzureStorage получает свои данные из настроек Android (из своего суперкласса), затем создает строку подключения, используемую для доступа к учетной записи хранения, создает клиент Blob и извлекает ссылку на контейнер изображений. Если пользователь изменил «картинки» контейнера по умолчанию в настройках, то будет создано новое (пустое) с этим новым именем. Контейнер — это любая группа BLOB-объектов под именем. За пределами контейнера не существует блобов.

// package here
// other imports
import com.windowsazure.samples.android.storageclient.BlobProperties;
import com.windowsazure.samples.android.storageclient.CloudBlob;
import com.windowsazure.samples.android.storageclient.CloudBlobClient;
import com.windowsazure.samples.android.storageclient.CloudBlobContainer;
import com.windowsazure.samples.android.storageclient.CloudBlockBlob;
import com.windowsazure.samples.android.storageclient.CloudStorageAccount;

public class AzureStorage extends Storage {

private CloudBlobContainer container;

/ * @throws OperationException
* */
public AzureStorage(Context ctx) throws OperationException {
  super(ctx);

  // set from prefs
  String acct_name = prefs.getAccountName();
  String access_key = prefs.getAccessKey();

  // get connection string
  String storageConn = "DefaultEndpointsProtocol=http;" +
  "AccountName=" + acct_name +
  ";AccountKey=" + access_key;

  // get CloudBlobContainer
  try {
     // Retrieve storage account from storageConn
     CloudStorageAccount storageAccount = CloudStorageAccount.parse(conn);

     // Create the blob client
     // to get reference objects for containers and blobs
     CloudBlobClient blobClient = storageAccount.createCloudBlobClient();

    // Retrieve reference to a previously created container
    container = blobClient.getContainerReference( prefs.getContainer() );
    container.createIfNotExist();
  }
  catch (Exception e) {
    throw new OperationException("Error from initBlob: " + e.getMessage(), e);
  }
 }
// code...

Мы будем использовать эту ссылку на контейнер CloudBlobContainer на протяжении всей нашей предстоящей облачной работы.

3.2. Загрузка изображений

Мы будем загружать файл из галереи Android в облако, сохраняя то же имя файла. «Screener» — это просто класс утилит (см. Репозиторий GitHub), который выполняет ряд полезных действий, например извлекает имя файла из его пути и устанавливает правильный тип пантомимы («image / jpeg», «image / png» и т. Д.). ).

Два вида BLOB-объектов — это блобные страницы и блочные блобы . (Очень) короткая история заключается в том, что BLOB-объекты страниц оптимизированы для операций чтения и записи, а блочные BLOB-объекты позволяют эффективно загружать большие файлы. В частности, мы можем загружать несколько блоков параллельно, чтобы уменьшить время загрузки. Здесь мы загружаем BLOB-объект (изображение галереи) в виде набора блоков.

/**
 * @throws OperationException */
 @Override
 public void uploadToStorage(String file_path) throws OperationException {

     try {
	   // Create or overwrite blob with contents from a local file
	   // use same name than in local storage
	   CloudBlockBlob blob = container.getBlockBlobReference(
	    			       Screener.getNameFromPath(file_path) );
	   File source = new File(file_path);
	   blob.upload( new FileInputStream(source), source.length() );
	   blob.getProperties().contentType = Screener.getImageMimeType(file_path);
	   blob.uploadProperties();
     }
     catch (Exception e) {
	   throw new OperationException("Error from uploadToStorage: "
                                          + e.getMessage(), e);
     }
 }

Помните, что мы не проверяем, существует ли файл в облачном хранилище. Поэтому мы перезапишем любой существующий файл с тем же именем, что и загружаемый файл. Это обычно нежелательно в рабочем коде. Вот последовательность операций загрузки:

3.3. Просмотр облака

Для просмотра мы храним все наши большие двоичные объекты в нашем контейнере в списке элементов, которые мы будем отображать в Android в виде прокручиваемого списка имен изображений в подклассе android.app.ListActivity . Когда пользователь щелкает один элемент в списке («трогает»), мы хотим отобразить некоторые свойства изображения, такие как размер изображения (важно при принятии решения о загрузке), его тип пантомимы и дата последней операции.

/**
  * @throws OperationException
 * */
 @Override
 public void browseStorage() throws OperationException{

    // reset uri list for refresh - no caching
    Item.itemList.clear();

    // Loop over blobs within the container
    try {
	for (CloudBlob blob : container.listBlobs()) {
	    blob.downloadAttributes();
	    BlobProperties props = blob.getProperties();
	    long ksize =  props.length/1024;
	    String type = props.contentType;
	    Date lastModified = props.lastModified;

	    Item item = new Item(blob.getUri(), blob.getName(), ksize, type, lastModified);
	    Item.itemList.add(item);
	} // end loop
    }
    catch (Exception e) {
	 throw new OperationException("Error from browseStorage: " + e.getMessage(), e);
    }
 }

Вот последовательность операций просмотра. Нажатие на элемент в списке отображает его детали и операции с изображением, что мы рассмотрим ниже:

3.4. Загрузка изображений

Наш метод загрузки довольно прост. Обратите внимание, что мы загружаем на SD-карту телефона Android, используя DOWNLOAD_PATH из суперкласса.

/**
 * @throws OperationException
 * */
 @Override
 public void downloadFromStorage(String file_name) throws OperationException{

     try {
	    for (CloudBlob blob : container.listBlobs()) {
	    // Download the item and save it to a file with the same name as arg
		if(blob.getName().equals(file_name)){
	             blob.download( new FileOutputStream(DOWNLOAD_PATH
                                   + blob.getName()) );
		     break;
		}
	    }
    }
    catch (Exception e) {
	throw new OperationException("Error from downloadFromStorage: "
                        + e.getMessage(), e);
    }
}

И соответствующий поток пользовательского интерфейса. Вместо того, чтобы отображать изображение сразу после загрузки, мы решили включить ссылку на Галерею (внизу экрана), где недавно извлеченное изображение появляется в верхней части стопки изображений Галереи:

3.5. Удаление изображений

Операция удаления, выполняемая над большим облаком в облаке, также довольно проста:

/**
 * @throws OperationException */
 @Override
 public void deleteInStorage(String file_name) throws OperationException{

    try {
   // Retrieve reference to a blob named file_name
   CloudBlockBlob blob = container.getBlockBlobReference(file_name);
   // Delete the blob
   blob.delete();
    }
    catch (Exception e) {
throw new OperationException("Error from deleteInStorage: "
                     + e.getMessage(), e);
    }
  }

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

3,6. Завершение

Методы AzureStorage вызываются в основном рабочем потоке, который позаботится обо всех облачных операциях:

// called inside a thread
try {
      // get storage instance from factory
      Storage store = StorageFactory.getStorageInstance(this,
                          StorageFactory.Provider.AZURE_STORAGE);
      // for the progress bar
      incrementWorkCount();

      // do ops
       switch(operation){

            case UPLOAD :   store.uploadToStorage(path);
                            break;
            case BROWSE :   store.browseStorage();
                            break;
            case DOWNLOAD : store.downloadFromStorage(path);
                            // refresh Gallery
                            sendBroadcast( new Intent( Intent.ACTION_MEDIA_MOUNTED,
                                Uri.parse("file://"+ Environment.getExternalStorageDirectory()) ) );
                            break;
             case DELETE :  store.deleteInStorage(path);
                            break;
       } // end switch
 }
 catch (OperationException e) {
     recordError(e);
 }

Обратите внимание, как мы говорим обновлению галереи изображений Android, отправляя широковещательную рассылку после загрузки нового файла из облака на SD-карту. Есть разные способы сделать это, но без этого вызова Галерея не покажет новое изображение до следующего запланированного сканирования системы. Опять же, полный код см. В этом проекте на GitHub.

Мы закончили с основными облачными операциями. Все, что нам нужно было сделать, это подключить наш класс реализации AzureStorage и получить его экземпляр на фабрике, с минимальным влиянием на существующий ранее код.

4. Push-уведомления

До этого момента мы демонстрировали инициируемую устройством связь с облаком. Для инициируемой облаком или Push-связи платформа Android использует Google Cloud Messaging (GCM).

В предыдущей статье я писал о том, как интегрировать GCM в существующее приложение для Android. Здесь мы добавим второй набор настроек для push сервера. Наш клиентский код будет подключен к любому серверу GCM, и он установит статус нашей основной деятельности (последний снимок экрана справа), как только информация в настройках Push будет правильно установлена.

5. Выводы

Документация по Toolkit довольно скудна (поэтому сообществу нужно больше подобных статей). Кроме того, пример приложения не охватывает много (может быть, поэтому оно называется «Простое»), и у него есть возможности для улучшения. Тем не менее, сама библиотека полностью функциональна, и как только мы выясним, API, все это работает довольно хорошо. Конечно, само приложение довольно простое и не охватывает множество других функций, таких как управление доступом, разрешения, метаданные и снимки. Но это только начало.