Android实现网络下载一(单任务下载–支持断点续传)

说起下载,在Android的一些类似游戏宝的项目中会频繁使用,比如说需求要下载一个apk文件,并且要支持暂停、继续等要求。当然在GitHib上也有一些优秀的下载框架可以供我们使用,但是更多时候的一些特殊需求可能要手动实现,所以类似的下载功能还是自己实现比较好。

附上demo源码,GitHub代码后续上传,这里的链接还是csdn的。

点这里下载源码,快,戳我戳我…

q:486789970
email:mr.cai_cai@foxmail.com

下图是一个单任务下载的动态图:

效果图如下(多任务下载将在下篇文章中实现)

上面的效果图就是单任务下载,支持断点续传、实时进度更新、下载暂停、下载继续,下载完成自动安装等功能;同时包括网络下载请求和本地文件的存储。

上图下载通过Service端下载,使用广播通知给activity更新UI界面

下载流程如下:

  • 将下载URL和文件名等信息赋值给info对象
  • 通过Intent将info对象传递给Service,使用子线程联网获取文件长度
  • 拿到长度后启动下载任务类,并将下载信息传递过去
  • 下载任务类中保存下载URL到数据库(为断点续传做准备)后开始下载
  • 下载任务执行完成后使用广播通知activty界面进度条更新。

说的比较详细,其实很简单,看看代码就知道啦。往下看:

