一、排坑
HttpClient、Header飘红
Android 6.0 已经移除了httpClient
module下的build.gradle中加入:
android{ useLibrary 'org.apache.http.legacy' }
导入jar包
放入libs文件夹,右键选择add as library
退出整个程序
MainActivity内加入
public static List<Activity> activityList = new LinkedList();
public void exit() { for(Activity act:activityList) { act.finish(); } System.exit(0); }
在每个activity的oncreat里面写上如下这么一句代码,记得要写在 setContentView 之后:
MainActivity.activityList.add(this)
退出
exit();
存储路径
sdcard:实际上只是一个快捷方式,指向了/storage/self/primary
下载速度
使用BufferedRandomAccessFile代替RandomAccessFile
真机比模拟器明显快很多
二、HttpClient
public class MainActivity extends Activity { Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { Toast.makeText(MainActivity.this, (String)msg.obj, 0).show(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void get(View v){ EditText et_name = (EditText) findViewById(R.id.et_name); EditText et_pass = (EditText) findViewById(R.id.et_pass); final String name = et_name.getText().toString(); final String pass = et_pass.getText().toString(); Thread t = new Thread(){ @Override public void run() { String path = "http://10.32.189.73:8080/d0301/LoginServlet?username=" + URLEncoder.encode(name) + "&password=" + pass; //使用httpClient框架做get方式提交 //1.创建HttpClient对象 HttpClient hc = new DefaultHttpClient(); //2.创建httpGet对象,构造方法的参数就是网址 HttpGet hg = new HttpGet(path); //3.使用客户端对象,把get请求对象发送出去 try { HttpResponse hr = hc.execute(hg); //拿到响应头中的状态行 StatusLine sl = hr.getStatusLine(); if(sl.getStatusCode() == 200){ //拿到响应头的实体 HttpEntity he = hr.getEntity(); //拿到实体中的内容,其实就是服务器返回的输入流 InputStream is = he.getContent(); String text = Utils.getTextFromStream(is); //发送消息,让主线程刷新ui显示text Message msg = handler.obtainMessage(); msg.obj = text; handler.sendMessage(msg); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); } public void post(View v){ EditText et_name = (EditText) findViewById(R.id.et_name); EditText et_pass = (EditText) findViewById(R.id.et_pass); final String name = et_name.getText().toString(); final String pass = et_pass.getText().toString(); Thread t = new Thread(){ @Override public void run() { String path = "http://10.32.189.73:8080/d0301/LoginServlet"; //1.创建客户端对象 HttpClient hc = new DefaultHttpClient(); //2.创建post请求对象 HttpPost hp = new HttpPost(path); //封装form表单提交的数据 BasicNameValuePair bnvp = new BasicNameValuePair("username", name); BasicNameValuePair bnvp2 = new BasicNameValuePair("password", pass); List<BasicNameValuePair> parameters = new ArrayList<BasicNameValuePair>(); //把BasicNameValuePair放入集合中 parameters.add(bnvp); parameters.add(bnvp2); try { //要提交的数据都已经在集合中了,把集合传给实体对象 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "utf-8"); //设置post请求对象的实体,其实就是把要提交的数据封装至post请求的输出流中 hp.setEntity(entity); //3.使用客户端发送post请求 HttpResponse hr = hc.execute(hp); if(hr.getStatusLine().getStatusCode() == 200){ InputStream is = hr.getEntity().getContent(); String text = Utils.getTextFromStream(is); //发送消息,让主线程刷新ui显示text Message msg = handler.obtainMessage(); msg.obj = text; handler.sendMessage(msg); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); } }
三、异步HttpClient框架
源码:https://github.com/loopj/android-async-http
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void get(View v){ EditText et_name = (EditText) findViewById(R.id.et_name); EditText et_pass = (EditText) findViewById(R.id.et_pass); final String name = et_name.getText().toString(); final String pass = et_pass.getText().toString(); String url = "http://10.32.189.73:8080/d0301/LoginServlet?username=" + URLEncoder.encode(name) + "&password=" + pass; //创建异步httpclient AsyncHttpClient ahc = new AsyncHttpClient(); //发送get请求提交数据 ahc.get(url, new MyResponseHandler()); } public void post(View v){ EditText et_name = (EditText) findViewById(R.id.et_name); EditText et_pass = (EditText) findViewById(R.id.et_pass); final String name = et_name.getText().toString(); final String pass = et_pass.getText().toString(); String url = "http://10.32.189.73:8080/d0301/LoginServlet"; //创建异步httpclient AsyncHttpClient ahc = new AsyncHttpClient(); //发送post请求提交数据 //把要提交的数据封装至RequestParams对象 RequestParams params = new RequestParams(); params.add("username", name); params.add("password", pass); ahc.post(url, params, new MyResponseHandler()); } class MyResponseHandler extends AsyncHttpResponseHandler { //请求服务器成功时,此方法调用 @Override public void onSuccess(int statusCode, org.apache.http.Header[] headers, byte[] responseBody) { Toast.makeText(MainActivity.this, new String(responseBody), Toast.LENGTH_SHORT).show(); } //请求失败此方法调用 @Override public void onFailure(int statusCode, org.apache.http.Header[] headers, byte[] responseBody, Throwable error) { Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT).show(); } } }
四、断点续传多线程下载器
添加BufferedRandomAccessFile
源码:https://files.cnblogs.com/files/index42/BufferedRandomAccessFile.zip
public class MainActivity extends AppCompatActivity { private static int ThreadCount = 3; private static int finishedThread = 0; public static List<Activity> activityList = new LinkedList(); private String filepath= Environment.getExternalStorageDirectory()+"/ahmk/"; private String downloadFilename=filepath+"test.zip"; private static final String TAG = "MainActivity"; int currentProgress; private int length; private String fileName = "test.zip"; private static class Lock{}; private static Object lock=new Lock(); //确定下载地址 private String path = "https://files.cnblogs.com/files/index42/" + fileName; private ProgressBar pb; TextView tv; Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { //把变量改成long,在long下运算 tv.setText((long) pb.getProgress() * 100 / pb.getMax() + "%"); } }; Handler handler2 = new Handler() { public void handleMessage(android.os.Message msg) { //把变量改成long,在long下运算 tv.setText("文件已存在"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MainActivity.activityList.add(this); Log.e(TAG, filepath); pb = (ProgressBar) findViewById(R.id.pb); tv = (TextView) findViewById(R.id.tv); if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED ){ //申请权限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},1); }else { //把动作告诉系统 } } public void click(View v) { Thread t = new Thread() { @Override public void run() { //发送get请求,请求这个地址的资源 try { File file = new File(downloadFilename); if(file.exists()) { handler2.sendEmptyMessage(1); Log.e(TAG, "已经存在文件"); return; } URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); Log.e(TAG, conn.getResponseCode()+""); if (conn.getResponseCode() == 200) { //拿到所请求资源文件的长度 length = conn.getContentLength(); Log.e(TAG, length+""); //设置进度条的最大值就是原文件的总长度 pb.setMax(length); Log.e(TAG, "run3: "); File dir = new File(filepath); if(!dir.exists()){ dir.mkdirs(); } //生成临时文件 BufferedRandomAccessFile raf = new BufferedRandomAccessFile(file, "rwd"); //设置临时文件的大小 raf.setLength(length); raf.close(); //计算出每个线程应该下载多少字节 int size = length / ThreadCount; for (int i = 0; i < ThreadCount; i++) { //计算线程下载的开始位置和结束位置 int startIndex = i * size; int endIndex = (i + 1) * size - 1; //如果是最后一个线程,那么结束位置写死 if (i == ThreadCount - 1) { endIndex = length - 1; } // System.out.println("线程" + i + "的下载区间是:" + startIndex + "---" + endIndex); new DownLoadThread(startIndex, endIndex, i).start(); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); } class DownLoadThread extends Thread{ int startIndex; int endIndex; int threadId; public DownLoadThread(int startIndex, int endIndex, int threadId) { super(); this.startIndex = startIndex; this.endIndex = endIndex; this.threadId = threadId; } @Override public void run() { //再次发送http请求,下载原文件 try { File progressFile = new File(filepath,threadId + ".txt"); //判断进度临时文件是否存在 if(progressFile.exists()){ FileInputStream fis = new FileInputStream(progressFile); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置 int lastProgress = Integer.parseInt(br.readLine()); startIndex += lastProgress; //把上次下载的进度显示至进度条 currentProgress += lastProgress; pb.setProgress(currentProgress); //发送消息,让主线程刷新文本进度 handler.sendEmptyMessage(1); fis.close(); } Log.e(TAG, "线程" + threadId + "的下载区间是:" + startIndex + "---" + endIndex); HttpURLConnection conn; URL url = new URL(path); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //设置本次http请求所请求的数据的区间 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); //请求部分数据,相应码是206 if(conn.getResponseCode() == 206){ //流里此时只有1/3原文件的数据 InputStream is = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; int total = 0; //拿到临时文件的输出流 File file = new File(downloadFilename); BufferedRandomAccessFile raf = new BufferedRandomAccessFile(file, "rwd"); //把文件的写入位置移动至startIndex raf.seek(startIndex); while((len = is.read(b)) != -1){ //每次读取流里数据之后,同步把数据写入临时文件 raf.write(b, 0, len); total += len; //Log.e(TAG, "线程" + threadId + "下载了" + total); //每次读取流里数据之后,把本次读取的数据的长度显示至进度条 currentProgress += len; pb.setProgress(currentProgress); //发送消息,让主线程刷新文本进度 handler.sendEmptyMessage(1); //生成一个专门用来记录下载进度的临时文件 BufferedRandomAccessFile progressRaf = new BufferedRandomAccessFile(progressFile, "rwd"); //每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中 progressRaf.write((total + "").getBytes()); progressRaf.close(); } Log.e(TAG, "线程" + threadId + "下载完毕-------------------小志参上!"); raf.close(); finishedThread++; synchronized (lock) { Log.e(TAG, "synchron"); if(finishedThread == ThreadCount){ for (int i = 0; i < ThreadCount; i++) { File f = new File(filepath, i + ".txt"); f.delete(); } Log.e(TAG, "over synchron"); finishedThread = 0; pb.setProgress(length); handler.sendEmptyMessage(1); } } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: //权限回调 if (grantResults[0]==PackageManager.PERMISSION_GRANTED&&grantResults[1]==PackageManager.PERMISSION_GRANTED){ }else { //提示用户权限未被授予 Log.e("MainActivity","未授予权限"); exit(); } break; } } public void exit() { for(Activity act:activityList) { act.finish(); } System.exit(0); } }
<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" tools:context=".MainActivity" android:orientation="vertical" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始下载" android:onClick="click" /> <ProgressBar android:id="@+id/pb" android:layout_width="match_parent" android:layout_height="wrap_content" style="@android:style/Widget.ProgressBar.Horizontal" /> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
五、HttpUtils的使用
源码:https://github.com/wyouflf/xUtils
public class MainActivity extends Activity { private TextView tv_failure; private TextView tv_progress; private ProgressBar pb; public static List<Activity> activityList = new LinkedList(); private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MainActivity.activityList.add(this); tv_failure = (TextView) findViewById(R.id.tv_failure); tv_progress = (TextView) findViewById(R.id.tv_progress); pb = (ProgressBar) findViewById(R.id.pb); if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED ){ //申请权限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},1); }else { //把动作告诉系统 } } public void click(View v){ HttpUtils utils = new HttpUtils(); String fileName = "test.zip"; //确定下载地址 String path = "https://files.cnblogs.com/files/index42/" + fileName; String filepath= Environment.getExternalStorageDirectory()+"/ahmk/"; String downloadFilename=filepath+"test.zip"; File file = new File(downloadFilename); if(file.exists()) { tv_progress.setText("已经存在文件"); Log.e(TAG, "已经存在文件"); return; } utils.download(path, //下载地址 downloadFilename, //文件保存路径 true,//是否支持断点续传 true, new RequestCallBack<File>() { //下载成功后调用 @Override public void onSuccess(ResponseInfo<File> arg0) { Toast.makeText(MainActivity.this, arg0.result.getPath(), Toast.LENGTH_SHORT).show(); } //下载失败调用 @Override public void onFailure(HttpException arg0, String arg1) { // TODO Auto-generated method stub tv_failure.setText(arg1); } @Override public void onLoading(long total, long current, boolean isUploading) { // TODO Auto-generated method stub super.onLoading(total, current, isUploading); pb.setMax((int)total); pb.setProgress((int)current); tv_progress.setText(current * 100 / total + "%"); } }); } public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: //权限回调 if (grantResults[0]==PackageManager.PERMISSION_GRANTED&&grantResults[1]==PackageManager.PERMISSION_GRANTED){ }else { //提示用户权限未被授予 Log.e("MainActivity","未授予权限"); exit(); } break; } } public void exit() { for(Activity act:activityList) { act.finish(); } System.exit(0); } }