2017-07-13 48 views
0

我正在構建一個應用程序,用於練習和學習,旨在從互聯網上下載文件。我相信我將來必須對其進行很多更改,但截至目前,我無法正確更新進度欄。當我點擊按鈕時,AsyncTask子類應該運行並獲取文件。當文件從互聯網上被讀取時,進度條應該被更新。問題在於有時進度條看起來立刻會立即更新,有時甚至會滯後很長一段時間,直到再次更新一次。我發現使用buffer.size()作爲publishProgress()的參數存在問題,但我不知道如何正確執行此操作。 onPostExecute()也需要很長時間才能運行。作爲一個小隊,我有一小段代碼,我註釋到使用rxjava來更新進度條。我正在考慮嘗試使用這樣的東西來取代onPostExecute()。這會是一個壞主意嗎?是「rxjava的正確用法?」這裏是我的MainActivity:android進度條沒有正確更新進度(onPostExecute()運行較晚)

public class MainActivity extends AppCompatActivity { 

private static final String TAG = "MAIN"; 
private static final String startURL = "https://www.google.com"; 
private static final int REQUEST_CODE_EXTERNAL = 0; 

private Button runButton; 
private EditText urlSpecBox; 
private ProgressBar progressBar; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    //request for permission to write to storage here 
    if(ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) 
      != PackageManager.PERMISSION_GRANTED){ 
     ActivityCompat.requestPermissions(this, (new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}), REQUEST_CODE_EXTERNAL); 
    } 

    progressBar = (ProgressBar) findViewById(R.id.progroessBar); 
    progressBar.setMax(100); 


    runButton = (Button) findViewById(R.id.dwnldButton); 
    runButton.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      try{ 
       progressBar.setVisibility(View.VISIBLE); 
       progressBar.setProgress(0); 
       new AsyncDownload(new URL(startURL), progressBar).execute(); 

      }catch (MalformedURLException me){ 
       Log.e(TAG, "error with url", me); 
      } 
     } 
    }); 

    urlSpecBox = (EditText) findViewById(R.id.urlSpecBox); 

} 
} 

和我的AsyncTask子類:

public class AsyncDownload extends AsyncTask<Void, Integer, Void>{ 
private static final String TAG = "AsyncDownload"; 
private static final String STORAGE_LOCATION = "/sdcard/"; //android directory picker is needed 

private URL url; 
private ProgressBar mProgessBar; 
//private ArrayList<Byte> bytes = new ArrayList<>(); 

public AsyncDownload(URL url, ProgressBar progressBar){ 
    mProgessBar = progressBar; 
    this.url = url; 
} 

@Override 
protected void onProgressUpdate(Integer... progress){ 
    mProgessBar.setProgress(progress[0]); 
} 

@Override 
protected Void doInBackground(Void... params){ 

    try{ 
     HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 
     BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); 

     ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 

     int c; 
     while ((c = in.read()) != -1){ 
      buffer.write(c); 
      publishProgress(buffer.size()); 
     } 

     Log.i(TAG, "response received"); 

     Random rand = new Random(4L); 
     String temp = String.valueOf(rand.nextInt()); 

     String finalLocation = STORAGE_LOCATION + temp; 

     File file = new File(finalLocation); 
     file.getParentFile().mkdirs(); 

     Log.i(TAG, file.getName()); 

     FileOutputStream fOut = new FileOutputStream(file); 
     fOut.write(buffer.toByteArray()); 
     buffer.close(); 
     fOut.flush(); 
     fOut.close(); 
     FileInputStream fIn = new FileInputStream(finalLocation); 

     String reRead = new String(); 
     int a; 
     while ((a = fIn.read()) != -1){ 
      reRead += a; 
     } 

     Log.i(TAG, "reRead" + reRead); 

     //this section is for automatic file naming 
     /*Random rand = new Random(5L); 
     String fileNumber = String.valueOf(rand.nextInt()); 
     StringBuilder sb = new StringBuilder(); 
     sb.append(fileNumber).append("download"); //definitely needs work 

     Log.i(TAG, sb.toString());*/ 

     //FileOutputStream fOut = new FileOutputStream() 

    }catch (IOException ioe){ 
     Log.e(TAG, "network error" + ioe.toString(), ioe); 
    } 

