由setProgress更新时,Android ProgressBar不会更新

时间:2011-06-24 18:50:45

标签: android progress-bar

我之前已经看过这个问题了,但我找不到一个有效的答案,而且我一直在看这个问题 glum

我有一个带有3个进度条的屏幕,我想在漫长的下载过程中更新它。无论我尝试什么,我都无法让进度条更新。

完整来源为here

我的FileDownloadManager类:

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.\n\nIt is highly recommended that you use a wi-fi connection for this.\n\nThese 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.\nNo copyright infringement is intended. Please contact me if you wish to assert copyright for any of these images.\nIf 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);

                    pb.setProgress(downloadedSize);
                    pb.invalidate();

                    Log.d(LOGid, "Set progress to " + downloadedSize);
            }
            pb.setProgress(totalSize);
            pb.invalidate();
            //close the output stream when done
            fileOutput.close();
            Log.d(LOGid, "closed the output file");

            tvCurrent.setText("Finished Downloading file " + _dest + "...");
            tvCurrent.invalidate();

            //catch some possible errors...
        } 
        catch (MalformedURLException e) 
        {
            Log.d(LOGid,  "Error dowloading file:" + e.getMessage() + "...malformed URL");
            return false;
        } 
        catch (IOException e) 
        {
            Log.d(LOGid,  "Error dowloading file:" + e.getMessage()+ "...IO Exception");
            return false;
        }
        catch (Exception e) 
        {
            Log.d(LOGid,  "Error dowloading file:" + e.getMessage()+ "...generic exception");
            return false;
        }
        return true;
    }

    private boolean getBaseFolder()
    {
        try 
        {
            msBaseFolder = Environment.getExternalStorageDirectory() + "/" + SpeakProperties.msAppFolder(getSharedPreferences(SpeakProperties.APP_NAME, 0));
            File f = new File(msBaseFolder);
            if (!f.exists())
            {
                msBaseFolder = Environment.getExternalStorageDirectory() + "/" + SpeakProperties.APP_DEF_FOLDER;
                f = new File(msBaseFolder);
            } 

            if (!f.exists())
            {
                return createNewBaseFolder();
            }
            else
            {
                Log.d(LOGid, "The base folder for this app" + msBaseFolder + " does exists.");
                return true;                
            }
        }
        catch (Exception ex)
        {
            Log.d(LOGid, "Could not get base folder:" + msBaseFolder + ". Error:" + ex.getMessage());
            return false;
        }
    }

    private boolean createNewBaseFolder()
    {

        tvCurrent.setText("Creating new base folder...");
        tvCurrent.invalidate();
        final String sNewBaseFld = Environment.getExternalStorageDirectory() + "/" + SpeakProperties.APP_DEF_FOLDER;
        AlertDialog adDelete = new AlertDialog.Builder(this).create();
        adDelete.setCancelable(false); // This blocks the 'BACK' button
        adDelete.setMessage("The base folder for the application does not seem to exist. \n\nWould you like to create one now based on the application default folder setting? \n\nThe App Default Folder is:" + sNewBaseFld);
        adDelete.setButton("OK", new DialogInterface.OnClickListener() 
        {
            @Override
            public void onClick(DialogInterface dialog, int which) 
            {
                try
                {
                    File fCreate = new File(sNewBaseFld);
                    if (!fCreate.exists())
                    {
                        fCreate.mkdirs();
                    }
                }                
                catch (Exception ex)
                {
                    Log.e(LOGid, "Could not delete file:" + ex.getMessage());
                    showToast("Could not create folder:" +sNewBaseFld + ". Error was:" + ex.getMessage());
                }
                dialog.dismiss();
            }
        });
        adDelete.setButton2("Cancel", new DialogInterface.OnClickListener() 
        {
            @Override
            public void onClick(DialogInterface dialog, int which) 
            {
                dialog.dismiss();
            }
        });

        adDelete.show();

        // Now did we create the folder?
        File f = new File(sNewBaseFld);
        tvCurrent.setText("Finished Creating new base folder...");
        tvCurrent.invalidate();
        return f.exists();
    }

    void showToast(String msg) {
        AlertDialog ad = new AlertDialog.Builder(this).create();
        ad.setCancelable(false); // This blocks the 'BACK' button
        ad.setMessage(msg);
        ad.setButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        ad.show();

    }

    private void downloadfiles()
    {
        CheckBox cb = (CheckBox) findViewById(R.id.cbDownload);
        if (!cb.isChecked())
        {
            return;
        }

        if (!getBaseFolder())
        {
            return;
        }

        if (downloadFile(FILE_MANIFEST, LOCAL_FILE_MANIFEST, msBaseFolder, true))
        {
            processManifest();
        }
        else
        {
            AlertDialog adConfirm = new AlertDialog.Builder(mContext).create();
            adConfirm.setCancelable(false); // This blocks the 'BACK' button
            adConfirm.setMessage("Could not download the list of image files, perhaps you are offline?");
            adConfirm.setButton("OK.", new DialogInterface.OnClickListener() 
            {
                @Override
                public void onClick(DialogInterface dialog, int which) 
                {
                    dialog.dismiss();
                }
            });

            adConfirm.show();

        }
    }

    private boolean isThereANewDownload()
    {
        tvCurrent.setText("Checking to see if there are new images...");
        tvCurrent.invalidate();
        // check if we have a TouchAndGoSpeak.manifest file on the sd card
        File file = new File(msBaseFolder,LOCAL_FILE_MANIFEST);
        if (!file.exists())
        {
            Log.d(LOGid, "No Local Manifest so returning true");
            return true;
        }

        // if we have one then the user has downloaded the manifest before
        // download the new file

        Log.d(LOGid, "We have a  Local Manifest getting again to check");
        if (downloadFile(FILE_MANIFEST, LOCAL_FILE_MANIFEST + ".new", msBaseFolder, false))
        {
            return (compare(LOCAL_FILE_MANIFEST, LOCAL_FILE_MANIFEST + ".new"));
        }
        else
        {
            AlertDialog adConfirm = new AlertDialog.Builder(mContext).create();
            adConfirm.setCancelable(false); // This blocks the 'BACK' button
            adConfirm.setMessage("Could not download the list of image files, perhaps you are offline? Unable to check if there are new files. Will try and download all files next.");
            adConfirm.setButton("OK.", new DialogInterface.OnClickListener() 
            {
                @Override
                public void onClick(DialogInterface dialog, int which) 
                {
                    dialog.dismiss();
                }
            });

            adConfirm.show();
            return true;
        }
    }

    private int getLineCount(String _File)
    {
        int iLineCount = 0;
        //Get the text file
        File file = new File(msBaseFolder,_File);
//        StringList sl_source = new StringList();

        try 
        {
            BufferedReader br = new BufferedReader(new FileReader(file));
            String line;

            while ((line = br.readLine()) != null) 
            {
                iLineCount++;
            }
        }
        catch (IOException e) 
        {
            //You'll need to add proper error handling here
        }
        return iLineCount;
    }


    private boolean compare(String _File1, String _File2)
    {
        Log.d(LOGid, "Comparing the 2 files");

        //Get the text file
        File file = new File(msBaseFolder,_File1);
//        StringList sl_source = new StringList();
        ArrayList<String> _original_data = new ArrayList<String>();
        ArrayList<String> _new_data = new ArrayList<String>();

        try {
            BufferedReader br = new BufferedReader(new FileReader(file));
            String line;

            while ((line = br.readLine()) != null) 
            {
                _original_data.add(line);
            }
        }
        catch (IOException e) 
        {
            //You'll need to add proper error handling here
        }

        file = new File(msBaseFolder,_File2);
        try 
        {
            BufferedReader br = new BufferedReader(new FileReader(file));
            String line;

            while ((line = br.readLine()) != null) 
            {
                _new_data.add(line);
            }
        }
        catch (IOException e) 
        {
            //You'll need to add proper error handling here
        }

        return _original_data.equals(_new_data);    
    }

    /**
     * @return the networkAvailable
     */
    public boolean isNetworkAvailable() 
    {

        boolean networkAvailable = true;
        try
        {
            Context c = getApplicationContext();
            ConnectivityManager connectionManager = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
            networkAvailable = connectionManager.getActiveNetworkInfo() != null && connectionManager.getActiveNetworkInfo().isConnected();
        }
        catch (Exception ex)
        {
            Log.d(LOGid, "Error determining the network connectivity:" + ex.getClass().toString() + " message:" + ex.getMessage());
        }
        return networkAvailable;
    }    

    private Context mContext;
}

