웹서버를 통한 파일 자동 업데이트

프로그래밍/Android (Java)|2017. 9. 27. 09:11
반응형

아래 내용은 안드로이드 6.x 까지만 적용되는 예제입니다.

안드로이드 7.x 부터는 다른 예제를 찾아보세요! ㅜ.ㅜ

참고 : http://duongame.tistory.com/263 (Android 7.0 APK 파일 설치)

 

 

 

웹서버 설정

 

1. 파일명 : application.properties

 

#messages for udpate
versionCode=9
versionName=1.1
fileName=TraMainActivity.apk
message=\ufffd\ufffd\u022d\ufffd\ufffd\ufffd\ufffd\u05f9\u6e6e\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd \ufffd\u05b5\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\u03ff\ufffd\ufffd\ufffd\ufffd\u03f4\ufffd. \ufffd\ufffd\u022d\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\u05b5\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0534\u03f4\ufffd.
title=\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u01ae\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\u0574\u03f4\ufffd.

 

message 에서 줄바꿈 하려면 한줄로 적되, 개행할 곳에 \r\n 를 사용하면 된다.

 

2. 원문파일명 : application.properties.source.txt

 

#messages for udpate
versionCode=2
versionName=1.12
fileName=TraMainActivity.apk
message=통화견적및방문견적을 볼수 있도록 수정하였습니다. 정화조 도면을 볼수있도록 수정중입니다.
title=업데이트를 시작합니다. 

 

기존에 설치된 app 의 AndroidManifest.xml 과 웹서버에 있는 application.properties 의 versionCode 를 비교해서

웹서버 versionCode 값이 더 클 경우 앱에서 팝업으로 알려주게 된다. (최대값이 

2147483647

 이다.)

versionName 은 필요시 사용자에게 보여주기 위한 번호일 뿐이다. (비교 안함)

 

AndroidManifest.xml 파일의 옵션값이 먹히지 않을 경우 (AndroidStudio를 사용할 경우) build.gradle (Module: app) 내의 옵션이 우선되기 때문이다.

 

3. 파일 형식 변환

 

원문 파일을 jdk 폴더 내의 bin폴더에 있는 native2ascii.exe 명령으로 바꾼것이다.

 

native2ascii.exe application.properties.source.txt application.properties

 

[사용 방법]

-- versionCode 와 앱의 manifest 파일의 versionCode와 일치시키면 된다. versionName도 마찬가지로 일치시키면 된다.

-- fileName은 앱의 파일 이름을 적어주면된다.

-- message에는 업데이트 된 내용을 적어주면 된다.

-- title은 다운로드 받는 progress dialog의 제목이다.

 

설정 파일 작성을 완료 했다면 설정 파일과 앱파일을 서버상에 같은 폴더에 업로드 한다.

아래 소스에서는 웹서버의 /download/ 디렉토리에 업로드 하도록 하였다.

 

 

 

필수 권한

 

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.INTERNET" />

 

 

 

AutoupdateActivity.java

package com.neulwon.study;


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;




public class AutoupdateActivity extends Activity {


    //새버전의 프로그램이 존재하는지 여부
    private int newver=0;
    private int oldver=0;
    private String strVer;
    
    private String fileName;
    private CharSequence updateMessage;
    private String updateTitle; 
    
    // Progress Dialog 
    private TextView textVersion;
    
    private TextView progressTitle;
    private TextView progressText;
    private ProgressBar progressBar;
    private RelativeLayout downloadUpdateLayout;
    
    //확인하고 싶은 패키지명 String
    private static final String CHECK_PACKAGE_NAME="com.neulwon.study";


    public static final String MSG_TAG = "AutoupdateActivity";
    
    private static final String IP_ADDRESS = "sysdocu.tistory.com";


    // Update Url
    private static final String APPLICATION_PROPERTIES_URL = "http://" + IP_ADDRESS + "/download/application.properties";
    private static final String APPLICATION_DOWNLOAD_URL = "http://" + IP_ADDRESS + "/download/";


    public static final int MESSAGE_DOWNLOAD_STARTING = 3;
    public static final int MESSAGE_DOWNLOAD_PROGRESS = 4;
    public static final int MESSAGE_DOWNLOAD_COMPLETE = 5; 
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_autoupdate);
     
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        progressText = (TextView) findViewById(R.id.progressText);
        progressTitle = (TextView) findViewById(R.id.progressTitle);
        downloadUpdateLayout = (RelativeLayout) findViewById(R.id.layoutDownloadUpdate);
        
        textVersion = (TextView) findViewById(R.id.textVersion );
        
