Ver mensagens sem resposta | Ver tópicos ativos Hoje é 18 Ago 2017, 00:26



Responder Tópico  [ 11 Mensagens ] 
 [Artigo] Criar um aplicativo com autoupdate 
Autor Mensagem
Google employee
Google employee
Avatar de usuário

Data de registro: 05 Abr 2011, 04:47
Mensagens: 1968
Localização: Rio de Janeiro
Mensagem [Artigo] Criar um aplicativo com autoupdate
[Artigo] Criar um aplicativo com autoupdate


Muitos de nós vamos acabar precisando desenvolver um aplicativo comercial que não poderemos publicá-lo no Google Play. Então todos se perguntam: Como vou fazer o meu aplicativo se atualizar?

Bem, tendo como premissa esse problema relatado, vamos, primeiramente, comentar as restrições.

Vai ser possível atualizar o meu aplicativo sem que tenha a interação do usuário??

A resposta é: Depende!!

Uma instalação em “background”, onde não precisa de uma interação do usuário, é relativamente simples e completamente possível, mas um pouco fora do do nosso domínio. Quem quiser ler mais sobre como viabilizar essa funcionalidade, pode ver no link abaixo de um amigo meu, Paulo Nonaka. Ajudou muito quando eu estava desenvolvendo o autoupdate.

http://paulononaka.wordpress.com/2011/0 ... n-android/

Agora vamos comentar as restrições do nosso exemplo.

1 - Nesse exemplo, a instalação da atualização é dependente de uma confirmação do usuário.

2 - A instalação do apk baixado só vai funcionar se o aplicativo instalado for assinado com a mesma keystore que o apk baixado. Ambos devem ser assinados e não pode ser com uma keystore de debug.

3 - Não vou entrar em detalhes do consumo de um WS, pois já sabemos como isso funciona. viewtopic.php?f=7&t=17465

O que vamos precisa?

1 - Um servidor web (pode ser um simples apache ou um tomcat);
2 - Ambiente configurado (Eclipse, SDK e ADT);

A ideia funciona da seguinte forma:

O aplicativo fará uma requisição para o servidor para saber se existe um apk novo para baixar.

Anexo:
1 (Custom).png


Se existir e a versão do servidor for maior que a instalada, o aplicativo irá baixar o apk do servidor,

Anexo:
2 (Custom).png


verificará a integridade do arquivo e, com um click na notificação,

Anexo:
3 (Custom).png


iniciará a instalação. Nesse ponto o usuário irá confirmar a instalação.

Anexo:
4 (Custom).png

Anexo:
5 (Custom).png

Anexo:
6 (Custom).png


Assim que terminar a instalação, clique em continuar que você terá uma nova versão instalada.

Anexo:
7 (Custom).png


Explicando em códigos, seria assim:

As versões estão sendo lidas diretamente do manifest.

Classe MainActivity.java

@Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        TextView versionCodeTxt = (TextView) findViewById(R.id.version_code_txt);
        TextView versionNameTxt = (TextView) findViewById(R.id.version_name_txt);
        Button updateBtn = (Button) findViewById(R.id.button_update);
       
        try
        {
            PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
           
            versionCodeTxt.setText(""+pInfo.versionCode);
            versionNameTxt.setText(pInfo.versionName);
           
        }
        catch (NameNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
       
        updateBtn.setOnClickListener(new View.OnClickListener()
        {
           
            @Override
            public void onClick(View v)
            {
                new UpdateAsyncTask(MainActivity.this).execute();
            }
        });
       
    }
 


Clicando no botão de update, será iniciado a função de verificação de atualização.

Classe UpdateAsyncTask.java

public class UpdateAsyncTask extends AsyncTask<String, String, Boolean>
{
   
    private static final String TAG = "UpdateAsyncTask";
   
    private Context context;
   
    private String pathSD;
   
    private String destino;
   
    private boolean hasExternalStorage;
   
    private Update update;
   
    public UpdateAsyncTask(Context context)
    {
        this.context = context;
    }
   
