Android类FileDownloadList分析

时间:2022-02-04 14:32:44

先上代码,再来分析

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
public class FileDownloadList {
 
 /**上下文*/
 private Context mContext;
 /**请求对象*/
 private BaseRequestLims fileRequest = null;
 /**进度条对话框*/
 private AlertDialog progressDialog = null;
 /**进度条控件变量*/
 private ProgressBar mProgress;
 /**百分比显示控件*/
 private TextView mProgressPercent;
 
 private File localFile = null;
 /**接收HttpHelper中获取到文件大小后发送的广播,确定文件大小*/
 private DownLoadReceiver receiver;
 /**文件大小*/
 private long fileLength = -1L;
 /**是否已注册广播标志*/
 private boolean castFlag = false;
 /**是否显示进度条标志*/
 private boolean showDialog = false;
 /**文件下载完的回调接口*/
 private Runnable mCallback = null;
 
 private Handler mHandler = new Handler(){
  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   int tempSize = (int)localFile.length();
   if(tempSize < fileLength){
//文件下载中
        if(showDialog){
//显示了进度条的情况下,更新进度条
          int progress = (int)((Double.valueOf(tempSize) / Double.valueOf(fileLength)) * 100);
          mProgress.setProgress(tempSize);
          mProgressPercent.setText(progress + "%");
        }
      }else{
//下载文件完毕
    if(castFlag){//如已注册广播,注销广播
      mContext.unregisterReceiver(receiver);
      castFlag = false
    }
    if(showDialog){
     mProgress.setProgress((int)fileLength);
     mProgressPercent.setText("100%");
     progressDialog.dismiss();
    }
          
    if(mCallback != null){
     try{
      Thread.sleep(500);
      mCallback.run();
     }catch (Exception e) {
      e.printStackTrace();
     }
    }
   }
  }
 };
 
 /**
  * 构造器
  * @param activity
  */
 
 /**
  * 构造器
  * @param activity
  * @param showDialog 显示进度条标志
  */
 public FileDownloadList(Context context, boolean showDialog){
  mContext = context;
  this.showDialog = showDialog;
  fileRequest = new BaseRequestLims(context,ClientServiceType.FILE_DOWN);
  fileRequest.setMethodType(BaseRequestLims.METHOD_TYPE_POST);
  fileRequest.setContext(mContext);
    
 }
  public BaseRequestLims getFileRequest(){
  return fileRequest;
 }
 
 /**
  * 通过关联类型来下载文件
  * @param fileName 文件名称或文件在服务器上的相对路径加名称
  * @param saveDir 保存在本地的文件目录
  * @param saveName 保存在本地的文件名称
  * @param gllx 关联类型
  * @param callback 下载后的处理线程
  */
 public void downloadFile(String fileName, String saveDir, String saveName, Runnable callback){
  if(callback != null){
   mCallback = callback;
  }
  
  File saveDirFile = new File(saveDir);
  
  //judge the save dir path exist or not
  if(!saveDirFile.exists()){
   saveDirFile.mkdirs();
  }
  localFile = new File(saveDir,saveName);
  
  if(localFile.isDirectory()){
   new AlertDialog.Builder(mContext).setTitle("提示").setMessage("the save file is directory").show();
   return;
  }
  if(fileRequest.getServiceType()==null){
   fileRequest.setServiceType(ClientServiceType.FILE_DOWN);
  }
  fileRequest.addParameter("fpath", fileName);
  fileRequest.addParameter("fname", saveName);
  fileRequest.setStreamPath(localFile.getAbsolutePath());
  fileRequest.setStream(true);
  if(localFile.exists()){
   if(localFile.length() == 0){
    invokeFile(fileRequest);
   }else{
    //文件存在直接打开
    if(showDialog)
     buildProgressDialog().show();
    mHandler.sendMessage(mHandler.obtainMessage());
   }
  }else{
   invokeFile(fileRequest);
  }
 }
 
 /**
  * 进入文件下载子线程
  * @param request
  */
 private void invokeFile(final BaseRequestLims request){
  try{
   if(showDialog){
    buildProgressDialog().show();
   }
   receiver = new DownLoadReceiver();
   IntentFilter filter = new IntentFilter();
   filter.addAction("SAVE_DOWNLOAD_FILE");
   mContext.registerReceiver(receiver, filter);
   castFlag = true;
   //下载的子线程
   new Thread(){
    @Override
    public void run() {
     super.run();
     HttpHelper.invoke(request);
    }
   }.start();
  }catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 /**
  * 创建进度对话框
  * @return
  */
 private AlertDialog buildProgressDialog(){
  AlertDialog.Builder builder = new Builder(mContext);
  builder.setTitle("正在下载文件,请稍候...");
  RelativeLayout container = new RelativeLayout(mContext);
  mProgress = new ProgressBar(mContext);
  mProgress.setId("progress".hashCode());
  BeanUtils.setFieldValue(mProgress, "mOnlyIndeterminate", Boolean.valueOf(false));
  mProgress.setIndeterminate(false);
  LayerDrawable layerDrawable = (LayerDrawable)mContext.getResources().getDrawable(android.R.drawable.progress_horizontal);
  ClipDrawable clipDrawable = (ClipDrawable)layerDrawable.getDrawable(2);
  clipDrawable.setColorFilter(Color.parseColor("#32B5E5"), Mode.SRC_IN);
  mProgress.setProgressDrawable(layerDrawable);
  mProgress.setPadding(0, 0, 0, 0);
  mProgress.setIndeterminateDrawable(
    mContext.getResources().getDrawable(android.R.drawable.progress_indeterminate_horizontal));
  mProgressPercent = new TextView(mContext);
  mProgressPercent.setId("percent".hashCode());
  mProgressPercent.setText("0%");
  mProgressPercent.setTextSize(18);
  
  int containerPadding = DimensionUtils.dip2Px(mContext, 10);
  container.setPadding(containerPadding, containerPadding, containerPadding, containerPadding);
  
  LayoutParams progressLayoutParams = new LayoutParams(
    LayoutParams.MATCH_PARENT, DimensionUtils.dip2Px(mContext, 4));
  progressLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
  progressLayoutParams.addRule(RelativeLayout.LEFT_OF, mProgressPercent.getId());
  mProgress.setLayoutParams(progressLayoutParams);
  
  LayoutParams percentLayoutParams = new LayoutParams(
    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  percentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
  percentLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
  mProgressPercent.setLayoutParams(percentLayoutParams);
  
  container.addView(mProgressPercent);
  container.addView(mProgress);
  builder.setView(container);
  builder.setNegativeButton("取消", new OnClickListener() {
   @Override
   public void onClick(DialogInterface dialog, int which) {
    dialog.dismiss();
   }
  });
  
  progressDialog = builder.create();
  return progressDialog;
 }
 
 class DownLoadReceiver extends BroadcastReceiver{
  @Override
  public void onReceive(Context context, Intent intent) {
   //显示进度条
   fileLength = intent.getLongExtra("FILE_LENGTH", -1);
   if(showDialog){
    mProgress.setMax((int)fileLength);
   }
   //更新进度条的线程
   new Thread(){
    @Override
    public void run() {
     super.run();
     while(true){
      try{
       Thread.sleep(500);
      }catch (Exception e) {
       e.printStackTrace();
      }
      mHandler.sendMessage(mHandler.obtainMessage());
      //获取下载文件的大小
      int loadedSize = (int)localFile.length();
      if(loadedSize >= fileLength){
       break;
      }
     }
    }
   }.start();
  }
 }
 
 public DownLoadReceiver getReciver()
 {
  return receiver;
 }
}

它的逻辑:

创建一个FileDownloadList对象后,就可以直接使用该下述方法来实现下载功能。

?
1
downloadFile(String fileName, String saveDir, String saveName, Runnable callback)

在实现上是这么个意思:

1.在当前上下文,开启下载线程。当获取到要下载的文件的大小时,发送一个广播过来(这部分没有展示在上述代码中)。

2.在当前上下文中,注册一个广播监听器,监听广播标识为SAVE_DOWNLOAD_FILE的广播。首次监听到发出来的广播后,首次发送过来的广播,包含了要下载的文件的大小信息,然后就每隔5毫秒检测本地文件的大小,直到本地文件的大小(loadedSize)大于等于要下载的文件(fileLength)大小时,退出该循环。

在不断检测的过程中,通过mHandler.sendMessage(mHandler.obtainMessage()); ,让UI线程更新进度条。

下载线程,会不断将服务器返回的数据流,写到本地文件中,所以,本地文件的大小会不断变化,直到,它的大小跟要下载的文件的大小相等时,就退出这个不断检测本地文件大小的线程。

其它没有在上述代码中表现出来的内容(在其它部分的代码中):

1.在invokeFile( final BaseRequestLims request)方法中,开了一个如下的下载线程.该下载线程,会将服务器返回的文件流,写到本地文件(localFile)中;然后,它还会发送一个标识为SAVE_DOWNLOAD广播,包含的信息有要下载文件的文件大小fileLength。

?
1
2
3
4
5
6
7
8
//下载的子线程
   new Thread(){
    @Override
    public void run() {
     super.run();
     HttpHelper.invoke(request);
    }
   }.start();

上述代码存在的问题:

1.上下文,使用的是某个Activity,如果发生系统调用了该Activity的onDestroy()时,下载线程还没有完成,也就意味着,loadedSize的大小还是小于fileLength。从而,那个不断检测本地文件大小的线程就一直在执行着。

即是检测本地文件大小的线程和下载线程还在执行着:

检测本地文件大小的线程:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
new Thread(){
    @Override
    public void run() {
     super.run();
     while(true){
      try{
       Thread.sleep(500);
      }catch (Exception e) {
       e.printStackTrace();
      }
      mHandler.sendMessage(mHandler.obtainMessage());
      //获取下载文件的大小
      int loadedSize = (int)localFile.length();
      if(loadedSize >= fileLength){
       break;
      }
     }
    }
   }.start();

下载线程:

?
1
2
3
4
5
6
7
new Thread(){
        @Override
        public void run() {
          super.run();
          HttpHelper.invoke(request);
        }
      }.start();

那么,会出现什么问题呢?

1).我可以确定的就是,mContext会出现泄漏。

2). DownLoadReceiver不能正常被取消注册。

分析,待续。