// 앱실행시 기존 다운로드 파일 삭제
String del_localpath = getDownloadDirectory();
File del_file = new File(del_localpath + "/update-debug.apk");
        del_file.delete();


        // 업데이트 체크
        checkForUpdate();
        
        PackageInfo oldversionInfo;
        try {
            oldversionInfo = getPackageManager().getPackageInfo(CHECK_PACKAGE_NAME,PackageManager.GET_META_DATA);       
            strVer = oldversionInfo.versionName;
        } catch (NameNotFoundException e1) {
            e1.printStackTrace();
        }        
           
        //textVersion.setText( "버전 " + strVer );
textVersion.setText( "IPTV 업데이트" );
    }
   
    //앱 업데이트 검사
    public void checkForUpdate() {           
      new Thread(new Runnable(){
          public void run(){
              Looper.prepare();   
              
              // 서버상의 Properties 얻기
              Properties updateProperties = queryForProperty(APPLICATION_PROPERTIES_URL);
              String verName;
              
              if ( updateProperties != null && updateProperties.containsKey("versionCode") ) {
                
                  int newversion = Integer.parseInt(updateProperties.getProperty("versionCode"));
                  //int installedVersion = TetherApplication.this.getVersionNumber();
                  fileName = updateProperties.getProperty("fileName", "");
                  updateMessage = updateProperties.getProperty("message", "");
                  updateTitle = updateProperties.getProperty("title", "업데이트가 가능합니다.");
                  verName = updateProperties.getProperty("versionName", "");
                  
                  newver = newversion;
                  
                  try {
                      //설치된 앱 정보 얻기
                      PackageInfo oldversionInfo = getPackageManager().getPackageInfo(CHECK_PACKAGE_NAME,PackageManager.GET_META_DATA);
                      oldver = Integer.valueOf(oldversionInfo.versionCode);
                      
                      //다운로드 폴더 얻어오기
                      String localpath = getDownloadDirectory();


                      Log.d("앱버전","앱버전 : " + oldver );
                      Log.d("서버상","서버에 있는 파일 버전 : " + newver );
                      Log.d("받아온경로","받아온 경로: " + localpath);


                      if ( oldver < newver ) {    //파일 버전비교                            
                          openUpdateDialog( APPLICATION_DOWNLOAD_URL + fileName , fileName , updateMessage , updateTitle,localpath);                
                      } else {
                          Log.d(MSG_TAG, " 최신버전입니다. 버전 : " + verName );
                      }
                      //textVersion.setText(" Ver : " + verName);
                  } catch ( Exception e ) {
                      e.printStackTrace();
                  }
              }
              Looper.loop();
          }
      }).start();
    }
   
    //다운로드 폴더 얻기
    private String getDownloadDirectory(){
        String sdcardPath="";
        String downloadpath = "";
        if ( isUsableSDCard(true)){    //외장메모리 사용가능할 경우
            sdcardPath = Environment.getExternalStorageDirectory().getPath();
            //downloadpath = sdcardPath + "/download/";
downloadpath = sdcardPath + "/data/local/tmp/";
        } else {                       //내장메모리 위치
            File file = Environment.getRootDirectory();
            sdcardPath = file.getAbsolutePath();
            //downloadpath = sdcardPath + "/download";
downloadpath = sdcardPath + "/data/local/tmp";
        }
    return downloadpath;
    }
    
    //외장메모리 사용 가능여부 확인   
    private boolean isUsableSDCard(boolean requireWriteAccess) {
        String state = Environment.getExternalStorageState();
        if ( Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        } else if( !requireWriteAccess &&
               Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {
           return true;
        }
        return false;
    }    
    
    
    //서버의 application.properties 파일 읽어오기
    public Properties queryForProperty(String url) {
        Properties properties = null;
        
        HttpClient client  = new DefaultHttpClient();
        HttpGet request = new HttpGet(String.format(url));
       
        try {
            HttpResponse response = client.execute(request);            
            StatusLine status = response.getStatusLine();
            
            if ( status.getStatusCode() == 200 ) {
                HttpEntity entity = response.getEntity();
                properties = new Properties();
                properties.load(entity.getContent());
            }
        } catch ( IOException e ) {
            Log.d("오류","Can't get property '" + url + "'.");
        }
        return properties;
    }
    
    //업데이트 할 것인지 확인
    public void openUpdateDialog(final String downloadFileUrl, final String fileName,
          final CharSequence message, final String updateTitle,final String localpath4down) {
      
        LayoutInflater li = LayoutInflater.from(this);
        Builder dialog;
        View view;
      
        view = li.inflate(R.layout.updateview, null);
        TextView messageView = (TextView) view.findViewById(R.id.updateMessage);
        TextView updateNowText = (TextView) view.findViewById(R.id.updateNowText);
      
        if (fileName.length() == 0)  // No filename, hide 'download now?' string              
            updateNowText.setVisibility(View.GONE);
       
        messageView.setText(message);                 
      
        dialog = new AlertDialog.Builder(AutoupdateActivity.this)
                               .setTitle(updateTitle)
                               .setView(view);
           
        if (fileName.length() > 0) {
            //dialog.setNeutralButton("취소", new DialogInterface.OnClickListener() {
            //    public void onClick(DialogInterface dialog, int whichButton) {
            //        Log.d(MSG_TAG, "No pressed");
            //    }
            //});
            dialog.setNegativeButton("확인", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    Log.d(MSG_TAG, "Yes pressed");
                    Log.d("경로명","경로명 : " + downloadFileUrl + " 파일명 : " + fileName );
                    downloadUpdate(downloadFileUrl, fileName, localpath4down);
                }
            });          
        } else {              
            dialog.setNeutralButton("확인",new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    Log.d(MSG_TAG, "Ok pressed");
                }
            });
        }
        dialog.show();
