Android多線(xiàn)程+單線(xiàn)程+斷點(diǎn)續(xù)傳+進(jìn)度條顯示下載功能
效果圖

白話(huà)分析:
多線(xiàn)程:肯定是多個(gè)線(xiàn)程咯
斷點(diǎn):線(xiàn)程停止下載的位置
續(xù)傳:線(xiàn)程從停止下載的位置上繼續(xù)下載,直到完成任務(wù)為止。
核心分析:
斷點(diǎn):
當(dāng)前線(xiàn)程已經(jīng)下載的數(shù)據(jù)長(zhǎng)度
續(xù)傳:
向服務(wù)器請(qǐng)求上次線(xiàn)程停止下載位置的數(shù)據(jù)
con.setRequestProperty("Range", "bytes=" + start + "-" + end);
分配線(xiàn)程:
int currentPartSize = fileSize / mThreadNum;
定義位置
定義線(xiàn)程開(kāi)始下載的位置和結(jié)束的位置
for (int i = 0; i < mThreadNum; i++) {
int start = i * currentPartSize;//計(jì)算每條線(xiàn)程下載的開(kāi)始位置
int end = start + currentPartSize-1;//線(xiàn)程結(jié)束的位置
if(i==mThreadNum-1){
end=fileSize;
}}
創(chuàng)建數(shù)據(jù)庫(kù):
由于每一個(gè)文件要分成多個(gè)部分,要被不同的線(xiàn)程同時(shí)進(jìn)行下載。當(dāng)然要?jiǎng)?chuàng)建線(xiàn)程表,保存當(dāng)前線(xiàn)程下載開(kāi)始的位置和結(jié)束的位置,還有完成進(jìn)度等。創(chuàng)建file表,保存當(dāng)前下載的文件信息,比如:文件名,url,下載進(jìn)度等信息
線(xiàn)程表:
public static final String CREATE_TABLE_SQL="create table "+TABLE_NAME+"(_id integer primary "
+"key autoincrement, threadId, start , end, completed, url)";
file表:
public static final String CREATE_TABLE_SQL="create table "+TABLE_NAME+"(_id integer primary" +
" key autoincrement ,fileName, url, length, finished)";
創(chuàng)建線(xiàn)程類(lèi)
無(wú)非就2個(gè)類(lèi),一個(gè)是線(xiàn)程管理類(lèi)DownLoadManager.Java,核心方法:start(),stop(),restart(),addTask().clear()。另一個(gè)是線(xiàn)程任務(wù)類(lèi)
DownLoadTask.java,就是一個(gè)線(xiàn)程類(lèi),用于下載線(xiàn)程分配好的任務(wù)。后面會(huì)貼出具體代碼。
創(chuàng)建數(shù)據(jù)庫(kù)方法類(lèi)
無(wú)非就是單例模式,封裝一些增刪改查等基礎(chǔ)數(shù)據(jù)庫(kù)方法,后面會(huì)貼出具體代碼。
創(chuàng)建實(shí)體類(lèi)
也就是創(chuàng)建ThreadInfo和FileInfo這2個(gè)實(shí)體類(lèi),把下載文件信息和線(xiàn)程信息暫時(shí)存儲(chǔ)起來(lái)。
引入的第三方開(kāi)源庫(kù)
NumberProgressBar是一個(gè)關(guān)于進(jìn)度條的開(kāi)源庫(kù),挺不錯(cuò)的。直達(dá)鏈接
代碼具體分析
1.首先是創(chuàng)建實(shí)體類(lèi),文件的實(shí)體類(lèi)FileInfo,肯定有fileName,url,length,finised,isStop,isDownloading這些屬性。線(xiàn)程的實(shí)體類(lèi)ThreadInfo肯定有threadId,start,end,completed,url這些屬性。這些都很簡(jiǎn)單
//ThredInfo.java
public class FileInfo {
private String fileName; //文件名
private String url; //下載地址
private int length; //文件大小
private int finished; //下載已完成進(jìn)度
private boolean isStop=false; //是否暫停下載
private boolean isDownloading=false; //是否正在下載
public FileInfo(){
}
public FileInfo(String fileName,String url){
this.fileName=fileName;
this.url=url;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getFinished() {
return finished;
}
public void setFinished(int finished) {
this.finished = finished;
}
public boolean isStop() {
return isStop;
}
public void setStop(boolean stop) {
isStop = stop;
}
public boolean isDownloading() {
return isDownloading;
}
public void setDownloading(boolean downloading) {
isDownloading = downloading;
}
@Override
public String toString() {
return "FileInfo{" +
"fileName='" + fileName + '\'' +
", url='" + url + '\'' +
", length=" + length +
", finished=" + finished +
", isStop=" + isStop +
", isDownloading=" + isDownloading +
'}';
}}
//FileInfo.java
public class FileInfo {
private String fileName; //文件名
private String url; //下載地址
private int length; //文件大小
private int finished; //下載已完成進(jìn)度
private boolean isStop=false; //是否暫停下載
private boolean isDownloading=false; //是否正在下載
public FileInfo(){
}
public FileInfo(String fileName,String url){
this.fileName=fileName;
this.url=url;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getFinished() {
return finished;
}
public void setFinished(int finished) {
this.finished = finished;
}
public boolean isStop() {
return isStop;
}
public void setStop(boolean stop) {
isStop = stop;
}
public boolean isDownloading() {
return isDownloading;
}
public void setDownloading(boolean downloading) {
isDownloading = downloading;
}
@Override
public String toString() {
return "FileInfo{" +
"fileName='" + fileName + '\'' +
", url='" + url + '\'' +
", length=" + length +
", finished=" + finished +
", isStop=" + isStop +
", isDownloading=" + isDownloading +
'}';
}}
2.實(shí)體類(lèi)寫(xiě)完了,那么接下來(lái)寫(xiě)創(chuàng)建一個(gè)類(lèi),繼承SQLiteOpenHelper類(lèi),來(lái)管理數(shù)據(jù)庫(kù)連接,主要作用:管理數(shù)據(jù)庫(kù)的初始化,并允許應(yīng)用程序通過(guò)該類(lèi)獲取SQLiteDatabase對(duì)象。
public class ThreadHelper extends SQLiteOpenHelper{
public static final String TABLE_NAME="downthread";
public static final String CREATE_TABLE_SQL="create table "+TABLE_NAME+"(_id integer primary "
+"key autoincrement, threadId, start , end, completed, url)";
public ThreadHelper(Context context, String name, int version) {
super(context, name, null, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_SQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}}
3.接下來(lái)封裝一些數(shù)據(jù)庫(kù)的增刪改查操作,用的單例模式,用雙重檢驗(yàn)鎖實(shí)現(xiàn)單例。好處:既能很大程度上確保線(xiàn)程安全,又能實(shí)現(xiàn)延遲加載。 缺點(diǎn):使用volatile關(guān)鍵字會(huì)使JVM對(duì)該代碼的優(yōu)化喪失,影響性能。并且在一些高并發(fā)的情況,仍然可能會(huì)創(chuàng)建多個(gè)實(shí)例,這稱(chēng)為雙重檢驗(yàn)鎖定失效。單例模式
public class Thread {
private SQLiteDatabase db;
public static final String DB_NAME="downthread.db3";
public static final int VERSION=1;
private Context mContext;
private volatile static Thread t=null;
private Thread(){
mContext= BaseApplication.getContext();
db=new ThreadHelper(mContext,DB_NAME,VERSION).getReadableDatabase();
}
public static Thread getInstance(){
if(t==null){
synchronized (Thread.class){
if(t==null){
t=new Thread();
}
}
}
return t;
}
public SQLiteDatabase getDb(){
return db;
}
//保存當(dāng)前線(xiàn)程下載進(jìn)度
public synchronized void insert(ThreadInfo threadInfo){
ContentValues values=new ContentValues();
values.put("threadId",threadInfo.getThreadId());
values.put("start",threadInfo.getStart());
values.put("end",threadInfo.getEnd());
values.put("completed",threadInfo.getCompeleted());
values.put("url",threadInfo.getUrl());
long rowId=db.insert(ThreadHelper.TABLE_NAME,null,values);
if(rowId!=-1){
UtilsLog.i("插入線(xiàn)程記錄成功");
}else{
UtilsLog.i("插入線(xiàn)程記錄失敗");
}
}
//查詢(xún)當(dāng)前線(xiàn)程 下載的進(jìn)度
public synchronized ThreadInfo query(String threadId,String queryUrl){
Cursor cursor=db.query(ThreadHelper.TABLE_NAME,null,"threadId= ? and url= ?",new String[]{threadId,queryUrl},null,null,null);
ThreadInfo info=new ThreadInfo();
if(cursor!=null){
while (cursor.moveToNext()){
int start=cursor.getInt(2);
int end=cursor.getInt(3);
int completed=cursor.getInt(4);
String url=cursor.getString(5);
info.setThreadId(threadId);
info.setStart(start);
info.setEnd(end);
info.setCompeleted(completed);
info.setUrl(url);
}
cursor.close();
}
return info;
}
//更新當(dāng)前線(xiàn)程下載進(jìn)度
public synchronized void update(ThreadInfo info){
ContentValues values=new ContentValues();
values.put("start",info.getStart());
values.put("completed",info.getCompeleted());
db.update(ThreadHelper.TABLE_NAME,values,"threadId= ? and url= ?",new String[]{info.getThreadId(),info.getUrl()});
}
//關(guān)閉db
public void close(){
db.close();
}
//判斷多線(xiàn)程任務(wù)下載 是否第一次創(chuàng)建線(xiàn)程
public boolean isExist(String url){
Cursor cursor=db.query(ThreadHelper.TABLE_NAME,null,"url= ?",new String[]{url},null,null,null);
boolean isExist=cursor.moveToNext();
cursor.close();
return isExist;
}
public synchronized void delete(ThreadInfo info){
long rowId=db.delete(ThreadHelper.TABLE_NAME,"url =? and threadId= ?",new String[]{info.getUrl(),info.getThreadId()});
if(rowId!=-1){
UtilsLog.i("刪除下載線(xiàn)程記錄成功");
}else{
UtilsLog.i("刪除下載線(xiàn)程記錄失敗");
}
}
public synchronized void delete(String url){
long rowId=db.delete(ThreadHelper.TABLE_NAME,"url =? ",new String[]{url});
if(rowId!=-1){
UtilsLog.i("刪除下載線(xiàn)程記錄成功");
}else{
UtilsLog.i("刪除下載線(xiàn)程記錄失敗");
}
}}
4.基本的準(zhǔn)備操作我們已經(jīng)完成了,那么開(kāi)始寫(xiě)關(guān)于下載的類(lèi)吧。首先寫(xiě)的肯定是DownLoadManager類(lèi),就是管理任務(wù)下載的類(lèi)。不多說(shuō),直接看代碼。
public class DownLoadManager {
private Map<String, FileInfo> map = new HashMap<>();
private static int mThreadNum;
private int fileSize;
private boolean flag = false; //true第一次下載 false不是第一次下載
private List<DownLoadTask> threads;
private static FileInfo mInfo;
private static ResultListener mlistener;
public static ExecutorService executorService = Executors.newCachedThreadPool();
public static File file;
private int totalComleted;
private DownLoadManager() {
threads = new ArrayList<>();
}
public static DownLoadManager getInstance(FileInfo info, int threadNum,ResultListener listener) {
mlistener = listener;
mThreadNum = threadNum;
mInfo = info;
return DownLoadManagerHolder.dlm;
}
private static class DownLoadManagerHolder {
private static final DownLoadManager dlm = new DownLoadManager();
}
public void start() {
totalComleted=0;
clear();
final FileInfo newInfo = DownLoad.getInstance().queryData(mInfo.getUrl());
newInfo.setDownloading(true);
map.put(mInfo.getUrl(),newInfo);
prepare(newInfo);
}
//停止下載任務(wù)
public void stop() {
map.get(mInfo.getUrl()).setDownloading(false);
map.get(mInfo.getUrl()).setStop(true);
}
public void clear(){
if(threads.size()>0){
threads.clear();
}
}
//重新下載任務(wù)
public void restart() {
stop();
try {
File file = new File(com.cmazxiaoma.downloader.download.DownLoadManager.FILE_PATH, map.get(mInfo.getUrl()).getFileName());
if (file.exists()) {
file.delete();
}
java.lang.Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
DownLoad.getInstance().resetData(mInfo.getUrl());
start();
}
//獲取當(dāng)前任務(wù)狀態(tài), 是否在下載
public boolean getCurrentState() {
return map.get(mInfo.getUrl()).isDownloading();
}
//添加下載任務(wù)
public void addTask(FileInfo info) {
//判斷數(shù)據(jù)庫(kù)是否已經(jīng)存在此下載信息
if (!DownLoad.getInstance().isExist(info)) {
DownLoad.getInstance().insertData(info);
map.put(info.getUrl(), info);
} else {
DownLoad.getInstance().delete(info);
DownLoad.getInstance().insertData(info);
UtilsLog.i("map已經(jīng)更新");
map.remove(info.getUrl());
map.put(info.getUrl(), info);
}
}
private void prepare(final FileInfo newInfo) {
new java.lang.Thread(){
@Override
public void run() {
HttpURLConnection con = null;
RandomAccessFile raf=null;
try {
//連接資源
URL url = new URL(newInfo.getUrl());
UtilsLog.i("url=" + url);
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(2 * 1000);
con.setRequestMethod("GET");
int length = -1;
UtilsLog.i("responseCode=" + con.getResponseCode());
if (con.getResponseCode() == 200) {
length = con.getContentLength();
UtilsLog.i("文件大小=" + length);
}
if (length <= 0) {
return;
}
//創(chuàng)建文件保存路徑
File dir = new File(com.cmazxiaoma.downloader.download.DownLoadManager.FILE_PATH);
if (!dir.exists()) {
dir.mkdirs();//建立多級(jí)文件夾
}
newInfo.setLength(length);
fileSize = length;
UtilsLog.i("當(dāng)前線(xiàn)程Id=" + java.lang.Thread.currentThread().getId() + ",name=" + java.lang.Thread.currentThread().getName());
int currentPartSize = fileSize / mThreadNum;
file = new File(com.cmazxiaoma.downloader.download.DownLoadManager.FILE_PATH, newInfo.getFileName());
raf = new RandomAccessFile(file, "rwd");
raf.setLength(fileSize);
if (Thread.getInstance().isExist(newInfo.getUrl())) {
flag = false;
} else {
flag = true;
}
for (int i = 0; i < mThreadNum; i++) {
if (flag) {
UtilsLog.i("第一次多線(xiàn)程下載");
int start = i * currentPartSize;//計(jì)算每條線(xiàn)程下載的開(kāi)始位置
int end = start + currentPartSize-1;//線(xiàn)程結(jié)束的位置
if(i==mThreadNum-1){
end=fileSize;
}
String threadId = "xiaoma" + i;
ThreadInfo threadInfo = new ThreadInfo(threadId, start, end, 0,newInfo.getUrl());
Thread.getInstance().insert(threadInfo);
DownLoadTask thread = new DownLoadTask(threadInfo,newInfo, threadId, start, end, 0);
DownLoadManager.executorService.execute(thread);
threads.add(thread);
} else {
UtilsLog.i("不是第一次多線(xiàn)程下載");
ThreadInfo threadInfo = Thread.getInstance().query("xiaoma" + i, newInfo.getUrl());
DownLoadTask thread = new DownLoadTask(threadInfo,newInfo,threadInfo.getThreadId(),threadInfo.getStart(),threadInfo.getEnd(),threadInfo.getCompeleted());//這里出現(xiàn)過(guò)問(wèn)題
DownLoadManager.executorService.execute(thread);
threads.add(thread);
}
}
boolean isCompleted=false;
while(!isCompleted){
isCompleted=true;
for(DownLoadTask thread:threads){
totalComleted+=thread.completed;
if(!thread.isCompleted){
isCompleted=false;
}
}
if(newInfo.isStop()){
totalComleted=0;
return;
}
Message message=new Message();
message.what=0x555;
message.arg1=fileSize;
message.arg2=totalComleted;
handler.sendMessage(message);
if(isCompleted){
totalComleted=0;
//任務(wù)線(xiàn)程全部完成,清空集合
clear();
handler.sendEmptyMessage(0x666);
return;
}
totalComleted=0;
java.lang.Thread.sleep(1000);
}
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (con != null) {
con.disconnect();
}
if(raf!=null){
raf.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0x555:
if(mlistener!=null){
mlistener.progress(msg.arg1,msg.arg2);
}
break;
case 0x666:
if(mlistener!=null){
mlistener.comleted();
}
break;
}
}
};}
5.接下來(lái)呢,就是DownLoadTask類(lèi)了,就是一個(gè)線(xiàn)程下載類(lèi)。
public class DownLoadTask extends java.lang.Thread{
private int start;//當(dāng)前線(xiàn)程的開(kāi)始下載位置
private int end;//當(dāng)前線(xiàn)程結(jié)束下載的位置
private RandomAccessFile raf;//當(dāng)前線(xiàn)程負(fù)責(zé)下載的文件大小
public int completed=0;//當(dāng)前線(xiàn)程已下載的字節(jié)數(shù)
private String threadId;//自己定義的線(xiàn)程Id
private FileInfo info;
private ThreadInfo threadInfo;
public boolean isCompleted=false; //true為當(dāng)前線(xiàn)程完成任務(wù),false為當(dāng)前線(xiàn)程未完成任務(wù)
//保存新的start
public int finshed=0;
public int newStart=0;
public DownLoadTask(ThreadInfo threadInfo,FileInfo info,String threadId, int start, int end,int completed){
this.threadInfo=threadInfo;
this.info=info;
this.threadId=threadId;
this.start=start;
this.end=end;
this.completed=completed;
}
@Override
public void run() {
HttpURLConnection con = null;
try {
UtilsLog.i("start="+start+",end="+end+",completed="+completed+",threadId="+getThreadId());
URL url = new URL(info.getUrl());
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(2 * 1000);
con.setRequestMethod("GET");
con.setRequestProperty("Range", "bytes=" + start + "-"+end);//重點(diǎn)
raf=new RandomAccessFile(DownLoadManager.file,"rwd");
//從文件的某一位置寫(xiě)入
raf.seek(start);
if (con.getResponseCode() == 206) { //文件部分下載 返回碼是206
InputStream is = con.getInputStream();
byte[] buffer = new byte[4096];
int hasRead = 0;
while ((hasRead = is.read(buffer)) != -1) {
//寫(xiě)入文件
raf.write(buffer, 0, hasRead);
//單個(gè)文件的完成程度
completed += hasRead;
threadInfo.setCompeleted(completed);
//保存新的start
finshed=finshed+hasRead;//這里出現(xiàn)過(guò)問(wèn)題,嘻嘻
newStart=start+finshed;
threadInfo.setStart(newStart);
//UtilsLog.i("Thread:"+getThreadId()+",completed=" + completed);
//停止下載
if (info.isStop()) {
UtilsLog.i("isStop="+info.isStop());
//保存下載進(jìn)度
UtilsLog.i("現(xiàn)在Thread:"+getThreadId()+",completed=" + completed);
Thread.getInstance().update(threadInfo);
return;
}
}
//刪除該線(xiàn)程下載記錄
Thread.getInstance().delete(threadInfo);
isCompleted=true;
Thread.getInstance().update(threadInfo);
UtilsLog.i("thread:"+getThreadId()+"已經(jīng)完成任務(wù)!--"+"completed="+completed);
}
} catch (Exception e) {
if (con != null) {
con.disconnect();
}
try {
if (raf != null) {
raf.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
public String getThreadId() {
return threadId;
}}
6.接口,就是一個(gè)監(jiān)聽(tīng)下載進(jìn)度的接口,也是很簡(jiǎn)單。
public interface ResultListener{
void progress(int max, int progress);
void comleted();}
結(jié)束
大致操作就是這樣,其實(shí)多線(xiàn)程也挺簡(jiǎn)單的。
以上所述是小編給大家介紹的Android多線(xiàn)程+單線(xiàn)程+斷點(diǎn)續(xù)傳+進(jìn)度條顯示下載功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- android實(shí)現(xiàn)多線(xiàn)程下載文件(支持暫停、取消、斷點(diǎn)續(xù)傳)
- Android FTP 多線(xiàn)程斷點(diǎn)續(xù)傳下載\上傳的實(shí)例
- Android多線(xiàn)程斷點(diǎn)續(xù)傳下載功能實(shí)現(xiàn)代碼
- Android多線(xiàn)程斷點(diǎn)續(xù)傳下載示例詳解
- Android 使用AsyncTask實(shí)現(xiàn)多任務(wù)多線(xiàn)程斷點(diǎn)續(xù)傳下載
- Android實(shí)現(xiàn)網(wǎng)絡(luò)多線(xiàn)程斷點(diǎn)續(xù)傳下載實(shí)例
- Android編程開(kāi)發(fā)實(shí)現(xiàn)多線(xiàn)程斷點(diǎn)續(xù)傳下載器實(shí)例
- PC版與Android手機(jī)版帶斷點(diǎn)續(xù)傳的多線(xiàn)程下載
- Android 使用AsyncTask實(shí)現(xiàn)多線(xiàn)程斷點(diǎn)續(xù)傳
- android原生實(shí)現(xiàn)多線(xiàn)程斷點(diǎn)續(xù)傳功能
相關(guān)文章
Android Activity中使用Intent實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)與參數(shù)傳遞的方法
這篇文章主要介紹了Android Activity中使用Intent實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)與參數(shù)傳遞的方法,結(jié)合實(shí)例形式簡(jiǎn)單分析了Android中的Activity交互操作相關(guān)技巧,需要的朋友可以參考下2016-07-07
Android學(xué)習(xí)教程之高仿安卓微信6.0(2)
這篇文章主要為大家詳細(xì)介紹了Android學(xué)習(xí)教程之高仿安卓微信6.0的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
Android實(shí)現(xiàn)記住賬號(hào)密碼功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)記住賬號(hào)密碼功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-10-10
Android開(kāi)發(fā)之利用Intent實(shí)現(xiàn)數(shù)據(jù)傳遞的方法
這篇文章主要介紹了Android開(kāi)發(fā)之利用Intent實(shí)現(xiàn)數(shù)據(jù)傳遞的方法,實(shí)例分析了Intent傳遞數(shù)據(jù)的原理與相關(guān)使用技巧,需要的朋友可以參考下2016-03-03
Android?ViewPager你可能不知道的刷新操作分享
這篇文章主要為大家詳細(xì)介紹了Android中ViewPager你可能不知道的刷新操作,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以參考一下2023-05-05
如何給Flutter界面切換實(shí)現(xiàn)點(diǎn)特效
這篇文章主要給大家介紹了關(guān)于如何給Flutter界面切換實(shí)現(xiàn)點(diǎn)特效的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
詳解android 用webview加載網(wǎng)頁(yè)(https和http)
這篇文章主要介紹了詳解android 用webview加載網(wǎng)頁(yè)(https和http),詳細(xì)的介紹了兩個(gè)錯(cuò)誤的解決方法,有興趣的可以了解一下2017-11-11
實(shí)現(xiàn)Android 獲取cache緩存的目錄路徑的方法
這篇文章主要介紹了實(shí)現(xiàn)Android 獲取cache緩存的目錄路徑的方法的相關(guān)資料,這里實(shí)現(xiàn)一個(gè)靜態(tài)類(lèi)來(lái)實(shí)現(xiàn)該功能,希望能幫助到大家,需要的朋友可以參考下2017-08-08

