概述

为什么是更好的Android多线程下载框架呢,原因你懂的,广告法嘛!

本篇我们我们就来聊聊多线程下载框架,先聊聊我们框架的特点:

  1. 多线程
  2. 多任务
  3. 断点续传
  4. 支持大文件
  5. 可以自定义下载数据库
  6. 高度可配置,像超时时间这类
  7. 业务数据和下载数据分离

下面我们在说下该框架能实现那些的应用场景:

  1. 该框架可以很方便的下载单个文件,并且显示各种状态,包括开始下载,下载中,下载失败,删除等状态。
  2. 也可以实现常见的需要下载功能应用,比如:某某手机助手,在该应用内可以说是下载是核心功能,所以对框架的稳定性,代码可靠性,框架扩展性依赖很大,所以该框架真是从这种出发点而生的。通常这类应用的表示形式分三个页面需要用到下载功能,一个列表用来显示来自业务数据的列表,在该列表右边可以点击单个条目,或者多选实现下载,点击每个条目进入详情,同时还有个一个下载管理,包括大概两个界面,正在下载,下载完成的,在这几个界面都需要一个核心的功能就是都可以暂停,恢复,删除并且能显示下载进度。在列表一个最重要的问题就是界面刷新,如果每次更新都刷新整个列表,那么这将是异常灾难,而我们这个框架正好解决了该问题,采用了回调单个条目并更新该条目的进度和状态。

该项目状态

该项目的雏形始于14年的公司项目需要用到多线程下载,但当时实现的单线程多任务断点续传,后面不断完善,在这之间遇到过很多坑,也对一个下载框架有了更深的认识,所以在16年又重写了该框架。

项目的Github地址:https://github.com/lifengsofts/AndroidDownloader

项目的官网地址:http://i.woblog.cn/AndroidDownloader

项目还处于发展状态,但已经趋于稳定,并且有一定的编码规范,同时采用了多个开源项目的质量控制方案以保证每次代码提交的可靠性。

下面上几张框架Demo的截图,这样用户在心中有一个自己的概念,但是推荐各位还是讲Demo下载到本地亲自,运行一下。

第一个界面是单独下载一个文件。

第二个界面是应用中最常用的一个界面,该界面来自业务数据。

第三个页面是离线管理中的下载中的界面。

第四个页面是离线管理中的下载完成的界面。

可以看到他们在每个界面都能暂停下载,继续下载,以及删除,并且都能拿到进度,状态等信息。

下面就来看看这么强大的下载框架那该如何来使用呢?

添加权限

我相信这一步任何一个项目都已经添加了,但是还是不得不提一下。

该框架需要网络访问权限,如果你是讲文件下载到存储卡,那相应的需要添加存储卡访问权限。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

配置Service

因为该框架采用才service中下载一个文件的。这样做的目的是下载任务一般都需要在后头下载,如果在Activity中来做这类任务,我想任何一个新手都知道这样不行。

<service android:name="cn.woblog.android.downloader.DownloadService">
<intent-filter>
<action android:name="cn.woblog.android.downloader.DOWNLOAD_SERVICE" />
</intent-filter>
</service>

添加依赖

我们提供了多种集成方式,比如:gradle,maven,jar。选择适合你自己的就行了。

Gradle

在module目录下面的build.gradle文件中添加如下内容:

compile 'cn.woblog.android:downloader:1.0.1'

Maven

或者你使用的Maven依赖管理工具。那道理其实是一样的,在pom文件中添加:

<dependency>
<groupId>cn.woblog.android</groupId>
<artifactId>downloader</artifactId>
<version>1.0.</version>
</dependency>

或者你也可以参考该链接使用Snapshots版本

混淆配置

如果你的项目使用了混淆规则,那么一定要加上。

-keep public class * implements cn.woblog.android.downloader.db.DownloadDBController
-keep class cn.woblog.android.downloader.domain.** { *; }

