Choreographer: Skipped frames : 应用程序可能在其主线程上做太多工作

Posted

技术标签:

【中文标题】Choreographer: Skipped frames : 应用程序可能在其主线程上做太多工作【英文标题】:Choreographer: Skipped frames : The application may be doing too much work on its main thread 【发布时间】:2022-01-11 11:47:58 【问题描述】:

在我的 android 应用程序中,我收到“该应用程序可能在其主线程上做太多工作”作为警告和 api 调用需要很长时间才能做出响应。我用一个例子展示了它。我有一个单选按钮2个案例每个都会在点击时进行api调用,结果将向用户显示数据列表。下面是我的代码。

protected void onCreate(Bundle savedInstanceState) 
    /**   Other code **/
    rdbGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() 
        @Override
        public void onCheckedChanged(RadioGroup radioGroup, int checkedId) 
            pos = radioGroup.indexOfChild(findViewById(checkedId));
            switch (pos) 
                case 0:
                    if (screenType == 1) 
                        getClassTeacherList();
                     else 
                        rcyTeachers.setVisibility(View.GONE);
                    
                    fabAddClassTeacher.setTitle("Add class teacher");
                    return;
                case 2:
                    if (screenType == 1) 
                        getAcademicTeachersList();
                     else 
                        rcyTeachers.setVisibility(View.GONE);
                    
                    fabAddClassTeacher.setTitle("Add Teacher");
                    return;
                default:
            
        
    );

private void getClassTeacherList() 
    academicPeriod = (AcademicPeriod) mSpnPeriod.getItemAtPosition(keyPos);
    if (isServerReachable(getApplicationContext())) 
        classTeacherList = serverAuthenticateService.getClassTeacherList(selClass.getId(),academicPeriod.getId(), authtoken, getApplicationContext());
        if (AppBackupCache.checkToken == 200) 
            showClassTeacherList();
         else if (AppBackupCache.checkToken == 401) 
            manager.invalidateAuthToken("com.lss.loop", authtoken);
            authtoken = null;
            final AccountManagerFuture<Bundle> future = manager.getAuthToken(mAccount, AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS, new Bundle(), true, null, null);
            new Thread(new Runnable() 
                public void run() 
                    try 
                        Bundle bnd = (Bundle) future.getResult();
                        authtoken = bnd.getString("authtoken");
                        if (authtoken != null) 
                            classTeacherList = serverAuthenticateService.getClassTeacherList(selClass.getId(),academicPeriod.getId(), authtoken, getApplicationContext());
                            if (AppBackupCache.checkToken == 200) 
                                showClassTeacherList();
                                return;
                             else 
                                getMsgBox("Error", "Something went wrong");
                                return;
                            
                        

                        getMsgBox("", "Token not refreshed....");
                     catch (Exception e) 
                        e.printStackTrace();
                    
                
            ).start();
        
        AppBackupCache.checkToken = 401;
        return;
    
    getMsgBox("No connection", "No connection");


public boolean isServerReachable(Context applicationContext) 
    ConnectivityManager connMan = (ConnectivityManager) applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = connMan.getActiveNetworkInfo();
    if (netInfo != null && netInfo.isConnected()) 
        try 
            URL urlServer = new URL(strUrl);
            HttpURLConnection urlConn = (HttpURLConnection) urlServer.openConnection();
            urlConn.setConnectTimeout(3000); //<- 3Seconds Timeout
            urlConn.connect();
            return urlConn.getResponseCode() == 200;
         catch (MalformedURLException e1) 
            return false;
         catch (IOException e) 
            return false;
        
    
    return false;
    //return true;



private void showClassTeacherList() 
    runOnUiThread(new Runnable() 
        @Override
        public void run() 
            if (classTeacherList.size() > 0) 
                rcyTeachers.setVisibility(View.VISIBLE);
                linLayColor1.setVisibility(View.VISIBLE);
                linLaySub.setVisibility(View.GONE);
                classTeacherGridAdapter = new ClassTeacherGridAdapter(ClassTeacherActivity.this, classTeacherList, fontFamily, screenType, 1);
                rcyTeachers.setAdapter(classTeacherGridAdapter);
                ViewCompat.setNestedScrollingEnabled(rcyTeachers, true);
                return;
            
            rcyTeachers.setVisibility(View.GONE);
            linLayColor1.setVisibility(View.GONE);
        
    );

ServerAuthenticateService.java:

