管理导航抽屉内片段中的线程(用于平滑动画)

Posted

技术标签:

【中文标题】管理导航抽屉内片段中的线程(用于平滑动画)【英文标题】:Managing Threads in Fragment inside Navigation Drawer (for smooth animation) 【发布时间】:2017-07-13 15:44:00 【问题描述】:

我想问一下你们中是否有人遇到过和我一样的问题。

I/art: Do partial code cache collection, code=250KB, data=130KB
I/art: After code cache collection, code=248KB, data=129KB
I/art: Increasing code cache capacity to 1024KB
I/Choreographer: Skipped 143 frames!  The application may be doing too much work on its main thread.

我的 android 应用程序的设计很简单,我的着陆页中有一个 Navigation Drawer,然后每个 Drawer Item 中都有 Multiple Fragments... 所以这意味着我只在使用 NavDrawer 的登陆页面上使用 Activity。

当我尝试在我的每个片段上添加一个公共存储库(如 日历、回收站视图)时,我收到了上述错误。

我在网上阅读了一些文章和示例,发现我应该使用 AsyncTaskrunOnUIThread 并运行与主线程/UI 线程。尽管我尝试将这些线程应用到我的 Fragment 上,但我得到了同样的错误。

但是操作并不是很重,我只是加载了日历库并在我的Fragment内的onCreateView上实例化它们......

我希望你能给我一些建议或最佳实践来管理这种情况......我知道有人遇到过同样的问题。呵呵。提前致谢。

I tried this link, but it is more likely another scenario

应要求提供代码:

public class CalendarFragment extends Fragment implements
        RobotoCalendarView.RobotoCalendarListener,
        CalendarListAdapter.OnItemClickListener
        // CalendarListAdapter.CallbackInterface // Should Implemented on the Main thread/Activity if used as fragment


    private RobotoCalendarView robotoCalendarView;
    private Context context;

    private CalendarListAdapter mListAdapter;
    private RecyclerView mSearchResultsList;
    private static final int REQUEST_CODE = 1234;

    private List<ServiceJobWrapper> results = null;


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        super.onCreateView(inflater, container, savedInstanceState);
        View view = /*(View)*/ inflater.inflate(
                R.layout.calendar_activity, container, false);
        setContext(container.getContext());

        /**NOTE FROM HERE**/
        setUpCalendarView(view);
//        UICalendarLibTask mCalendarTask = new UICalendarLibTask(view);
//        mCalendarTask.execute((Void) null);

       /* setUpRecyclerView(view);
        setupResultsList(view);
        if (results == null) 
            //final View view = v;
            *//*new Thread(new Runnable() 
                public void run() 
                    populateCardList();
                
            ).start();*//*
            UITask mAuthTask = new UITask();
            mAuthTask.execute((Void) null);
            // populateCardList();

        */
        return view;
    

    /*@Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        Log.d("CalendarFragment:",  "I'm on the onCreate");
    */
    /**
     * These Two Lines should be included on every Fragment to maintain the state and donnot load again
     *
     * @param outState
     */
    @Override
    public void onSaveInstanceState(Bundle outState) 
        super.onSaveInstanceState(outState);
        setRetainInstance(true);
        Log.d("CalendarFragment: ", "I'm on the onSaveInstanceState");
    

    @Override
    public void onActivityCreated(Bundle savedInstanceState) 
        super.onActivityCreated(savedInstanceState);
        if (results == null)  // If Data is Null then fetch the Data List again
//            UpdateJobServiceTask task = new UpdateJobServiceTask(this.getView());
//            task.execute("");
            //populateCardList();
         else  // Restore the Data List again
            mListAdapter.swapData(results);
        
        Log.d("CalendarFragment: ", "I'm on the onActivityCreated");
    

    private void setContext(Context c) 
        this.context = c;
    

    public void setUpRecyclerView(View upRecyclerView) 
        mSearchResultsList = (RecyclerView) upRecyclerView.findViewById(R.id.calendar_service_job_list);
    

    public void setupResultsList(View view) 
        mListAdapter = new CalendarListAdapter(view.getContext());
        mSearchResultsList.setAdapter(mListAdapter);
        mSearchResultsList.setLayoutManager(new LinearLayoutManager(view.getContext()));
    

    /**
     * Set up the Calendar View Listener on Click of the Date/Month
     *
     * @param view
     */
    private void setUpCalendarView(View view) 
        // Gets the calendar from the view
        robotoCalendarView = (RobotoCalendarView) view.findViewById(R.id.robotoCalendarPicker);
        Button markDayButton = (Button) view.findViewById(R.id.markDayButton);
        markDayButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                Calendar calendar = Calendar.getInstance();
                Random random = new Random(System.currentTimeMillis());
                int style = random.nextInt(2);
                int daySelected = random.nextInt(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
                calendar.set(Calendar.DAY_OF_MONTH, daySelected);

                switch (style) 
                    case 0:
                        robotoCalendarView.markCircleImage1(calendar);
                        break;
                    case 1:
                        robotoCalendarView.markCircleImage2(calendar);
                        break;
                    default:
                        break;
                
            
        );

        // Set listener, in this case, the same activity
        robotoCalendarView.setRobotoCalendarListener(this);

        robotoCalendarView.setShortWeekDays(false);

        robotoCalendarView.showDateTitle(true);

         robotoCalendarView.updateView();
    