编辑:使用进度条更新的工作downloadFiles方法

我的最终downloadFile看起来像这样添加了Thread和handler。 mHandler是在UI的onCreate中实例化的私有类变量。

感谢所有人的帮助!

private boolean downloadFile(String _source, String _dest, String _Folder,
        boolean isManifest) 
{
    m_downloadSuccess = true;
    m_source = _source;
    m_dest = _dest;
    m_Folder = _Folder;
    if (isManifest) 
    {
        m_pb = (ProgressBar) findViewById(R.id.barManifest);
    } 
    else 
    {
        m_pb = (ProgressBar) findViewById(R.id.barOneFile);
    }

    // Start lengthy operation in a background thread
    new Thread(new Runnable() 
    {
        public void run() 
        {
            try 
            {
                // Update the progress bar
                mHandler.post(new Runnable() 
                {
                    public void run() 
                    {
                        Log.d(LOGid, "Set File max progress to " + mFileTotal);
                        Log.d(LOGid, "Set File progress to 0");
                        Log.d(LOGid, "Downloading file " + m_dest + "...");
                        tvCurrent.setText("Downloading file " + m_dest + "...");
                    }
                });

                // 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(m_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(m_Folder, m_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
                mTotalSize = urlConnection.getContentLength();
                Log.d(LOGid, "Got total size " + mTotalSize);

                // 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

                mProgressCurrentFileStatus = 0;
                // m_pb.setProgress(0);
                Log.d(LOGid, "Set progress to zero");

                // Update the progress bar
                mHandler.post(new Runnable() 
                {
                    public void run() 
                    {
                        Log.d(LOGid, "Set progress totalsize to " + mTotalSize);
                        m_pb.setMax(mTotalSize);

                        Log.d(LOGid, "Set File progress to " + mFileCount);


                        Log.d(LOGid, "Set progress to:" + mProgressCurrentFileStatus);
                        m_pb.setProgress(mProgressCurrentFileStatus);
                    }
                });

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

                    mProgressCurrentFileStatus = downloadedSize;
                    // Update the progress bar
                    mHandler.post(new Runnable() 
                    {
                        public void run() 
                        {
                            Log.d(LOGid, "Set progress size:" + mTotalSize);
                            m_pb.setMax(mTotalSize);

                            Log.d(LOGid, "Set progress to:" + mProgressCurrentFileStatus);
                            m_pb.setProgress(mProgressCurrentFileStatus);
                        }
                    });

                    Log.d(LOGid, "Set progress to " + downloadedSize);
                }
                // close the output stream when done
                fileOutput.close();
                Log.d(LOGid, "closed the output file");

                mHandler.post(new Runnable() 
                {
                    public void run() 
                    {
                        tvCurrent.setText("Finished Downloading file " + m_dest + "...");
                    }
                });
                m_downloadSuccess = true;
                // catch some possible errors...
            } catch (MalformedURLException e) 
            {
                Log.d(LOGid, "Error dowloading file:" + e.getMessage()
                        + "...malformed URL");
                m_downloadSuccess = false;
            } catch (IOException e) 
            {
                Log.d(LOGid, "Error dowloading file:" + e.getMessage()
                        + "...IO Exception");
                m_downloadSuccess = false;
            } catch (Exception e) 
            {
                Log.d(LOGid, "Error dowloading file:" + e.getMessage()
                        + "...generic exception");
                m_downloadSuccess = false;
            }
        }
    }).start();

    return m_downloadSuccess;
}