    /*rx.Observable.just(0) //is it correct to use rxjava this way? 
      .observeOn(AndroidSchedulers.mainThread()) 
      .subscribe(
        new Action1<Integer>() { 
         @Override 
         public void call(Integer integer) { 
          mProgessBar.setProgress(integer); 
          mProgessBar.setVisibility(View.VISIBLE); 
         } 
        } 
      );*/ 

    return null; 
} 

@Override 
protected void onPostExecute(Void result){ // METHOD IS NEVER CALLED 
    super.onPostExecute(result); 
    Log.i(TAG, "onPostExecute called! - Task Completed!"); 
    mProgessBar.setProgress(0); 
    mProgessBar.setVisibility(View.GONE); 
} 

} 

我道歉,如果我的問題似乎還不清楚。我所要求的基本上是如何更有效地執行與從互聯網閱讀相關的進度更新,並減少被調用的doInBackground()和被調用的onPostExecute()之間的延遲。

編輯我的代碼:

int c; 
     int progress = 0; 
     int count = buffer.size(); 
     int fileSize = connection.getContentLength(); 

     while ((c = in.read()) != -1){ 
      buffer.write(c); 
      try{ 
       Thread.sleep(TimeUnit.MILLISECONDS.toMillis(100L)); 
      }catch (InterruptedException ie){ 
       Log.e(TAG, "thread interrupted", ie); 
      }finally { 
       if (count > 0){ 
        publishProgress((int) ((progress+=count)*100/fileSize)); 
       } 
      } 
      //publishProgress(buffer.size()); 
     } 
+0

嘗試從doInBackground方法返回的字符串。因此,您可以檢查onPostExecute中的狀態任務是否已完成。 –

+0

你爲什麼不嘗試'在AsyncTask類中保護無效onProgressUpdate' –

+0

@AshutoshSagar你的意思是我應該打電話onProgressUpdate?我沒有在我的AsyncTask中覆蓋它。我是否需要調用super.onProgressUpdate?或者我錯過了什麼? –

回答

1

你有滯後,因爲你在循環中公開的進步,它將使主線程調用了很多時間。我們在這裏有一些解決方案:

  1. 請延遲使用Thread.sleep。至少有1億密耳。

    try { Thread.sleep(100);如果(fileLength> 0)this.publishProgress((int)((progress + = count)* 100/fileLength));如果(fileLength> 0){catch(InterruptedException e){ } if } }

  2. 公衆進步,當它比前一個百分比增加1%。

  3. 更新代碼:不需要使用緩衝

    FileOutputStream fOut = new FileOutputStream(file); 
    FileInputStream fIn = new FileInputStream(finalLocation); 
    byte data[] = new byte[4096]; 
    long progress = 0; 
    int count; 
    int fileSize = connection.getContentLength(); 
    
    while ((c = in.read()) != -1){ 
        //we should write the data before publish progress 
        fOut.write(data, 0, count) 
        try{ 
         Thread.sleep(100); 
        }catch (InterruptedException ie){ 
         Log.e(TAG, "thread interrupted", ie); 
        }finally { 
         if (fileSize > 0){ 
          publishProgress((int) ((progress+=count)*100/fileSize)); 
         } 
        } 
    } 
    

if (fileSize > 0) { 
    currentProgress = ((progress += count) * 100/fileSize); 
    // Publish only on increments of 1% 
    if (currentProgress >= previousProgress + 1) { 
     this.publishProgress(currentProgress); 
     previousProgress = currentProgress; 
    } 

} 
+0

我只是不理解我應該用什麼進步和計數。我正在嘗試進展爲一個int初始化爲0,但我不清楚在哪裏得到計數。我是否必須使用for循環代替while? –

+0

我發佈了一個編輯我的代碼,這是我試圖遵循你的建議。似乎我做的不正確,雖然滯後增加。 –

+0

我更新我的答案 – GiaLe

0

使用此在AsyncDownload類

@Override 
     protected void onProgressUpdate(Integer... values) { 
      progressBar.setProgress(values[0]); 
     } 
+0

'@Override protected void onProgressUpdate(Integer ... progress){ mProgessBar.setProgress進展[0]); }'這是我的用法。那是對的嗎? –