服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android线程池控制并发数多线程下载

Android线程池控制并发数多线程下载

2022-09-09 15:22Knick_Zhang Android

这篇文章主要为大家详细介绍了Android线程池控制并发数多线程下载,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

多线程下载并不是并发下载线程越多越好,因为当用户开启太多的并发线程之后,应用程序需要维护每条线程的开销,线程同步的开销。

这些开销反而会导致下载速度降低。因此需要避免在代码中直接开启大量线程执行下载。

主要实现步奏:

1、定义一个DownUtil类,下载工作基本在此类完成,在构造器中初始化UI线程的Handler。用于子线程和UI线程传递下载进度值。

2、所有的下载任务都保存在LinkedList。在init()方法中开启一个后台线程,不断地从LinkedList中取任务交给线程池中的空闲线程执行。

3、每当addTask方法添加一个任务,就向 mPoolThreadHandler发送条消息,就从任务队列中取出一个任务交给线程池执行。这里使用了使用了Semaphore信号量,也就是说只有当一个任务执行完成之后,release()一个信号量,才能从LinkedList中取出一个任务再去执行,否则acquire()方法会一直阻塞线程,直到上一个任务完成。

?
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
247
public class DownUtil
{
 //定义下载资源的路径
 private String path;
 //指定下载文件的保存位置
 private String targetFile;
 //定义下载文件的总大小
 private int fileSize;
 
 //线程池
 private ExecutorService mThreadPool;
 //线程数量
 private static final int DEFAULT_THREAD_COUNT = 5;
 //任务队列
 private LinkedList<Runnable> mTasks;
 
 //后台轮询线程
 private Thread mPoolThread;
 //后台线程的handler
 private Handler mPoolThreadHandler;
 //UI线程的Handler
 private Handler mUIThreadHandler;
 //信号量
 private Semaphore semaphore;
 private Semaphore mHandlerSemaphore = new Semaphore(0);
 //下载线程数量
 private int threadNum;
 
 public DownUtil(String path , String targetFile , int threadNum , final ProgressBar bar)
 {
  this.path = path;
  this.targetFile = targetFile;
  this.threadNum = threadNum;
  init();
 
  mUIThreadHandler = new Handler()
  {
   int sumSize = 0;
   @Override
   public void handleMessage(Message msg)
   {
    if (msg.what == 0x123)
    {
     int size = msg.getData().getInt("upper");
     sumSize += size;
     Log.d("sumSize" , sumSize + "");
     bar.setProgress((int) (sumSize * 1.0 / fileSize * 100));
    }
   }
  };
 }
 
 private void init()
 {
  mPoolThread = new Thread()
  {
   public void run()
   {
    Looper.prepare();
    mPoolThreadHandler = new Handler()
    {
     public void handleMessage(Message msg)
     {
      if (msg.what == 0x111)
      {
       mThreadPool.execute(getTask());
       try
       {
        semaphore.acquire();
       }
       catch (InterruptedException e)
       {
        e.printStackTrace();
       }
      }
     }
    };
    mHandlerSemaphore.release();
    Looper.loop();
   }
  };
  mPoolThread.start();
 
  mThreadPool = Executors.newFixedThreadPool(DEFAULT_THREAD_COUNT);
  mTasks = new LinkedList<>();
  semaphore = new Semaphore(DEFAULT_THREAD_COUNT);
 }
 
