executePendingTransactions 的递归入口

Posted

技术标签:

【中文标题】executePendingTransactions 的递归入口【英文标题】:Recursive entry to executePendingTransactions 【发布时间】:2014-04-04 17:57:33 【问题描述】:

我有一个 MainDrawer 到 Fragment 活动,它有一个导航抽屉布局我和我的主要内容,我可以在其中加载新的片段。我加载的一个片段是 calle StatisticsTab 片段。这个片段包含一个标签主机,每个标签都是它自己的列表视图项目片段。一旦我单击一个 ListView 项目,该项目加载另一个新片段并且不再在 tabHost 中,我尝试使用 navigationdrawer 返回到我的 StatisticsTab 片段,我收到此错误:

03-03 10:32:06.884  24185-24185/com.beerportfolio.beerportfoliopro E/androidRuntime﹕ FATAL EXCEPTION: main
    java.lang.IllegalStateException: Recursive entry to executePendingTransactions
            at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1439)
            at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:472)
            at android.support.v4.app.FragmentTabHost.onAttachedToWindow(FragmentTabHost.java:283)
            at android.view.View.dispatchAttachedToWindow(View.java:12307)
            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2457)
            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2464)
            at android.view.ViewGroup.addViewInner(ViewGroup.java:3567)
            at android.view.ViewGroup.addView(ViewGroup.java:3399)
            at android.view.ViewGroup.addView(ViewGroup.java:3344)
            at android.view.ViewGroup.addView(ViewGroup.java:3320)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:938)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
            at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
            at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
            at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
            at android.os.Handler.handleCallback(Handler.java:730)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:158)
            at android.app.ActivityThread.main(ActivityThread.java:5789)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:843)
            at dalvik.system.NativeStart.main(Native Method)

如果我没有在 StatisticsTab 片段上单击我的任何选项卡,并通过 navdrawer 导航到另一个片段,然后返回 StatisticsTab,那么它将不会启用 FC。此外,当我返回它们时,NavDrawer 中的其他片段都不会强制关闭,只有带有标签的片段。

MainDrawer2:

public class MainDrawer2 extends FragmentActivity

    private static final String EXTRA_NAV_ITEM    = "extraNavItem";
    private static final String STATE_CURRENT_NAV = "stateCurrentNav";

    private ActionBarDrawerToggle mDrawerToggle;
    private DrawerLayout mDrawerLayout;

    private NavDrawerListAdapter mDrawerAdapter;
    private ListView mDrawerList;

    private CharSequence mTitle;
    private CharSequence mDrawerTitle;

    private MainNavItem mCurrentNavItem;


    public static Intent createLaunchFragmentIntent(Context context, MainNavItem navItem)
    
        return new Intent(context, MainDrawer2.class)
                .putExtra(EXTRA_NAV_ITEM, navItem.ordinal());
    

    @Override
    protected void onCreate(Bundle savedInstanceState)
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_main);

        mTitle = mDrawerTitle = getTitle();
        mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
        mDrawerList   = (ListView)findViewById(R.id.drawer);

        getActionBar().setDisplayHomeAsUpEnabled(true);
        enableHomeButtonIfRequired();

        mDrawerAdapter = new NavDrawerListAdapter(getApplicationContext());
        mDrawerList.setAdapter(mDrawerAdapter);
        mDrawerList.setOnItemClickListener(new ListView.OnItemClickListener()
        
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id)
            
                displayNavFragment((MainNavItem)parent.getItemAtPosition(position));
            
        );

        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
                R.drawable.ic_drawer, R.string.app_name, R.string.app_name)
        
            public void onDrawerClosed(View view)
            
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu();
            

            public void onDrawerOpened(View drawerView)
            
                getActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu();
            
        ;

        mDrawerLayout.setDrawerListener(mDrawerToggle);

        if(getIntent().hasExtra(EXTRA_NAV_ITEM))
            MainNavItem navItem = MainNavItem.values()
                    [getIntent().getIntExtra(EXTRA_NAV_ITEM,
                    MainNavItem.STATISTICS.ordinal())];
            displayNavFragment(navItem);
        
        else if(savedInstanceState != null)
            mCurrentNavItem = MainNavItem.values()
                    [savedInstanceState.getInt(STATE_CURRENT_NAV)];
            setCurrentNavItem(mCurrentNavItem);
        
        else
            displayNavFragment(MainNavItem.STATISTICS);
        
    

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void enableHomeButtonIfRequired()
    
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
            getActionBar().setHomeButtonEnabled(true);
        
    

    @Override
    public void setTitle(CharSequence title)
    
        mTitle = title;
        getActionBar().setTitle(mTitle);
    

    @Override
    protected void onPostCreate(Bundle savedInstanceState)
    
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    

    @Override
    public void onConfigurationChanged(Configuration newConfig)
    
        super.onConfigurationChanged(newConfig);
        // Pass any configuration change to the drawer toggles
        mDrawerToggle.onConfigurationChanged(newConfig);
    

    @Override
    protected void onSaveInstanceState(Bundle outState)
    
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_CURRENT_NAV, mCurrentNavItem.ordinal());
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    

    /*
    @Override
    public boolean onPrepareOptionsMenu(Menu menu)
    
        // if nav drawer is opened, hide the action items
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        menu.findItem(R.id.action_settings).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    
    */



    private void displayNavFragment(MainNavItem navItem)
    
        if(navItem == mCurrentNavItem)
            return;
        
        Fragment fragment = Fragment.instantiate(this,
                navItem.getFragClass().getName());
        if(fragment != null)

            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.main, fragment)
                    .commit();
            setCurrentNavItem(navItem);
        
    

    private void setCurrentNavItem(MainNavItem navItem)
    
        int position = navItem.ordinal();
        // If navItem is in DrawerAdapter
        if(position >= 0 && position < mDrawerAdapter.getCount())
            mDrawerList.setItemChecked(position, true);
        
        else
            // navItem not in DrawerAdapter, de-select current item
            if(mCurrentNavItem != null)
                mDrawerList.setItemChecked(mCurrentNavItem.ordinal(), false);
            
        
        mDrawerLayout.closeDrawer(mDrawerList);
        setTitle(navItem.getTitleResId());
        mCurrentNavItem = navItem;
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        switch (item.getItemId()) 
            case android.R.id.home:
                if(mDrawerLayout.isDrawerOpen(mDrawerList)) 
                    mDrawerLayout.closeDrawer(mDrawerList);
                
                else 
                    mDrawerLayout.openDrawer(mDrawerList);
                
                return true;
            default:
                return super.onOptionsItemSelected(item);
        
    




    public void goToSearch(MenuItem item)

        //go to search page
        Fragment Fragment_one;
        FragmentManager man= getSupportFragmentManager();
        FragmentTransaction tran = man.beginTransaction();
        Fragment_one = new Search();

        tran.replace(R.id.main, Fragment_one);//tran.
        tran.addToBackStack(null);
        tran.commit();

    




