Github托管地址: https://github.com/xiebudong/HttpEngine
因为做的项目是网络视频相关的, 所以需要把业务无关的逻辑独立出来, 其中一大块就是对Server的请求, 其中包括对直播点播列表的请求和对海报的请求, 在外加一些额外的和服务器同步的请求, 总之归结起来就只有两种: ①请求一个字符串(一般是基于json通信) , ②请求文件(海报或者升级apk)
接下来就可以把对网络的请求独立出来单独维护, 这样不但增加了模块的复用性, 而且降低了代码的耦合度。
于是我把网络请求封装成了一个library, 在项目中使用起来就方便多了。
库的名称叫HttpEngine, 其中核心的两个API是:
requestString(...);
requestFile(...);
这样业务相关的逻辑可以再对这个模块的使用进行封装, 一层一层最终使代码的层次性非常清晰。
下面详细记录这个库的使用 :
HttpEngine是android平台的一个简单的http引擎(简单、实用),支持并发请求字符串和下载文件, 同时支持断点续传下载。
引擎打包为lib工程。在项目中直接以lib工程引用方式使用,也可以直接将生产的jar包放在项目的libs目录下。
另外,项目还附带了示例应用 - HttpEngineSample, 可以用从这个示例程序了解HttpEngine的使用, 也可以直接看看这个库的使用效果怎么样。
HttpEngineSample的运行效果:
引擎以单例模式使用。
API使用很简单:
HttpEngine.instance().init();
HttpEngine.instance().uninit();
HttpEngine.instance().needDebug(boolean isNeed);
HttpEngine.instance().requestString(...);
HttpEngine.instance().requestFile(...);
更详细的API使用说明见 HttpEngine/doc/index.html
要注意几点: init()和uninit()要成对调用, 而且不能漏调, 否则会引发IllegalStateException.
比如请求一个网页:
void requestString()
{
StringRequest req = new StringRequest("http://www.baidu.com");
HttpEngine.instance().requestString(req, new StringObserver() {
@Override
public void done(StringRequest req, StringResponse resp)
{
// TODO Auto-generated method stub
Log.i(LOG_TAG, resp.getRespString());
}
});
}
void requestString()
{
StringRequest req = new StringRequest("http://www.baidu.com");
HttpEngine.instance().requestString(req, new StringObserver() {
@Override
public void done(StringRequest req, StringResponse resp)
{
// TODO Auto-generated method stub
Log.i(LOG_TAG, resp.getRespString());
}
});
}
再比如下载一个文件并决定是否支持断点续传,(并支持下载过程中终止下载, 下次再下载同一个文件时只要上次下载一半的文件没有被手动删掉,则会从已下载的部分开始断点续传下载):
void downloadFile(int id, boolean isBreakpoint, String downloadUrl, String fileSavePath)
{
Log.i(LOG_TAG, "downloadFile id: " + id);
FileRequest req = null;
if (isBreakpoint)
{
req = new FileRequest(downloadUrl, fileSavePath);
req.setIsSupportBreakpointResume(true); // 显示设置支持断点续传。
}
else
{
// 默认不支持断点续传, 无论上次有没有下完,第二次下载时会删除之前下载的文件。
req = new FileRequest(downloadUrl, fileSavePath);
}
req.setId(id);
Controller controller = HttpEngine.instance().requestFile(req, new FileObserver() {
@Override
public void done(FileRequest req, FileResponse resp) {
// TODO Auto-generated method stub
// Log.i(LOG_TAG, req.toString());
// Log.i(LOG_TAG, resp.toString());
if (resp.getResponseState() != State.OK)
{
Log.w(LOG_TAG, "req " + req.getId() + " failed");
}
runOnUiThread(new UiUpdater(req.getId(), resp.getDownloadPercent()));
}
});
if (controller == null)
{
Log.w(LOG_TAG, "request failed , check your network, or filter HttpEngine log for more detail info.");
}
else
{
mControllerMap.put(id, controller); // controller保存起来以便后面可以终止下载, 比如正在下载时
}
}
void downloadFile(int id, boolean isBreakpoint, String downloadUrl, String fileSavePath)
{
Log.i(LOG_TAG, "downloadFile id: " + id);
FileRequest req = null;
if (isBreakpoint)
{
req = new FileRequest(downloadUrl, fileSavePath);
req.setIsSupportBreakpointResume(true); // 显示设置支持断点续传。
}
else
{
// 默认不支持断点续传, 无论上次有没有下完,第二次下载时会删除之前下载的文件。
req = new FileRequest(downloadUrl, fileSavePath);
}
req.setId(id);
Controller controller = HttpEngine.instance().requestFile(req, new FileObserver() {
@Override
public void done(FileRequest req, FileResponse resp) {
// TODO Auto-generated method stub
// Log.i(LOG_TAG, req.toString());
// Log.i(LOG_TAG, resp.toString());
if (resp.getResponseState() != State.OK)
{
Log.w(LOG_TAG, "req " + req.getId() + " failed");
}
runOnUiThread(new UiUpdater(req.getId(), resp.getDownloadPercent()));
}
});
if (controller == null)
{
Log.w(LOG_TAG, "request failed , check your network, or filter HttpEngine log for more detail info.");
}
else
{
mControllerMap.put(id, controller); // controller保存起来以便后面可以终止下载, 比如正在下载时
}
}
以上无论是请求字符串还是请求文件都是异步并发执行的。 所以函数的执行都不会阻塞主线程, 注意如果同时发出的请求比较多的话,最好设置请求id,以便在callback中区分是哪个请求的响应。
另外, 可以通过调用 HttpEngine.instance().needDebug(boolean isNeed); 来决定要不要查看引擎的log输出, 如果打开了这个开关, 便可以在logcat过滤HttpEngine来查看HttpEngine库的执行log(当请求失败时,对调试是非常有帮助的)。
当然,如果请求失败也可以把callback回传的stringRequest req, StringResponse resp 或者 FileRequest req, FileResponse resp 打印出来(toString), 来查看为什么失败了。