簡體   English   中英

android進度條無法正確更新進度(onPostExecute()延遲運行)

[英]android progress bar not updating progress correctly (onPostExecute() runs late)

我正在構建一個用於練習和學習的應用程序,旨在從互聯網上下載文件。 我確定將來我將不得不對其進行很多更改,但是截至目前,我無法使進度條正確更新。 當我單擊按鈕時,應該運行AsyncTask子類並獲取文件。 從互聯網讀取文件時,進度條應該會更新。 問題在於進度條有時似乎一次全部立即更新,有時進度條滯后很長時間保持空白直到再次更新,一次全部更新。 我看到使用buffer.size()作為publishProgress()的參數存在問題,但是我不確定如何正確執行此操作。 onPostExecute()也需要很長時間才能運行。 作為附帶的問題,我有一小段代碼被我注釋掉,它使用rxjava更新進度條。 我正在考慮嘗試使用類似這樣的方法替換onPostExecute()。 那是個壞主意嗎? 是“ rxjava的正確用法”嗎? 這是我的MainActivity:

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);
}

}

如果我的問題不清楚,我深表歉意。 我要問的基本上是我如何才能更有效地執行與從Internet讀取相關的進度更新,並減少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());
        }

您之所以會滯后,是因為您在一個循環中進行了公共進度,這會使主線程多次調用它。 我們在這里有一些解決方案:

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

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

  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;
    }

}

在您的AsyncDownload類中使用它

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM