Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
327 views
in Technique[技术] by (71.8m points)

progress bar - Android ProgressBar does not update when updated by setProgress

I have seen this question asked before, but I can't find an answer that works and I've been looking at this same issue for days glum.

I have a screen with 3 progress bars which I want to update during a lengthy download process. No matter what I try I cannot get the progressbar to update.

The full source is here:

My FileDownloadManager class:

package org.touchandgo.speak;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;

public class FileDownloadManager  extends Activity 
{
    public static final String FILE_MANIFEST = "http://dl.dropbox.com/u/25690498/TouchAndGoSpeakImages/TouchAndGoSpeak_Download.manifest";
    public static final String LOCAL_FILE_MANIFEST = "TouchAndGoSpeak_Download.manifest";
    public static final String LOGid = "TouchAndGoSpeak";  

    private String msBaseFolder = "";
    private TextView tvCurrent;

    public void onCreate(Bundle icicle)  
    {  
        super.onCreate(icicle);
        Log.d(LOGid, "Create activity");
        setContentView(R.layout.download);

        mContext = this;


        tvCurrent = (TextView) findViewById(R.id.tvCurrentAction);
        tvCurrent.setText("Awaiting user input");
        tvCurrent.invalidate();

        Button bDownload = (Button) findViewById(R.id.btnStartDownload);
        bDownload.setOnClickListener(new View.OnClickListener() 
        {
            public void onClick(View arg0) 
            {
                if (!isNetworkAvailable())
                {
                    AlertDialog adConfirm = new AlertDialog.Builder(mContext).create();
                    adConfirm.setCancelable(false); // This blocks the 'BACK' button
                    adConfirm.setMessage("It appears you are offline, phone should be online for this to work.");
                    adConfirm.setButton("I'm online, go ahead.", new DialogInterface.OnClickListener() 
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which) 
                        {
                            dialog.dismiss();
                            downloadfiles();
                        }
                    });
                    adConfirm.setButton2("I'll try later", new DialogInterface.OnClickListener() 
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which) 
                        {
                            dialog.dismiss();
                        }
                    });

                    adConfirm.show();

                }
                else
                {
                    downloadfiles();
                }
            } 
        });

        Button bDownloadInfo = (Button) findViewById(R.id.btnDownloadInfo);
        bDownloadInfo.setOnClickListener(new View.OnClickListener() 
        {
            public void onClick(View arg0) 
            {
                AlertDialog adConfirm = new AlertDialog.Builder(mContext).create();
                adConfirm.setCancelable(false); // This blocks the 'BACK' button
                adConfirm.setMessage("This function will go to the internet and download the Touch And Go 'Speak' free image set.

It is highly recommended that you use a wi-fi connection for this.

These images are provided free of charge and as is. They are not my work, but are based upon various free resources from the internet that I have pulled together.
No copyright infringement is intended. Please contact me if you wish to assert copyright for any of these images.
If you own a set of images from another source (for example the {PECS image set) the you can copy these to the applocation folder on the sdcard (" + msBaseFolder + ") - for every folder you want to display in the list you must have a corresponding image file for the app to display (for example if you have a folder called '2. Sentence Starters' then in the base folder you must have am image file called '2. Sentence Starters.jpg'");
                adConfirm.setButton("OK", new DialogInterface.OnClickListener() 
                {
                    @Override
                    public void onClick(DialogInterface dialog, int which) 
                    {
                        dialog.dismiss();
                    }
                });

                adConfirm.show();
            } 
        });


    }


    private void processManifest()
    {
        tvCurrent.setText("Processing files to download...");
        tvCurrent.invalidate();

        //Get the text file
        File file = new File(msBaseFolder,LOCAL_FILE_MANIFEST);
        Log.d(LOGid, "Created new file " + msBaseFolder+ "/" + LOCAL_FILE_MANIFEST);

        //Read text from file
//        StringBuilder text = new StringBuilder();
        ProgressBar pb = (ProgressBar) findViewById(R.id.barFiles);
        int iLineCnt=getLineCount(LOCAL_FILE_MANIFEST);

        int iCurrentLine=0;
        pb.setMax(iLineCnt);
        Log.d(LOGid, "Set progress line count to " + iCurrentLine + " of " + iLineCnt);

        try 
        {
            BufferedReader br = new BufferedReader(new FileReader(file));
            Log.d(LOGid, "Created buffered reader");
            String line;

            while ((line = br.readLine()) != null) 
            {
                try
                {
                    iCurrentLine++;
                    pb.setProgress(iCurrentLine);
                    pb.invalidate();
                    Log.d(LOGid, "Set progress line count to " + iCurrentLine + " of " + iLineCnt);
                }
                catch (Exception ex)
                { // swallow the error
                }
                String[] sDownloads = line.split(";");
                Log.d(LOGid, "Line read was" + line + " of which there are " + sDownloads.length + " parts");

                // line should have 3 parts
                // 1. Source url
                // 2. Target name
                // 3. Target folder
                if (sDownloads.length == 3)
                {
                    // Does the file already exist
                    File fil;
                    File fld;
                    if (sDownloads[2].equals("."))
                    {
                        fil = new File(msBaseFolder, sDownloads[1]);
                        fld = new File(msBaseFolder);
                    }
                    else
                    {
                        fil = new File(msBaseFolder + "/" + sDownloads[2], sDownloads[1]);
                        fld = new File(msBaseFolder + "/" + sDownloads[2]);
                    }

                    // Does the folder exist
                    Log.d(LOGid, "Ensure the dest folder exists: " + fld.getPath());
                    fld.mkdirs();

                    if (!fil.exists() )
                    {
                        downloadFile(sDownloads[0], sDownloads[1], fld.getPath(), true);
                    }
                    else
                    {
                        // Skipping a file
                    }
                }
            }
        }
        catch (Exception e) 
        {
            //You'll need to add proper error handling here
            Log.e(LOGid, "Error in processmanifest:" + e.getMessage());
        }

        //Set the text
        tvCurrent.setText("Finished Downloading Files...");
        tvCurrent.invalidate();
    }

    private boolean downloadFile(String _source, String _dest, String _Folder, boolean isManifest)
    { 
        try
        {
            tvCurrent.setText("Downloading file " + _dest + "...");
            tvCurrent.invalidate();
            //set the download URL, a url that points to a file on the internet
            //this is the file to be downloaded
            URL url = new URL(_source);
            Log.d(LOGid, "Created URL");

            //create the new connection
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            Log.d(LOGid, "Created HTTP Connection");

            //set up some things on the connection
            urlConnection.setRequestMethod("GET");
            Log.d(LOGid, "Set the request method");
            urlConnection.setDoOutput(true);
            Log.d(LOGid, "Set doOutput");

            //and connect!
            urlConnection.connect();
            Log.d(LOGid, "Connected the url connection");

            //set the path where we want to save the file
            //in this case, going to save it on the root directory of the
            //sd card.
            File SDCardRoot = Environment.getExternalStorageDirectory();
            //create a new file, specifying the path, and the filename
            //which we want to save the file as.
            File file = new File(_Folder, _dest);
            Log.d(LOGid, "Created dest file, path=" + file.getPath() + "...file=" + file.getName());

            //this will be used to write the downloaded data into the file we created
            FileOutputStream fileOutput = new FileOutputStream(file);
            Log.d(LOGid, "Created file output stream");

            //this will be used in reading the data from the internet
            InputStream inputStream = urlConnection.getInputStream();
            Log.d(LOGid, "Created Input stream");

            //this is the total size of the file
            int totalSize = urlConnection.getContentLength();
            Log.d(LOGid, "Got total size " + totalSize);

            //variable to store total downloaded bytes
            int downloadedSize = 0;

            //create a buffer...
            byte[] buffer = new byte[1024];
            Log.d(LOGid, "Created buffer");

            int bufferLength = 0; //used to store a temporary size of the buffer
            ProgressBar pb;
            if (isManifest)
            {
                pb = (ProgressBar) findViewById(R.id.barManifest);    
            }
            else
            {
                pb = (ProgressBar) findViewById(R.id.barOneFile);    
            }
            pb.setMax(totalSize);
            Log.d(LOGid, "Set progressbar size");

            pb.setProgress(0);
            pb.invalidate();

            Log.d(LOGid, "Set progress to zero");

            //now, read through the input buffer and write the contents to the file
            while ( (bufferLength = inputStream.read(buffer)) > 0 ) 
            {
                    //add the data in the buffer to the file in the file output stream (the file on the sd card
                    fileOutput.write(buffer, 0, bufferLength);
                    Log.d(LOGid, "Wrote infor to the buffer");
                    //add up the size so we know how much is downloaded
                    downloadedSize += bufferLength;
                    //this is where you would do something to report the prgress, like this maybe
            //        updateProgress(downloadedSize, totalSize);

             

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It looks like you're doing the work that you're measuring progress on within your UI thread (called directly from a callback, not positioned within a new thread). That doesn't give the system any CPU time to respond to your interim progress updates.

The proper way to handle this is to do your work in a background thread, and periodically update the progress. Those updates must happen from within the UI thread, but your Activity's runOnUiThread(Runnable) method can accomplish that for you. (Alternatively you could post the runnable to your Activity's handler for UI-thread execution.)

Edit: added example for thread creation Your work is done in your downloadFiles() method, so this is what you want in a separate thread. Replace your call of downloadFiles() with, for example,

Thread worker = new Thread(new Runnable() {
    @Override
    public void run() {
        downloadFiles();
    }
});
worker.start();

Now you also need to modify downloadFiles() -- since it's not on the UI thread, it's not permitted to modify the UI. Instead of calling pb.setMax(totalSize);, you'll need to get that performed within the UI thread. You can write a runnable (or several of them) to modify the UI from the proper thread in a similar manner to running a new thread:

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        // do things that modify the UI
    }
});

The only thing you need to be careful of here -- your runnable's run() method will probably need access to some of the local variables in the method containing the runOnUiThread() call. To legally provide that access, the variables it uses must be final. If you have variables which cannot be final, you need to copy their value to a final variable that the runnable uses. For example:

final ProgressBar final_pb = pb;
final int final_downloadedSize = downloadedSize;
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        final_pb.setProgress(final_downloadedSize);
    }
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...