 public void downLoad()
 {
 
 
  try {
   URL url = new URL(path);
   HttpURLConnection conn = (HttpURLConnection) url.openConnection();
   conn.setConnectTimeout(5 * 1000);
   conn.setRequestMethod("GET");
   conn.setRequestProperty(
     "Accept",
     "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
       + "application/x-shockwave-flash, application/xaml+xml, "
       + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
       + "application/x-ms-application, application/vnd.ms-excel, "
       + "application/vnd.ms-powerpoint, application/msword, */*");
   conn.setRequestProperty("Accept-Language", "zh-CN");
   conn.setRequestProperty("Charset", "UTF-8");
   conn.setRequestProperty("Connection", "Keep-Alive");
 
   //得到文件的大小
   fileSize = conn.getContentLength();
   conn.disconnect();
 
   int currentPartSize = fileSize / threadNum + 1;
   RandomAccessFile file = new RandomAccessFile(targetFile , "rw");
   file.setLength(fileSize);
   file.close();
 
 
   for (int i = 0 ; i < threadNum ; i++)
   {
    //计算每条线程下载的开始位置
    int startPos = i * currentPartSize;
    //每条线程使用一个RandomAccessFile进行下载
    RandomAccessFile currentPart = new RandomAccessFile(targetFile , "rw");
    //定位该线程的下载位置
    currentPart.seek(startPos);
 
    //将任务添加到任务队列中
    addTask(new DownThread(startPos , currentPartSize , currentPart));
   }
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }
 
 private Runnable getTask()
 {
  if (!mTasks.isEmpty())
  {
   return mTasks.removeFirst();
  }
  return null;
 }
 
 private synchronized void addTask(Runnable task)
 {
  mTasks.add(task);
  try
  {
   if (mPoolThreadHandler == null)
   {
    mHandlerSemaphore.acquire();
   }
  }
  catch (InterruptedException e)
  {
   e.printStackTrace();
  }
  mPoolThreadHandler.sendEmptyMessage(0x111);
 }
 
 private class DownThread implements Runnable
 {
  //当前线程的下载位置
  private int startPos;
  //定义当前线程负责下载的文件大小
  private int currentPartSize;
  //当前线程需要下载的文件块
  private RandomAccessFile currentPart;
  //定义该线程已经下载的字节数
  private int length;
 
  public DownThread(int startPos , int currentPartSize , RandomAccessFile currentPart)
  {
   this.startPos = startPos;
   this.currentPartSize = currentPartSize;
   this.currentPart = currentPart;
  }
 
  @Override
  public void run()
  {
   try
   {
    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setConnectTimeout(5 * 1000);
    conn.setRequestMethod("GET");
    conn.setRequestProperty(
      "Accept",
      "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
        + "application/x-shockwave-flash, application/xaml+xml, "
        + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
        + "application/x-ms-application, application/vnd.ms-excel, "
        + "application/vnd.ms-powerpoint, application/msword, */*");
    conn.setRequestProperty("Accept-Language", "zh-CN");
    conn.setRequestProperty("Charset", "UTF-8");
    conn.setRequestProperty("Connection", "Keep-Alive");
 
    InputStream inStream = conn.getInputStream();
    //跳过startPos个字节
    skipFully(inStream , this.startPos);
 
    byte[] buffer = new byte[1024];
    int hasRead = 0;
    while (length < currentPartSize && (hasRead = inStream.read(buffer)) > 0)
    {
     currentPart.write(buffer , 0 , hasRead);
     //累计该线程下载的总大小
     length += hasRead;
    }
 
    Log.d("length" , length + "");
 
    //创建消息
    Message msg = new Message();
    msg.what = 0x123;
    Bundle bundle = new Bundle();
    bundle.putInt("upper" , length);
    msg.setData(bundle);
    //向UI线程发送消息
    mUIThreadHandler.sendMessage(msg);
 
    semaphore.release();
    currentPart.close();
    inStream.close();
   }
   catch (Exception e)
   {
    e.printStackTrace();
   }
  }
 }
 
 public static void skipFully(InputStream in , long bytes) throws IOException
 {
  long remaining = bytes;
  long len = 0;
  while (remaining > 0)
  {
   len = in.skip(remaining);
   remaining -= len;
  }
 }
}

以下是MainActivity的代码:

?
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
public class MainActivity extends Activity
{
 EditText url;
 EditText target;
 Button downBn;
 ProgressBar bar;
 DownUtil downUtil;
 private String savePath;
 
 @Override
 protected void onCreate(Bundle savedInstanceState)
 {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  //获取界面中的四个界面控件
  url = (EditText) findViewById(R.id.address);
  target = (EditText) findViewById(R.id.target);
  try
  {
   File sdCardDir = Environment.getExternalStorageDirectory();
   savePath = sdCardDir.getCanonicalPath() + "/d.chm";
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
  target.setText(savePath);
  downBn = (Button) findViewById(R.id.down);
  bar = (ProgressBar) findViewById(R.id.bar);
  downBn.setOnClickListener(new View.OnClickListener()
  {
   @Override
   public void onClick(View view)
   {
    downUtil = new DownUtil(url.getText().toString() , target.getText().toString() , 7 , bar);
    new Thread()
    {
     @Override
     public void run()
     {
      try
      {
       downUtil.downLoad();
      }
      catch (Exception e)
      {
       e.printStackTrace();
      }
     }
    }.start();
   }
  });
 }
}

页面布局比较简单这里一并贴出:

?
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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">
 
 <TextView
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@string/title1"/>
 
 <EditText
  android:id="@+id/address"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@string/address"/>
 
 <TextView
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@string/targetAddress"/>
 
 <EditText
  android:id="@+id/target"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"/>
 
 <Button
  android:id="@+id/down"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@string/down"/>
 
 <!-- 定义一个水平进度条,用于显示下载进度 -->
 <ProgressBar
  android:id="@+id/bar"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:max="100"
  style="?android:attr/progressBarStyleHorizontal"/>
 
</LinearLayout>

此例主要是在李刚老师的《疯狂Java的讲义》的多线程的例子上修改,感谢李刚老师,如有不足之处,欢迎批评指正。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/qq_33022345/article/details/52416278

延伸 · 阅读

精彩推荐