统计标签:

public class StatisticsTab extends Fragment  


    private FragmentTabHost mTabHost;

    //Mandatory Constructor
    public StatisticsTab() 
    

    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

    

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) 

        View rootView = inflater.inflate(R.layout.fragment_tabs,container, false);


        mTabHost = (FragmentTabHost)rootView.findViewById(android.R.id.tabhost);

        mTabHost.setup(getActivity(), getFragmentManager(), R.id.realtabcontent);

        mTabHost.addTab(mTabHost.newTabSpec("Basic").setIndicator("Basic"),
                StatisticsPage.class, null);
        mTabHost.addTab(mTabHost.newTabSpec("Brewery").setIndicator("Brewery"),
                BreweryStatistics.class, null);
        mTabHost.addTab(mTabHost.newTabSpec("Style").setIndicator("Style"),
                StyleStatistics.class, null);
        mTabHost.addTab(mTabHost.newTabSpec("Taste").setIndicator("Taste"),
                TasteStatisticsPage.class, null);



        return rootView;
    





我的一个片段用于 tabhost 中具有列表视图的选项卡:

public class BreweryStatistics extends Fragment implements GetBreweryStatisticsJSON.OnArticleSelectedListener 

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) 
        View v = inflater.inflate(R.layout.brewery_statistics_layout, container, false);

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
        String userName = prefs.getString("userName", null);
        String userID = prefs.getString("userID", null);

        String url = "myURL";


        //async task to get beer taste tag percents
        GetBreweryStatisticsJSON task = new GetBreweryStatisticsJSON(getActivity());
        task.setOnArticleSelectedListener(this);
        task.execute(url);


        // Inflate the layout for this fragment
        return v;

    


    @Override
    public void onArticleSelected(String bID)

        //code to execute on click
        Fragment Fragment_one;
        FragmentManager man= getFragmentManager();
        FragmentTransaction tran = man.beginTransaction();
        Fragment_one = new BreweryPage2();
        final Bundle bundle = new Bundle();
        bundle.putString("breweryIDSent", bID);
        Fragment_one.setArguments(bundle);

        tran.replace(R.id.main, Fragment_one);//tran.
        tran.addToBackStack(null);
        tran.commit();

    



我为上述片段加载列表视图的异步任务:

