Android:尽管使用了 AsyncTask,但主线程被阻止

Posted

技术标签:

【中文标题】Android:尽管使用了 AsyncTask,但主线程被阻止【英文标题】:Android: Main Thread blocked despite using AsyncTask 【发布时间】:2017-03-18 23:25:41 【问题描述】:

我有一个包含 100 多个复杂视图(包括图像、文本视图等)的 Activity。如果没有异步加载视图的线程,显示这些视图是很难做到的。所以我试着用 AsyncTask 来做。我不确定这是否是正确的方法,因为“硬员工”是必须在 UI 线程中完成的事情。

现在我遇到了 UI 冻结的问题,尽管我使用 onProgressUpdate 将视图添加到父视图中。我认为这会导致单个加载视图在父视图中连续出现。但这种情况并非如此。

doInBackground 触发所有 publishProgress 调用,然后主线程被阻塞(活动冻结,加载栏不再旋转)。有没有办法实现我想要的?我寻找解决方案,但最后总是想到使用 AsyncTask 的想法,没有人必须像“硬员工”那样做视图工作。我没有在 AsyncTask 中使用“get”,这似乎是 AsyncTask 的问题。

这是我的代码。如果您需要任何进一步的信息,请告诉我!

有没有其他方法可以解决这个问题?我的 AsyncTask 实现不正确吗?我正在寻找一种在不阻塞主线程的情况下加载这些与父视图异步的复杂视图的方法。

提前致谢!

public class LoadKraut extends AsyncTask<Integer,Kraut,Void> 

private Context context;
private LinearLayout parent;
private HashMap<String,HeadlineAlphabet> headlinesAlphabet = new HashMap<String, HeadlineAlphabet>();
private long time;
private Integer kategorie;
private char letter = 'X';
private int counter = 0;
private ProgressDialog dialog;


public LoadKraut(Context context) 

    /**
     * Kategorie:
     * 1 - A-Z
     * 2 - Notiz
     * 3 - Favorit
     * 4 - Giftig
     */

    Log.i("Kraut", "Start thread" + (System.currentTimeMillis()-time) + "ms");

    this.context = context;
    this.dialog = new ProgressDialog(context);
    this.time = System.currentTimeMillis();


@Override
protected void onPreExecute() 

    dialog.setMessage("Lade Kräuter. Dieser Vorgang kann einen Moment dauern.");
    dialog.show();



@Override
protected Void doInBackground(Integer... params) 

    this.kategorie = params[0];

    //Create overview
    try 
        DatabaseHelper databaseHelper = new DatabaseHelper(context);
        Dao<Kraut,Integer> dao = databaseHelper.getKrautDAO();

        parent = (LinearLayout) ((Activity) context).findViewById(R.id.ll_conainter_sv_uebersicht_kraeuter);

        //setKraeuter(list, linearLayout, giftig)

        long test = System.currentTimeMillis();

        List<Kraut> list = new ArrayList<>();

        switch (kategorie) 

            case 1:
                list = dao.queryForAll();
                break;

            case 2:
                list = dao.queryBuilder().where().ne("notiz","").query();
                break;

            case 3:
                list = dao.queryBuilder().where().eq("favorit",true).query();
                break;

            case 4:
                list = dao.queryBuilder().where().eq("toedlichBunny",true).query();
                break;

        

        Log.i("Kraut","Fetching duration: " + String.valueOf(System.currentTimeMillis() - test));

        Iterator<Kraut> iterator = list.iterator();

        while(iterator.hasNext()) 

            Kraut kraut = iterator.next();
            Log.i("Kraut","called pp for" + kraut.getName());
            publishProgress(kraut);

        
     catch (SQLException e) 
        e.printStackTrace();
    

    Log.i("Kraut", "End " + (System.currentTimeMillis()-time) + "ms");

    return null;


