AppSearch

O AppSearch é uma solução de pesquisa no dispositivo de alto desempenho para gerenciar dados estruturados armazenados localmente. Ela contém APIs para indexar e recuperar dados usando a pesquisa de texto completo. Os aplicativos podem usar o AppSearch para oferecer recursos personalizados de pesquisa no app, permitindo que os usuários pesquisem conteúdo mesmo quando estão off-line.

Diagrama ilustrando a indexação e a pesquisa com o AppSearch

O AppSearch oferece os seguintes recursos:

  • Uma implementação de armazenamento rápida, que prioriza dispositivos móveis e tem baixo uso de E/S
  • Indexação e consulta altamente eficientes em grandes conjuntos de dados
  • Suporte para vários idiomas, como inglês e espanhol
  • Classificação de relevância e pontuação de uso

Devido ao menor uso de E/S, o AppSearch oferece menor latência para indexação e pesquisa em grandes conjuntos de dados em comparação com o SQLite. O AppSearch simplifica as consultas entre tipos, oferecendo suporte a consultas únicas, enquanto o SQLite mescla resultados de várias tabelas.

Para ilustrar os recursos do AppSearch, vamos usar o exemplo de um aplicativo de música que gerencia as músicas favoritas dos usuários e permite que eles as pesquisem com facilidade. Os usuários gostam de músicas de todo o mundo com títulos em diferentes idiomas, que o AppSearch oferece suporte nativo para indexação e consulta. Quando o usuário pesquisa uma música pelo título ou nome do artista, o aplicativo simplesmente transmite a solicitação ao AppSearch para recuperar as músicas correspondentes de forma rápida e eficiente. O aplicativo mostra os resultados, permitindo que os usuários comecem a tocar as músicas favoritas rapidamente.

Configuração

Para usar o AppSearch no seu aplicativo, adicione as seguintes dependências ao arquivo build.gradle do aplicativo:

Groovy

dependencies {
    def appsearch_version = "1.2.0-alpha02"

    implementation "androidx.appsearch:appsearch:$appsearch_version"
    // Use kapt instead of annotationProcessor if writing Kotlin classes
    annotationProcessor "androidx.appsearch:appsearch-compiler:$appsearch_version"

    implementation "androidx.appsearch:appsearch-local-storage:$appsearch_version"
    // PlatformStorage is compatible with Android 12+ devices and supports sharing data
    // with other apps. Feature support varies by device. Before calling an API that
    // requires a feature, check Features.isFeatureSupported().
    implementation "androidx.appsearch:appsearch-platform-storage:$appsearch_version"

    // PlayServicesStorage is compatible with all devices that support Google Play Services on
    // all API levels. It offers similar capabilities to PlatformStorage and is the
    // recommended solution for lower API levels on which PlatformStorage is not supported.
    implementation "androidx.appsearch:appsearch-play-services-storage:$appsearch_version"
}

Kotlin

dependencies {
    val appsearch_version = "1.2.0-alpha02"

    implementation("androidx.appsearch:appsearch:$appsearch_version")
    // Use annotationProcessor instead of kapt if writing Java classes
    kapt("androidx.appsearch:appsearch-compiler:$appsearch_version")

    implementation("androidx.appsearch:appsearch-local-storage:$appsearch_version")
    // PlatformStorage is compatible with Android 12+ devices and supports sharing data
    // with other apps. Feature support varies by device. Before calling an API that
    // requires a feature, check Features.isFeatureSupported().
    implementation("androidx.appsearch:appsearch-platform-storage:$appsearch_version")

    // PlayServicesStorage is compatible with all devices that support Google Play Services on
    // all API levels. It offers similar capabilities to PlatformStorage and is the
    // recommended solution for lower API levels on which PlatformStorage is not supported.
    implementation("androidx.appsearch:appsearch-play-services-storage:$appsearch_version")

}

Conceitos do AppSearch

O diagrama a seguir ilustra os conceitos do AppSearch e as interações deles.

Diagrama
de um aplicativo cliente e as interações dele com os seguintes
conceitos do AppSearch: banco de dados, esquema, tipos de esquema, documentos,
sessão e pesquisa. Figura 1. Diagrama de conceitos do AppSearch: banco de dados, esquema, tipos de esquema, documentos, sessão e pesquisa.

Banco de dados e sessão