public class GetBreweryStatisticsJSON extends AsyncTask<String, Void, String> 

    Context c;
    private ProgressDialog Dialog;


    public GetBreweryStatisticsJSON(Context context)
    
        c = context;
        Dialog = new ProgressDialog(c);
    

    //***************************code for on click
    OnArticleSelectedListener listener;
    public interface OnArticleSelectedListener
        public void onArticleSelected(String myString);


    
    public void setOnArticleSelectedListener(OnArticleSelectedListener listener)
        this.listener = listener;


    
    //*****************************end code for onClick

    protected void onPreExecute() 
        Dialog.setMessage("Analyzing breweries");

        Dialog.setTitle("Loading");
        Dialog.setCancelable(false);
        Dialog.show();
    


    @Override
    protected String doInBackground(String... arg0) 
        // TODO Auto-generated method stub
        return readJSONFeed(arg0[0]);
    

    protected void onPostExecute(String result)
        //decode json here
        try
            JSONArray jsonArray = new JSONArray(result);


            //acces listview
            ListView lv = (ListView) ((Activity) c).findViewById(R.id.yourBreweryStatistics);

            //make array list for beer
            final List<BreweryInfo> tasteList = new ArrayList<BreweryInfo>();



            for(int i = 0; i < jsonArray.length(); i++) 

                String brewery = jsonArray.getJSONObject(i).getString("brewery");
                String rate = jsonArray.getJSONObject(i).getString("rate");
                String breweryID = jsonArray.getJSONObject(i).getString("id");

                int count = i + 1;

                brewery = count + ". " + brewery;

                Log.d("brewery stats", brewery);

                //create object
                BreweryInfo tempTaste = new BreweryInfo(brewery, breweryID, rate);

                //add to arraylist
                tasteList.add(tempTaste);


                //add items to listview
                BreweryInfoAdapter adapter1 = new BreweryInfoAdapter(c ,R.layout.brewer_stats_listview, tasteList);
                lv.setAdapter(adapter1);

                //set up clicks
                lv.setOnItemClickListener(new AdapterView.OnItemClickListener() 
                    @Override
                    public void onItemClick(AdapterView<?> arg0, View arg1,
                                            int arg2, long arg3) 
                        BreweryInfo o=(BreweryInfo)arg0.getItemAtPosition(arg2);

                        String bID = o.breweryID;

                        Log.d("breweryID" , bID);

                        //todo: add brewery page link
                        //********************* add listener
                        listener.onArticleSelected(bID);


                    
                );

            

        
        catch(Exception e)

        

        Dialog.dismiss();

    

    public String readJSONFeed(String URL) 
        StringBuilder stringBuilder = new StringBuilder();
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet(URL);
        try 
            HttpResponse response = httpClient.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if (statusCode == 200) 
                HttpEntity entity = response.getEntity();
                InputStream inputStream = entity.getContent();
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(inputStream));
                String line;
                while ((line = reader.readLine()) != null) 
                    stringBuilder.append(line);
                
                inputStream.close();
             else 
                Log.d("JSON", "Failed to download file");
            
         catch (Exception e) 
            Log.d("readJSONFeed", e.getLocalizedMessage());
        
        return stringBuilder.toString();
    




【问题讨论】:

ViewPager: Recursive entry to executePendingTransactions 的可能重复项 【参考方案1】:

您正试图通过 FragmentTabHost 使用嵌套在其他片段中的片段。

在您的 StatisticsTab 片段中,更改以下内容:

mTabHost.setup(getActivity(), getFragmentManager(), R.id.realtabcontent);

到这里:

mTabHost.setup(getActivity(), getChildFragmentManager(), R.id.realtabcontent);

然后,确保使用父 FragmentManager 提交主片段更改,方法是将BreweryStatistics.onArticleSelected() 中的getFragmentManager() 更改为getActivity().getSupportFragmentManager()

见:ViewPager: Recursive entry to executePendingTransactionsNested Fragments using support library v4 revision 11

【讨论】:

我将在哪里更改为 getSupportFragmentManager() 与 getChildFragmentManager()? 抱歉,误读了您的活动代码,您在抽屉活动中正确使用了 getSupportFragmentManager()。请参阅我的更新答案以获得澄清。 太棒了,但这实际上是我最初的想法,当我尝试从选项卡中的 ListView 中进行选择时,我得到了一个奇怪的 ID 错误。我会很快把它改回来并告诉你错误。 这是我在啤酒厂统计选项卡中遇到的错误,并尝试从列表视图中选择一个项目:gist.github.com/anonymous/14131f62e19f8e0b04cc 在 BreweryStatistics.onArticleSelected() 中,调用 getFragmentManager() 返回最初由 FragmentTabHost 设置提供的子 FragmentManager。这个片段管理器独立于 Activity 的主片段管理器,因此不会对主片段管理器的视图有任何引用。您需要将 getFragmentManager() 替换为 getActivity().getSupportFragmentManager() ,一切都应该很好。

以上是关于executePendingTransactions 的递归入口的主要内容,如果未能解决你的问题,请参考以下文章

即使在 FragmentManager.executePendingTransactions() 之后,也不会立即调用 Fragment 中的 onCreateView()

java.lang.IllegalStateException: Recursive entry to executePendingTransactions