//dialog.setCancelable(false); // back 키 무효. 동작 안함
        //dialog.setCanceledOnTouchOutside(false); // dialog 바깥 클릭 무효. 동작 안함

    
    //다운로드 받은 앱을 설치, 이전 실행 앱 종료
    public void downloadUpdate(final String downloadFileUrl, final String fileName,final String localpath) {
        new Thread(new Runnable(){
            public void run(){
                Message msg = Message.obtain();
                msg.what = MESSAGE_DOWNLOAD_STARTING;
                msg.obj = localpath + fileName ;
              
                File apkFile = new File ( localpath + fileName );
                Log.d("downloadUpdate","경로1:"+ localpath + fileName  );
                viewUpdateHandler.sendMessage(msg);
              
                downloadUpdateFile(downloadFileUrl, fileName, localpath);              
              
                //다운로드 받은 패키지를 인스톨한다.
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);


               /*
               * 안드로이드 프로세스는  단지 finish() 만 호출 하면 죽지 않는다.
               * 만약 프로세스를 강제로 Kill 하기위해서는 화면에 떠있는 Activity를 BackGround로 보내고
               * 강제로 Kill하면 프로세스가  완전히 종료가 된다.
               * 종료 방법에 대한 Source는 아래 부분을 참조 하면 될것 같다.
               */
                moveTaskToBack(true);
                finish();
                android.os.Process.sendSignal(android.os.Process.myPid(), android.os.Process.SIGNAL_KILL);
          }
        }).start();
       }
      
    public Handler viewUpdateHandler = new Handler(){
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case MESSAGE_DOWNLOAD_STARTING :
                    Log.d(MSG_TAG, "프로그레스바 시작");
                    progressBar.setIndeterminate(true);
                    progressTitle.setText((String)msg.obj + " 다운로드");
                    progressText.setText("시작중...");
                    downloadUpdateLayout.setVisibility(View.VISIBLE);
                    break;
                case MESSAGE_DOWNLOAD_PROGRESS :
                    progressBar.setIndeterminate(false);
                    progressText.setText(msg.arg1 + "k /" + msg.arg2 + "k");
                    progressTitle.setText("최신 버전을 다운로드 하고 있습니다.");
                    progressBar.setProgress(msg.arg1*100/msg.arg2);
                    break;
                case MESSAGE_DOWNLOAD_COMPLETE :
                    Log.d(MSG_TAG, "다운로드 완료.");
                    progressText.setText("");
                    progressTitle.setText("");
                    downloadUpdateLayout.setVisibility(View.GONE);
                    break;              
            }
            super.handleMessage(msg);
        }
    };
     
    public boolean downloadUpdateFile(String downloadFileUrl, String destinationFilename, String localPath) {
        if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED) == false) {
            return false;
        }
      
        File downloadDir = new File( localPath );
        Log.d("DOWNLOAD","다운로드중");
        if (downloadDir.exists() == false) {
            downloadDir.mkdirs();
        } else {
            File downloadFile = new File( localPath + destinationFilename );
            if (downloadFile.exists()) {
                downloadFile.delete();
            }
        }
        return this.downloadFile(downloadFileUrl, localPath, destinationFilename );
    }    
    
    //파일 다운로드 과정 표시
    public boolean downloadFile(String url, String destinationDirectory, String destinationFilename ) {
        boolean filedownloaded = true;
        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(String.format(url));
        Message msg = Message.obtain();
        
        try {
            HttpResponse response = client.execute(request);
            StatusLine status = response.getStatusLine();
            Log.d(MSG_TAG, "Request returned status " + status);
               
            if (status.getStatusCode() == 200) {
                HttpEntity entity = response.getEntity();
                InputStream instream = entity.getContent();
                int fileSize = (int)entity.getContentLength();
                FileOutputStream out = new FileOutputStream(new File(destinationDirectory + destinationFilename));
                byte buf[] = new byte[8192];
                int len;
                int totalRead = 0;


                while((len = instream.read(buf)) > 0) {
                    msg = Message.obtain();
                    msg.what = MESSAGE_DOWNLOAD_PROGRESS;
                    totalRead += len;
                    msg.arg1 = totalRead / 1024;
                    msg.arg2 = fileSize / 1024;
                    viewUpdateHandler.sendMessage(msg);
                    out.write(buf,0,len);
                }
                   out.close();
            } else {
                throw new IOException();
            }
        } catch (IOException e) {  
            filedownloaded = false;
        }
        msg = Message.obtain();
        msg.what = MESSAGE_DOWNLOAD_COMPLETE;
        viewUpdateHandler.sendMessage(msg);
        return filedownloaded;
    }  


}

 

 

 

