Android線程池控制并發(fā)數(shù)多線程下載
多線程下載并不是并發(fā)下載線程越多越好,因為當用戶開啟太多的并發(fā)線程之后,應用程序需要維護每條線程的開銷,線程同步的開銷。
這些開銷反而會導致下載速度降低。因此需要避免在代碼中直接開啟大量線程執(zhí)行下載。
主要實現(xiàn)步奏:
1、定義一個DownUtil類,下載工作基本在此類完成,在構造器中初始化UI線程的Handler。用于子線程和UI線程傳遞下載進度值。
2、所有的下載任務都保存在LinkedList。在init()方法中開啟一個后臺線程,不斷地從LinkedList中取任務交給線程池中的空閑線程執(zhí)行。
3、每當addTask方法添加一個任務,就向 mPoolThreadHandler發(fā)送條消息,就從任務隊列中取出一個任務交給線程池執(zhí)行。這里使用了使用了Semaphore信號量,也就是說只有當一個任務執(zhí)行完成之后,release()一個信號量,才能從LinkedList中取出一個任務再去執(zhí)行,否則acquire()方法會一直阻塞線程,直到上一個任務完成。
public class DownUtil
{
//定義下載資源的路徑
private String path;
//指定下載文件的保存位置
private String targetFile;
//定義下載文件的總大小
private int fileSize;
//線程池
private ExecutorService mThreadPool;
//線程數(shù)量
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);
//下載線程數(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++)
{
//計算每條線程下載的開始位置
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;
//定義該線程已經(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();
//跳過startPos個字節(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);
//累計該線程下載的總大小
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);
//獲取界面中的四個界面控件
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" style="?android:attr/progressBarStyleHorizontal"/> </LinearLayout>
此例主要是在李剛老師的《瘋狂Java的講義》的多線程的例子上修改,感謝李剛老師,如有不足之處,歡迎批評指正。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
使用Android開發(fā)接入第三方原生SDK實現(xiàn)微信登錄
這篇文章主要介紹了使用Android開發(fā)接入第三方原生SDK實現(xiàn)微信登錄,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
AndroidStudio中AVD虛擬機設備空間不足調試過程出現(xiàn)的黑屏問題及解決方案
這篇文章主要介紹了解決AndroidStudio中AVD虛擬機設備空間不足調試過程出現(xiàn)的黑屏問題,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
Android監(jiān)聽系統(tǒng)來電并彈出提示窗口
本篇文章主要介紹了Android監(jiān)聽系統(tǒng)來電并彈出提示窗口,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
Android帶進度條的下載圖片示例(AsyncTask異步任務)
本文主要介紹Android帶進度條的下載圖片示例(AsyncTask異步任務)的方法解析。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04
Android便攜式熱點的開啟狀態(tài)檢測和SSID的獲取方法
WIFI熱點的開啟狀態(tài)和開啟后的SSID如何獲取呢?接下來通過本文給大家分享Android便攜式熱點的開啟狀態(tài)檢測和SSID的獲取方法,需要的朋友參考下吧2017-01-01