创建下载管理器

现在万事俱备只欠东风了,接下来只需要创建一个下载管理器,该框架所有的操作都是通过该来实现的:

downloadManager = DownloadService.getDownloadManager(context.getApplicationContext());

或者你可以使用更详细的来配置该框架:

Config config = new Config();
//set database path.
// config.setDatabaseName("/sdcard/a/d.db");
// config.setDownloadDBController(dbController); //set download quantity at the same time.
config.setDownloadThread(); //set each download info thread number
config.setEachDownloadThread(); // set connect timeout,unit millisecond
config.setConnectTimeout(); // set read data timeout,unit millisecond
config.setReadTimeout();
downloadManager = DownloadService.getDownloadManager(this.getApplicationContext(), config);

下载一个文件

//create download info set download uri and save path.
final DownloadInfo downloadInfo = new DownloadInfo.Builder().setUrl("http://example.com/a.apk")
.setPath("/sdcard/a.apk")
.build(); //set download callback.
downloadInfo.setDownloadListener(new DownloadListener() { @Override
public void onStart() {
tv_download_info.setText("Prepare downloading");
} @Override
public void onWaited() {
tv_download_info.setText("Waiting");
bt_download_button.setText("Pause");
} @Override
public void onPaused() {
bt_download_button.setText("Continue");
tv_download_info.setText("Paused");
} @Override
public void onDownloading(long progress, long size) {
tv_download_info
.setText(FileUtil.formatFileSize(progress) + "/" + FileUtil
.formatFileSize(size));
bt_download_button.setText("Pause");
} @Override
public void onRemoved() {
bt_download_button.setText("Download");
tv_download_info.setText("");
downloadInfo = null;
} @Override
public void onDownloadSuccess() {
bt_download_button.setText("Delete");
tv_download_info.setText("Download success");
} @Override
public void onDownloadFailed(DownloadException e) {
e.printStackTrace();
tv_download_info.setText("Download fail:" + e.getMessage());
}
}); //submit download info to download manager.
downloadManager.download(downloadInfo); 下载一个文件时直接创建一个DownloadInfo,然后设置下载链接和下载路径。再添加一个监听。就可以提交到下载框架了。 通过下载监听器我们可以获取到很多状态。开始下载,等待中,暂停完成,下载中,删除成功,下载成功,下载失败等状态。 在列表控件使用 我们这里演示如何在RecyclerView这类列表控件使用。当然如果你用的是ListView那道理是一样的。 class ViewHolder extends RecyclerView.ViewHolder { private final ImageView iv_icon;
private final TextView tv_size;
private final TextView tv_status;
private final ProgressBar pb;
private final TextView tv_name;
private final Button bt_action;
private DownloadInfo downloadInfo; public ViewHolder(View view) {
super(view); iv_icon = (ImageView) view.findViewById(R.id.iv_icon);
tv_size = (TextView) view.findViewById(R.id.tv_size);
tv_status = (TextView) view.findViewById(R.id.tv_status);
pb = (ProgressBar) view.findViewById(R.id.pb);
tv_name = (TextView) view.findViewById(R.id.tv_name);
bt_action = (Button) view.findViewById(R.id.bt_action);
} @SuppressWarnings("unchecked")
public void bindData(final MyDownloadInfo data, int position, final Context context) {
Glide.with(context).load(data.getIcon()).into(iv_icon);
tv_name.setText(data.getName()); // Get download task status
downloadInfo = downloadManager.getDownloadById(data.getUrl().hashCode()); // Set a download listener
if (downloadInfo != null) {
downloadInfo
.setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) {
// Call interval about one second
@Override
public void onRefresh() {
if (getUserTag() != null && getUserTag().get() != null) {
ViewHolder viewHolder = (ViewHolder) getUserTag().get();
viewHolder.refresh();
}
}
}); } refresh(); // Download button
bt_action.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (downloadInfo != null) { switch (downloadInfo.getStatus()) {
case DownloadInfo.STATUS_NONE:
case DownloadInfo.STATUS_PAUSED:
case DownloadInfo.STATUS_ERROR: //resume downloadInfo
downloadManager.resume(downloadInfo);
break; case DownloadInfo.STATUS_DOWNLOADING:
case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
case STATUS_WAIT:
//pause downloadInfo
downloadManager.pause(downloadInfo);
break;
case DownloadInfo.STATUS_COMPLETED:
downloadManager.remove(downloadInfo);
break;
}
} else {
// Create new download task
File d = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "d");
if (!d.exists()) {
d.mkdirs();
}
String path = d.getAbsolutePath().concat("/").concat(data.getName());
downloadInfo = new Builder().setUrl(data.getUrl())
.setPath(path)
.build();
downloadInfo
.setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) { @Override
public void onRefresh() {
if (getUserTag() != null && getUserTag().get() != null) {
ViewHolder viewHolder = (ViewHolder) getUserTag().get();
viewHolder.refresh();
}
}
});
downloadManager.download(downloadInfo);
}
}
}); } private void refresh() {
if (downloadInfo == null) {
tv_size.setText("");
pb.setProgress();
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
} else {
switch (downloadInfo.getStatus()) {
case DownloadInfo.STATUS_NONE:
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
break;
case DownloadInfo.STATUS_PAUSED:
case DownloadInfo.STATUS_ERROR:
bt_action.setText("Continue");
tv_status.setText("paused");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
break; case DownloadInfo.STATUS_DOWNLOADING:
case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
bt_action.setText("Pause");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
tv_status.setText("downloading");
break;
case STATUS_COMPLETED:
bt_action.setText("Delete");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
tv_status.setText("success");
break;
case STATUS_REMOVED:
tv_size.setText("");
pb.setProgress();
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
case STATUS_WAIT:
tv_size.setText("");
pb.setProgress();
bt_action.setText("Pause");
tv_status.setText("Waiting");
break;
} }
}
}