    @Override
    protected void onPreExecute()
    {
       
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
        {
            pathSD = Environment.getExternalStorageDirectory().toString();
            destino = pathSD + "/AutoUpdate";
            hasExternalStorage = true;
        }
        else
        {
            hasExternalStorage = false;
        }
       
    }
   
    @Override
    protected Boolean doInBackground(String... arg0)
    {
        if (!hasExternalStorage)
        {
           
            Log.i(TAG, "Não tem ExternalStorage. Não vai buscar atualização!");
           
            return false;
        }
       
        update = new UpdateApp().getUpdate();
       
        if (update == null)
        {
           
            Log.i(TAG, "Objeto update é null! Retornando false e não fará o update!");
           
            return false;
        }
       
        Log.i(TAG, "Objeto update não é null, vai iniciar atualização!");
       
        return true;
    }
   
    @Override
    protected void onPostExecute(Boolean result)
    {
       
        int versionCode;
        try
        {
            versionCode = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
           
            if (result && (update.getVersion() > versionCode))
            {
               
                Log.i(TAG, "result:" + result + " | version local: " + versionCode + " | versão remota: " + update.getVersion());
               
                File apkFileLocal = new File(destino, update.getNameApk());
                String md5sumLocal = "";
               
                if (apkFileLocal.exists())
                {
                    md5sumLocal = Util.getHash(apkFileLocal);
                }
               
                if (update.getSha1sum().equalsIgnoreCase(md5sumLocal))
                {
                   
                    Log.i(TAG, "Encontrou um apk no sdcard com o mesmo hash do apk remoto, vai atualizar com o arquivo local!");
                   
                    Intent i = new Intent(context, InstallActivity.class);
                   
                    i.putExtra("dstFile", apkFileLocal.getAbsolutePath());
                    context.startActivity(i);
                   
                    return;
                   
                }
               
                // iniciar async para realizar o download
                new DownloadUpdateAsyncTask(context).execute(update.getPath(), update.getSha1sum(), destino);
               
            }
            else
            {
               
                Log.i(TAG, "Não fará a atualização! Result é false e/ou a versão remota não é maior que a local!");
               
            }
           
        }
        catch (NameNotFoundException e)
        {
            Log.e(TAG, "onPostExecute - NameNotFoundException", e);
        }
        catch (Exception e)
        {
            Log.e(TAG, "onPostExecute", e);
        }
       
    }
}
 


Nesse ponto será baixado objeto JSON com as informações de update. (Mais adiante explicarei esse processo!)

Classe UpdateAsyncTask.java:60

update = new UpdateApp().getUpdate();
 


Se a versão que veio no JSON for maior que a instalada,

Classe UpdateAsyncTask.java:84

if (result && (update.getVersion() > versionCode)){...}
 


o processo de download, verificação de integridade e notificação será iniciado.

Classe UpdateAsyncTask.java:112

new DownloadUpdateAsyncTask(context).execute(update.getPath(), update.getSha1sum(), destino);
 


Com a notificação de progresso lançada, o download começa.

Classe DownloadUpdateAsyncTask.java

      InputStream input = new BufferedInputStream(urlOrigem.openStream(), 8192);
      OutputStream output = new FileOutputStream(fileDestino);
                   
      byte data[] = new byte[1024];
      int countRead;
      int progress = 0;
      int percent = 0;
                   
      while ((countRead = input.read(data)) != -1)
      {
          progress += countRead;
          percent = (int) progress * 100 / lenghtOfFile;
          notification.contentView.setProgressBar(R.id.notification_progress, lenghtOfFile, progress, false);
          notification.contentView.setTextViewText(R.id.notification_porcentagem, percent + "%");
          notificationManager.notify(NOTIFICATION_ID, notification);
          output.write(data, 0, countRead);
      }
                   
     output.flush();
     output.close();
     input.close();
                   
     Log.i(TAG, "Download do arquivo completo! ");
 


Terminando o download, será feita a verificação de integridade.

Classe DownloadUpdateAsyncTask.java:129

