简体   繁体   English

android进度条无法正确更新进度(onPostExecute()延迟运行)

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

i am building an application for practice and learning that is meant to download files from the internet. 我正在构建一个用于练习和学习的应用程序,旨在从互联网上下载文件。 i am sure that i am going to have to make many changes to it in the future but as of now i am having trouble getting the progress bar to update correctly. 我确定将来我将不得不对其进行很多更改,但是截至目前,我无法使进度条正确更新。 when i click the button an AsyncTask subclass is supposed to run and get the file. 当我单击按钮时,应该运行AsyncTask子类并获取文件。 the progress bar is supposed to be updated as the file is read from the internet. 从互联网读取文件时,进度条应该会更新。 the problem is that sometimes the progress bar updates seemingly all at once, immediately, and sometimes it lags for a long time staying blank until again, updating all at once. 问题在于进度条有时似乎一次全部立即更新,有时进度条滞后很长时间保持空白直到再次更新,一次全部更新。 i see that there is a problem with my use of buffer.size() as the argument for publishProgress(), but i am not sure how i can do this correctly. 我看到使用buffer.size()作为publishProgress()的参数存在问题,但是我不确定如何正确执行此操作。 onPostExecute() also takes a very long time to run. onPostExecute()也需要很长时间才能运行。 as a side quetion i have a small section of code that i commented out that uses rxjava to update the progress bar. 作为附带的问题,我有一小段代码被我注释掉,它使用rxjava更新进度条。 i was considering trying to use something like this to replace onPostExecute(). 我正在考虑尝试使用类似这样的方法替换onPostExecute()。 would that be a bad idea? 那是个坏主意吗? is that a "correct usage of rxjava?" 是“ rxjava的正确用法”吗? here is my MainActivity: 这是我的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);

}
}

and my asynctask subclass: 和我的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);
}

}

i apologize if my question seems unclear. 如果我的问题不清楚,我深表歉意。 what i am asking is basically how i can more efficiently perform the progress update related to reading from the internet, and reduce the delay between doInBackground() being called and onPostExecute() being called. 我要问的基本上是我如何才能更有效地执行与从Internet读取相关的进度更新,并减少doInBackground()被调用和onPostExecute()之间的延迟。

an edit to my code: 编辑我的代码:

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

You got lag because you public progress inside a loop, it will make main thread call it many time. 您之所以会滞后,是因为您在一个循环中进行了公共进度,这会使主线程多次调用它。 We have some solution here: 我们在这里有一些解决方案:

  1. Please delay use Thread.sleep. 请延迟使用Thread.sleep。 at least 100 mil. 至少1亿。

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

  2. Just public progress when it increase 1% compare to previous percent. 与之前的百分比相比增加1%只是公共进度。

  3. Update code: Dont need to use buffer 更新代码:不需要使用缓冲区

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

or 要么

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

}

Use this in your AsyncDownload class 在您的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