关键代码就是bindData方法中先通过业务的id,我们这里使用的url来获取该业务数据是否有对应的下载任务。如果有,则从新绑定监听器,也就是这段代码

//create download info set download uri and save path.
final DownloadInfo downloadInfo = new DownloadInfo.Builder().setUrl("http://example.com/a.apk")
.setPath("/sdcard/a.apk")
.build(); //set download callback.
downloadInfo.setDownloadListener(new DownloadListener() { @Override
public void onStart() {
tv_download_info.setText("Prepare downloading");
} @Override
public void onWaited() {
tv_download_info.setText("Waiting");
bt_download_button.setText("Pause");
} @Override
public void onPaused() {
bt_download_button.setText("Continue");
tv_download_info.setText("Paused");
} @Override
public void onDownloading(long progress, long size) {
tv_download_info
.setText(FileUtil.formatFileSize(progress) + "/" + FileUtil
.formatFileSize(size));
bt_download_button.setText("Pause");
} @Override
public void onRemoved() {
bt_download_button.setText("Download");
tv_download_info.setText("");
downloadInfo = null;
} @Override
public void onDownloadSuccess() {
bt_download_button.setText("Delete");
tv_download_info.setText("Download success");
} @Override
public void onDownloadFailed(DownloadException e) {
e.printStackTrace();
tv_download_info.setText("Download fail:" + e.getMessage());
}
}); //submit download info to download manager.
downloadManager.download(downloadInfo); 下载一个文件时直接创建一个DownloadInfo,然后设置下载链接和下载路径。再添加一个监听。就可以提交到下载框架了。 通过下载监听器我们可以获取到很多状态。开始下载,等待中,暂停完成,下载中,删除成功,下载成功,下载失败等状态。 在列表控件使用 我们这里演示如何在RecyclerView这类列表控件使用。当然如果你用的是ListView那道理是一样的。 class ViewHolder extends RecyclerView.ViewHolder { private final ImageView iv_icon;
private final TextView tv_size;
private final TextView tv_status;
private final ProgressBar pb;
private final TextView tv_name;
private final Button bt_action;
private DownloadInfo downloadInfo; public ViewHolder(View view) {
super(view); iv_icon = (ImageView) view.findViewById(R.id.iv_icon);
tv_size = (TextView) view.findViewById(R.id.tv_size);
tv_status = (TextView) view.findViewById(R.id.tv_status);
pb = (ProgressBar) view.findViewById(R.id.pb);
tv_name = (TextView) view.findViewById(R.id.tv_name);
bt_action = (Button) view.findViewById(R.id.bt_action);
} @SuppressWarnings("unchecked")
public void bindData(final MyDownloadInfo data, int position, final Context context) {
Glide.with(context).load(data.getIcon()).into(iv_icon);
tv_name.setText(data.getName()); // Get download task status
downloadInfo = downloadManager.getDownloadById(data.getUrl().hashCode()); // Set a download listener
if (downloadInfo != null) {
downloadInfo
.setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) {
// Call interval about one second
@Override
public void onRefresh() {
if (getUserTag() != null && getUserTag().get() != null) {
ViewHolder viewHolder = (ViewHolder) getUserTag().get();
viewHolder.refresh();
}
}
}); } refresh(); // Download button
bt_action.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (downloadInfo != null) { switch (downloadInfo.getStatus()) {
case DownloadInfo.STATUS_NONE:
case DownloadInfo.STATUS_PAUSED:
case DownloadInfo.STATUS_ERROR: //resume downloadInfo
downloadManager.resume(downloadInfo);
break; case DownloadInfo.STATUS_DOWNLOADING:
case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
case STATUS_WAIT:
//pause downloadInfo
downloadManager.pause(downloadInfo);
break;
case DownloadInfo.STATUS_COMPLETED:
downloadManager.remove(downloadInfo);
break;
}
} else {
// Create new download task
File d = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "d");
if (!d.exists()) {
d.mkdirs();
}
String path = d.getAbsolutePath().concat("/").concat(data.getName());
downloadInfo = new Builder().setUrl(data.getUrl())
.setPath(path)
.build();
downloadInfo
.setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) { @Override
public void onRefresh() {
if (getUserTag() != null && getUserTag().get() != null) {
ViewHolder viewHolder = (ViewHolder) getUserTag().get();
viewHolder.refresh();
}
}
});
downloadManager.download(downloadInfo);
}
}
}); } private void refresh() {
if (downloadInfo == null) {
tv_size.setText("");
pb.setProgress();
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
} else {
switch (downloadInfo.getStatus()) {
case DownloadInfo.STATUS_NONE:
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
break;
case DownloadInfo.STATUS_PAUSED:
case DownloadInfo.STATUS_ERROR:
bt_action.setText("Continue");
tv_status.setText("paused");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
break; case DownloadInfo.STATUS_DOWNLOADING:
case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
bt_action.setText("Pause");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
tv_status.setText("downloading");
break;
case STATUS_COMPLETED:
bt_action.setText("Delete");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
tv_status.setText("success");
break;
case STATUS_REMOVED:
tv_size.setText("");
pb.setProgress();
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
case STATUS_WAIT:
tv_size.setText("");
pb.setProgress();
bt_action.setText("Pause");
tv_status.setText("Waiting");
break;
} }
}
}

其中要注意到的是缓存每个条目我们使用了SoftReference,这样做的目的内容在吃紧的情况下而已及时的是否这些条目。

接下来又一个重要的点是,设置按钮的点击事件,通常在这样的列表中有一个或多个按钮控制下载状态。

if (downloadInfo != null) {

  switch (downloadInfo.getStatus()) {
case DownloadInfo.STATUS_NONE:
case DownloadInfo.STATUS_PAUSED:
case DownloadInfo.STATUS_ERROR: //resume downloadInfo
downloadManager.resume(downloadInfo);
break; case DownloadInfo.STATUS_DOWNLOADING:
case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
case STATUS_WAIT:
//pause downloadInfo
downloadManager.pause(downloadInfo);
break;
case DownloadInfo.STATUS_COMPLETED:
downloadManager.remove(downloadInfo);
break;
}
} else {
// Create new download task
File d = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "d");
if (!d.exists()) {
d.mkdirs();
}
String path = d.getAbsolutePath().concat("/").concat(data.getName());
downloadInfo = new Builder().setUrl(data.getUrl())
.setPath(path)
.build();
downloadInfo
.setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) { @Override
public void onRefresh() {
if (getUserTag() != null && getUserTag().get() != null) {
ViewHolder viewHolder = (ViewHolder) getUserTag().get();
viewHolder.refresh();
}
}
});
downloadManager.download(downloadInfo);
}

关键点就是如果没有下载任务就创建一个下载任务,如果已有下载任务就根据任务现在的状态执行相应的操作,比如当前是没有下载,点击就是创建一个下载任务。

接下还有一个重点就是,我们在回调监听中调用了refresh方法,在该方法中根据状态显示进度和相应的操作按钮。这样做的好处上面已经提到了。

private void refresh() {
if (downloadInfo == null) {
tv_size.setText("");
pb.setProgress();
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
} else {
switch (downloadInfo.getStatus()) {
case DownloadInfo.STATUS_NONE:
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
break;
case DownloadInfo.STATUS_PAUSED:
case DownloadInfo.STATUS_ERROR:
bt_action.setText("Continue");
tv_status.setText("paused");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
break; case DownloadInfo.STATUS_DOWNLOADING:
case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
bt_action.setText("Pause");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
tv_status.setText("downloading");
break;
case STATUS_COMPLETED:
bt_action.setText("Delete");
try {
pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
} catch (Exception e) {
e.printStackTrace();
}
tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
.formatFileSize(downloadInfo.getSize()));
tv_status.setText("success");
break;
case STATUS_REMOVED:
tv_size.setText("");
pb.setProgress();
bt_action.setText("Download");
tv_status.setText("not downloadInfo");
case STATUS_WAIT:
tv_size.setText("");
pb.setProgress();
bt_action.setText("Pause");
tv_status.setText("Waiting");
break;
} }
}

到这里改下框架的核心使用方法就介绍完了。

支持

如有任何问题可以在加我们的QQ群或者在Github上提Issue,另外请提Issue或者的PR的一定要看下项目的贡献代码的方法以及一要求,因为如果要保证一个开源项目的质量就必须在各方面都规范化。

