공부/안드로이드(Android)

다운로드, 프로그래스 진행창 - WebView(웹뷰) 기반

도도-도윤 2017. 11. 26. 22:33

다운로드 - WebView(웹뷰) 기반


안드로이드에서 URL 주소 기반으로 다운로드를 받는 방법에 대해서 소개합니다.

응용분야로는 SQLlite에 사용되는 DB를 가져와서 사용할 수 있을 것으로 보입니다.

안드로이드 6.0 마시멜로로 태스트 하였습니다.



1. 시연 영상





   





2. 코드



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

<!-- 인터넷 연결 허용하는 퍼미션 -->
<uses-permission android:name="android.permission.INTERNET"/>

<!-- SD카드 기록 허용하는 퍼미션 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<!-- SD카드 읽기 허용하는 퍼미션, 킷캣이후로는 필요없음 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<!-- CPU 상태 유지 및 화면 꺼짐 제어를 위한 퍼미션 -->
<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>


 AndroidManifest.xml

 

package com.localhost.kr.download;

import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity {

static final int PERMISSION_REQUEST_CODE = 1;
String[] PERMISSIONS = {"android.permission.READ_EXTERNAL_STORAGE","android.permission.WRITE_EXTERNAL_STORAGE"};

private File outputFile; //파일명까지 포함한 경로
private File path;//디렉토리경로

private boolean hasPermissions(String[] permissions) {

//스트링 배열에 있는 퍼미션들의 허가 상태 여부 확인
for (String perms : permissions){
if (!(checkCallingOrSelfPermission(perms) == 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
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

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

initWidget();
}

private void initWidget(){

Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) { //1
//웹브라우저에 아래 링크를 입력하면 Alight.avi 파일이 다운로드됨.
final String fileURL = "http://localhost:8080/JWeb/fileDown.do?fileName=filedb.db";

path= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
//path = Environment.getExternalStorageDirectory();
outputFile= new File(path, "filedb.db"); //파일명까지 포함함 경로의 File 객체 생성

if (outputFile.exists()) { //이미 다운로드 되어 있는 경우

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("파일 다운로드");
builder.setMessage("이미 SD 카드에 존재합니다. 다시 다운로드 받을까요?");
builder.setNegativeButton("아니오",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
Toast.makeText(getApplicationContext(),"기존 파일을 플레이합니다.",
Toast.LENGTH_LONG).show();
playVideo(outputFile.getPath());
}
});
builder.setPositiveButton("예",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
outputFile.delete(); //파일 삭제

final DownloadFilesTask downloadTask = new DownloadFilesTask(MainActivity.this);
downloadTask.execute(fileURL);


}
});
builder.show();

} else { //새로 다운로드 받는 경우
final DownloadFilesTask downloadTask = new DownloadFilesTask(MainActivity.this);
downloadTask.execute(fileURL);

}
}
});

}

@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( 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();
}

private void playVideo(String path) {
Uri videoUri = Uri.fromFile(new File(path));
Intent videoIntent = new Intent(Intent.ACTION_VIEW);
videoIntent.setDataAndType(videoUri, "video/*");
if (videoIntent.resolveActivity(getPackageManager()) != null) {
startActivity(Intent.createChooser(videoIntent, null));
}
}

private void playImage(String path){

//저장한 이미지 열기
Intent i = new Intent(Intent.ACTION_VIEW);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
String targetDir = Environment.getExternalStorageDirectory().toString();

File file = new File(path);

//type 지정 (이미지)
i.setDataAndType(Uri.fromFile(file), "image/*");
startActivity(i);
}

}


 MainActivity.java

 

package com.localhost.kr.download;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.PowerManager;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

/**
* Created by lab on 2017-11-26.
*/

public class DownloadFilesTask extends AsyncTask<String, String, Long> {

private Context context;
private PowerManager.WakeLock mWakeLock;
private ProgressDialog progressBar;

private File outputFile; //파일명까지 포함한 경로
private File path;//디렉토리경로

public DownloadFilesTask(Context context) {
this.context = context;

progressBar = new ProgressDialog(context);
progressBar.setMessage("다운로드중");
progressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressBar.setIndeterminate(true);
progressBar.setCancelable(true);

progressBar.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancel(true);
}
});

}

//파일 다운로드를 시작하기 전에 프로그레스바를 화면에 보여줍니다.
@Override
protected void onPreExecute() { //2
super.onPreExecute();

//사용자가 다운로드 중 파워 버튼을 누르더라도 CPU가 잠들지 않도록 해서
//다시 파워버튼 누르면 그동안 다운로드가 진행되고 있게 됩니다.
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
mWakeLock.acquire();

progressBar.show();
}

//파일 다운로드를 진행합니다.
@Override
protected Long doInBackground(String... string_url) { //3

int count;
long FileSize = -1;

InputStream input = null;
OutputStream output = null;
URLConnection connection = null;

try {
URL url = new URL(string_url[0]);
connection = url.openConnection();
connection.connect();


//파일 크기를 가져옴
FileSize = connection.getContentLength();

//URL 주소로부터 파일다운로드하기 위한 input stream
input = new BufferedInputStream(url.openStream(), 8192);

path= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
outputFile= new File(path, "filedb.db"); //파일명까지 포함함 경로의 File 객체 생성

// SD카드에 저장하기 위한 Output stream
output = new FileOutputStream(outputFile);


byte data[] = new byte[1024];
long downloadedSize = 0;
while ((count = input.read(data)) != -1) {
//사용자가 BACK 버튼 누르면 취소가능
if (isCancelled()) {
input.close();
return Long.valueOf(-1);
}

downloadedSize += count;

if (FileSize > 0) {
float per = ((float)downloadedSize/FileSize) * 100;
String str = "Downloaded " + downloadedSize + "KB / " + FileSize + "KB (" + (int)per + "%)";
publishProgress("" + (int) ((downloadedSize * 100) / FileSize), str);

}

//파일에 데이터를 기록합니다.
output.write(data, 0, count);
}
// Flush output
output.flush();

// Close streams
output.close();
input.close();


} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}

mWakeLock.release();

}
return FileSize;
}


//다운로드 중 프로그레스바 업데이트
@Override
protected void onProgressUpdate(String... progress) { //4
super.onProgressUpdate(progress);

// if we get here, length is known, now set indeterminate to false
progressBar.setIndeterminate(false);
progressBar.setMax(100);
progressBar.setProgress(Integer.parseInt(progress[0]));
progressBar.setMessage(progress[1]);
}

//파일 다운로드 완료 후
@Override
protected void onPostExecute(Long size) { //5
super.onPostExecute(size);

progressBar.dismiss();

if ( size > 0) {
Toast.makeText(context, "다운로드 완료되었습니다. 파일 크기=" + size.toString(), Toast.LENGTH_LONG).show();

Intent mediaScanIntent = new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(Uri.fromFile(outputFile));
context.sendBroadcast(mediaScanIntent);

//playVideo(outputFile.getPath());

}
else
Toast.makeText( context , "다운로드 에러", Toast.LENGTH_LONG).show();
}

}


 DownloadFilesTask.java


[소스코드]

171126-androidDownload.zip



3. 참고 자료

1. http://webnautes.tistory.com/1085, Android 예제 - URL 주소로 부터 동영상 다운로드 및 재생( AsyncTask )

-> 가장 정확하게 구현되어 있습니다.

2. http://kwon8999.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-AsyncTask%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C, 안드로이드 AsyncTask를 이용하여 파일 다운로드

-> playImage() 함수 참고.


171126-androidDownload.zip
0.08MB