设计文件下载
一个开放的上机面试题,快手第一面就问到这个,听起来很随意,让你能想到什么就写什么,但我觉得没那么简单,我总结面试官考的是以下几点吧:
第一:考文件下载功能的实现
第二:考代码习惯,代码风格是否规范
第三:考框架搭建能力,线程切换之类的
大家觉得黏?
思路:
1、下载文件你想用啥都行,能写多少写多少,写不出来就上伪代码也行,我用HttpUrlConnection;
2、代码规范尽量多看看阿里文档规范手册,成员变量要规范,接口名,方法名等见名识义
3、考虑简单实现,一个Manager管理类,供程序调用;再设计两个接口,一个负责核心逻辑编写,一个负责数据监听回调;Manager管理类除了实现核心逻辑接口,还要实现Runnable接口;至于线程切换用Handler就行
那一起code一下吧
先设计第一个接口IDownLoadData,只有一个下载文件的方法downloadFile(String path, ResponseListener responseListener, Context context);
public interface IDownLoadData {
FileOutputStream downloadFile(String path, ResponseListener responseListener, Context context);
}
异步下载的数据如何回调给调用者呢,再设计一个监听下载数据结果的接口ResponseListener,只有成功和异常的两个方法
public interface ResponseListener {
void responseSuccess(FileOutputStream fileOutputStream);
void responseException(Exception e);
}
最后设计一下DownLoadDataManager,分别实现核心业务接口IDownLoadData以及Runnable实现异步;再拿到http返回的响应数据以后,用Hander切换到主线程,然后利用回调接口传回FileOutPutStream给调用者即可
/**
* 下载文件管理类
*/
public class DownLoadDataManager implements IDownLoadData, Runnable {
private static final String TAG = DownLoadDataManager.class.getSimpleName();
private String path;
private ResponseListener responseListener;
private Context context;
@Override
public FileOutputStream downloadFile(String path, ResponseListener responseListener, Context context) {
return null;
}
@Override
public void run() {
InputStream inputStream = null;
HttpURLConnection httpURLConnection = null;
FileOutputStream fileOutputStream = null;
try {
URL url = new URL(path);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(5000);
fileOutputStream = new FileOutputStream("c:/abc.gif");
final int responseCode = httpURLConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = httpURLConnection.getInputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, length);
}
// 成功 切换主线程
final FileOutputStream finalFileOutputStream = fileOutputStream;
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// 回调成功
responseListener.responseSuccess(finalFileOutputStream);
}
});
} else {
// 失败 切换主线程
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// 回调失败
responseListener.responseException(new IllegalStateException("请求失败,请求码:" + responseCode));
}
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, "run: 关闭 inputStream.close(); e:" + e.getMessage());
}
}
if (fileOutputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, "run: 关闭 inputStream.close(); e:" + e.getMessage());
}
}
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
}
}
最后调用者会调用downloadFile方法,很简单
new DownLoadDataManager().downloadFile("url",responseListener,context);
我的思路以及代码仅供大家参考,只是提供了一个思路,我觉得这个是实现下载文件必备的,不能再少写什么了,面试现场确实会因为思路不清晰或者紧张不知道如何思考,至少这个实现方案看起来很简单,也好记,如果Manager里面实在不知道怎么写,也可以把框架搭出来,写伪代码,只写重点部分,譬如线程切换部分,数据返回成功以后,通过监听的接口回调给调用者。
如果我没记错,Glide源码内部活动缓存部分下载外部资源的逻辑就是通过这种设计方案实现的,有兴趣的同学可以去看看哦~