@Override
public List<ClassTeacher> getClassTeacherList(int classId, int academicId, String authtoken, Context applicationContext) 
    HttpHeaders headers = new HttpHeaders();
    headers.set("Authorization", "bearer " + authtoken);
    MultiValueMap<String, String> map = new LinkedMultiValueMap();
    map.add("classId", String.valueOf(classId));
    map.add("academicId", String.valueOf(academicId));
    ResponseEntity<String> restRes = this.restTemplate.exchange(apiUrl + "/getClassTeacherList", HttpMethod.POST, new HttpEntity(map, headers), String.class, new Object[0]);
    if (restRes.getStatusCode() == HttpStatus.OK) 
        AppBackupCache.checkToken = ItemTouchHelper.Callback.DEFAULT_DRAG_ANIMATION_DURATION;
        String resBody = (String) restRes.getBody();
        Type listType = new TypeToken<List<ClassTeacher>>() .getType();
        List<ClassTeacher> allActPgmMap = (List) this.gson.fromJson(resBody, listType);
        return allActPgmMap;
     else if (restRes.getStatusCode() == HttpStatus.UNAUTHORIZED) 
        AppBackupCache.checkToken = 401;
        return null;
     else 
        AppBackupCache.checkToken = 402;
        return null;
    

下面是我得到的变暖。

I/Choreographer: Skipped 687 frames!  The application may be doing too much work on its main thread.
I/OpenGLRenderer: Davey! duration=11662ms; Flags=0, IntendedVsync=1315104621338, Vsync=1326554620880, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=1326567046366, AnimationStart=1326567174126, PerformTraversalsStart=1326567180324, DrawStart=1326747726176, SyncQueued=1326760565187, SyncStart=1326761318573, IssueDrawCommandsStart=1326761742323, SwapBuffers=1326766654823, FrameCompleted=1326767746542, DequeueBufferDuration=298000, QueueBufferDuration=332000, 

 I/Choreographer: Skipped 682 frames!  The application may be doing too much work on its main thread.
 I/OpenGLRenderer: Davey! duration=11418ms; Flags=0, IntendedVsync=1354296057548, Vsync=1365662723760, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=1365665791303, AnimationStart=1365665859063, PerformTraversalsStart=1365665871094, DrawStart=1365707384015, SyncQueued=1365711047505, SyncStart=1365711676880, IssueDrawCommandsStart=1365712054224, SwapBuffers=1365714258860, FrameCompleted=1365715220318, DequeueBufferDuration=243000, QueueBufferDuration=341000, 

在我的应用程序中,我有很多 API 调用,并且大多数时候我都会收到此警告,我应该怎么做才能避免这种情况并让我的请求和应用程序运行得更快?

【问题讨论】:

【参考方案1】:

如果我没记错的话,至少在调用 onCheckedChange 回调时,在主 (UI) 线程上调用您的 ServerAuthenticateServicegetClassTeacherList 方法。您应该将 Web API 调用移至另一个线程,这样主线程就不会“冻结”等待 Web API 调用的结果。

来自https://developer.android.com/guide/components/processes-and-threads:

...如果一切都发生在 UI 线程中,执行长操作(例如网络访问或数据库查询)将阻塞整个 UI。当线程被阻塞时,不能派发任何事件,包括绘图事件。从用户的角度来看,应用程序似乎挂起。更糟糕的是,如果 UI 线程被阻塞超过几秒(目前大约 5 秒),用户会看到臭名昭著的“应用程序无响应”(ANR)对话框。如果他们不满意,用户可能会决定退出您的应用程序并卸载它。

编辑:也不应该在主线程上调用 isServerReachable 方法。

EDIT2:您可以尝试这种方式,尽管我不知道在另一个线程上调用您的代码的所有含义:

private void getClassTeacherList() 
academicPeriod = (AcademicPeriod) mSpnPeriod.getItemAtPosition(keyPos);
new Thread(new Runnable() 
            public void run() 
if (isServerReachable(getApplicationContext())) 
    classTeacherList = serverAuthenticateService.getClassTeacherList(selClass.getId(),academicPeriod.getId(), authtoken, getApplicationContext());
    if (AppBackupCache.checkToken == 200) 
        showClassTeacherList();
     else if (AppBackupCache.checkToken == 401) 
        manager.invalidateAuthToken("com.lss.loop", authtoken);
        authtoken = null;
        final AccountManagerFuture<Bundle> future = manager.getAuthToken(mAccount, AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS, new Bundle(), true, null, null);
        

【讨论】:

你建议我在 new Thread(new Runnable() public void run() ).start(); 中编写 webapi 调用和 isServerReachable 代码; ?? @KJEjava48 是的! 如果我这样写,如何将结果列表从 ServerAuthenticateService 中的线程传递给活动类。你能在你的答案中显示它吗? 好的...完成。应该这样做。 现在“应用程序可能在其主线程上做太多工作”错误消失了,但在单选按钮切换后很长时间后才列出结果。用户可能会在切换单选按钮后对旧列表感到困惑.我该怎么办??

以上是关于Choreographer: Skipped frames : 应用程序可能在其主线程上做太多工作的主要内容,如果未能解决你的问题,请参考以下文章

编舞(697):跳过152帧!调试日志 [重复]

偶尔的 I/Choreographer(17165):跳过 ## 帧黑屏锁定,Android Xamarin c#

为啥 logcat 显示这个? [复制]

将 webview 添加到 sherlock 片段中

由于异步 Firebase 调用,主线程做了太多工作?

在主 ui 线程上处理来自其他线程的太多动画请求 - android