先是xml和activity

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".activity.MainActivity"><ProgressBarandroid:id="@+id/probar"style="?android:attr/progressBarStyleHorizontal"android:layout_width="match_parent"android:layout_height="30dp"android:padding="10dp"android:max="100" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="30dp"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:weightSum="3"android:orientation="horizontal"><TextViewandroid:id="@+id/txt_filename"android:layout_weight="1"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_alignParentTop="true"android:gravity="center"android:textSize="15dp"android:textColor="#000000"android:text="文件名" /><Buttonandroid:id="@+id/bt_start"android:layout_weight="1"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="15dp"android:textColor="#000000"android:text="开始" /><Buttonandroid:id="@+id/bt_stop"android:layout_weight="1"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="15dp"android:textColor="#000000"android:text="暂停" /></LinearLayout></RelativeLayout>
package com.cc.downloaddemo.activity;import android.Manifest;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.cc.downloaddemo.R;
import com.cc.downloaddemo.info.FileInfo;
import com.cc.downloaddemo.services.DownloadService;/*** 实现断点续传的Activity* @author*/
public class MainActivity extends Activity implements View.OnClickListener {//需要动态获取的权限private static final int REQUEST_EXTERNAL_STORAGE = 1;private static String[] PERMISSIONS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE};//        http://tfyxb2017-1251304591.file.myqcloud.com/qsfz/qsfz_mzyw_1663647040.apk
//        http://down.shouji.kuwo.cn/star/mobile/kwplayer_ar_pcguanwangmobile.apk//默认下载一个apk包private static final String URL = "http://down.shouji.kuwo.cn/star/mobile/kwplayer_ar_pcguanwangmobile.apk";//上下文public final Activity activity = MainActivity.this;//显示下载进度private ProgressBar probar;//文件名private TextView txt_filename;//开始暂停按钮private Button bt_start, bt_stop;//定义两个info实体类FileInfo fileInfo;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//获取权限MainActivity.verifyStoragePermissions(activity);//获取控件initView();//初始化数据initData();}/*** 给实体类添加一些数据*/private void initData() {//添加静态数据fileInfo = new FileInfo(0, URL, "酷我音乐", 0, 0);txt_filename.setText(fileInfo.getFilename());}/*** 实例化控件*/private void initView() {txt_filename = findViewById(R.id.txt_filename);probar = findViewById(R.id.probar);bt_start = findViewById(R.id.bt_start);bt_stop = findViewById(R.id.bt_stop);bt_start.setOnClickListener(this);bt_stop.setOnClickListener(this);//注册广播接收器,并设置flag(更新)IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(DownloadService.ACTION_UPDATE);registerReceiver(broadcastReceiver, intentFilter);}@Overrideprotected void onDestroy() {super.onDestroy();//销毁广播unregisterReceiver(broadcastReceiver);}/*** 点击事件* @param v*/@Overridepublic void onClick(View v) {int temdId = v.getId();if(temdId == R.id.bt_start){//通知Service开始下载,设置flag(开始),并将下载文件传过去//这里注意,服务需要在xml里面注册Intent intent = new Intent(activity, DownloadService.class);intent.setAction(DownloadService.ACTION_START);intent.putExtra("fileInfo", fileInfo);startService(intent);}else if(temdId == R.id.bt_stop){bt_start.setText("继续");//通知Service暂停或者继续下载,并设置Stop(暂停或者继续),并将下载文件传过去Intent intent = new Intent(activity, DownloadService.class);intent.setAction(DownloadService.ACTION_STOP);intent.putExtra("fileInfo", fileInfo);startService(intent);}}/*** 定义广播更新UI*/BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {//拿到广播通知的更新tag,并获取到当前下载的长度百分比,显示在下载进度条上if(intent.getAction().equals(DownloadService.ACTION_UPDATE)){int finish = intent.getIntExtra("finished", 0);probar.setProgress(finish);//当finish为100时,说明已经全部下载结束。if(finish == 100){Toast.makeText(getApplicationContext(), fileInfo.getFilename() + "下载完成", Toast.LENGTH_SHORT).show();bt_start.setVisibility(View.INVISIBLE);bt_stop.setText("下载完成");}}}};/*** 验证读写权限* @param activity*/public static void verifyStoragePermissions(Activity activity) {// Check if we have write permissionint permission = ActivityCompat.checkSelfPermission(activity,Manifest.permission.WRITE_EXTERNAL_STORAGE);if (permission != PackageManager.PERMISSION_GRANTED) {// We don't have permission so prompt the userActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);}}}
注:在activity中记得申请读写权限,避免报错;

下来就是Service的:

package com.cc.downloaddemo.services;import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import com.cc.downloaddemo.info.FileInfo;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;/*** 下载service*/
public class DownloadService extends Service {//通过URL地址下载apk文件并保存在本地存储路径public static final String DOWNLOAD_PATH =Environment.getExternalStorageDirectory().getAbsolutePath() +"/downloads/";//开始下载public static final String ACTION_START = "ACTION_START";//暂停下载public static final String ACTION_STOP = "ACTION_STOP";//更新下载进度public static final String ACTION_UPDATE = "ACTION_UPDATE";//定义handler的flagpublic static final int MSG_INIT = 0;//异步下载任务类private DownloadTask downloadTask = null;/*** 执行下载、暂停、继续下载* @param intent* @param flags* @param startId* @return*/@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//获取从Activity传过去的参数,判断intent的值。if(ACTION_START.equals(intent.getAction())){FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");//启动初始化子线程,联网下载文件内容。new InitThread(fileInfo).start();} else if (ACTION_STOP.equals(intent.getAction())) {//暂停下载时,更改下载任务类的flag便会自动暂停。if(downloadTask != null){downloadTask.ispause = true;}}return super.onStartCommand(intent, flags, startId);}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}/*** 网络内容下载完成后* 通过handler启动异步任务类开始正式下载文件。*/Handler handler = new Handler(){@Overridepublic void handleMessage(Message msg) {//是否为初始化消息if(msg.what == MSG_INIT){//获取发回来的信息FileInfo fileInfo = (FileInfo) msg.obj;//启动一个下载任务,将文件内容传递过去。downloadTask = new DownloadTask(DownloadService.this, fileInfo);//启动下载方法downloadTask.download();}}};/*** 定义子线程用来下载*/class InitThread extends Thread{//定义一个文件用来接收下载信息private FileInfo mFileInfo = null;//启动线程时传入的对象public InitThread(FileInfo mFileInfo) {this.mFileInfo = mFileInfo;}@Overridepublic void run() {//定义connHttpURLConnection conn = null;//定义文件内容访问类RandomAccessFile raf = null;try {//连接网络文件URL url = new URL(mFileInfo.getUrl());conn = (HttpURLConnection) url.openConnection();conn.setReadTimeout(3000);conn.setRequestMethod("GET");//自定义一个长度,注意尽量用long(处理下载消息进度百分比时好计算)long length = -1;//下载完成if(conn.getResponseCode() == HttpURLConnection.HTTP_OK ){//获取文件总长度赋值给lengthlength = conn.getContentLength();}//长度不能小于0if(length <= 0){return;}//判断该路径存在与否File dir = new File(DOWNLOAD_PATH);//文件不存在时创建if(!dir.exists()){dir.mkdir();}//本地创建一个文件File file = new File(dir, mFileInfo.getFilename());//输出流raf = new RandomAccessFile(file, "rwd");//设置本地的文件长度(等于下载文件的总长度。杜绝浪费资源)raf.setLength(length);//赋值给定义的对象长度mFileInfo.setLength(length);//启动flag,将文件发送给handler处理handler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();}catch (Exception e){e.printStackTrace();}finally {try {//关闭资源raf.close();conn.disconnect();}catch (Exception e){e.printStackTrace();}}}}}
注:记得下载完之后关闭conn连接和流;

异步下载任务类(重中之重):

package com.cc.downloaddemo.services;import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import com.cc.downloaddemo.db.dao.ThreadDao;
import com.cc.downloaddemo.db.impl.ThreadDaoImpl;
import com.cc.downloaddemo.info.FileInfo;
import com.cc.downloaddemo.info.ThreadInfo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;/*** 任务下载类* @author*/
public class DownloadTask {//上下文private Context context;//定义数据库线程对象文件private FileInfo fileInfo;//实例化接口public static ThreadDao threadDao = null;//定义finished(当前已下载的长度)private int finished = 0;//默认直接下载public boolean ispause = false;/*** 构造方法* @param context* @param fileInfo*/public DownloadTask(Context context, FileInfo fileInfo) {this.context = context;//拿到handler传过来的文件this.fileInfo = fileInfo;//实例化接口实现类threadDao = new ThreadDaoImpl(context);}/*** 下载方法*/public void download(){//下载任务一开始,先查询数据库,看是否有文件在等待下载。//读取数据库的所有线程信息List<ThreadInfo> threads = threadDao.getThreads(fileInfo.getUrl());//定义一个线程内容类ThreadInfo threadInfo;//当list的size为0时,说明没有等待下载的线程,为1时则有。if(threads.size() == 0){//开始初始化线程文件信息threadInfo = new ThreadInfo(0, fileInfo.getUrl(), 0, (int)fileInfo.getLength(), 0);}else{Log.i("download", "threads = " + threads.size());//因为一开始的静态内容只有一条,所以这里就直接拿下标为0的啦。//正式开发中,如果有多条的话,需要循环取出threadInfo = threads.get(0);}//创建子线程进行下载new DownloadThread(threadInfo).start();}/*** 下载线程*/class DownloadThread extends Thread{//继续定义一个临时线程对象private ThreadInfo threadInfo;//构造public DownloadThread(ThreadInfo threadInfo) {this.threadInfo = threadInfo;}@Overridepublic void run() {//当前线程信息在数据库没有时,说明是新线程下载,将新线程同步给数据库。if(!threadDao.isExits(threadInfo.getUrl(), threadInfo.getId())){threadDao.insertThread(threadInfo);}//定义conn、输入流和文件内容访问类HttpURLConnection conn = null;InputStream inputStream = null;RandomAccessFile raf = null;try{//开始联网下载URL url = new URL(threadInfo.getUrl());conn = (HttpURLConnection) url.openConnection();conn.setReadTimeout(3000);conn.setRequestMethod("GET");//设置下载位置(通过当前开始值和现在值判断下载位置)int start = threadInfo.getStart() + threadInfo.getFinished();conn.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd());//设置文件写入位置File file = new File(DownloadService.DOWNLOAD_PATH, fileInfo.getFilename());raf = new RandomAccessFile(file, "rwd");raf.seek(start);//定义一个广播,用来更新下载进度Intent intent = new Intent(DownloadService.ACTION_UPDATE);//finished += threadInfo.getFinished();//开始下载if(conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){//读取数据inputStream = conn.getInputStream();byte[] buffer = new byte[1024 * 4];int len = -1;long time = System.currentTimeMillis();while ((len = inputStream.read(buffer)) != -1){//写入文件raf.write(buffer, 0, len);//把下载进度发送给广播finished += len;if(System.currentTimeMillis() - time > 500){time = System.currentTimeMillis();intent.putExtra("finished", (int)(finished / (float)fileInfo.getLength() * 100));Log.i("cghangdu===", (int)(finished / (float)fileInfo.getLength() * 100) + "");context.sendBroadcast(intent);}//在下载暂停时,保存下载进度if(ispause){threadDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), finished);Log.i("下载暂停", "finished---->" + finished);return;}}intent.putExtra("finished", 100);context.sendBroadcast(intent);//下载完成,删除数据库所保存的线程信息threadDao.deleteThread(threadInfo.getUrl(), threadInfo.getId());openFile(file);}}catch (Exception e){e.printStackTrace();;}finally {conn.disconnect();try {inputStream.close();raf.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 自动安装* @param file*/private void openFile(File file) {// TODO Auto-generated method stubLog.e("OpenFile", file.getName());Intent intent = new Intent();intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setAction(android.content.Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");context.startActivity(intent);}}
注:数据库代码在下面

先创建数据库:

package com.cc.downloaddemo.db;import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;/*** 数据库帮助类* @author*/
public class DBHelper extends SQLiteOpenHelper {public static final int DB_VERSION = 1;public static final String DB_NAME = "download.db";public static final String DB_TABLE_NAME = "thread_info";public static final String DB_CREATE_SQL = "create table " + DB_TABLE_NAME + "("+ "_id integer primary key autoincrement,"+ "thread_id Integer,"+ "url text,"+ "start Integer,"+ "end Integer,"+ "finished Integer)";public static final String DB_DROP_SQL = "drop table if exists thread_info";public DBHelper(Context context) {super(context, DB_NAME, null, DB_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(DB_CREATE_SQL);}@Overridepublic void onUpgrade(SQLiteDatabase db, int i, int i1) {db.execSQL(DB_DROP_SQL);db.execSQL(DB_CREATE_SQL);}
}

再定义保存下载进度等接口:

package com.cc.downloaddemo.db.dao;import com.cc.downloaddemo.info.ThreadInfo;import java.util.List;/*** 线程数据访问接口* @author*/
public interface ThreadDao {/*** 插入一条线程信息* @param threadInfo*/public void insertThread(ThreadInfo threadInfo);/*** 更新线程信息* @param url* @param thread_id* @param finished*/public void updateThread(String url, int thread_id, int finished);/*** 删除线程* @param url* @param thread_id*/public void deleteThread(String url, int thread_id);/*** 查询线程* @param url* @return*/public List<ThreadInfo> getThreads(String url);/*** 判断线程是否已存在* @param url* @param thread_id* @return*/public boolean isExits(String url, int thread_id);}

接口实现类:

package com.cc.downloaddemo.db.impl;import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;import com.cc.downloaddemo.db.DBHelper;
import com.cc.downloaddemo.db.dao.ThreadDao;
import com.cc.downloaddemo.info.ThreadInfo;import java.util.ArrayList;
import java.util.List;/*** 线程数据接口实现类* @author*/
public class ThreadDaoImpl implements ThreadDao {private DBHelper dbHelper = null;public ThreadDaoImpl(Context context) {dbHelper = new DBHelper(context);}/*** 实现插入方法* @param threadInfo*/@Overridepublic void insertThread(ThreadInfo threadInfo) {SQLiteDatabase db = dbHelper.getWritableDatabase();db.execSQL("insert into thread_info(thread_id, url, start, end, finished) values(?, ?, ?, ?, ?)",new Object[]{threadInfo.getId(), threadInfo.getUrl(), threadInfo.getStart(), threadInfo.getEnd(), threadInfo.getFinished()});db.close();}/*** 更新* @param url* @param thread_id* @param finished*/@Overridepublic void updateThread(String url, int thread_id, int finished) {SQLiteDatabase db = dbHelper.getWritableDatabase();ContentValues values = new ContentValues();values.put("finished", finished);long ret = -1;do {ret = db.update("thread_info", values, "thread_id = ? and url = ?",new String[] { thread_id + "", url });} while (ret < 0);}@Overridepublic void deleteThread(String url, int thread_id) {SQLiteDatabase db = dbHelper.getWritableDatabase();db.execSQL("delete from thread_info");db.close();}@Overridepublic List<ThreadInfo> getThreads(String url) {List<ThreadInfo> infos = new ArrayList<>();SQLiteDatabase db = dbHelper.getWritableDatabase();Cursor cursor = db.rawQuery("select * from thread_info where url = ?", new String[]{url});while (cursor.moveToNext()){ThreadInfo info = new ThreadInfo();info.setId(cursor.getInt(cursor.getColumnIndex("thread_id")));info.setUrl(cursor.getString(cursor.getColumnIndex("url")));info.setStart(cursor.getInt(cursor.getColumnIndex("start")));info.setEnd(cursor.getInt(cursor.getColumnIndex("end")));info.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));infos.add(info);}db.close();return infos;}@Overridepublic boolean isExits(String url, int thread_id) {SQLiteDatabase db = dbHelper.getWritableDatabase();Cursor cursor =db.rawQuery("select * from thread_info where url = ? and thread_id = ?", new String[]{url, thread_id + ""});boolean exits = cursor.moveToNext();cursor.close();db.close();return exits;}
}
注:实体类在最后面:

两个实体类:

package com.cc.downloaddemo.info;import java.io.Serializable;/*** 本地存储文件信息* @author*/
public class FileInfo implements Serializable {//idprivate int id;//urlprivate String url;//文件名private String filename;//长度private long length;//下载了多少private int finished;public FileInfo() {super();}public FileInfo(int id, String url, String filename, long length, int finished) {this.id = id;this.url = url;this.filename = filename;this.length = length;this.finished = finished;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getFilename() {return filename;}public void setFilename(String filename) {this.filename = filename;}public long getLength() {return length;}public void setLength(long length) {this.length = length;}public int getFinished() {return finished;}public void setFinished(int finished) {this.finished = finished;}@Overridepublic String toString() {return "FileInfo{" +"id=" + id +", url='" + url + '\'' +", filename='" + filename + '\'' +", length=" + length +", finished=" + finished +'}';}
}

第二个:

package com.cc.downloaddemo.info;/*** 数据库线程信息* @author*/
public class ThreadInfo {//idprivate int id;//urlprivate String url;//从哪里下载private int start;//结束private int end;//下载了多少private int finished;public ThreadInfo() {super();}public ThreadInfo(int id, String url, int start, int end, int finished) {this.id = id;this.url = url;this.start = start;this.end = end;this.finished = finished;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public int getStart() {return start;}public void setStart(int start) {this.start = start;}public int getEnd() {return end;}public void setEnd(int end) {this.end = end;}public int getFinished() {return finished;}public void setFinished(int finished) {this.finished = finished;}@Overridepublic String toString() {return "ThreadInfo{" +"id=" + id +", url='" + url + '\'' +", start=" + start +", end=" + end +", finished=" + finished +'}';}}

以上就是所有的代码

附上demo源码,GitHub代码后续上传,这里的链接还是csdn的。

点这里下载源码,快,戳我戳我…

q:486789970
email:mr.cai_cai@foxmail.com

如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。