Um banco de dados do AppSearch é uma coleção de documentos que segue o esquema do banco de dados. Os aplicativos clientes criam um banco de dados fornecendo o contexto do aplicativo e um nome para o banco de dados. Os bancos de dados só podem ser abertos pelo aplicativo que os criou. Quando um banco de dados é aberto, uma sessão é retornada para interagir com ele. A sessão é o ponto de entrada para chamar as APIs AppSearch e permanece aberta até ser fechada pelo aplicativo cliente.

Esquema e tipos de esquema

Um esquema representa a estrutura organizacional dos dados em um banco de dados do AppSearch.

O esquema é composto por tipos de esquema que representam tipos exclusivos de dados. Os tipos de esquema consistem em propriedades que contêm um nome, um tipo de dados e uma cardinalidade. Depois que um tipo de esquema é adicionado ao esquema do banco de dados, os documentos desse tipo podem ser criados e adicionados ao banco de dados.

Documentos

No AppSearch, uma unidade de dados é representada como um documento. Cada documento em um banco de dados do AppSearch é identificado de forma exclusiva pelo namespace e pelo ID. Os namespaces são usados para separar dados de fontes diferentes quando apenas uma fonte precisa ser consultada, como contas de usuário.

Os documentos contêm um carimbo de data/hora de criação, um time to live (TTL) e uma pontuação que pode ser usada para classificação durante a recuperação. Um documento também recebe um tipo de esquema que descreve outras propriedades de dados que o documento precisa ter.

Uma classe de documento é uma abstração de um documento. Ele contém campos anotados que representam o conteúdo de um documento. Por padrão, o nome da classe do documento define o nome do tipo de esquema.

Os documentos são indexados e podem ser pesquisados com uma consulta. Um documento é correspondido e incluído nos resultados da pesquisa se contiver os termos na consulta ou corresponder a outra especificação de pesquisa. Os resultados são ordenados com base na pontuação e na estratégia de classificação. Os resultados da pesquisa são representados por páginas que podem ser recuperadas sequencialmente.

O AppSearch oferece personalizações para pesquisa, como filtros, configuração de tamanho de página e snippets.

Armazenamento da plataforma, armazenamento local ou armazenamento do Google Play Services

O AppSearch oferece três soluções de armazenamento: LocalStorage, PlatformStorage e PlayServicesStorage. Com o LocalStorage, o aplicativo gerencia um índice específico do app que fica no diretório de dados do aplicativo. Com PlatformStorage e PlayServicesStorage, seu aplicativo contribui para um índice central em todo o sistema. O índice de PlatformStorage é hospedado no servidor do sistema, e o índice de PlayServicesStorage é hospedado no armazenamento do Google Play Services. O acesso aos dados nesses índices centrais é restrito aos dados que seu aplicativo contribuiu e aos dados que foram explicitamente compartilhados com você por outro aplicativo. Todas essas opções de armazenamento compartilham a mesma API e podem ser intercambiadas com base na versão do dispositivo.

O suporte aos recursos varia de acordo com a opção de armazenamento e o dispositivo. Para mais informações, consulte Verificar a disponibilidade de recursos.

O código a seguir cria uma sessão PlatformStorage no Android 12 (nível 31 da API) e versões mais recentes, e uma sessão PlayServicesStorage ou LocalStorage em versões anteriores:

Kotlin

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    appSearchSessionFuture.setFuture(
        PlatformStorage.createSearchSession(
            PlatformStorage.SearchContext.Builder(mContext, DATABASE_NAME)
               .build()
        )
    )
} else {
    if (usePlayServicesStorageBelowS) {
        appSearchSessionFuture.setFuture(
            PlayServicesStorage.createSearchSession(
                PlayServicesStorage.SearchContext.Builder(mContext, DATABASE_NAME)
                    .build()
            )
        )
    } else {
        appSearchSessionFuture.setFuture(
            LocalStorage.createSearchSession(
                LocalStorage.SearchContext.Builder(mContext, DATABASE_NAME)
                    .build()
            )
        )
    }
}

Java

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    mAppSearchSessionFuture.setFuture(PlatformStorage.createSearchSession(
            new PlatformStorage.SearchContext.Builder(mContext, DATABASE_NAME)
                    .build()));
} else {
    if (usePlayServicesStorageBelowS) {
        mAppSearchSessionFuture.setFuture(PlayServicesStorage.createSearchSession(
                new PlayServicesStorage.SearchContext.Builder(mContext, DATABASE_NAME)
                        .build()));
    } else {
        mAppSearchSessionFuture.setFuture(LocalStorage.createSearchSession(
                new LocalStorage.SearchContext.Builder(mContext, DATABASE_NAME)
                        .build()));
    }
}

