这篇文章给大家介绍怎么在Android中利用线程池控制并发数,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。
主要实现步奏:
1、定义一个DownUtil类,下载工作基本在此类完成,在构造器中初始化UI线程的Handler。用于子线程和UI线程传递下载进度值。
2、所有的下载任务都保存在LinkedList。在init()方法中开启一个后台线程,不断地从LinkedList中取任务交给线程池中的空闲线程执行。
3、每当addTask方法添加一个任务,就向 mPoolThreadHandler发送条消息,就从任务队列中取出一个任务交给线程池执行。这里使用了使用了Semaphore信号量,也就是说只有当一个任务执行完成之后,release()一个信号量,才能从LinkedList中取出一个任务再去执行,否则acquire()方法会一直阻塞线程,直到上一个任务完成。
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的代码:
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();
}
});
}
}
页面布局比较简单这里一并贴出:
<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"
/>
</LinearLayout>
关于怎么在Android中利用线程池控制并发数就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。