第一步:设计布局文件如下:
<?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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.multithread_download.MainActivity" android:orientation="vertical"> <EditText android:id="@+id/id_url" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="input download url"/> <EditText android:id="@+id/id_targetFile" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="input targetFile"/> <Button android:id="@+id/id_download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="download" /> <ProgressBar android:layout_marginTop="5dp" android:id="@+id/id_progress" android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:max="100" /> <ImageView android:id="@+id/id_show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@mipmap/ic_launcher" /> </LinearLayout>
运行效果如下:(以下载图片为例)
第二步:编写下载工具类(多线程下载)
第三步:在MainActivity中编写代码,实现下载功能并预览图片,因为下载任务是个耗时操作应放在线程中操作,对于UI的改变操作应该使用handler进行处理,使用定时器和ProgressBard对下载状态进行更新和显示:public class DownUtil{ // 定义下载路径 private String path; // 要下载到的文件路径 private String targetFile; private int threadNum; private DownThread[] threads; // 定义下载文件的总大小 private int fileSize; public DownUtil(){ } // 初始化下载所需数据 public DownUtil(String path,String targetFile,int threadNum){ this.path = path; this.targetFile = targetFile; this.threadNum = threadNum; threads = new DownThread[threadNum]; } // 定义下载方法 public void download(){ try { URL url = new URL(path); // 获得到资源的链接 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("Accept-Encoding","identity"); conn.setConnectTimeout(5*10000); conn.setRequestMethod("GET"); conn.setRequestProperty("Charset","UTF-8"); conn.setRequestProperty("Accept-language","zh-CN"); conn.setRequestProperty("Connection","Keep-Alive"); // 获取要下载的文件的大小 fileSize = conn.getContentLength(); conn.disconnect(); int currentPartSize = fileSize / threadNum + 1; // 创建一个文件读取流去读取targetFile所定义的文件,权限为读写 RandomAccessFile file = new RandomAccessFile(targetFile,"rw"); file.setLength(fileSize); file.close(); for (int i = 0; i <threadNum ; i++) { int startPos = i*currentPartSize; RandomAccessFile currentPart = new RandomAccessFile(targetFile,"rw"); currentPart.seek(startPos); threads[i] = new DownThread(startPos,currentPartSize,currentPart); threads[i].start(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public double getConpleteRate(){ int sumSize = 0; for(int i=0;i<threadNum;i++){ sumSize += threads[i].length; } return sumSize * 1.0 / fileSize; } // 定义下载线程类 private class DownThread extends Thread{ private int startPos; private int currentPartSize; // 定义当前线程需要下载的文件块 private RandomAccessFile currentPart; // 定义该线程需要下载的字节数 public int length; public DownThread(int startPos,int currentPartSize,RandomAccessFile currentPart){ this.startPos = startPos; this.currentPartSize = currentPartSize; this.currentPart = currentPart; } public void run(){ try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5*1000); conn.setRequestMethod("GET"); conn.setRequestProperty("Charset","utf-8"); conn.setRequestProperty("Connection","Keep-Alive"); InputStream is = conn.getInputStream(); skipFully(is,this.startPos); byte buf[] = new byte[1024]; int hasRead = 0; while(length < currentPartSize && (hasRead=is.read(buf))>0){ currentPart.write(buf,0,hasRead); length +=hasRead; } currentPart.close(); is.close(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } // 定义一个为InputStream跳过bytes字节的方法 // 因为输入流的skip方法有时候会读取不完整 所以创建一个读取方法循环读取知道读完为止 public static void skipFully(InputStream is,long bytes){ long remainning = bytes; long len = 0; while(remainning > 0){ try { len = is.skip(remainning); remainning -= len; } catch (IOException e) { e.printStackTrace(); } } } }
注意事项:需要在AndroidManifest.xml中定义如下权限:public class MainActivity extends Activity { EditText url,target; Button btn_download; ProgressBar bar; ImageView show; DownUtil downUtill; // 定义进度条的状态值 private int mDownStatus; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); url = (EditText) findViewById(R.id.id_url); target = (EditText) findViewById(R.id.id_targetFile); btn_download = (Button) findViewById(R.id.id_download); bar = (ProgressBar) findViewById(R.id.id_progress); show = (ImageView) findViewById(R.id.id_show); final Handler handler = new Handler(){ public void handleMessage(Message msg){ if(msg.what == 0x123){ if(mDownStatus == 100){ Toast.makeText(MainActivity.this, "download task done", Toast.LENGTH_SHORT).show(); Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/a.chm"); show.setImageBitmap(bitmap); } bar.setProgress(mDownStatus); } } }; btn_download.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { downUtill = new DownUtil(url.getText().toString(),target.getText().toString(),6); Log.i("KKK",url.getText().toString()+"-----"+target.getText().toString()); new Thread(){ public void run(){ downUtill.download(); final Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { double completeRate = downUtill.getConpleteRate(); mDownStatus = (int) completeRate * 100; Log.i("KKK",mDownStatus+"%"); handler.sendEmptyMessage(0x123); if(mDownStatus>=100){ Log.i("KKK","下载完成"); timer.cancel(); } } },0,100); } }.start(); } }); } }
<uses-permission android:name="android.permission.INTERNET"/> 联网权限 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 创建删除文件权限 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 写入SD卡权限所出现的问题:1. is.getContentLength结果为0,可能是要下载的资源没有给出下载权限 或是服务器没有给出Content-Length字段,需要换一个资源下载进行测试。2. getContentLength返回结果-1,可能是缺少 conn.setRequestProperty("Accept-Encoding","identity");这行代码。