                                             ---财财亲笔

Android实现网络下载一(单任务下载--支持断点续传)相关推荐

  1. 基于android的网络音乐播放器-下载完成后下拉音乐列表刷新(八)

    作为android初学者,最近把疯狂android讲义和疯狂Java讲义看了一遍,看到书中介绍的知识点非常多,很难全部记住,为了更好的掌握基础知识点,我将开发一个网络音乐播放器-EasyMusic来巩 ...

  2. Android实现网络下载二(多任务下载--支持断点续传)

    Android实现网络下载二(多任务下载–支持断点续传) 上文中说了单任务的断点续传,这篇文章就说说多任务下载,不啰嗦了,直接进入正题. 附上demo源码,GitHub代码后续上传,这里的链接还是cs ...

  3. Android开发本地及网络Mp3音乐播放器(十五)网络音乐及歌词下载功能实现

    实现功能: 实现网络音乐歌词下载功能(下载音乐的同时,下载对应歌词) 下载好的歌词目前不在播放器内,可以通过文件浏览器查看. 后续将博文,将实现本地音乐歌词下载和已下载音乐扫描功能. 因为,没有自己的 ...

  4. Android FrameWork学习(一)Android 7 0系统源码下载 编译

    最近计划着研究下 Android 7.0 的系统源码,之前也没做过什么记录,这次正好将学习的内容记录下来,方便以后复习巩固. 既然要学习我们的系统源码,那我们第一步要做的就是下载源码并进行编译了. # ...

