进度对话框UI卡顿/缓慢

10

我有一个进度对话框,用于我程序中执行耗时操作的一部分,但当对话框显示时,UI或旋转图标会冻结/变慢/犹豫不决,使程序似乎像已经冻结。在我的AsyncTaskonPostExecute方法中,我会解散对话框。

由于我正在后台处理所有工作,为什么会出现这种情况?

以下是我的代码:

pDialog = ProgressDialog.show(FullGame.this,"Starting New Game","Please Wait...", true);
new StartNewGame().execute(); 

    private class StartNewGame extends AsyncTask<Void,Void,Boolean>{

    @Override
    protected Boolean doInBackground(Void... params) {
        try{
            ContentValues values = new ContentValues();
            Cursor c = getContentResolver().query(Games.PART1_URI,new String[] {Games.PART1_NUM},
                    Games.PART1_GAME_ID+"="+gameID+" AND "+Games.PART1_FRAME_NUM+"="+10,null,null);
            c.moveToFirst();
            String num = c.getString(0);
            int part1 =0;
            if(num.equals("-")){
                part1=0;
            }else{
                part1=Integer.parseInt(num);
            }

            c = getContentResolver().query(Games.PART2_URI,new String[] {Games.PART2_NUM},
                    Games.PART2_GAME_ID+"="+gameID+" AND "+Games.PART2_FRAME_NUM+"="+10,null,null);
            c.moveToFirst();
            int part2 = 0;
            if(num.equals("-")){
                part2=0;
            }else{
                part2=Integer.parseInt(num);
            }

            c = getContentResolver().query(Games.PART3_URI,new String[] {Games.PART3_NUM},
                    Games.PART3_GAME_ID+"="+gameID,null,null);
            c.moveToFirst();
            int part3 = 0;
            if(num.equals("-")){
                part3=0;
            }else{
                part3=Integer.parseInt(num);
            }

            if(part1 == 10){
                values.clear();
                values.put(Games.STRIKES_FRAME_NUM,10);
                values.put(Games.STRIKES_BOWLER_ID,bowlerClickedID);
                values.put(Games.STRIKES_GAME_ID,gameID);
                getContentResolver().insert(Games.STRIKES_URI, values);
            }
            if(part2 == 10){
                values.clear();
                values.put(Games.STRIKES_FRAME_NUM,10);
                values.put(Games.STRIKES_BOWLER_ID,bowlerClickedID);
                values.put(Games.STRIKES_GAME_ID,gameID);
                getContentResolver().insert(Games.STRIKES_URI, values);
            }
            if(((part2+part3) == 10) && !score.checkSpare(10)){
                values.clear();
                values.put(Games.SPARES_BOWLER_ID,bowlerClickedID);
                values.put(Games.SPARES_FRAME_NUM,10);
                values.put(Games.SPARES_GAME_ID,gameID);
                getContentResolver().insert(Games.SPARES_URI, values);
            }
            if(part3 == 10){
                values.clear();
                values.put(Games.STRIKES_FRAME_NUM,10);
                values.put(Games.STRIKES_BOWLER_ID,bowlerClickedID);
                values.put(Games.STRIKES_GAME_ID,gameID);
                getContentResolver().insert(Games.STRIKES_URI, values);
            }
        c.close();
        }catch(Exception e){
            Log.d("FullGame",e.toString());
        }

        Date date = new Date(System.currentTimeMillis());
        DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
        String newDate = df.format(date);

        ContentValues values = new ContentValues();
        values.put(Games.GAMES_BOWLER_ID,bowlerClickedID);
        values.put(Games.GAMES_TEAM_ID,1);
        values.put(Games.GAMES_DATE,newDate);
        values.put(Games.GAME_SEASON, pref.getLong(Preferences.SELECTED_SEASON, 1));
        values.put(Games.GAMES_TOURNAMENT_ID, tournamentID);
        Uri uri = getContentResolver().insert(Games.GAMES_URI, values);
        gameID = ContentUris.parseId(uri);
        int gameid = Integer.valueOf(String.valueOf(gameID));
        values.clear();
        Cursor cName = getContentResolver().query(BowlersDB.CONTENT_URI,new String[] {BowlersDB.FIRST_NAME},BowlersDB.ID+"="+bowlerClickedID,null,null);
        cName.moveToFirst();
        String name = cName.getString(0);
        for(int i = 0;i<10;i++){
            int num = i+1;
            values.put(Games.NAMES_FRAME_NUM,num);
            values.put(Games.NAMES_GAME_ID,gameid);
            values.put(Games.NAMES_NAME,name);
            getContentResolver().insert(Games.NAMES_URI, values);
            names(i,name);
            values.clear();
            values.put(Games.PART1_FRAME_NUM,num);
            values.put(Games.PART1_NUM,"0");
            values.put(Games.PART1_GAME_ID,gameid);
            getContentResolver().insert(Games.PART1_URI, values);
            values.clear();
            values.put(Games.PART2_FRAME_NUM,num);
            values.put(Games.PART2_NUM,"0");
            values.put(Games.PART2_GAME_ID,gameid);
            getContentResolver().insert(Games.PART2_URI, values);
            values.clear();
            values.put(Games.TOTALS_FRAME_NUM,num);
            values.put(Games.TOTALS_FRAME_TOTAL,"0");
            values.put(Games.TOTALS_GAME_ID,gameid);
            getContentResolver().insert(Games.TOTALS_URI, values);
            values.clear();
            values.put(Games.POCKETS_BOWLER_ID,bowlerClickedID);
            values.put(Games.POCKETS_FRAME_NUM,i);
            values.put(Games.POCKETS_GAME_ID,gameID);
            values.put(Games.POCKETS_TEAM_ID, teamSelectedID);
            values.put(Games.POCKETS_TOURNAMENT_ID, tournamentID);
            values.put(Games.POCKETS_NUM, 0);
            values.put(Games.POCKETS_SEASON, pref.getLong(Preferences.SELECTED_SEASON, 1));
            getContentResolver().insert(Games.POCKETS_URI, values);
            values.clear();
        }

        values.put(Games.PART3_GAME_ID,gameid);
        values.put(Games.PART3_NUM,"0");
        getContentResolver().insert(Games.PART3_URI, values);
        cName.close();
        part1Array = new int[10];
        part2Array = new int[10];
        totalsArray = new int[10];
        part3 = 0;
        mPinsUp = new ArrayList<Long>();
        mPinsUp.add((long) 1);
        mPinsUp.add((long) 2);
        mPinsUp.add((long) 3);
        mPinsUp.add((long) 4);
        mPinsUp.add((long) 5);
        mPinsUp.add((long) 6);
        mPinsUp.add((long) 7);
        mPinsUp.add((long) 8);
        mPinsUp.add((long) 9);
        mPinsUp.add((long) 10);
        return true;
    }

    protected void onPostExecute(Boolean result){
            pDialog.dismiss();
    }

}
更新:昨晚在调试模式下运行代码,似乎它开始在for循环中执行,但所有这些都在单独的线程中完成,我只是将值插入到我的数据库中。 更新2:如果我注释掉for循环,进度对话框显示不到一秒钟,所以即使我在AsyncTask中做了所有事情,插入仍然必须在UI线程中运行。