1 个答案:

答案 0 :(得分:4)

看起来你正在做的工作就是测量你的UI线程中的进度(直接从回调中调用,而不是在新线程中)。这不会给系统任何CPU时间来响应您的临时进度更新。

处理此问题的正确方法是在后台线程中完成工作,并定期更新进度。这些更新必须在UI线程内进行,但您的Activity的runOnUiThread(Runnable)方法可以为您完成。 (或者,您可以将runnable发布到Activity的处理程序以执行UI线程。)

编辑:添加了线程创建示例 你的工作是在downloadFiles()方法中完成的,所以这就是你想要的单独线程。用例如

替换你的downloadFiles()调用
Thread worker = new Thread(new Runnable() {
    @Override
    public void run() {
        downloadFiles();
    }
});
worker.start();

现在你还需要修改downloadFiles() - 因为它不在UI线程上,所以不允许修改UI。您需要在UI线程中执行该操作,而不是调用pb.setMax(totalSize);。您可以编写一个runnable(或其中几个)来从正确的线程修改UI,其方式与运行新线程类似:

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

这里唯一需要注意的事项是 - runnable的run()方法可能需要访问包含runOnUiThread()调用的方法中的一些局部变量。要合法地提供该访问权限,它使用的变量必须final。如果您有变量不能是final,则需要将它们的值复制到runnable使用的最终变量。例如:

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