Android線程池控制并發(fā)數(shù)多線程下載
多線程下載并不是并發(fā)下載線程越多越好,因?yàn)楫?dāng)用戶開(kāi)啟太多的并發(fā)線程之后,應(yīng)用程序需要維護(hù)每條線程的開(kāi)銷,線程同步的開(kāi)銷。
這些開(kāi)銷反而會(huì)導(dǎo)致下載速度降低。因此需要避免在代碼中直接開(kāi)啟大量線程執(zhí)行下載。
主要實(shí)現(xiàn)步奏:
1、定義一個(gè)DownUtil類,下載工作基本在此類完成,在構(gòu)造器中初始化UI線程的Handler。用于子線程和UI線程傳遞下載進(jìn)度值。
2、所有的下載任務(wù)都保存在LinkedList。在init()方法中開(kāi)啟一個(gè)后臺(tái)線程,不斷地從LinkedList中取任務(wù)交給線程池中的空閑線程執(zhí)行。
3、每當(dāng)addTask方法添加一個(gè)任務(wù),就向 mPoolThreadHandler發(fā)送條消息,就從任務(wù)隊(duì)列中取出一個(gè)任務(wù)交給線程池執(zhí)行。這里使用了使用了Semaphore信號(hào)量,也就是說(shuō)只有當(dāng)一個(gè)任務(wù)執(zhí)行完成之后,release()一個(gè)信號(hào)量,才能從LinkedList中取出一個(gè)任務(wù)再去執(zhí)行,否則acquire()方法會(huì)一直阻塞線程,直到上一個(gè)任務(wù)完成。
public class DownUtil
{
//定義下載資源的路徑
private String path;
//指定下載文件的保存位置
private String targetFile;
//定義下載文件的總大小
private int fileSize;
//線程池
private ExecutorService mThreadPool;
//線程數(shù)量
private static final int DEFAULT_THREAD_COUNT = 5;
//任務(wù)隊(duì)列
private LinkedList<Runnable> mTasks;
//后臺(tái)輪詢線程
private Thread mPoolThread;
//后臺(tái)線程的handler
private Handler mPoolThreadHandler;
//UI線程的Handler
private Handler mUIThreadHandler;
//信號(hào)量
private Semaphore semaphore;
private Semaphore mHandlerSemaphore = new Semaphore(0);
//下載線程數(shù)量
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++)
{
//計(jì)算每條線程下載的開(kāi)始位置
int startPos = i * currentPartSize;
//每條線程使用一個(gè)RandomAccessFile進(jìn)行下載
RandomAccessFile currentPart = new RandomAccessFile(targetFile , "rw");
//定位該線程的下載位置
currentPart.seek(startPos);
//將任務(wù)添加到任務(wù)隊(duì)列中
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
{
//當(dāng)前線程的下載位置
private int startPos;
//定義當(dāng)前線程負(fù)責(zé)下載的文件大小
private int currentPartSize;
//當(dāng)前線程需要下載的文件塊
private RandomAccessFile currentPart;
//定義該線程已經(jīng)下載的字節(jié)數(shù)
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();
//跳過(guò)startPos個(gè)字節(jié)
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);
//累計(jì)該線程下載的總大小
length += hasRead;
}
Log.d("length" , length + "");
//創(chuàng)建消息
Message msg = new Message();
msg.what = 0x123;
Bundle bundle = new Bundle();
bundle.putInt("upper" , length);
msg.setData(bundle);
//向UI線程發(fā)送消息
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);
//獲取界面中的四個(gè)界面控件
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();
}
});
}
}
頁(yè)面布局比較簡(jiǎn)單這里一并貼出:
<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"/> <!-- 定義一個(gè)水平進(jìn)度條,用于顯示下載進(jìn)度 --> <ProgressBar android:id="@+id/bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" style="?android:attr/progressBarStyleHorizontal"/> </LinearLayout>
此例主要是在李剛老師的《瘋狂Java的講義》的多線程的例子上修改,感謝李剛老師,如有不足之處,歡迎批評(píng)指正。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Android XML中引用自定義內(nèi)部類view的四個(gè)why
本篇文章主要介紹了詳解Android XML中引用自定義內(nèi)部類view,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。2016-12-12
Android學(xué)習(xí)筆記(二)之電話撥號(hào)器
目前手機(jī)市場(chǎng)上android已經(jīng)具有強(qiáng)大的霸主地位,吸引了很多的追棒者,android學(xué)習(xí)越來(lái)越火熱,本文給大家介紹android學(xué)習(xí)筆記(二)之電話撥號(hào)器,感興趣的朋友一起學(xué)習(xí)吧2015-11-11
Kotlin中ListView與RecyclerView的應(yīng)用講解
這篇文章主要介紹了Kotlin中ListView與RecyclerView的應(yīng)用講解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
Android實(shí)現(xiàn)圖片拖動(dòng)效果
本文主要介紹了Android實(shí)現(xiàn)圖片拖動(dòng)效果的實(shí)例,具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-03-03
Android本地驗(yàn)證碼的簡(jiǎn)易實(shí)現(xiàn)方法(防止暴力登錄)
驗(yàn)證馬真是無(wú)處不在啊,主要作用是防止惡意暴力破解登錄,這篇文章主要介紹了Android本地驗(yàn)證碼的簡(jiǎn)易實(shí)現(xiàn)方法(防止暴力登錄),需要的朋友可以參考下2017-04-04
Android BLE 藍(lán)牙開(kāi)發(fā)之實(shí)現(xiàn)掃碼槍基于BLESSED開(kāi)發(fā)
這篇文章主要介紹了Android BLE 藍(lán)牙開(kāi)發(fā)之實(shí)現(xiàn)掃碼槍基于BLESSED開(kāi)發(fā),示例代碼介紹了第三方庫(kù)BLESSED for Android的使用,需要的朋友可以參考下2022-03-03
Android 中在有序廣播中添加自定義權(quán)限的實(shí)例
這篇文章主要介紹了Android 中在有序廣播中添加自定義權(quán)限的實(shí)例的相關(guān)資料,這里對(duì)有序廣播的用法進(jìn)行了詳細(xì)介紹并附有簡(jiǎn)單實(shí)例,需要的朋友可以參考下2017-07-07
詳解flutter如何實(shí)現(xiàn)局部導(dǎo)航管理
這篇文章主要為大家介紹了詳解flutter如何實(shí)現(xiàn)局部導(dǎo)航管理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01