//    @Override
//    protected void attachBaseContext(Context newBase) 
//        super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
//    

    /*@Override
    public void onAttach(Context context) 
        super.onAttach(context);
        //super.onAttach(CalligraphyContextWrapper.wrap(context));
    */

    @Override
    public void onDayClick(Calendar daySelectedCalendar) 
        Log.d("onDayClick: ", daySelectedCalendar.getTime() + "");
    

    @Override
    public void onDayLongClick(Calendar daySelectedCalendar) 
        Log.d("onDayLongClick: ", daySelectedCalendar.getTime() + "");
    

    @Override
    public void onRightButtonClick()  Log.d("onRightButtonClick!", "Calendar"); 

    @Override
    public void onLeftButtonClick() 
        Log.d("onLeftButtonClick!", "Calendar");
        // Toast.makeText(this.context, "onLeftButtonClick!", Toast.LENGTH_SHORT).show();
    

    private void activityResultIntent() 
        Intent check = new Intent();
        startActivityForResult(check, REQUEST_CODE);
    

    /**
     * Setting up for the Result OnClick inside the CardView on the List Adapter
     *
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) 
        super.onActivityResult(requestCode, resultCode, data);
        switch (resultCode) 

            case RESULT_OK:

                /*if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) 
                    System.out.println("I'm in onActivityResult");
                    _speaker = new Speaker(getApplicationContext());
                    ImageButton buttonSpeakKoreanAlphabet = (ImageButton) findViewById(R.id.buttonSpeakKorean_alphabet);
                    buttonSpeakKoreanAlphabet.setClickable(true);
                 else 
                    Intent install = new Intent();
                    install.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
                    startActivity(install);
                */

                break;

            case RESULT_CANCELED:
                // ... Handle this situation
                break;
            default:
                break;
        
    

    private void populateCardList() 
        // Data to be Fetch to the CardView
        // List<ServiceJobWrapper> results = null;//getAllData(this.getContext());
        /*new UIThreadHandler(getContext()).runOnUiThread(new Runnable() 
            @Override
            public void run() 

            
        );*/
        results = getAllDetailsOfLetters();
        mSearchResultsList.setHasFixedSize(true);
        mSearchResultsList.setLayoutManager(new LinearLayoutManager(context));
        mSearchResultsList.setItemAnimator(new DefaultItemAnimator());
        mListAdapter.swapData(results);

    

    private class UpdateJobServiceTask extends AsyncTask<String, Void, String> 

        private View mView;

        public UpdateJobServiceTask(View view) 
            mView = view;
        

        @Override
        protected String doInBackground(String... params) 
            populateCardList();

            return null;
        

        @Override
        protected void onPostExecute(String result) 
            //textView.setText(result);
        
    

    /***********************************
     * TEST                            *
     ***********************************/

    // Getting Details Data of all ALPHABET
    public List<ServiceJobWrapper> getAllDetailsOfLetters() 
        ArrayList<ServiceJobWrapper> translationList = new ArrayList<>();
        int x = 0;
        do 
            ServiceJobWrapper alpha = new ServiceJobWrapper();
            alpha.setID(Integer.parseInt(x + ""));
            alpha.setDay(x + "");
            alpha.setDate("TestDate" + x);
            alpha.setServiceNumber("00" + x);
            alpha.setCustomer("Customer" + x);
            alpha.setEngineer("Engineer" + x);
            alpha.setStatus((x % 2 == 1) ? "Pending" : "Completed");
            translationList.add(alpha);
            x++;
         while (x != 10);

        return translationList;
    


    @Override
    public void onClick(ServiceJobWrapper colorWrapper) 

    

    /**
     * Represents an asynchronous Task
     * UITask mAuthTask = new UITask();
     mAuthTask.execute((Void) null);
     */
    public class UITask extends AsyncTask<Void, Void, Boolean> 
        UITask() 
        

        @Override
        protected Boolean doInBackground(Void... params) 
            // TODO: attempt authentication against a network service.

            /*try 
                // Simulate network access.
                Thread.sleep(2000);
                // init_DrawerNav();
                //populateCardList();
             catch (InterruptedException e) 
                return false;
            */

            results = getAllDetailsOfLetters();
            // TODO: register the new account here.
            return true;
        

        @Override
        protected void onPostExecute(final Boolean success) 
            mSearchResultsList.setHasFixedSize(true);
            mSearchResultsList.setLayoutManager(new LinearLayoutManager(context));
            mSearchResultsList.setItemAnimator(new DefaultItemAnimator());
            mListAdapter.swapData(results);
        

        @Override
        protected void onCancelled() 
        
    

    public class UICalendarLibTask extends AsyncTask<Void, Void, Boolean> 
        private View view;
        UICalendarLibTask(View v) 
            this.view = v;
        

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

            return true;
        

        @Override
        protected void onPostExecute(final Boolean success) 
            setUpCalendarView(view);
        

        @Override
        protected void onCancelled() 
        
    