Com o PlatformStorage e o PlayServicesStorage, seu aplicativo pode compartilhar dados com segurança com outros aplicativos para permitir que eles também pesquisem os dados do seu app. O compartilhamento de dados de aplicativos somente leitura é concedido usando um handshake de certificado para garantir que o outro aplicativo tenha permissão para ler os dados. Leia mais sobre essa API na documentação de setSchemaTypeVisibilityForPackage().

Além disso, com PlatformStorage, os dados indexados podem ser mostrados nas superfícies da interface do sistema. Os aplicativos podem desativar a exibição de alguns ou de todos os dados nas plataformas da interface do sistema. Leia mais sobre essa API na documentação de setSchemaTypeDisplayedBySystem().

Recursos LocalStorage (compatível com o Android 5.0 ou mais recente) PlatformStorage (compatível com o Android 12 ou versões mais recentes) PlayServicesStorage (compatível com o Android 5.0 ou mais recente)
Pesquisa de texto completo eficiente
Suporte para vários idiomas.
Tamanho binário reduzido
Compartilhamento de dados pessoais entre aplicativos
Capacidade de mostrar dados em plataformas da interface do sistema
É possível indexar um número ilimitado de documentos de qualquer tamanho
Operações mais rápidas sem latência adicional do Binder

Há outras vantagens e desvantagens a serem consideradas ao escolher entre LocalStorage e PlatformStorage. Como o PlatformStorage envolve as APIs do Jetpack no serviço do sistema AppSearch, o impacto no tamanho do APK é mínimo em comparação com o uso do LocalStorage. No entanto, isso também significa que as operações do AppSearch incorrem em latência adicional do binder ao chamar o serviço do sistema do AppSearch. Com o PlatformStorage, o AppSearch limita o número e o tamanho dos documentos que um aplicativo pode indexar para garantir um índice central eficiente. PlayServicesStorage também tem as mesmas limitações que PlatformStorage e só é compatível com dispositivos que têm o Google Play Services.

Verificar a disponibilidade dos recursos

Embora todas as opções de armazenamento compartilhem a mesma API, alguns recursos não estão disponíveis com todas as opções de armazenamento ou em todos os dispositivos. Na referência da API, a anotação @RequiresFeature identifica as APIs que exigem um desses recursos, e a classe Features define uma constante para cada recurso. Por exemplo, INDEXING_TYPE_RANGE requer Features.NUMERIC_SEARCH.

Para PlatformStorage, o suporte ao recurso depende da versão do Android do dispositivo. A partir do Android 13 (nível 33 da API), o suporte a recursos também depende das atualizações do sistema do Google Play que o dispositivo recebeu. Por exemplo, PlatformStorage é compatível com pesquisa numérica (Features.NUMERIC_SEARCH), pesquisa literal (Features.VERBATIM_SEARCH) e mesclagens (Features.JOIN_SPEC_AND_QUALIFIED_ID) apenas nos seguintes dispositivos:

  • Dispositivos com o Android 14 (nível 34 da API) ou versões mais recentes.
  • Dispositivos com o Android 13 (nível 33 da API) que receberam a atualização do sistema do Google Play necessária.

Esses recursos não são compatíveis com dispositivos que executam o Android 12 ou o Android 12L (níveis 31 e 32 da API).

O número máximo de propriedades indexadas em um documento também varia de acordo com o dispositivo e a opção de armazenamento. Para acessar o limite no ambiente de execução, chame getMaxIndexedProperties().

Não use apenas a versão do Android para determinar se um recurso é compatível. Antes de chamar uma API que requer um recurso, verifique se ele é compatível chamando isFeatureSupported() no objeto Features que AppSearchSession.getFeatures() retorna. Se um recurso obrigatório não for compatível, a operação vai falhar com uma exceção. Um exemplo de caso de falha é definir um esquema que inclui uma propriedade INDEXING_TYPE_RANGE em dispositivos que não são compatíveis com Features.NUMERIC_SEARCH.