String sha1sumLocal = Util.getHash(fileDestino);
Log.i(TAG, "sha1sum local: " + sha1sumLocal + " | sha1sum remota: " + sha1sum);
if (sha1sum.equalsIgnoreCase(sha1sumLocal)){...}
 


Esse é o código para gerar um hash SHA-1 de um arquivo.


public static String getHash(File file)
{
    StringBuffer sb = new StringBuffer();
   
    try
    {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        FileInputStream fis = new FileInputStream(file);
        byte[] dataBytes = new byte[1024];
       
        int nread = 0;
       
        while ((nread = fis.read(dataBytes)) != -1)
        {
            md.update(dataBytes, 0, nread);
        }
       
        byte[] mdbytes = md.digest();
       
        for (int i = 0; i < mdbytes.length; i++)
        {
            sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
    return sb.toString();
}
 


OBS.: Se quiser usar MD5, basta mudar onde está “SHA-1” por “MD5”. Simples assim!! =D

Com o final do download e a verificação de integridade OK, uma nova notificação será enviada para ser iniciado a instalação.

Classe DownloadUpdateAsyncTask.java:138

Util.sendNotificationUpdate(context,
    android.R.drawable.stat_sys_download_done,
    context.getString(R.string.app_name),
    context.getString(R.string.download_notification_install_tittle),
    context.getString(R.string.download_notification_install_message),
    NOTIFICATION_ID,
    true,
    fileDestino.getAbsolutePath());
 


Ao clicar na notificação, uma activity será chamada para iniciar a instalação.

Classe InstallActivity.java

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
   
    File dstFile = new File(getIntent().getExtras().getString("dstFile"));
   
    if (dstFile.exists())
    {
       
        Log.i(TAG, "Instalando o novo apk. Parametros: Path Destino[" + dstFile.getAbsolutePath() + "]");
       
        Intent intentInstall = new Intent(Intent.ACTION_VIEW);
        intentInstall.setDataAndType(Uri.fromFile(dstFile), "application/vnd.android.package-archive");
        startActivity(intentInstall);
       
    }
   
    finish();
   
}
 


No final da instalação teremos a nova versão instalada e pronta para usar!!

Como funciona a parte do servidor??

Para esse exemplo não foi desenvolvido um WS e nem precisa, já que a resposta sempre será um simples JSON. Porém, se quiser, podem desenvolver um WS para gerenciar essas informações, vocês já sabem como fazer: viewtopic.php?f=7&t=17389

Nesse caso eu criei apenas um semples arquivo:

update.json

{
  "version": 2,
  "sha1sum": "9cf57e73719da377dda8adad0117a8243eaf0205",
  "path": "http://192.168.0.104:8080/AutoUpdate.apk"
}
 


Se você escolheu usar um apache, basta colocar o arquivo “update.json” dentro da raiz do servidor, onde o acesso será “http://localhost/update.json”.

Se você escolheu usar o tomcat, basta colocar o arquivo dentro do diretório ROOT. Uma opção também é o uso de um JSP.

update.jsp

<%@ page language="java" contentType="application/json; charset=UTF-8" pageEncoding="UTF-8"%>
{
  "version": 2,
  "sha1sum": "9cf57e73719da377dda8adad0117a8243eaf0205",
  "path": "http://192.168.0.104:8080/AutoUpdate.apk"
}
 


Agora, explicando o conteúdo desse JSON:

O parâmetro version é o número do “versionCode” que foi colocado no manifest, geralmente um número maior que o aplicativo que foi instalado.


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="br.com.autoupdate"
    android:versionCode="1"
    android:versionName="1.0.0" >

 


O parâmetro hash é bem conhecido do pessoal que usa linux e serve para verificar a integridade de arquivos. Normalmente usam MD5, mas eu gosto do SHA-1. =D

Para quem usa linux esse processo é bem simples. Basta digitar no terminal:
$ sha1sum AutoUpdate.apk


Para que os usuários de windows não reclamem, eu criei um simples projeto java, usando o método “getHash()” ali de cima, chamado HashGenerator, que está em anexo nesse post, assim como todo código fonte do projeto.


public static void main(String[] args) {
    // TODO Auto-generated method stub
    File f = new File("c:\\AutoUpdate.apk");

    System.out.println(getHash(f));
}
 


Basta colocar o caminho do seu apk ali e executar a classe como “Java Project”. Ele vai mostrar no console o hash do arquivo.

O parâmetro path é o caminho do servidor web onde está o apk. No meu caso, coloquei no mesmo lugar onde está o arquivo “update.json”.

Para organizar o teste, o processo funciona assim:

1 - Faça um build do projeto com o “versionCode” 1 e o “versionName” 1.0.0;
2 - Instale essa versão no aparelho (coloque o apk do sdcard e instale usando o ASTRO, por exmplo);
3 - Faça um build do projeto com o “versionCode” 2 e o “versionName” 1.0.1;
4 - Configure o “update.json” com os dados da versão mais nova (versionCode e hash);
5 - Coloque o “update.json” e o .apk da versão mais nova no servidor (apache ou tomcat);
6 - Execute o aplicativo no aparelho e clique no botão de update e a mágica acontecerá.

PS.: Todos os arquivos usados no projeto (update.json, update.jsp, keystore e senha) estão no diretório "helper-files", dentro do projeto.
PS2.: Se tiver algum erro, por favor, me avise para que a correção seja feita.


Você não tem permissões suficientes para ver os arquivos anexados nesta mensagem.

_________________
Alexandre Antunes
@a3b @bemobi
Moto X

App@Bemobi: Apps Club - Loja de Apps baseada em assinatura que dá direito a usar ~400 Apps.

Campanha: Facilite sua vida e a dos outros usuários.
Objetivo: Vamos colocar a tag [RESOLVIDO] no título do tópico quando o problema for resolvido.


Use a ferramenta de busca do fórum e não espere tanto para que a sua dúvida seja respondida!

Quer trabalhar comigo??


25 Abr 2013, 03:32
Perfil WWW
Google employee
Google employee

Data de registro: 17 Jul 2011, 11:55
Mensagens: 2657
Localização: São Paulo
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Muito bom, Antunes!

Bem didático.

Parabéns!

_________________
Abraços
___________
Novo App: CalcMat - Calculadora de materiais para concreto
Blog: Agorandroid - sobre programação Android
Twitter: @Agorandroid
___________
Campanha: Facilite sua vida e a dos outros usuários
Cuide do ciclo de vida do seu tópico:
no onCreate(): seja claro, se necessário poste o código e as mensagens de erro.
no onClick(): responda às sugestões.
no onStop(): evite "ninguém?", "alguém?", etc. Procure acrescentar alguma nova informação.
no onDestroy(): resolvido o assunto, poste imediatamente a solução, e, coloque no título do primeiro post [Resolvido].


25 Abr 2013, 09:48
Perfil
Google employee
Google employee
Avatar de usuário

Data de registro: 05 Abr 2011, 04:47
Mensagens: 1968
Localização: Rio de Janeiro
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
A H Gusukuma escreveu:
Muito bom, Antunes!

Bem didático.

Parabéns!


Valeu, meu amigo!! Sempre tento melhorar!! =D

Abraços!

_________________
Alexandre Antunes
@a3b @bemobi
Moto X

App@Bemobi: Apps Club - Loja de Apps baseada em assinatura que dá direito a usar ~400 Apps.

Campanha: Facilite sua vida e a dos outros usuários.
Objetivo: Vamos colocar a tag [RESOLVIDO] no título do tópico quando o problema for resolvido.


Use a ferramenta de busca do fórum e não espere tanto para que a sua dúvida seja respondida!

Quer trabalhar comigo??


25 Abr 2013, 12:41
Perfil WWW
What is a Activity?
What is a Activity?

Data de registro: 16 Mai 2012, 12:05
Mensagens: 13
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Muito bom, parabéns.

Só não entendi essa parte

2 - A instalação do apk baixado só vai funcionar se o aplicativo instalado for assinado com a mesma keystore que o apk baixado. Ambos devem ser assinados e não pode ser com uma keystore de debug.


Como funciona isso? onde gero essa keystore e onde coloco ela?


07 Mai 2013, 20:17
Perfil
Google employee
Google employee
Avatar de usuário

Data de registro: 05 Abr 2011, 04:47
Mensagens: 1968
Localização: Rio de Janeiro
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Dê uma lida nesses links que vc vai entender.

http://developer.android.com/tools/publ ... gning.html
http://escoladeandroid.blogspot.com.br/ ... droid.html
http://www.klebermota.eti.br/2010/09/29 ... o-publico/
http://zarelli.wordpress.com/2012/10/02 ... lications/

Abraços!

_________________
Alexandre Antunes
@a3b @bemobi
Moto X

App@Bemobi: Apps Club - Loja de Apps baseada em assinatura que dá direito a usar ~400 Apps.

Campanha: Facilite sua vida e a dos outros usuários.
Objetivo: Vamos colocar a tag [RESOLVIDO] no título do tópico quando o problema for resolvido.


Use a ferramenta de busca do fórum e não espere tanto para que a sua dúvida seja respondida!

Quer trabalhar comigo??


08 Mai 2013, 02:07
Perfil WWW
Application Life Cycle
Application Life Cycle

Data de registro: 06 Dez 2011, 18:47
Mensagens: 257
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Ola antunes

vlw mesmo cara pelo poste estava procurando
por algo do tipo ajudou de mais cara vlw /uu


06 Jun 2013, 16:38
Perfil
Android application
Android application

Data de registro: 06 Jan 2011, 09:31
Mensagens: 59
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Muito bom! Parabéns !


19 Jun 2015, 13:36
Perfil
Android application
Android application

Data de registro: 28 Abr 2014, 16:57
Mensagens: 89
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Fala Antunes,

Primeiramente gostaria de parabenizar pelo post, realmente mto bom.

Implementei conforme seu artigo e tudo está funcionando certinho, porem identifiquei que o celular fica extremamente lento, praticamente inoperante (travado mesmo) quando inicia o donwload do apk.

Você chegou a perceber isso?
Os downloads e atualizações estão sendo executadas normalmente, apenas o do meu app que está gerando esse comportamento.

Do seu artigo para o meu app, o que mudou foi o momento da execução. Eu coloquei a chamada do método async de verificação, no onCreate da Activity principal e não na chamada do botão.

Alguma ideia? Não é logado nenhum erro.


20 Jul 2015, 17:25
Perfil
Android application
Android application

Data de registro: 28 Abr 2014, 16:57
Mensagens: 89
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Pessoal bom dia,

Debugando o código identifiquei que o que está causando lentidão é o notificationManager.notify(NOTIFICATION_ID, notification); do while enquanto faz o download do arquivo.


                    while ((countRead = input.read(data)) != -1)
                    {
                        progress += countRead;
                        percent = (int) progress * 100 / lenghtOfFile;
                        notification.contentView.setProgressBar(R.id.notification_progress, lenghtOfFile, progress, false);
                        notification.contentView.setTextViewText(R.id.notification_porcentagem, percent + "%");
                        notificationManager.notify(NOTIFICATION_ID, notification); //Linha que causa lentidão
                        output.write(data, 0, countRead);
                        Thread.sleep(100); //essa linha inclui para verificar se o problema de lentidão diminuia


                    }
 


Tentei colocar um thread.sleep(100), mas depois de um tempo, é possivel perceber uma lentidão, sem contar que o download demora mais do q realmente deveria.

Alguem consegue me ajudar para minimizar esse problema?

Muito obrigado,


22 Jul 2015, 11:58
Perfil
Google employee
Google employee
Avatar de usuário

Data de registro: 05 Abr 2011, 04:47
Mensagens: 1968
Localização: Rio de Janeiro
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Igor,

Desculpa pela demora.

Como esse exemplo é bem antigo e o android já evoluiu bastante, tem formas melhores de fazer esse download com notificação de progresso.

http://developer.android.com/training/n ... gress.html

Abraços!

_________________
Alexandre Antunes
@a3b @bemobi
Moto X

App@Bemobi: Apps Club - Loja de Apps baseada em assinatura que dá direito a usar ~400 Apps.

Campanha: Facilite sua vida e a dos outros usuários.
Objetivo: Vamos colocar a tag [RESOLVIDO] no título do tópico quando o problema for resolvido.


Use a ferramenta de busca do fórum e não espere tanto para que a sua dúvida seja respondida!

Quer trabalhar comigo??


29 Set 2015, 15:27
Perfil WWW
Android application
Android application

Data de registro: 13 Mar 2013, 22:27
Mensagens: 80
Mensagem Re: [Artigo] Criar um aplicativo com autoupdate
Olá,

pesquisando no Google cheguei até esse tópico.

Não estou conseguindo seguir as dicas.
O que houve com a classe
private Update update


16 Mar 2017, 18:49
Perfil
Mostrar mensagens anteriores:  Organizar por  
Responder Tópico   [ 11 Mensagens ] 

Quem está online

Usuários vendo este fórum: ademilson, adolfo, albinoneto, alex, alexandrefett, Alexandrercarvalho, alexsander.miranda, Alexsandro Tirloni, am2net, Anderson MarkZ, andi, andrigo, antoniodourado, arkanjo, arnaldo.miranda, betofigu, bomcabelo, brcamp, brenda, BrunoO, cabelo, Carlos Almeida, carlos rodrigues, cassianotartari, Catapan, cehills, Chanceler Supremo Finis Valorum, claudio, comolatti, compto, Cr-Informática, Darkluna, debal, dirceuconte, dnakamashi, edsonel, eduzortea, elisa, epsilva, erikopa, espinhara.net, euguns, eXagon, Fernando Cardia, Francisco_Geraldo, frederico.affini, furlanrapha, gabrielpg, gamito, garretereis, geco, gedoor, genildof, Geovanne Duarte, glmsistemas, Gomes, Google Android, grandebaro, guiba_picolino, gustavo, henrikesilva1, henrique.cardoso, icarodavi, ICCrawler - ICjobs, ice, Igor_M, IgorBrum, infonlinebr, interservic, JairoCN, Jalerson, Jar Jar Binks, jhonguitar, Jless@, JMurray, jonasminas, Jorge Machin, José Guilherme, juliancesar, juliherms, juniorsk8, kennedyximenes, lafamac, lanlan, leandroviana, Lelinho, leofernandesmo, lucasB, lucasmadeira, LucasNascimento, lucianoedipo, Luke Skywalker, Lúcio Zanette, Maiquell, marciosoliveira, marcosandreao, marcosrogel, marinho5, Marini, masf_33, mauriciobreide, mauriciomag, Meticore, miguel, mikasjau, mmfsndroid, neimarguerra, Nice, nsansilva, onaiggac, otpor, pedro, pgsnit, phfmendes, piantino, pumadeejay, Rafael, rafaelmonoh, rafaelvital, raulcca, renegheller, Ricardo Chikasawa, rogerio, rosano, rsl_master, samuel.cavanieri, shadow, shibutani, sjta, snonca, taluna, thanaptos, Thelemita, thiagotomais, thiaguim, tiago, tiagofalcao, tnarnold, unnamedd, vaniuz, vicfalmac, viniciusllima, wilson.slima, WiseNut, zeantonio e 2 visitantes


Você não pode criar novos tópicos neste fórum
Você não pode responder tópicos neste fórum
Você não pode editar suas mensagens neste fórum
Você não pode excluir suas mensagens neste fórum
Você não pode enviar anexos neste fórum

Procurar por:

© 2007 - 2016 Portal Android - Comunidade de Desenvolvedores Android

Estamos no Linkedin    Siga-nos no twitter


Powered by phpBB - Hospedado por Bemobi