안드로이드 학습 노트의 다선정 다운로드

세부 사항 및 방법
  • 다중 루틴은 데이터베이스 조작과 관련이 있지만 데이터베이스는 여러 루틴이 동시에 조작하는 것을 허용하지 않기 때문에 매 시간에 한 개의 데이터베이스 대상만 조작할 수 있다
  • 데이터를 삽입할 때 대응하는 데이터가 있는지 확인하고 있으면 업데이트하여 데이터가 덮어쓰여 데이터 착란을 방지해야 한다
  • 다운로드 대상을 잘 봉하여 데이터 처리 효율을 높여야 한다
  • 다중 스레드 다운로드의 원리는 한 작업을 여러 스레드로 나누어 진행하는 것이다
  • 다운로드 작업은 시간이 걸리므로 다운로드를 위한 서비스를 만들어야 합니다
  • 실현 절차
  • 다운로드 링크를 분석하고 다운로드 작업 정보를 얻기
  • 다운로드를 확인하고 서비스를 시작하며 새 작업을 다운로드 대기열에 추가
  • 다운로드 시작, 서브스레드 세그먼트 다운로드 시작
  • 다운로드 링크 확인 및 다운로드 작업 표시
    package com.yu.threadsdownload;
    
    import java.io.IOException;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.w3c.dom.Text;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.os.Parcelable;
    import android.text.TextUtils;
    import android.text.format.Formatter;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.ListView;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.yu.bean.DownloadInfo;
    
    
    public class MainActivity extends Activity {
    //     
        public static final String QQ = "http://a5.pc6.com/cx3/QQmobile2016.pc6.apk";
        public static final String WEIXIN = "http://a5.pc6.com/lffaz3/weixin2016.apk";
        public static final String PC6 = "http://a3.pc6.com/cx/pc6shichang.pc6.apk";
    // handler   
        public static final int MSG_DATA_LOADED = 0; //        
        public static final int MSG_PROGRESS_UPDATE = 1;//        
        public static final int MSG_TASK_DONE = 2; //      
    
        public  static final int MSG_ONCLICK = 3; //      
        static ListView lv;
        static List<DownloadInfo> infos;
        static DownloadAdapter adapter;
        static MainActivity ma;
        static Handler handler = new Handler(){
            public void handleMessage(android.os.Message msg) {
                switch (msg.what) {
                case MSG_DATA_LOADED:
                    infos.add((DownloadInfo) msg.obj);
                    if (adapter == null) {
                        adapter = ma.new DownloadAdapter();
                    }else{
                        adapter.notifyDataSetChanged();
                    }
                    lv.setAdapter(adapter);
                    break;
    
                case MSG_PROGRESS_UPDATE :
                    DownloadInfo info  =(DownloadInfo) msg.obj;
                    ProgressBar pb = (ProgressBar) lv.findViewWithTag(info.getUrl());
                    TextView tvProgress = (TextView) lv.findViewWithTag(info.getUrl()+"tvProgress");
                    if (pb != null) {
                        pb.setProgress(info.getPercent());
                    }
                    if (tvProgress != null) {
                        tvProgress.setText(info.getPercent()+"%");
                    }
                    if (info.getPercent() == 100) {
                        Toast.makeText(ma, info.getFileName()+"    !", 1).show();
                    }
                    break;
                }
    
            };
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            initData();
    
        }
    
        private void initView() {
            lv = (ListView) findViewById(R.id.lv_download);
            infos = new ArrayList<DownloadInfo>();
            ma = this;
            adapter = new DownloadAdapter();
        }
    
    
        private void initData() {
            String[] urls = {QQ,WEIXIN,PC6};
            for (final String url : urls) {
                newTask(url);
            }
        }
    
        /** *        * @param url */
        public void newTask(final String url) {
            new Thread(){
                HttpURLConnection conn;
                public void run() {
                    try {
                        conn = (HttpURLConnection) new URL(url).openConnection();
                        conn.setRequestMethod("GET");
                        conn.setConnectTimeout(5000);
                        conn.setReadTimeout(5000);
                        if (conn.getResponseCode() == 200) {
                            String fileSize = Formatter.formatFileSize(MainActivity.this, conn.getContentLength());
                            String fileName = getFileName(url);
                            DownloadInfo info = new DownloadInfo(fileName, fileSize, conn.getContentLength(),url,0);
                            Message msg = handler.obtainMessage();
                            msg.what = MSG_DATA_LOADED;
                            msg.obj = info;
                            handler.sendMessage(msg);
                        }
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }finally{
                        conn.disconnect();
                    }
    
                };
            }.start();
        }
    
    
        /** *            * @param url * @return */
        public String getFileName(String url) {
            int index = url.lastIndexOf("/");
            return url.substring(index+1);
        }
    
        //     
        class DownloadAdapter extends BaseAdapter{
            @Override
            public int getCount() {
                return infos.size();
            }
    
            @Override
            public Object getItem(int position) {
                return infos.get(position);
            }
    
            @Override
            public long getItemId(int position) {
                return position;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                ViewHolder holder ;
                if (convertView == null) {
                    holder = new ViewHolder();
                    convertView = View.inflate(MainActivity.this, R.layout.item_threads_download, null);
                    holder.tvFileName = (TextView) convertView.findViewById(R.id.tv_fileName);
                    holder.tvProgress = (TextView) convertView.findViewById(R.id.tv_progress);
    
                    holder.pb = (ProgressBar) convertView.findViewById(R.id.pb_progress);
                    holder.pb.setMax(100);
                    holder.btDownload = (Button) convertView.findViewById(R.id.bt_download);
                    convertView.setTag(holder);
                }else{
                    holder = (ViewHolder) convertView.getTag();
                }
                final DownloadInfo info = infos.get(position);
                holder.tvFileName.setText(info.getFileName()+"/"+info.getFileSize());
                holder.tvProgress.setText(info.getProgress()+"%");
                holder.pb.setTag(info.getUrl());
                holder.tvProgress.setTag(info.getUrl()+"tvProgress");
                holder.btDownload.setOnClickListener(new OnClickListener() {
                    public void onClick(View v) {
                        startService(new Intent(MainActivity.this,DownloadService.class));
                        Message msg = DownloadService.handler.obtainMessage();
                        msg.what = MSG_ONCLICK;
                        msg.obj = info;
                        DownloadService.handler.sendMessage(msg );
                    }
                });
                return convertView;
            }
        }
    
        class ViewHolder{
            TextView  tvFileName,tvProgress;
            ProgressBar pb;
            Button btDownload;
        }
    
        /** *        * @param view */
        public void addTask(View view) {
            EditText et = (EditText) findViewById(R.id.et_url);
            String url = et.getText().toString().trim();
            if (TextUtils.isEmpty(url)) {
                return;
            }
            newTask(url);
        }
    }
    

    다운로드 서비스, 다운로드 관련 정보 처리
    package com.yu.threadsdownload;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import com.yu.bean.DownloadInfo;
    import com.yu.db.DownloadDBImpl;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.IBinder;
    import android.util.Log;
    import android.widget.Toast;
    
    public class DownloadService extends Service {
        static DownloadService dls ;
        static DownloadTask task;
        static List<DownloadTask> tasks; //     
        static DownloadDBImpl db ;
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            dls = DownloadService.this;
            tasks = new ArrayList<DownloadTask>();
            db = new DownloadDBImpl(dls);//   service   ,  db    
    
        }
    
    
        public static Handler handler = new Handler(){
            public void handleMessage(android.os.Message msg) {
                switch (msg.what) {
                case MainActivity.MSG_ONCLICK:   //           
                    DownloadInfo info  = (DownloadInfo) msg.obj;
                    // Log.e("", "info:"+info.toString());
                    task = new DownloadTask(info, 3,dls, db);
                    if (tasks.size() >=2) {
                        Toast.makeText(dls, "         !", 0).show();
                    }
                    tasks.add(task);
                    info.setTaskId(tasks.indexOf(task));
                    for (DownloadTask task : tasks) {       
                        if (tasks.indexOf(task)<2 && !task.isRunning) {   //            
                            task.start();
                        }
                    }
                    break;
    
                case MainActivity.MSG_TASK_DONE:  //        
                    int taskId = msg.arg1;
                    if (taskId  < tasks.size()) {
                        DownloadTask task2 = tasks.remove(taskId);
                        Log.e("", "  "+task2);
                    }
                    for (DownloadTask currTask : tasks) {           //           
                        if (tasks.indexOf(task)<2 && !currTask.isRunning) {
                            task.start();
                        }
                    }
                    break;
                }
    
            };
        };
    
    }
    

    다운로드 퀘스트 클래스, 다운로드 퀘스트의 구체적인 실현
    package com.yu.threadsdownload;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    import android.content.Context;
    import android.content.SharedPreferences;
    import android.os.Environment;
    import android.os.Message;
    import android.util.Log;
    
    import com.yu.bean.DownloadInfo;
    import com.yu.bean.ThreadInfo;
    import com.yu.db.DownloadDBImpl;
    
    /** *      * @author Administrator * */
    public class DownloadTask {
        DownloadInfo info;
        int threadCount;
        DownloadDBImpl db ;
        long progress = 0;
        boolean isPurse;
        boolean isRunning;
        List<DownloadThread> taskThreads;
        public DownloadTask(DownloadInfo info,int threadCount,Context context,DownloadDBImpl db) {
            super();
            this.info = info;
            this.threadCount = threadCount;
            this.db = db;
            taskThreads = new ArrayList<DownloadTask.DownloadThread>();
            isPurse = false;
        }
    
        /** *       * @param url */
        public void start(){
            isRunning = true;
            long length = info.getFileLength();
            long part = length / threadCount;
            for (int i = 0; i < threadCount; i++) {
                long start = part*i;
                long end = start+part-1;
                if (i == threadCount - 1) {
                    end = length;
                }
                int threadId = i;
                Log.e("", "start:"+start+"end:"+end);
                ThreadInfo threadInfo = new ThreadInfo(threadId, 0, info.getUrl()   , start, end);
                db.insertThread(threadInfo );
                DownloadThread thread = new DownloadThread(start, end, info.getUrl(),threadId);
                thread.start();
                taskThreads.add(thread);
            }
    
        }
    
        /** *      * @author Administrator * */
        class DownloadThread extends Thread{
            long start,end;
            int threadId;
            String url;
            ThreadInfo threadInfo;
            long threadProgress = 0;
            boolean finished;
            public DownloadThread(long start, long end, String url, int threadId) {
                super();
                finished = false;
                this.start = start;
                this.end = end;
                this.url = url;
                this.threadId = threadId; 
                threadInfo = new ThreadInfo( start,  end,  url,threadId);
            }
    
            @Override
            public void run() {
                super.run();
                HttpURLConnection conn = null;
                RandomAccessFile raf = null;
                try {
                    long threadProgressed = db.getThreadProgress(threadInfo);
                    final long currStart = start + threadProgressed;
                    progress = db.getTaskProgress(url);
                    // Log.e("","threadProgressed:"+ threadProgressed+",taskProgress"+progress);
    
                    conn = (HttpURLConnection) new URL(info.getUrl()).openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(10000);
                    conn.setReadTimeout(10000);
                    //              206
                    conn.addRequestProperty("Range", "bytes="+currStart+"-"+end);
                    File file = new File(Environment.getExternalStorageDirectory(), info.getFileName());
                    //   SD        
                    raf = new RandomAccessFile(file,"rw");
                    //           
                    raf.seek(currStart);
                    raf.setLength(info.getFileLength());
                    if (conn.getResponseCode() == 206) {
                        InputStream is =  conn.getInputStream();
                        byte[] buff = new byte[1024];
                        int len = -1;
                        long currTime = System.currentTimeMillis();
                        while((len = is.read(buff)) != -1){
                            raf.write(buff, 0, len);
                            progress+=len;
                            threadProgress+=len;
                            if (System.currentTimeMillis() - currTime > 1000) {
                                currTime = System.currentTimeMillis();
                                threadInfo.setThreadProgress(threadProgress);
                                info.setProgress(progress);
                                db.addThreadProgress(threadInfo);
                                db.addTaskProgress(info);
                                Message msg = MainActivity.handler.obtainMessage();
                                msg.what = MainActivity.MSG_PROGRESS_UPDATE;
                                info.setPercent((int) (progress * 100/info.getFileLength()));
                                msg.obj = info;
                                MainActivity.handler.sendMessage(msg);
                            }
                        }
                        finished = true;
                        checkFinfish();
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally{
                    conn.disconnect();
                    if (raf != null) {
                        try {
                            raf.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
    
            };
        }
    
        /** *            */
        public void checkFinfish() {
            for (DownloadThread thread : taskThreads) {
                if (!thread.finished) {
                    return;
                }
            }
            //       
            Message msg = MainActivity.handler.obtainMessage();
            msg.what = MainActivity.MSG_PROGRESS_UPDATE;
            info.setPercent(100);
            msg.obj = info;
            MainActivity.handler.sendMessage(msg);
    
            //         
            Message msg2 = DownloadService.handler.obtainMessage();
            msg2.what = MainActivity.MSG_TASK_DONE;
            msg2.arg1 = info.getTaskId();
            DownloadService.handler.sendMessage(msg2);
            //          
            db.deleteThread(info.getUrl());
        }
    }
    

    데이터베이스 생성 및 작업
    package com.yu.db;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteDatabase.CursorFactory;
    import android.database.sqlite.SQLiteOpenHelper;
    
    public class ThreadsDBHelper extends SQLiteOpenHelper {
        private static final String DB_NAME  = "download.db";
        private static ThreadsDBHelper sDbHelper;
        private ThreadsDBHelper(Context context) {
            super(context, DB_NAME, null, 1);
        }
    
        /** *    * @param context * @return */
        public static  ThreadsDBHelper getInstance(Context context) {
            if (sDbHelper == null) {
                synchronized (ThreadsDBHelper.class) {
                    if (sDbHelper == null) {
                        sDbHelper = new ThreadsDBHelper(context);
                    }
                }
            }
            return sDbHelper;
    
        }
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("create table downloadInfo(_id integer primary key autoincrement,threadId integer,taskProgress integer,finished integer,start integer,end integer,url text)");
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    
    }
    
    package com.yu.db;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.util.Log;
    
    import com.yu.bean.DownloadInfo;
    import com.yu.bean.ThreadInfo;
    
    
    public class DownloadDBImpl implements DownloadDBInterface{
        ThreadsDBHelper helper;
        SQLiteDatabase db;
        public DownloadDBImpl(Context context) {
            super();
            helper = ThreadsDBHelper.getInstance(context);
        }
    
        @Override
        public synchronized boolean insertThread(ThreadInfo threadInfo) {
            if (isTheradExist(threadInfo.getUrl(),threadInfo.getThreadId())) {
                return false;
            }else{
                db = helper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("threadId", threadInfo.getThreadId());
                values.put("url", threadInfo.getUrl());
                values.put("start", threadInfo.getStart());
                values.put("end", threadInfo.getEnd());
                long raw = db.insert("downloadInfo", null, values);
                if (raw != -1) {
                    db.close();
                    return true;
                }
                db.close();
            }
            return false;
        }
    
    
        @Override
        public synchronized boolean deleteThread(String url) {
            db = helper.getWritableDatabase();
            int raw = db.delete("downloadInfo", "url=?", new String[]{url});
            if (raw != 0) {
                db.close();
                return true;
            }
            db.close();
            return false;
        }
    
        @Override
        public synchronized ThreadInfo findThread(String url,int threadId) {
            db = helper.getReadableDatabase();
            Cursor cursor = db.rawQuery("select threadProgress,start,end from downloadInfo where url=? and threadId=?", new String[]{url,threadId+""});
            if (cursor.moveToNext()) {
                int  threadProgress = cursor.getInt(0);
                int  start = cursor.getInt(1);
                int  end = cursor.getInt(2);
                ThreadInfo threadInfo = new ThreadInfo(threadProgress, start, end);
                return threadInfo;
            }
            return null;
        }
    
        @Override
        public synchronized boolean   isTheradExist(String url, int threadId) {
            db = helper.getReadableDatabase();
            Cursor cursor = db.rawQuery("select * from downloadInfo where url=? and threadId=?", new String[]{url,threadId+""});
            boolean exist = cursor.moveToNext();
            cursor.close();
            db.close();
            return exist;
        }
    
        @Override
        public synchronized long getTaskProgress(String url) {
            db = helper.getReadableDatabase();
            Cursor cursor = db.rawQuery("select taskProgress from downloadInfo where url=?", new String[]{url});
            if(cursor.moveToNext()){
                long taskProgress = cursor.getInt(0);
                cursor.close();
                db.close();
                return taskProgress;
            }
            cursor.close();
            db.close();
    
            return 0;
        }
    
        @Override
        public synchronized boolean addTaskProgress(DownloadInfo downloadInfo) {
            if (isTaskExist(downloadInfo.getUrl())) {
                db = helper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("taskProgress", downloadInfo.getProgress());
                values.put("url", downloadInfo.getUrl());
                long raw = db.update("downloadInfo", values, "url=?", new String[]{downloadInfo.getUrl()});
                if (raw != -1){
                    db.close();
                    return true;
                }
                db.close();
            }else{
                db = helper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("taskProgress", downloadInfo.getProgress());
                values.put("url", downloadInfo.getUrl());
                long raw = db.insert("downloadInfo", null, values );
                if (raw != -1){
                    db.close();
                    return true;
                }
                db.close();
            }
            return false;
        }
    
        @Override
        public synchronized long getThreadProgress(ThreadInfo info) {
            db = helper.getReadableDatabase();
            Cursor cursor = db.rawQuery("select finished from downloadInfo where url=? and threadId=?", new String[]{info.getUrl(),info.getThreadId()+""});
            if(cursor.moveToNext()){
                long threadProgress = cursor.getInt(0);
                    cursor.close();
                    db.close();
                Log.e("", "threadProgress----"+threadProgress);
    
                    return threadProgress;
            }
    
            cursor.close();
            db.close();
            return 0;
        }
    
        @Override
        public synchronized boolean addThreadProgress(ThreadInfo threadInfo) {
            if (isTheradExist(threadInfo.getUrl(), threadInfo.getThreadId())) {
                db = helper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("finished", threadInfo.getThreadProgress());
                int raw = db.update("downloadInfo", values , "url=? and threadId=?", new String[]{threadInfo.getUrl(),threadInfo.getThreadId()+""});
                if (raw > 0) {
                    db.close();
                    return true;
                }
                db.close();
            }else{
                db = helper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("finished", threadInfo.getThreadProgress());
                values.put("threadId", threadInfo.getThreadId());
                values.put("url", threadInfo.getUrl());
                long raw = db.insert("downloadInfo", null ,values);
                if (raw != -1) {
                    db.close();
                    return true;
                }
                db.close();
            }
    
            return false;
        }
    
        @Override
        public boolean isTaskExist(String url) {
            db = helper.getReadableDatabase();
            Cursor cursor = db.rawQuery("select taskProgress from downloadInfo where url=?", new String[]{url});
            if(cursor != null) {
                if (cursor.moveToNext()) {
                    cursor.close();
                    db.close();
                    return true;
                }
            }
            cursor.close();
            db.close();
            return false;
        }
    }
    

    데이터베이스 조작 인터페이스
    package com.yu.db;
    
    import com.yu.bean.DownloadInfo;
    import com.yu.bean.ThreadInfo;
    
    /** *        * @author Administrator * */
    public abstract interface DownloadDBInterface {
    
        /** *        * @param threadInfo * @return */
        public  abstract    boolean insertThread(ThreadInfo threadInfo);
    
        /** *   url       * @param url * @return */
        public abstract boolean  deleteThread(String  url);
    
        /** *   url       * @param url * @return */
        public abstract ThreadInfo findThread(String  url,int threadId);
    
        /** *   url threadId           * @param url * @param threadId * @return */
        public abstract boolean isTheradExist(String url,int threadId);
    
        /** *   url           * @param url * @return */
        public abstract boolean isTaskExist(String url);
    
        /** *        * @return */
        public abstract long getTaskProgress(String url);
    
        /** *        * @return */
        public abstract boolean addTaskProgress(DownloadInfo downloadInfo);
        /** *        * @param url * @return */
        public abstract boolean addThreadProgress(ThreadInfo threadInfo);
    
        /** *        * @return */
        public abstract long getThreadProgress(ThreadInfo threadInfo);
    
    }
    

    좋은 웹페이지 즐겨찾기