O código a seguir verifica se um AppSearchSession aberto é compatível com a pesquisa numérica:

Kotlin

val features = session.features
if (features.isFeatureSupported(Features.NUMERIC_SEARCH)) {
    // Numeric search is supported. You can index properties with
    // LongPropertyConfig.INDEXING_TYPE_RANGE and enable numeric search with
    // SearchSpec.Builder.setNumericSearchEnabled(true).
} else {
    // Numeric search isn't supported. Set a schema without range-indexed
    // properties, or switch to a different storage option.
}

Java

Features features = session.getFeatures();
if (features.isFeatureSupported(Features.NUMERIC_SEARCH)) {
    // Numeric search is supported. You can index properties with
    // LongPropertyConfig.INDEXING_TYPE_RANGE and enable numeric search with
    // SearchSpec.Builder.setNumericSearchEnabled(true).
} else {
    // Numeric search isn't supported. Set a schema without range-indexed
    // properties, or switch to a different storage option.
}

Começar a usar o AppSearch

O exemplo nesta seção mostra como usar as APIs AppSearch para integração com um aplicativo hipotético de anotações.

Escrever uma classe de documento

A primeira etapa para integrar com o AppSearch é escrever uma classe de documento para descrever os dados que serão inseridos no banco de dados. Marque uma classe como uma classe de documento usando a anotação @Document.É possível usar instâncias da classe de documento para colocar documentos no banco de dados e recuperá-los.

O código a seguir define uma classe de documento Note com um campo anotado @Document.StringProperty para indexar o texto de um objeto Note.

Kotlin