activity_autoupdate.xml

 

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" android:layout_gravity="center" >
    <TextView        android:id="@+id/textVersion"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:text="Version"        android:textSize="14sp" />
    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content" android:layout_gravity="center_vertical">                <RelativeLayout android:id="@+id/layoutDownloadUpdate"            android:layout_width="match_parent"            android:layout_height="60dp"            android:visibility="gone"            android:layout_gravity="center_horizontal"            android:gravity="center"            android:background="#A0909090">
            <TextView                android:id="@+id/progressTitle"                android:layout_width="match_parent"                 android:layout_height="wrap_content"                 android:layout_alignParentTop="true"                android:paddingLeft="5dp"                android:paddingRight="5dp"                android:text=""                android:textStyle="bold"                android:textColor="#000000" />
            <ProgressBar                android:id="@+id/progressBar"                android:layout_width="match_parent"                android:layout_height="wrap_content"                style="?android:attr/progressBarStyleHorizontal"                android:paddingTop="22dp"                android:paddingLeft="5dp"                android:paddingRight="5dp"                android:layout_alignParentTop="true"                android:max="100" />
            <TextView                  android:id="@+id/progressText"                android:layout_width="match_parent"                android:layout_height="wrap_content"                 android:paddingTop="40dp"                android:paddingLeft="5dp"                android:paddingRight="5dp"                android:layout_alignParentTop="true"                android:text=""                android:textStyle="bold"                android:textColor="#000000" />
        </RelativeLayout>
    </LinearLayout>  
</FrameLayout>

 

 

 

updateview.xml (dialog 창)

 

<ScrollView     xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent" >         <LinearLayout        android:orientation="vertical"        android:layout_width="match_parent"        android:layout_height="wrap_content">
      <TextView              android:id="@+id/updateMessage"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:autoLink="web"              android:paddingLeft="15dp"              android:paddingRight="15dp"              android:paddingTop="15dp"              android:text=""              android:textColor="#00ffff"              android:textStyle="bold" />       <TextView               android:text="Update"               android:id="@+id/updateNowText"               android:layout_width="match_parent"                android:layout_height="wrap_content"               android:paddingTop="15dp"               android:paddingLeft="15dp"               android:paddingRight="15dp"               android:textStyle="bold"               android:textColor="#000000" />  
    </LinearLayout>
</ScrollView>

 

 

[출처] http://blog.daum.net/wonky12/1401710

 

반응형

댓글()