在 onCreateView 上调用这个结果相同:

setUpCalendarView(view); // First try without asynTask
// Second try with asynTask
//        UICalendarLibTask mCalendarTask = new UICalendarLibTask(view);
//        mCalendarTask.execute((Void) null);

这是我的片段 XML 布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical"
android:padding="20sp"
android:background="@drawable/background">


<FrameLayout
    android:layout_
    android:layout_
    android:id="@+id/frameLayoutCalendar">

    <com.marcohc.robotocalendar.RobotoCalendarView
        android:id="@+id/robotoCalendarPicker"
        android:layout_
        android:layout_ />

</FrameLayout>

<FrameLayout
    android:layout_
    android:id="@+id/frameLayoutButton"
    android:layout_>

    <Button
        android:text="TEST RANDOM"
        android:layout_
        android:layout_
        android:id="@+id/markDayButton" />
</FrameLayout>

<FrameLayout
    android:layout_
    android:layout_
    android:isScrollContainer="true"
    android:id="@+id/frameLayoutListOfServices">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/calendar_service_job_list"
        android:layout_
        android:layout_
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:clipToPadding="false"
        android:paddingTop="60dp" />
</FrameLayout>

好吧,我的线程没有看到任何错误,尤其是在填充 cardview 时,但是,当我尝试在同一个 Fragment 上同时渲染 recyclerView 和 Calendar 时,我得到了上面的错误

注意: 我正在使用来自 github 的这个 repo 作为日历视图 RobotoCalendar Github

【问题讨论】:

显示一些代码,尤其是你的异步操作。确保跳帧的原因不是其他原因,例如省略了一些操作。 你有什么帮助吗?谢谢 代码量很大。如果您将其减少到相关部分,这将使尝试帮助您的人更容易,但也可以帮助您缩小跳帧的原因。 UI 交互(日历设置)必须发生在 UI 线程上。如果确实存在性能问题,您只能留下一些东西。你有表演问题还是只有编舞在抱怨?另见***.com/questions/15963969/… 是的,我认为这只是与 Choreographer 相关的性能问题......而且我想我只会使用线程函数。 真的有问题(即真实设备上的动画卡顿)还是只是编舞者在抱怨?如果是后者,则可能不是真正的问题,例如可能由慢速模拟器引起。此外,当您的问题是 UI 的操作时,再多的线程也无济于事。不允许在单独的线程上操作 UI。 【参考方案1】:

我不明白我的解决方案背后的奥秘,但这解决了我的问题......

之前

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="vertical"
    android:padding="20sp"
    android:background="@drawable/background">

<FrameLayout
    android:layout_
    android:layout_
    android:id="@+id/frameLayoutCalendar">

<com.marcohc.robotocalendar.RobotoCalendarView
    android:id="@+id/robotoCalendarPicker"
    android:layout_
    android:layout_ />

之后(我刚刚删除了android:background="@drawable/background"这一行)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical"
android:padding="20sp">

<FrameLayout
    android:layout_
    android:layout_
android:id="@+id/frameLayoutCalendar">

<com.marcohc.robotocalendar.RobotoCalendarView
    android:id="@+id/robotoCalendarPicker"
    android:layout_
    android:layout_ />

谁能给我解释一下这个场景... 为了避免出现这个 Choreagrapher 错误,我是否真的不需要设置布局的背景,谢谢!

我也希望有人能帮忙,

【讨论】:

以上是关于管理导航抽屉内片段中的线程(用于平滑动画)的主要内容,如果未能解决你的问题,请参考以下文章

在导航抽屉管理的片段之间移动

在导航抽屉中管理片段

导航抽屉的片段管理

如何在导航抽屉中的两个片段之间传递数据

从片段中的按钮单击打开导航抽屉(Kotlin)

导航组件替换/更改后台堆栈