  5. android通知栏应用程序更新,Android App自动更新之通知栏下载

    本文实例为大家分享了Android App自动更新通知栏下载的具体代码,供大家参考,具体内容如下 版本更新说明 这里有调用UpdateService启动服务检查下载安装包等 1. 文件下载,下完后写入 ...

  6. Android FrameWork学习(一)Android 7.0系统源码下载\编译

    重点内容 转载. http://blog.csdn.net/cjpx00008/article/details/60474883 硬件环境要求 1. 编译环境 按照官方的说法,编译Android 2. ...

  7. Swift基础之使用Alamofire库进行网络请求和断点下载

    好久没有写过Swift相关的文章博客了,这里我就展示一下关于使用Alamofire库的方法 1.什么是Alamofire (1)Alamofire 的前身是 AFNetworking.AFNetwor ...

  8. Android自带的下载功能,不需要断点续传、大文件下载、通知栏显示等窝心问题,四行代码全部搞定(经测)

    简单使用:         完成一个下载任务只需要4行代码,什么断点续传,大文件下载,通知栏进度显示-.都不需要你操心. //创建下载任务,downloadUrl就是下载链接 DownloadMana ...

  9. android源码下载方法 批量下载 基于windows os

    安装win版的Gitbash, 在这里 http://msysgit.googlecode.com/files/Git-1.6.0.2-preview20080923.exe. 选择默认安装路径(否则 ...

最新文章