@Document
public data class Note(

    // Required field for a document class. All documents MUST have a namespace.
    @Document.Namespace
    val namespace: String,

    // Required field for a document class. All documents MUST have an Id.
    @Document.Id
    val id: String,

    // Optional field for a document class, used to set the score of the
    // document. If this is not included in a document class, the score is set
    // to a default of 0.
    @Document.Score
    val score: Int,

    // Optional field for a document class, used to index a note's text for this
    // document class.
    @Document.StringProperty(indexingType = AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
    val text: String
)

Java

@Document
public class Note {

  // Required field for a document class. All documents MUST have a namespace.
  @Document.Namespace
  private final String namespace;

  // Required field for a document class. All documents MUST have an Id.
  @Document.Id
  private final String id;

  // Optional field for a document class, used to set the score of the
  // document. If this is not included in a document class, the score is set
  // to a default of 0.
  @Document.Score
  private final int score;

  // Optional field for a document class, used to index a note's text for this
  // document class.
  @Document.StringProperty(indexingType = StringPropertyConfig.INDEXING_TYPE_PREFIXES)
  private final String text;

  Note(@NonNull String id, @NonNull String namespace, int score, @NonNull String text) {
    this.id = Objects.requireNonNull(id);
    this.namespace = Objects.requireNonNull(namespace);
    this.score = score;
    this.text = Objects.requireNonNull(text);
  }

  @NonNull
  public String getNamespace() {
    return namespace;
  }

  @NonNull
  public String getId() {
    return id;
  }

  public int getScore() {
    return score;
  }

  @NonNull
  public String getText() {
     return text;
  }
}

Abrir um banco de dados

É preciso criar um banco de dados antes de trabalhar com documentos. O código a seguir cria um novo banco de dados com o nome notes_app e recebe um ListenableFuture para um AppSearchSession, que representa a conexão com o banco de dados e fornece as APIs para operações de banco de dados.

Kotlin

val context: Context = getApplicationContext()
val sessionFuture = LocalStorage.createSearchSession(
    LocalStorage.SearchContext.Builder(context, /*databaseName=*/"notes_app")
    .build()
)

Java

Context context = getApplicationContext();
ListenableFuture<AppSearchSession> sessionFuture = LocalStorage.createSearchSession(
       new LocalStorage.SearchContext.Builder(context, /*databaseName=*/ "notes_app")
               .build()
);

Definir um esquema

É preciso definir um esquema antes de colocar e recuperar documentos do banco de dados. O esquema do banco de dados consiste em diferentes tipos de dados estruturados, chamados de "tipos de esquema". O código a seguir define o esquema fornecendo a classe de documento como um tipo de esquema.

Kotlin

val setSchemaRequest = SetSchemaRequest.Builder().addDocumentClasses(Note::class.java)
    .build()
val setSchemaFuture = Futures.transformAsync(
    sessionFuture,
    { session ->
        session?.setSchema(setSchemaRequest)
    }, mExecutor
)

Java

SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder().addDocumentClasses(Note.class)
       .build();
ListenableFuture<SetSchemaResponse> setSchemaFuture =
       Futures.transformAsync(sessionFuture, session -> session.setSchema(setSchemaRequest), mExecutor);

Colocar um documento no banco de dados

Depois que um tipo de esquema é adicionado, é possível adicionar documentos desse tipo ao banco de dados. O código a seguir cria um documento do tipo de esquema Note usando o builder de classe de documento Note. Ele define o namespace do documento user1 para representar um usuário arbitrário deste exemplo. Em seguida, o documento é inserido no banco de dados, e um listener é anexado para processar o resultado da operação de inserção.

Kotlin

val note = Note(
    namespace="user1",
    id="noteId",
    score=10,
    text="Buy fresh fruit"
)

val putRequest = PutDocumentsRequest.Builder().addDocuments(note).build()
val putFuture = Futures.transformAsync(
    sessionFuture,
    { session ->
        session?.put(putRequest)
    }, mExecutor
)

Futures.addCallback(
    putFuture,
    object : FutureCallback<AppSearchBatchResult<String, Void>?> {
        override fun onSuccess(result: AppSearchBatchResult<String, Void>?) {

            // Gets map of successful results from Id to Void
            val successfulResults = result?.successes

            // Gets map of failed results from Id to AppSearchResult
            val failedResults = result?.failures
        }

        override fun onFailure(t: Throwable) {
            Log.e(TAG, "Failed to put documents.", t)
        }
    },
    mExecutor
)

Java

Note note = new Note(/*namespace=*/"user1", /*id=*/
                "noteId", /*score=*/ 10, /*text=*/ "Buy fresh fruit!");

PutDocumentsRequest putRequest = new PutDocumentsRequest.Builder().addDocuments(note)
       .build();
ListenableFuture<AppSearchBatchResult<String, Void>> putFuture =
       Futures.transformAsync(sessionFuture, session -> session.put(putRequest), mExecutor);

Futures.addCallback(putFuture, new FutureCallback<AppSearchBatchResult<String, Void>>() {
   @Override
   public void onSuccess(@Nullable AppSearchBatchResult<String, Void> result) {

     // Gets map of successful results from Id to Void
     Map<String, Void> successfulResults = result.getSuccesses();

     // Gets map of failed results from Id to AppSearchResult
     Map<String, AppSearchResult<Void>> failedResults = result.getFailures();
   }

   @Override
   public void onFailure(@NonNull Throwable t) {
      Log.e(TAG, "Failed to put documents.", t);
   }
}, mExecutor);

É possível pesquisar documentos indexados usando as operações de pesquisa abordadas nesta seção. O código a seguir realiza consultas para o termo "fruit" no banco de dados para documentos que pertencem ao namespace user1.

Kotlin

val searchSpec = SearchSpec.Builder()
    .addFilterNamespaces("user1")
    .build();

val searchFuture = Futures.transform(
    sessionFuture,
    { session ->
        session?.search("fruit", searchSpec)
    },
    mExecutor
)
Futures.addCallback(
    searchFuture,
    object : FutureCallback<SearchResults> {
        override fun onSuccess(searchResults: SearchResults?) {
            iterateSearchResults(searchResults)
        }

        override fun onFailure(t: Throwable?) {
            Log.e("TAG", "Failed to search notes in AppSearch.", t)
        }
    },
    mExecutor
)

Java

SearchSpec searchSpec = new SearchSpec.Builder()
       .addFilterNamespaces("user1")
       .build();

ListenableFuture<SearchResults> searchFuture =
       Futures.transform(sessionFuture, session -> session.search("fruit", searchSpec),
       mExecutor);

Futures.addCallback(searchFuture,
       new FutureCallback<SearchResults>() {
           @Override
           public void onSuccess(@Nullable SearchResults searchResults) {
               iterateSearchResults(searchResults);
           }

           @Override
           public void onFailure(@NonNull Throwable t) {
               Log.e(TAG, "Failed to search notes in AppSearch.", t);
           }
       }, mExecutor);

Iterar por SearchResults

As pesquisas retornam uma instância SearchResults, que dá acesso às páginas de objetos SearchResult. Cada SearchResult contém o GenericDocument correspondente, a forma geral de um documento para o qual todos os documentos são convertidos. O código a seguir recebe a primeira página de resultados da pesquisa e converte o resultado de volta em um documento Note.

Kotlin

Futures.transform(
    searchResults?.nextPage,
    { page: List<SearchResult>? ->
        // Gets GenericDocument from SearchResult.
        val genericDocument: GenericDocument = page!![0].genericDocument
        val schemaType = genericDocument.schemaType
        val note: Note? = try {
            if (schemaType == "Note") {
                // Converts GenericDocument object to Note object.
                genericDocument.toDocumentClass(Note::class.java)
            } else null
        } catch (e: AppSearchException) {
            Log.e(
                TAG,
                "Failed to convert GenericDocument to Note",
                e
            )
            null
        }
        note
    },
    mExecutor
)

Java

Futures.transform(searchResults.getNextPage(), page -> {
  // Gets GenericDocument from SearchResult.
  GenericDocument genericDocument = page.get(0).getGenericDocument();
  String schemaType = genericDocument.getSchemaType();

  Note note = null;

  if (schemaType.equals("Note")) {
    try {
      // Converts GenericDocument object to Note object.
      note = genericDocument.toDocumentClass(Note.class);
    } catch (AppSearchException e) {
      Log.e(TAG, "Failed to convert GenericDocument to Note", e);
    }
  }

  return note;
}, mExecutor);

Remover um documento

Quando o usuário exclui uma nota, o aplicativo exclui o documento Note correspondente do banco de dados. Isso garante que a nota não vai mais aparecer em consultas. O código a seguir faz uma solicitação explícita para remover o documento Note do banco de dados por ID.

Kotlin

val removeRequest = RemoveByDocumentIdRequest.Builder("user1")
    .addIds("noteId")
    .build()

val removeFuture = Futures.transformAsync(
    sessionFuture, { session ->
        session?.remove(removeRequest)
    },
    mExecutor
)

Java

RemoveByDocumentIdRequest removeRequest = new RemoveByDocumentIdRequest.Builder("user1")
       .addIds("noteId")
       .build();

ListenableFuture<AppSearchBatchResult<String, Void>> removeFuture =
       Futures.transformAsync(sessionFuture, session -> session.remove(removeRequest), mExecutor);

Persistir no disco

As atualizações em um banco de dados precisam ser mantidas periodicamente no disco chamando requestFlush(). O código a seguir chama requestFlush() com um listener para determinar se a chamada foi bem-sucedida.

Kotlin

val requestFlushFuture = Futures.transformAsync(
    sessionFuture,
    { session -> session?.requestFlush() }, mExecutor
)

Futures.addCallback(requestFlushFuture, object : FutureCallback<Void?> {
    override fun onSuccess(result: Void?) {
        // Success! Database updates have been persisted to disk.
    }

    override fun onFailure(t: Throwable) {
        Log.e(TAG, "Failed to flush database updates.", t)
    }
}, mExecutor)

Java

ListenableFuture<Void> requestFlushFuture = Futures.transformAsync(sessionFuture,
        session -> session.requestFlush(), mExecutor);

Futures.addCallback(requestFlushFuture, new FutureCallback<Void>() {
    @Override
    public void onSuccess(@Nullable Void result) {
        // Success! Database updates have been persisted to disk.
    }

    @Override
    public void onFailure(@NonNull Throwable t) {
        Log.e(TAG, "Failed to flush database updates.", t);
    }
}, mExecutor);

Fechar uma sessão

Um AppSearchSession precisa ser fechado quando um aplicativo não chamar mais nenhuma operação de banco de dados. O código a seguir fecha a sessão do AppSearch que foi aberta anteriormente e mantém todas as atualizações no disco.

Kotlin

val closeFuture = Futures.transform<AppSearchSession, Unit>(sessionFuture,
    { session ->
        session?.close()
        Unit
    }, mExecutor
)

Java

ListenableFuture<Void> closeFuture = Futures.transform(sessionFuture, session -> {
   session.close();
   return null;
}, mExecutor);

Outros recursos

Para saber mais sobre o AppSearch, consulte os recursos abaixo:

Amostras

Enviar feedback

Envie comentários e ideias usando os recursos abaixo:

Issue tracker

Informe os bugs para que possamos corrigi-los.