Andoid 更好的Android多线程下载框架的更多相关文章

  1. 更好的Android多线程下载框架

    /** * 作者:Pich * 原文链接:http://me.woblog.cn/ * QQ群:129961195 * Github:https://github.com/lifengsofts */ ...

  2. 无废话Android之smartimageview使用、android多线程下载、显式意图激活另外一个activity,检查网络是否可用定位到网络的位置、隐式意图激活另外一个activity、隐式意图的配置,自定义隐式意图、在不同activity之间数据传递(5)

    1.smartimageview使用 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&q ...

  3. android程序---->android多线程下载(一)

    多线程下载是加快下载速度的一种方式,通过开启多个线程去执行一个任务,可以使任务的执行速度变快.多线程的任务下载时常都会使用得到断点续传下载,就是我们在一次下载未结束时退出下载,第二次下载时会接着第一次 ...

  4. android程序---->android多线程下载(二)

    上篇我们讲到了android中下载的断点续传问题,今天我们开始学习下载的多线程问题.本次的多线程源码下载:androdi中多线程下载的实现代码.有关断点续传的问题,请参见博客:android程序--- ...

  5. android 多线程下载 断点续传

    来源:网易云课堂Android极客班第八次作业练习 练习内容: 多线程 asyncTask handler 多线程下载的原理 首先获取到目标文件的大小,然后在磁盘上申请一块空间用于保存目标文件,接着把 ...

  6. *Android 多线程下载 仿下载助手

    今天带来一个多线程下载的 样例.先看一下效果.点击 下载 開始下载,同一时候显示下载进度.完成下载,变成程 安装,点击安装 提示 安装应用. 界面效果 线程池 ThreadPoolExecutor , ...

  7. *Android 多线程下载 仿下载助手(改进版)

    首先声明一点: 这里的多线程下载 并非指的 多个线程下载一个 文件.而是 每一个线程 负责一个文件. 真正的多线程 希望后面能给大家带来.  -------------  欢迎 爱学习的小伙伴 加群 ...

  8. Android 多线程下载,断点续传,线程池

    你可以在这里看到这个demo的源码: https://github.com/onlynight/MultiThreadDownloader 效果图 这张效果图是同时开启三个下载任务,限制下载线程数量的 ...

  9. Android -- 多线程下载, 断点下载

    1. 原理图 2. 示例代码 需要权限 <uses-permission android:name="android.permission.INTERNET"/> &l ...

随机推荐

  1. 怎样用批处理来执行多个exe文件

    怎样用批处理来运行多个exe文件 @echo off start *****.exe start *****.exe start *****.exe start *****.exe 接着我们就能够运行 ...

  2. NOIP2017提高组模拟赛 7(总结)

    NOIP2017提高组模拟赛 7(总结) 第一题 斯诺克 考虑这样一个斯诺克球台,它只有四个袋口,分别在四个角上(如下图所示).我们把所有桌子边界上的整数点作为击球点(除了4个袋口),在每个击球点我们 ...

  3. js中cookie的使用 以及缺点

      什么是Cookie Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制.目前Cookie已经成为标准,所有的主流浏览器如IE.Netscape.Firefox. ...

  4. Kali linux 2016.2(Rolling)中的Exploits模块详解

    简单来将,这个Exploits模块,就是针对不同的已知漏洞的利用程序. root@kali:~# msfconsole Unable to handle kernel NULL pointer der ...

  5. Elasticsearch之下载源码

    第一步:进入github.com 第二步: 第三步: 第四步: 第五步: 第六步: 第七步:认识下es的源码目录结构

  6. C#~异步编程再续~你必须要知道的ThreadPool里的throw

    问题依旧存在 之前写过相关文章异步编程的文章,本文主要还是一点补充,之前在IIS经常发w3wp进程无做挂了的情况,但一直没能找到真正的原因,而查找相关资料,找了一些相关的文章,如await和async ...

  7. 对称平方数(to_string函数,stoi函数真香)

    题目描述 打印所有不超过n(n<256)的,其平方具有对称性质的数.如11*11=121. 输入描述: 无 输出描述: 每行一个数,表示对称平方数. 示例1 输入 复制 无 输出 复制 无解题思 ...

  8. POJ 3093 Margaritas on the River Walk(背包)

    题意 n个有体积的物品,问选取一些物品,且不能再继续选有多少方法? n<=1000 题解 以前的考试题.当时是A了,但发现是数据水,POJ上WA了. 把体积从小到大排序枚举没选的物品中体积最小的 ...

  9. Object-C,对象和方法

    学习Object-C,买了2本书,其中1本是用C币买的,总体质量一般,比较基础. 另外一本是,疯狂-李阳写的,大部头,比较吓人,比较全一点. 对于面向对象,之前还是有个大概的理解,再多点基础的例子. ...

  10. solr启动时报错org.apache.solr.common.SolrException: undefined field text的解决办法

    solr启动时报错org.apache.solr.common.SolrException: undefined field text的解决办法 原创 2015年08月21日 20:47:40 标签: ...