  1. 创建ContentProvider的详细步骤
  2. Python 技术篇 - pip下载、安装慢解决方法,更改pip数据源实现秒速下载,配置阿里云国内镜像实例演示
  3. 通俗易懂解释一下C++的构造函数是怎样的呢?
  4. Enterprise Library 4.0
  5. 【数据结构总结】第七章 查找
  6. @Autowired @Primary @Qualifier
  7. mysql乐观锁与事务_[数据库事务与锁]详解七: 深入理解乐观锁与悲观锁
  8. react-native gradle
  9. “猜你喜欢”是怎么猜中你心思的?
  10. 如何在验证集加噪声_图像去噪:如何去其糟粕,取其精华?
  11. VB.NET的数据库基础编程[zz]
  12. 程序员生存定律--如何尽快变的稍微专业一点
  13. DP--Tetrahedron
  14. Xmind模板文档分享——知识结构(5)
  15. 微信小程序集成融云 SDK (即时通讯) 集成必备条件
  16. 推荐网络带宽控制软件Netlimiter
  17. flv播放器V2.6.5官方版【免费的FLV视频播放器】
  18. 决策树入门案例:泰坦尼克号幸存者预测
  19. mysql8.0.21官网安装步骤_MySQL8.0.21安装步骤及出现问题解决方案
  20. 骆昊python100天 github_GitHub - zj775230/Python-100-Days: Python - 100天从新手到大师

热门文章

  1. 真相,为什么大厂一边裁员,一边招人?
  2. Win7下连远程桌面 窗口 全屏 切换
  3. erdas裁剪影像_ERDAS软件应用(一)遥感影像数据裁剪
  4. 我们研究了853场世界杯比赛,发现了这几条稳赔不赚的竞猜攻略
  5. 操作系统 进程调度之轮换调度(RR调度)
  6. PLSA概率潜在语义分析数学推导
  7. 网络安全(一):信息收集之玩转nmap(理论篇)
  8. C++实现双人中国象棋(一)——算法篇(附完整代码)
  9. Mybatis入门学习七:日志
  10. OpenMV物品清单