Téléchargement de fichiers Android (2)
Introduction à cette section :
Cette section vous apporte l'analyse du code de la reprise des points d'arrêt multithread dans Android Haha, pourquoi s'appelle-t-elle analyse ? à cause de moi Je ne peux même pas l'écrire, ( ╯□╰ ) ! Parlons d’abord de la signification des points d’arrêt ! Le soi-disant point d'arrêt est le suivant : utilisez la base de données pour enregistrer ce que le fil fait chaque jour Téléchargez la progression ! A chaque démarrage, la progression du téléchargement d'un thread est interrogée en fonction de l'ID du thread, et le téléchargement continue ! Cela semble assez simple, Si vous essayez d'écrire, vous ne pourrez probablement pas l'écrire. C'est normal, il est donc préférable de comprendre cette section. Peu importe si vous ne la comprenez pas, tant que vous pouvez l'utiliser et la modifier. il! D'accord, commençons cette section ~
Analyse du processus de code du téléchargement du point d'arrêt multithread Android :
Exécution des rendus :
Analyse complète du processus :
Étape 1 : Créer un tableau pour enregistrer les informations de téléchargement du fil
Créer un table de base de données, nous créons donc une classe de gestionnaire de base de données qui hérite de la classe SQLiteOpenHelper En remplaçant les méthodes onCreate() et onUpgrade(), les champs de table que nous avons créés sont les suivants :
DBOpenHelper.java:
package com.jay.example.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; public class DBOpenHelper extends SQLiteOpenHelper { public DBOpenHelper(Context context) { super(context, "downs.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { //数据库的结构为:表名:filedownlog 字段:id,downpath:当前下载的资源, //threadid:下载的线程id,downlength:线程下载的最后位置 db.execSQL("CREATE TABLE IF NOT EXISTS filedownlog " + "(id integer primary key autoincrement," + " downpath varchar(100)," + " threadid INTEGER, downlength INTEGER)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //当版本号发生改变时调用该方法,这里删除数据表,在实际业务中一般是要进行数据备份的 db.execSQL("DROP TABLE IF EXISTS filedownlog"); onCreate(db); } }
Étape 2 : Créer une classe d'opérations de base de données
Quels types de méthodes devons-nous créer ?
- ①Nous avons besoin d'une méthode pour obtenir la longueur de téléchargement actuelle de chaque fil en fonction de l'URL
- ②Ensuite, lorsque notre fil est nouvellement ouvert, nous devons l'insérer dans la base de données Méthodes pour les paramètres liés au fil
- ③Nous devons également définir une méthode qui peut mettre à jour la longueur du fichier téléchargé en temps réel
- ④Une fois notre fil téléchargé, nous devons également supprimez l'enregistrement correspondant en fonction de l'identifiant du fil
FileService.java
package com.jay.example.db; import java.util.HashMap; import java.util.Map; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; /* * 该类是一个业务bean类,完成数据库的相关操作 * */ public class FileService { //声明数据库管理器 private DBOpenHelper openHelper; //在构造方法中根据上下文对象实例化数据库管理器 public FileService(Context context) { openHelper = new DBOpenHelper(context); } /** * 获得指定URI的每条线程已经下载的文件长度 * @param path * @return * */ public Map getData(String path) { //获得可读数据库句柄,通常内部实现返回的其实都是可写的数据库句柄 SQLiteDatabase db = openHelper.getReadableDatabase(); //根据下载的路径查询所有现场的下载数据,返回的Cursor指向第一条记录之前 Cursor cursor = db.rawQuery("select threadid, downlength from filedownlog where downpath=?", new String[]{path}); //建立一个哈希表用于存放每条线程已下载的文件长度 Map data = new HashMap(); //从第一条记录开始遍历Cursor对象 cursor.moveToFirst(); while(cursor.moveToNext()) { //把线程id与该线程已下载的长度存放到data哈希表中 data.put(cursor.getInt(0), cursor.getInt(1)); data.put(cursor.getInt(cursor.getColumnIndexOrThrow("threadid")), cursor.getInt(cursor.getColumnIndexOrThrow("downlength"))); } cursor.close();//关闭cursor,释放资源; db.close(); return data; } /** * 保存每条线程已经下载的文件长度 * @param path 下载的路径 * @param map 现在的di和已经下载的长度的集合 */ public void save(String path,Map map) { SQLiteDatabase db = openHelper.getWritableDatabase(); //开启事务,因为此处需要插入多条数据 db.beginTransaction(); try{ //使用增强for循环遍历数据集合 for(Map.Entry entry : map.entrySet()) { //插入特定下载路径特定线程ID已经下载的数据 db.execSQL("insert into filedownlog(downpath, threadid, downlength) values(?,?,?)", new Object[]{path, entry.getKey(), entry.getValue()}); } //设置一个事务成功的标志,如果成功就提交事务,如果没调用该方法的话那么事务回滚 //就是上面的数据库操作撤销 db.setTransactionSuccessful(); }finally{ //结束一个事务 db.endTransaction(); } db.close(); } /** * 实时更新每条线程已经下载的文件长度 * @param path * @param map */ public void update(String path,int threadId,int pos) { SQLiteDatabase db = openHelper.getWritableDatabase(); //更新特定下载路径下特定线程已下载的文件长度 db.execSQL("update filedownlog set downlength=? where downpath=? and threadid=?", new Object[]{pos, path, threadId}); db.close(); } /** *当文件下载完成后,删除对应的下载记录 *@param path */ public void delete(String path) { SQLiteDatabase db = openHelper.getWritableDatabase(); db.execSQL("delete from filedownlog where downpath=?", new Object[]{path}); db.close(); } }
Étape 3 : Créer une classe de téléchargement de fichiers
D'accord, gestion de la base de données Les classes de téléchargement et d'opération sont terminées, puis il est temps de créer une classe de téléchargement de fichiers, et dans cette classe, il faut terminer Quelle opération ? Il y a tellement de choses à faire :
①Définissez un tas de variables. Le noyau est constitué des threads du pool de threads et de la collection de synchronisation ConcurrentHashMap, qui est utilisée pour mettre en cache la longueur de téléchargement du thread
<🎜. >
. ②Définissez une méthode d'obtention du pool de threads pour le nombre de threads ;
③Définissez une méthode pour quitter le téléchargement,
④Méthode pour obtenir la taille actuelle du fichier.
⑤Cumulatif Pour la méthode de longueur actuellement téléchargée, un mot-clé synchronisé doit être ajouté ici pour résoudre le problème d'accès simultané
⑥ pour mettre à jour la dernière position de téléchargement du thread spécifié. La synchronisation doit également être utilisée
⑦Téléchargement complet du fichier, ouverture du fil et autres opérations dans la méthode de construction
⑧Comment obtenir le nom du fichier. : interceptez d'abord la chaîne après le dernier '/' de l'URL fournie. Si vous obtenez Sinon, recherchez à nouveau dans le champ d'en-tête, toujours. S'il ne peut pas être trouvé, utilisez le numéro d'identification de la carte réseau + le numéro unique du CPU pour générer un binaire de 16 octets comme nom de fichier
⑨Comment commencer à télécharger le fichier
⑩Méthodes pour obtenir les champs d'en-tête de réponse http
⑪Méthodes pour imprimer les champs d'en-tête http
12 Méthodes pour imprimer les informations du journal
FileDownloadered.java:
package com.jay.example.service; import java.io.File; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.util.Log; import com.jay.example.db.FileService; public class FileDownloadered { private static final String TAG = "文件下载类"; //设置一个查log时的一个标志 private static final int RESPONSEOK = 200; //设置响应码为200,代表访问成功 private FileService fileService; //获取本地数据库的业务Bean private boolean exited; //停止下载的标志 private Context context; //程序的上下文对象 private int downloadedSize = 0; //已下载的文件长度 private int fileSize = 0; //开始的文件长度 private DownloadThread[] threads; //根据线程数设置下载的线程池 private File saveFile; //数据保存到本地的文件中 private Map data = new ConcurrentHashMap(); //缓存个条线程的下载的长度 private int block; //每条线程下载的长度 private String downloadUrl; //下载的路径 /** * 获取线程数 */ public int getThreadSize() { //return threads.length; return 0; } /** * 退出下载 * */ public void exit() { this.exited = true; //将退出的标志设置为true; } public boolean getExited() { return this.exited; } /** * 获取文件的大小 * */ public int getFileSize() { return fileSize; } /** * 累计已下载的大小 * 使用同步锁来解决并发的访问问题 * */ protected synchronized void append(int size) { //把实时下载的长度加入到总的下载长度中 downloadedSize += size; } /** * 更新指定线程最后下载的位置 * @param threadId 线程id * @param pos 最后下载的位置 * */ protected synchronized void update(int threadId,int pos) { //把指定线程id的线程赋予最新的下载长度,以前的值会被覆盖掉 this.data.put(threadId, pos); //更新数据库中制定线程的下载长度 this.fileService.update(this.downloadUrl, threadId, pos); } /** * 构建文件下载器 * @param downloadUrl 下载路径 * @param fileSaveDir 文件的保存目录 * @param threadNum 下载线程数 * @return */ public FileDownloadered(Context context,String downloadUrl,File fileSaveDir,int threadNum) { try { this.context = context; //获取上下文对象,赋值 this.downloadUrl = downloadUrl; //为下载路径赋值 fileService = new FileService(this.context); //实例化数据库操作的业务Bean类,需要传一个context值 URL url = new URL(this.downloadUrl); //根据下载路径实例化URL if(!fileSaveDir.exists()) fileSaveDir.mkdir(); //如果文件不存在的话指定目录,这里可创建多层目录 this.threads = new DownloadThread[threadNum]; //根据下载的线程数量创建下载的线程池 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //创建远程连接句柄,这里并未真正连接 conn.setConnectTimeout(5000); //设置连接超时事件为5秒 conn.setRequestMethod("GET"); //设置请求方式为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("Referer", downloadUrl); //设置请求的来源页面,便于服务端进行来源统计 conn.setRequestProperty("Charset", "UTF-8"); //设置客户端编码 //设置用户代理 conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; " + "Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727;" + " .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); conn.setRequestProperty("Connection", "Keep-Alive"); //设置connection的方式 conn.connect(); //和远程资源建立正在的链接,但尚无返回的数据流 printResponseHeader(conn); //打印返回的Http的头字段集合 //对返回的状态码进行判断,用于检查是否请求成功,返回200时执行下面的代码 if(conn.getResponseCode() == RESPONSEOK) { this.fileSize = conn.getContentLength(); //根据响应获得文件大小 if(this.fileSize <= 0)throw new RuntimeException("不知道文件大小"); //文件长度小于等于0时抛出运行时异常 String filename = getFileName(conn); //获取文件名称 this.saveFile = new File(fileSaveDir,filename); //根据文件保存目录和文件名保存文件 Map logdata = fileService.getData(downloadUrl); //获取下载记录 //如果存在下载记录 if(logdata.size() > 0) { //遍历集合中的数据,把每条线程已下载的数据长度放入data中 for(Map.Entry entry : logdata.entrySet()) { data.put(entry.getKey(), entry.getValue()); } } //如果已下载的数据的线程数和现在设置的线程数相同时则计算所有现场已经下载的数据总长度 if(this.data.size() == this.threads.length) { //遍历每条线程已下载的数据 for(int i = 0;i 0) randOut.setLength(this.fileSize); randOut.close(); //关闭该文件,使设置生效 URL url = new URL(this.downloadUrl); if(this.data.size() != this.threads.length){ //如果原先未曾下载或者原先的下载线程数与现在的线程数不一致 this.data.clear(); //遍历线程池 for (int i = 0; i < this.threads.length; i++) { this.data.put(i+1, 0);//初始化每条线程已经下载的数据长度为0 } this.downloadedSize = 0; //设置已经下载的长度为0 } for (int i = 0; i < this.threads.length; i++) {//开启线程进行下载 int downLength = this.data.get(i+1); //通过特定的线程id获取该线程已经下载的数据长度 //判断线程是否已经完成下载,否则继续下载 if(downLength < this.block && this.downloadedSize<this.fileSize){ //初始化特定id的线程 this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1); //设置线程优先级,Thread.NORM_PRIORITY = 5; //Thread.MIN_PRIORITY = 1;Thread.MAX_PRIORITY = 10,数值越大优先级越高 this.threads[i].setPriority(7); this.threads[i].start(); //启动线程 }else{ this.threads[i] = null; //表明线程已完成下载任务 } } fileService.delete(this.downloadUrl); //如果存在下载记录,删除它们,然后重新添加 fileService.save(this.downloadUrl, this.data); //把下载的实时数据写入数据库中 boolean notFinish = true; //下载未完成 while (notFinish) { // 循环判断所有线程是否完成下载 Thread.sleep(900); notFinish = false; //假定全部线程下载完成 for (int i = 0; i < this.threads.length; i++){ if (this.threads[i] != null && !this.threads[i].isFinish()) { //如果发现线程未完成下载 notFinish = true; //设置标志为下载没有完成 if(this.threads[i].getDownLength() == -1){ //如果下载失败,再重新在已下载的数据长度的基础上下载 //重新开辟下载线程,设置线程的优先级 this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1); this.threads[i].setPriority(7); this.threads[i].start(); } } } if(listener!=null) listener.onDownloadSize(this.downloadedSize); //通知目前已经下载完成的数据长度 } if(downloadedSize == this.fileSize) fileService.delete(this.downloadUrl); //下载完成删除记录 } catch (Exception e) { print(e.toString()); throw new Exception("文件下载异常"); } return this.downloadedSize; } /** * 获取Http响应头字段 * @param http * @return */ public static Map getHttpResponseHeader(HttpURLConnection http) { //使用LinkedHashMap保证写入和便利的时候的顺序相同,而且允许空值 Map header = new LinkedHashMap(); //此处使用无线循环,因为不知道头字段的数量 for (int i = 0;; i++) { String mine = http.getHeaderField(i); //获取第i个头字段的值 if (mine == null) break; //没值说明头字段已经循环完毕了,使用break跳出循环 header.put(http.getHeaderFieldKey(i), mine); //获得第i个头字段的键 } return header; } /** * 打印Http头字段 * @param http */ public static void printResponseHeader(HttpURLConnection http){ //获取http响应的头字段 Map header = getHttpResponseHeader(http); //使用增强for循环遍历取得头字段的值,此时遍历的循环顺序与输入树勋相同 for(Map.Entry entry : header.entrySet()){ //当有键的时候则获取值,如果没有则为空字符串 String key = entry.getKey()!=null ? entry.getKey()+ ":" : ""; print(key+ entry.getValue()); //打印键和值得组合 } } /** * 打印信息 * @param msg 信息字符串 * */ private static void print(String msg) { Log.i(TAG, msg); } }
Étape 4 : Personnaliser une classe de fil de téléchargementCe que fait cette classe de fil personnalisée est la suivante :
① Tout d'abord, vous devez hériter de la classe Thread, puis réécrire la méthode Run() ② Méthode Run() : déterminez d'abord si le téléchargement est terminé, sinon : ouvrez le lien URLConnection, puis RandomAccessFile Lisez et écrivez les données. Une fois terminé, définissez la marque d'achèvement sur true. Si une exception se produit, définissez la longueur sur -1. Imprimer les informations d'exception ③ Comment imprimer les informations du journal ④ Comment. pour déterminer si le téléchargement est terminé (en fonction de la note d'achèvement) ⑤ Obtenez la taille du contenu téléchargé
DownLoadThread.java :
package com.jay.example.service; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import android.util.Log; public class DownloadThread extends Thread { private static final String TAG = "下载线程类"; //定义TAG,在打印log时进行标记 private File saveFile; //下载的数据保存到的文件 private URL downUrl; //下载的URL private int block; //每条线程下载的大小 private int threadId = -1; //初始化线程id设置 private int downLength; //该线程已下载的数据长度 private boolean finish = false; //该线程是否完成下载的标志 private FileDownloadered downloader; //文件下载器 public DownloadThread(FileDownloadered downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) { this.downUrl = downUrl; this.saveFile = saveFile; this.block = block; this.downloader = downloader; this.threadId = threadId; this.downLength = downLength; } @Override public void run() { if(downLength < block){//未下载完成 try { HttpURLConnection http = (HttpURLConnection) downUrl.openConnection(); http.setConnectTimeout(5 * 1000); http.setRequestMethod("GET"); http.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, */*"); http.setRequestProperty("Accept-Language", "zh-CN"); http.setRequestProperty("Referer", downUrl.toString()); http.setRequestProperty("Charset", "UTF-8"); int startPos = block * (threadId - 1) + downLength;//开始位置 int endPos = block * threadId -1;//结束位置 http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围 http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); http.setRequestProperty("Connection", "Keep-Alive"); InputStream inStream = http.getInputStream(); //获得远程连接的输入流 byte[] buffer = new byte[1024]; //设置本地数据的缓存大小为1MB int offset = 0; //每次读取的数据量 print("Thread " + this.threadId + " start download from position "+ startPos); //打印该线程开始下载的位置 RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd"); threadfile.seek(startPos); //用户没有要求停止下载,同时没有达到请求数据的末尾时会一直循环读取数据 while (!downloader.getExited() && (offset = inStream.read(buffer, 0, 1024)) != -1) { threadfile.write(buffer, 0, offset); //直接把数据写入到文件中 downLength += offset; //把新线程已经写到文件中的数据加入到下载长度中 downloader.update(this.threadId, downLength); //把该线程已经下载的数据长度更新到数据库和内存哈希表中 downloader.append(offset); //把新下载的数据长度加入到已经下载的数据总长度中 } threadfile.close(); inStream.close(); print("Thread " + this.threadId + " download finish"); this.finish = true; //设置完成标记为true,无论下载完成还是用户主动中断下载 } catch (Exception e) { this.downLength = -1; //设置该线程已经下载的长度为-1 print("Thread "+ this.threadId+ ":"+ e); } } } private static void print(String msg){ Log.i(TAG, msg); } /** * 下载是否完成 * @return */ public boolean isFinish() { return finish; } /** * 已经下载的内容大小 * @return 如果返回值为-1,代表下载失败 */ public long getDownLength() { return downLength; } }
Étape 5 : Créez une interface DownloadProgressListener pour surveiller la progression du téléchargementDownloadProgressListener est utilisé dans FileDownloader pour surveiller la progression. Vous devez donc ici créer une interface et définir une implémentation vide d'une méthode :
DownloadProgressListener.java:
package com.jay.example.service; public interface DownloadProgressListener { public void onDownloadSize(int downloadedSize); }
Étape 6 : Écrivez notre code de mise en page De plus, appelez android:enabled="false" pour définir si le composant est cliquable. Le code est le suivant
activity_main.xml:
.
Étape 7 : Écriture de MainActivityLa dernière étape est notre MainActivity, qui termine l'initialisation des composants et des variables associées ; Utilisez des gestionnaires pour effectuer les opérations de mise à jour de l'interface. De plus, les opérations fastidieuses ne peuvent pas être effectuées dans le thread principal. Un nouveau fil de discussion doit donc être ouvert ici, qui est implémenté avec Runnable. Pour plus de détails, voir le code
MainActivity.java:
package com.jay.example.multhreadcontinuabledemo; import java.io.File; import com.jay.example.service.FileDownloadered; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private EditText editpath; private Button btndown; private Button btnstop; private TextView textresult; private ProgressBar progressbar; private static final int PROCESSING = 1; //正在下载实时数据传输Message标志 private static final int FAILURE = -1; //下载失败时的Message标志 private Handler handler = new UIHander(); private final class UIHander extends Handler{ public void handleMessage(Message msg) { switch (msg.what) { //下载时 case PROCESSING: int size = msg.getData().getInt("size"); //从消息中获取已经下载的数据长度 progressbar.setProgress(size); //设置进度条的进度 //计算已经下载的百分比,此处需要转换为浮点数计算 float num = (float)progressbar.getProgress() / (float)progressbar.getMax(); int result = (int)(num * 100); //把获取的浮点数计算结果转换为整数 textresult.setText(result+ "%"); //把下载的百分比显示到界面控件上 if(progressbar.getProgress() == progressbar.getMax()){ //下载完成时提示 Toast.makeText(getApplicationContext(), "文件下载成功", 1).show(); } break; case FAILURE: //下载失败时提示 Toast.makeText(getApplicationContext(), "文件下载失败", 1).show(); break; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editpath = (EditText) findViewById(R.id.editpath); btndown = (Button) findViewById(R.id.btndown); btnstop = (Button) findViewById(R.id.btnstop); textresult = (TextView) findViewById(R.id.textresult); progressbar = (ProgressBar) findViewById(R.id.progressBar); ButtonClickListener listener = new ButtonClickListener(); btndown.setOnClickListener(listener); btnstop.setOnClickListener(listener); } private final class ButtonClickListener implements View.OnClickListener{ public void onClick(View v) { switch (v.getId()) { case R.id.btndown: String path = editpath.getText().toString(); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File saveDir = Environment.getExternalStorageDirectory(); download(path, saveDir); }else{ Toast.makeText(getApplicationContext(), "sd卡读取失败", 1).show(); } btndown.setEnabled(false); btnstop.setEnabled(true); break; case R.id.btnstop: exit(); btndown.setEnabled(true); btnstop.setEnabled(false); break; } } /* 由于用户的输入事件(点击button, 触摸屏幕....)是由主线程负责处理的,如果主线程处于工作状态, 此时用户产生的输入事件如果没能在5秒内得到处理,系统就会报“应用无响应”错误。 所以在主线程里不能执行一件比较耗时的工作,否则会因主线程阻塞而无法处理用户的输入事件, 导致“应用无响应”错误的出现。耗时的工作应该在子线程里执行。 */ private DownloadTask task; /** * 退出下载 */ public void exit(){ if(task!=null) task.exit(); } private void download(String path, File saveDir) {//运行在主线程 task = new DownloadTask(path, saveDir); new Thread(task).start(); } /* * UI控件画面的重绘(更新)是由主线程负责处理的,如果在子线程中更新UI控件的值,更新后的值不会重绘到屏幕上 * 一定要在主线程里更新UI控件的值,这样才能在屏幕上显示出来,不能在子线程中更新UI控件的值 */ private final class DownloadTask implements Runnable{ private String path; private File saveDir; private FileDownloadered loader; public DownloadTask(String path, File saveDir) { this.path = path; this.saveDir = saveDir; } /** * 退出下载 */ public void exit(){ if(loader!=null) loader.exit(); } public void run() { try { loader = new FileDownloadered(getApplicationContext(), path, saveDir, 3); progressbar.setMax(loader.getFileSize());//设置进度条的最大刻度 loader.download(new com.jay.example.service.DownloadProgressListener() { public void onDownloadSize(int size) { Message msg = new Message(); msg.what = 1; msg.getData().putInt("size", size); handler.sendMessage(msg); } }); } catch (Exception e) { e.printStackTrace(); handler.sendMessage(handler.obtainMessage(-1)); } } } } }
Étape 8. : Fichier AndroidManifest.xml Ajoutez les autorisations pertinentes dans
Téléchargement de point d'arrêt multithread + lecteur de musique en ligne : Téléchargement de point d'arrêt multithread + lecteur de musique en ligne.zip
Résumé de cette section :
D'accord, cette section ne contient que peu d'analyses de code sur le téléchargement de points d'arrêt multithread Android. C'est assez ennuyeux, mais quand même. Cela dit, si quelqu’un d’autre a fabriqué la roue, pourquoi la fabriquer soi-même ? De plus, nous sommes encore capables de le créer aujourd’hui. N'est-ce pas ? Alors, je le comprends pour l'instant, je sais comment l'utiliser, je sais juste comment le changer~ Bon, c'est tout, merci~