你是在主线程还是UI线程上执行这个操作?关于这个问题,Stack Overflow上有其他相关话题,或许可以帮到你。https://dev59.com/ynA65IYBdhLWcg3wvxaE - Rod Burns
正如您所看到的,它是一个AsyncTask,因此它将在另一个线程中运行。 - tyczj
你的 onPreExecute 部分在哪里?同时,你可以将 doInBackground 中的所有代码放入一个方法中,因为它们都应该在新线程中执行,这样可以使代码更易读。另外,你在测试什么硬件? - Graham Smith
我没有 onPreExecute 方法,它真的需要吗?我在 Galaxy Nexus、Nexus 7 和 Samsung Galaxy Tab 10.1 上进行了测试,所有测试结果都相同。 - tyczj
1
也许这个问题及其答案可以帮助您 [链接](https://dev59.com/HGgu5IYBdhLWcg3wEzHP) - SteveR
显示剩余2条评论
5个回答

2
这可能是因为你的后台线程消耗了设备CPU的100%。当CPU正在处理一个线程时,UI线程不会更新,因此您会看到它被冻结。
尝试检测最具攻击性的操作,通过从doInBackground中删除代码部分并再次运行应用程序来解决问题。还可以尝试查看设备未通过USB连接时的性能情况-有时会提供奇怪的结果。

问题出在 for 循环中,它正在将字段插入到我的数据库中。如果我将其注释掉,进度对话框会显示不到一秒钟,因此显然即使我在 AsyncTask 中执行所有操作,插入仍然必须在 UI 线程中运行。 - tyczj

2

我明白了,我在UI线程上有一个失控的方法,但我没有注意到。


0

你正在错误地使用 ProgressDialog

你需要添加一个 onPreExecute 方法,在那里定义并显示你的 ProgressDialog。然后 doInBackground 在另一个线程上执行,最终在 onPostExecute 中关闭对话框。

这里是一个简单的例子:

class RefreshChanges extends AsyncTask<String, Void, String> {
        private ProgressDialog mProgressDialog = new ProgressDialog(
                mContext);

        @Override
        protected void onPreExecute() {
            mProgressDialog.setTitle("Whatever title");
            mProgressDialog.setMessage("Whatever message");
            mProgressDialog.show();
        }

        protected String doInBackground(String... strings) {
            // Do whatever processing you want...
            return "";
        }

        protected void onPostExecute(String result) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
    }
    new RefreshChanges().execute();

顺便提一下,我也建议您不要使用硬编码字符串。相反,您可以转到res\values\下的strings.xml文件并定义一个字符串。然后在您的代码中,您可以使用getString(R.string.yourStringId)R.string.yourStringId。这取决于方法是否接受ID(接受ID的方法实际上执行您发送的ID的getString)。

但是如果我使用onPostExecute或者在开始AsyncTask之前声明进度对话框,有什么区别呢?我只是不明白这样做如何解决问题,因为两者都在UI线程中,对吗? - tyczj
@tyczj,老实说我不知道确切的区别(但是通过这样说,你引发了为什么需要onPostExecute的问题)。 看起来有所不同,因为我曾经遇到过你现在遇到的问题。我也将ProgressDialog定义在AsyncTask之外,但当我将其移动到内部时,它完美地运行了。我建议你尝试一下,应该会奏效。 - RE6
我把它放在onPreExecute里面,但不幸的是它仍然做了同样的事情。 - tyczj

0

我已经成功地以这种方式实现了对话框。

private ProgressDialog progress;

private class AsynTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected void onPreExecute() {
           progress = ProgressDialog.show(context, "", "Please wait...", true);

       }

        @Override
        protected void onPostExecute(Void params) {
             if (progress.isShowing()) 
                  progress.dismiss();

       }

        @Override
        protected Void doInBackground(Void... arg0) {
             // Do some work
             return null;
       }
 }

0
我并没有看到你代码中的错误,但是你必须明白,在另一个线程中编写代码并不意味着它会获得另一个处理器。如果你的设备只有单核处理器,它会进行时间片分配,并以轮询方式工作。如果你的设备有多个内核,则会实际进行多线程处理。因此,如果你有单核处理器,进度条会显示一些滞后。

所有测试过的设备都具有多核心。 - tyczj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接