@Override
protected void onProgressUpdate(Kraut... value) 

    //Set all Krauts and headlines A-Z

    long test = System.currentTimeMillis();

    Kraut kraut = value[0];

    Log.i("Kraut", String.valueOf(counter));


    if((kategorie==1 || kategorie==4) && kraut.getName().charAt(0)!=letter) 
        letter = kraut.getName().charAt(0);

        HeadlineAlphabet letterHeadline = new HeadlineAlphabet(context);
        letterHeadline.setText(String.valueOf(kraut.getName().charAt(0)));
        headlinesAlphabet.put(String.valueOf(letterHeadline.getText()),letterHeadline);

        parent.addView(letterHeadline);

    

    KrautView krautView=null;

    if(kategorie==1 || kategorie==3) 
        krautView = new KrautUebersicht(context,kategorie);
     else if(kategorie==2) 
        krautView = new KrautUebersichtNotiz(context);
    

    if(krautView!=null) 
        krautView.setKraut(kraut);
        parent.addView((LinearLayout) krautView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    

    parent.getRootView().invalidate();

    try 
        Thread.sleep(100);
     catch (InterruptedException e) 
        e.printStackTrace();
    

    counter++;

    Log.i("Kraut","Kraut View creation duration: " + String.valueOf(System.currentTimeMillis() - test));



@Override
protected void onPostExecute(Void aVoid) 
    super.onPostExecute(aVoid);

    if(kategorie==1) 
        //Set Alphabet Column right side
        ArrayList<String> anfangsbuchstaben = Kraut.getAnfangsbuchstaben(context);

        // Do this with an xml !
        for (int i = 1; i <= 26; i++) 

            //Log.i("Kraut", String.valueOf(i));

            String currentLetter = Helper.getCharForNumber(i);

            int id = context.getResources().getIdentifier("tv_"+currentLetter.toLowerCase(),"id",context.getPackageName());
            TextView textView = (TextView) ((Activity) context).findViewById(id);

            //If no Kraut contains Letter
            if (!anfangsbuchstaben.contains(currentLetter)) 
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
                    textView.setTextColor(context.getResources().getColor(R.color.darkgrey, context.getTheme()));
                 else 
                    textView.setTextColor(context.getResources().getColor(R.color.darkgrey));
                
                //Make clickable to jump to A-Z Headlines
             else 

                textView.setOnClickListener(new JumpToLetterOnClickListener(headlinesAlphabet));

            
        
    

    parent.invalidate();

    if(dialog.isShowing()) 
        dialog.dismiss();
    

【问题讨论】:

必须在 UI 线程上完成的“硬员工”是什么意思? 你不应该在onProgressUpdate()中调用Thread.sleep() “硬员工”意味着我有 100 个复杂的视图需要添加到活动中。每个视图包含 5 个必须设置的图像和不同的文本。除此之外,他们使用自己的 TextView 实现和非标准字体。我认为这很难让android在短时间内加载。谢谢@MikeM。我删除了它并根据 Code-Apprentice 写入 while 循环的内容移动了 Thread.sleep。我用它来给主线程时间来更新视图。 【参考方案1】:

请注意,onProgressView() 在您的 AsyncTask 运行时会被重复调用。因此,它应尽可能短。这也意味着您当前的代码正在创建 很多 视图并将它们添加到 UI。相反,您应该只添加一次视图,然后在 onProgressView() 中更新其数据。

此外,正如 Mike M. 在 cmets 中所说,您不应在 onProgressView() 中调用 Thread.sleep(),因为它在 UI 线程上运行。这很可能是您的应用冻结的主要原因。

【讨论】:

感谢您的解释。问题是我必须创建大量视图并将它们添加到 UI。我实际上不明白的是为什么在onProgressUpdate() 中完成的更新在一次迭代后不会变得可见。 @Jan-PeterSchmidt 您应该使用 ListView 或 RecyclerView。 @CodeApprentice 谢谢!那就是我一直在寻找的东西。想知道为什么我以前没有考虑过这个...这是 ListViews 的典型用法。我试试看,再次感谢!

以上是关于Android:尽管使用了 AsyncTask,但主线程被阻止的主要内容,如果未能解决你的问题,请参考以下文章

Android AsyncTask 和登录

Android AsyncTask doInBackground 行为在 Android7 中发生了变化

Android:如何将参数传递给AsyncTask的onPreExecute()?

Android中的异步任务不使用AsyncTask

Android:使用 Asynctask 时 Recyclerview 出现问题

Android:AsyncTask 的替代品?