728x90

어플 기능 개선이나 버그 수정 등으로 업그레이드를 해야 할 경우가 생긴다.

이때 업그레이드 여부 검사를 하는 로직과 파일 다운로드 기능을 구현했다.


구글링으로 검색하면 파일 다운로드 로직은 많이 나온다.

http://www.androidbegin.com/tutorial/android-download-progress-bar/

약간씩 설명이 다르지만 대부분 유사한 설명이 나온다.


서버의 버전과 어플의 버전을 비교하여 서버의 버전이 높으면 파일을 자동으로 다운로드하는 로직으로 구현했다.

기본적인 개념은 http://link2me.tistory.com/1277 파일을 실행해보면 알 수 있다.

파일을 다운로드하면 상태바가 활성화되고, 다운로드 하지 않을 경우에는 화면에 보이지 않도록 UI 를 선택적으로 처리했다.


 import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {
    public SharedPreferences settings;
    static final int PERMISSION_REQUEST_CODE = 1;
    String[] PERMISSIONS = {"android.permission.READ_EXTERNAL_STORAGE","android.permission.WRITE_EXTERNAL_STORAGE"};

    LinearLayout linearLayout;
    TextView textView;
    ProgressBar progressBar;
    DownloadFileFromURL downloadFileAsyncTask;
    static boolean isUpgrade = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 네트워크 연결 검사
        if(NetworkConnection() == false){
            NotConnected_showAlert();
        }

        if (!hasPermissions(PERMISSIONS)) { //퍼미션 허가를 했었는지 여부를 확인
            requestNecessaryPermissions(PERMISSIONS);//퍼미션 허가안되어 있다면 사용자에게 요청
        }

        // Linear layout 정의
        linearLayout = (LinearLayout) findViewById(R.id.downloadprogress_layout);
        textView = (TextView)findViewById(R.id.txtView01);
        progressBar = (ProgressBar)findViewById(R.id.progressBar);

        // 업그레이드 유무 검사
        VersionCheck();

        if(isUpgrade == false){
            linearLayout.setVisibility(View.GONE);
        } else {
            linearLayout.setVisibility(View.VISIBLE);
        }

        //취소 버튼 이벤트 처리 및 리스너 등록
        Button btn_Cancel = (Button)findViewById(R.id.btnCancel);
        btn_Cancel.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                downloadFileAsyncTask.cancel(true);
            }
        });

    }

    private void VersionCheck(){
        settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);
        Uri.Builder builder = new Uri.Builder()
                .appendQueryParameter("idx", "2"); // settings.getString("idx","" )
        String postParams = builder.build().getEncodedQuery();

        new isUpgradeTask().execute(Value.IPADDRESS + "/file/UpgradeChk.php", postParams);
    }

    private void UpgradeChk(final String serverVersion){

        try {
            String Appversion = Value.VERSION;
            Appversion = Appversion.replaceAll("[^0-9]", ""); // 버전에서 숫자만 추출
            System.out.println("App Version : " + Appversion + " Server Version : " + serverVersion);

            if (Integer.parseInt(Appversion) < Integer.parseInt(serverVersion)) { // 서버 버전이 더 높으면
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("버전 업그레이드");
                builder.setMessage("버전 업그레이드 되었습니다. 새로운 버전으로 다시 설치해주십시오.");
                builder.setPositiveButton("업데이트", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(getApplicationContext(), "파일 다운로드 중입니다.", Toast.LENGTH_SHORT).show();
                        isUpgrade = true;
                        linearLayout.setVisibility(View.VISIBLE);
                        // 백그라운드 객체를 만들어 줌
                        downloadFileAsyncTask = new DownloadFileFromURL(MainActivity.this);
                        downloadFileAsyncTask.execute(Value.IPADDRESS + "/file/apkDownload.php");
                    }
                });
                builder.setNegativeButton("취소", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 업데이트 취소시 어플을 완전 종료시킴
                        moveTaskToBack(true);
                        finish();
                        android.os.Process.killProcess(android.os.Process.myPid());
                    }
                });
                builder.create();
                builder.show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    class isUpgradeTask extends AsyncTask<String,String,String>{
        @Override
        protected String doInBackground(String... params) {
            BufferedReader bufferedReader = null;
            String Response = null;
            try {
                URL url = new URL(params[0]);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();

                if(conn != null){ // 연결되었으면
                    //add request header
                    conn.setRequestMethod("POST");
                    conn.setRequestProperty("USER-AGENT", "Mozilla/5.0");
                    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    conn.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
                    conn.setConnectTimeout(10000);
                    conn.setReadTimeout(10000);
                    conn.setUseCaches(false);
                    conn.setDefaultUseCaches(false);
                    conn.setDoOutput(true); // POST 로 데이터를 넘겨주겠다는 옵션
                    conn.setDoInput(true);

                    // Send post request
                    DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
                    wr.writeBytes(params[1]);
                    wr.flush();
                    wr.close();

                    int responseCode = conn.getResponseCode();
                    System.out.println("GET Response Code : " + responseCode);
                    if(responseCode == HttpURLConnection.HTTP_OK){ // 연결 코드가 리턴되면
                        bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                        Response = bufferedReader.readLine();
                        Response = Response.replaceAll("[^0-9]", ""); // 버전에서 숫자만 추출
                    }
                    bufferedReader.close();
                }
                return Response;
                // 수행이 끝나고 리턴하는 값은 다음에 수행될 onProgressUpdate 의 파라미터가 된다
            } catch(Exception e){
                return new String("Exception: " + e.getMessage());
            }
        }

        protected void onPostExecute(String result) {
            UpgradeChk(result);
        }

    }

    class DownloadFileFromURL extends AsyncTask<String,Integer,String>{
        private Context context;
        private String savePath;
        private File file, dir;
        int count;

        public DownloadFileFromURL(Context context) {
            this.context = context;
        }

        protected void onPreExecute() {
            super.onPreExecute();
            progressBar.setProgress(0);
        }

        protected String doInBackground(String... params) {
            URLConnection connection = null;
            InputStream input = null;
            OutputStream output = null;
            int lenghtOfFile =0;

            savePath = "/Android/data/" + getPackageName();
            dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), savePath);
            if (!dir.exists()) dir.mkdirs(); // make dir
            file = new File(dir, Value.APKNAME);  // APK 이름 변경

            try {
                URL url = new URL(params[0]);
                try {
                    connection = url.openConnection();
                    connection.connect();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                lenghtOfFile  = connection.getContentLength(); //파일 크기를 가져옴
                if (file.exists()) {
                    file.delete(); // 파일이 존재하면 기존 파일을 삭제
                }

                try {
                    input = new BufferedInputStream(url.openStream());
                    output = new FileOutputStream(file);
                    byte data[] = new byte[1024];
                    long total = 0;

                    while ((count = input.read(data)) != -1) {
                        if (isCancelled()) {
                            input.close();
                            return String.valueOf(-1);
                        }
                        total = total + count;
                        if (lenghtOfFile > 0) { // 파일 총 크기가 0 보다 크면
                            publishProgress((int)(total * 100 / lenghtOfFile));
                        }
                        System.out.println("Download Percent === " + (int)(total * 100 / lenghtOfFile));
                        System.out.println("Total === " + total);
                        output.write(data, 0, count); //파일에 데이터를 기록
                    }
                    System.out.println("Download total Size === " + lenghtOfFile);

                    output.flush();
                    output.close();
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            super.onProgressUpdate(progress);
            //백그라운드 작업의 진행상태를 표시하기 위해서 호출하는 메소드
            progressBar.setProgress(progress[0]);
            textView.setText("진행정도 : "+progress[0]+"%");
        }

        protected void onPostExecute(String result) {
            //백그라운 작업이 끝난후에 호출되는 메소드
            System.out.println("result === " + result);
            linearLayout.setVisibility(View.GONE);
            if ( result == null ) {
                Toast.makeText(getApplicationContext(), "다운로드 완료되었습니다.", Toast.LENGTH_LONG).show();
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
                context.startActivity(intent);
            } else {
                Toast.makeText(getApplicationContext(), "다운로드 에러", Toast.LENGTH_LONG).show();
            }
        }

        protected void onCancelled(){
            // cancel메소드를 호출하면 자동으로 호출되는 메소드
            // 작업을 취소했을 경우에 사용되는 메소드
            progressBar.setProgress(0);
            textView.setText("다운로드 진행 취소됨");
        }
    }

    private boolean hasPermissions(String[] permissions) {
        int res = 0;
        //스트링 배열에 있는 퍼미션들의 허가 상태 여부 확인
        for (String perms : permissions){
            res = checkCallingOrSelfPermission(perms);
            if (!(res == PackageManager.PERMISSION_GRANTED)){
                //퍼미션 허가 안된 경우
                return false;
            }
        }
        return true;  //퍼미션이 허가된 경우
    }

    private void requestNecessaryPermissions(String[] permissions) {
        //마시멜로( API 23 )이상에서 런타임 퍼미션(Runtime Permission) 요청
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(permissions, PERMISSION_REQUEST_CODE);
        }
    }

    @Override
    public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
        switch(permsRequestCode){
            case PERMISSION_REQUEST_CODE:
                if (grantResults.length > 0) {
                    boolean readAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    boolean writeAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if( !readAccepted || !writeAccepted  ) {
                            showDialogforPermission("앱을 실행하려면 퍼미션을 허가하셔야합니다.");
                            return;
                        }
                    }
                }
                break;
        }
    }

    private void showDialogforPermission(String msg) {

        final AlertDialog.Builder myDialog = new AlertDialog.Builder(  MainActivity.this);
        myDialog.setTitle("알림");
        myDialog.setMessage(msg);
        myDialog.setCancelable(false);
        myDialog.setPositiveButton("예", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    requestPermissions(PERMISSIONS, PERMISSION_REQUEST_CODE);
                }

            }
        });
        myDialog.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                finish();
            }
        });
        myDialog.show();
    }

    public void NotConnected_showAlert() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("네트워크 연결 오류");
        builder.setMessage("사용 가능한 무선네트워크가 없습니다.\n" + "먼저 무선네트워크 연결상태를 확인해 주세요.")
                .setCancelable(false)
                .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        finish(); // exit
                        //application 프로세스를 강제 종료
                        android.os.Process.killProcess(android.os.Process.myPid() );
                    }
                });
        AlertDialog alert = builder.create();
        alert.show();
    }

    public boolean NetworkConnection() {
        int[] networkTypes = {ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_WIFI};
        try {
            ConnectivityManager manager = (ConnectivityManager) getSystemService (Context.CONNECTIVITY_SERVICE);
            for (int networkType : networkTypes) {
                NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
                if(activeNetwork != null && activeNetwork.getType() == networkType){
                    return true;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return false;
    }
}

 import android.support.v7.app.AppCompatActivity;

public class Value extends AppCompatActivity {

    public static final String IPADDRESS = "http://192.168.0.2"; // SERVER IP
    public static final String VERSION = "1.0.0"; // App Version
    public static final String APKNAME = "AddressBook.apk" ; //APK name

}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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:orientation="vertical"
    tools:context="com.tistory.link2me.filedownload.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="10dp"
        android:text="업그레이드 체크"  />

    <LinearLayout
        android:id="@+id/downloadprogress_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/txtView01"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18dp"
            android:padding="10dp"
            android:text="Progress Status"/>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ProgressBar
                android:id="@+id/progressBar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
                android:layout_marginTop="5dp"
                android:max="100"
                android:padding="10dp"
                android:layout_weight="8" />
            <Button
                android:id="@+id/btnCancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginTop="5dp"
                android:layout_marginLeft="10dp"
                android:text="취소"
                android:textSize="12dp"
                />
        </LinearLayout>

    </LinearLayout>

</LinearLayout>


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tistory.link2me.filedownload">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application android:allowBackup="true" android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


블로그 이미지

Link2Me

,