¿Hilo principal haciendo demasiado trabajo debido a llamadas asincrónicas de Firebase?
Sigo recibiendo un error en mi aplicación que dice I/Choreographer: Skipped 252 frames! The application may be doing too much work on its main thread.
I/Choreographer: Skipped 252 frames! The application may be doing too much work on its main thread.
Creo que esto causa un cierto retraso en mi interfaz de usuario que no quiero. Creo que es porque cuando realizo una consulta de Firebase, cuando hago onDataChange()
, parece ser siempre ejecutado en el hilo de interfaz de usuario principal. Tengo alrededor de 5 consultas Firebase similares a lo que tengo a continuación. Como resultado, intenté mover mi código del método onDataChange()
a AsyncTask
y actualizar el subproceso de interfaz de usuario en el método onPostExecute()
de AsyncTask
. Sin embargo, cuando intento esto, el método onPostExecute()
nunca se completa. Aquí está mi intento:
public void getPublicPosts(final View progressOverlay, final View fragmentView, final Context context) { //Need to do order by / equal to. Firebase postsRef = firebaseRef.child("Posts"); Query query = postsRef.orderByChild("privacy").equalTo("Public"); query.keepSynced(true); query.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { for (final DataSnapshot postSnapShot : dataSnapshot.getChildren()) { AsyncTask task = new AsyncTask<URL, Integer, Long>() { @Override protected Long doInBackground(URL... params) { Post post = postSnapShot.getValue(Post.class); List<Post> publicPosts = application.getPublicAdapter().getPosts(); if (post.getPrivacy().equals("Public") && application.getPublicAdapter().containsId(publicPosts, post.getId()) == null) { application.getPublicAdapter().getPosts().add(0, post); } return null; } @Override protected void onProgressUpdate(Integer... progress) { } @Override protected void onPostExecute(Long result) { System.out.println("Finished executing public"); populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context); if (progressOverlay.getVisibility() == View.VISIBLE) { System.out.println("getPublicPosts: DONE"); AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200); fragmentView.findViewById(R.id.rv_public_feed).setVisibility(View.VISIBLE); } } }; `task.execute();` } } @Override public void onCancelled(FirebaseError firebaseError) { } }); }
Cualquier ayuda con esto sería útil. Si alguien pudiera ayudarme, eso sería genial. ¡Gracias!
- Enrutado del reproductor multimedia de Android
- ¿Están encadenando AsyncTasks consideradas malas prácticas?
- ¿Cómo descargar múltiples archivos simultáneamente usando intentservice en Android?
- ¿Por qué un AsyncTask no se inicia si se inicia / se detiene repetidamente?
- ¿Cómo puedo coordinar dos tareas de fondo?
EDIT: Agregar en la función para crear AsyncTask
public AsyncTask asyncTaskWrapper(final DataSnapshot dataSnapshot, final View progressOverlay, final View fragmentView, final Context context) { AsyncTask task = new AsyncTask<URL, Integer, Long>() { @Override protected Long doInBackground(URL... params) { for (final DataSnapshot postSnapShot : dataSnapshot.getChildren()) { Post post = postSnapShot.getValue(Post.class); List<Post> publicPosts = application.getPublicAdapter().getPosts(); if (post.getPrivacy() == PrivacyEnum.Public && application.getPublicAdapter().containsId(publicPosts, post.getId()) == null) { application.getPublicAdapter().getPosts().add(0, post); } } return null; } @Override protected void onProgressUpdate(Integer... progress) { } @Override protected void onPostExecute(Long result) { System.out.println("Finished executing public"); TabsUtil.populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context); if (progressOverlay.getVisibility() == View.VISIBLE) { System.out.println("getPublicPosts: GONE"); AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200); fragmentView.findViewById(R.id.rv_public_feed).setVisibility(View.VISIBLE); } } }; return task; }
Función PublicPosts:
public void getPublicPosts(final View progressOverlay, final View fragmentView, final Context context) { //Need to do order by / equal to. Firebase postsRef = firebaseRef.child("Posts"); Query query = postsRef.orderByChild("privacy").equalTo(PrivacyEnum.Public.toString()); query.keepSynced(true); query.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { asyncTaskWrapper(dataSnapshot, progressOverlay, fragmentView, context); } @Override public void onCancelled(FirebaseError firebaseError) { TabsUtil.populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context); } }); }
- Ayúdame a usar correctamente los Servicios y los Hilos
- Pausa con handler y postDelayed en android
- Monitor de estado de subprocesos. ¿Cómo depurar esto? ¿Qué lo causa?
- ¿Los hilos son lo suficientemente fiables para calcular segundos?
- Glide assert: java.lang.IllegalArgumentException: Debe llamar a este método en el hilo principal
- Android SQLite Query, insertar, actualizar, eliminar, siempre tiene que estar en el hilo de fondo?
- Iniciar nuevo subproceso en Async Task
- AsyncTask hilo sigue allí después de ejecutar, ¿es normal?
Su hilo principal probablemente es lento porque está ejecutando un bucle con muchos objetos. Sin embargo, no saber qué es exactamente lo que está ejecutando, que es sólo una suposición.
Pero yo un problema con su AsyncTask, no creo que nunca funcionará con el código que publicó.
AsyncTask está esperando una matriz de URL. No estás pasando nada. Si no necesita URL como entrada, utilice:
AsyncTask task = new AsyncTask<Void, Boolean, Boolean>()
¿Qué te parece si intentas solucionar problemas:
AsyncTask task = new AsyncTask<URL, Boolean, Boolean>() { @Override protected Boolean doInBackground(URL... params) { for (final DataSnapshot postSnapShot : dataSnapshot.getChildren()) { Post post = postSnapShot.getValue(Post.class); List<Post> publicPosts = application.getPublicAdapter().getPosts(); if (post.getPrivacy() == PrivacyEnum.Public && application.getPublicAdapter().containsId(publicPosts, post.getId()) == null) { application.getPublicAdapter().getPosts().add(0, post); } } return true; } @Override protected void onPostExecute(Boolean result) { if(result){ System.out.println("Finished executing public"); TabsUtil.populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context); if (progressOverlay.getVisibility() == View.VISIBLE) { System.out.println("getPublicPosts: GONE"); AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200); fragmentView.findViewById(R.id.rv_public_feed).setVisibility(View.VISIBLE); } } } };
Luego ejecuta la tarea disparando:
URL[] urls = new URL[2]; urls[0] = new URL(...); urls[1] = new URL(...); task.execute(urls);
Utilice la función de depuración en el IDE y devuelva el lugar exacto si el código falla.
No está llamando a execute () en el AsyncTask que creó para que nunca se dispare.
Después de crear AsyncTask, llame a execute (URL … params) para que se ejecute.
También veo que usted no está utilizando los params que usted está pasando, así que usted puede apenas pasar nada y él trabajará.
Es mejor hacer las solicitudes de red en IntentService. Es fácil e impide la congelación de la interfaz y / o "La aplicación puede estar haciendo demasiado trabajo en su hilo principal".
Echa un vistazo a:
IntentService en la documentación para desarrolladores de Android
En el código que está llamando a la AsynTast n tiempo mejor que debe llamar una vez El para: cada ciclo utilizado para debe estar dentro de la doinbackground () y en onProgressUpdate () puede actualizar la interfaz de usuario en lugar de onPostExecute (), esto mejorará la Código poco pero no completamente, debe usar una barra de progreso en onPreexecute y descartar la barra de progreso en onPostEcecute (), sólo para su idea Tengo el escribir el código de abajo, pero no se refieren completamente, ya que no he probado esto, pero Forma esto obtendrá alguna idea.
AsyncTask task = new AsyncTask<URL, Integer, Long>() { @Override protected Long doInBackground(URL... params) { for (final DataSnapshot postSnapShot : dataSnapshot.getChildren()) { Post post = postSnapShot.getValue(Post.class); List<Post> publicPosts = application.getPublicAdapter().getPosts(); if (post.getPrivacy().equals("Public") && application.getPublicAdapter().containsId(publicPosts, post.getId()) == null) { application.getPublicAdapter().getPosts().add(0, post); } publishProgress((1); } return null; } @Override protected void onProgressUpdate(Integer... progress) { System.out.println("Finished executing public"); populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context); if (progressOverlay.getVisibility() == View.VISIBLE) { System.out.println("getPublicPosts: DONE"); AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200); fragmentView.findViewById(R.id.rv_public_feed).setVisibility(View.VISIBLE); } } } @Override protected void onPostExecute(Long result) { } } };
Habría sido mucho más limpio y más fácil de mantener si ha creado una clase que extiende un AsyncTask y lo llaman así en su método ondatachange:
DoingStuff doTask = new DoingStuff(); doTask.execute(...);
De esta manera usted ejecuta con seguridad su conexión Firebase cosas en un hilo de trabajo y actualizar su interfaz de usuario con los resultados que haya recuperado.
Puede probar algunas de las siguientes opciones:
- En lugar de crear y ejecutar varias N AsyncTasks, sólo cree y ejecute una sola con el bucle N snapshots dentro.
- En onPostExecute () comprobar que el método populateNewsFeedList no está realizando manipulaciones intensivas de datos, si es así, también puede ejecutarlo en un nuevo AsyncTask y actualizar la interfaz de usuario después de que termine.
- También puede intentar actualizar las vistas pasando un método runnable al post , esto enqueue los runnables en la cola de mensajes del subproceso principal.
- Puede ejecutar sus operaciones largas en un Servicio o un IntentService y actualizar su UI a través de un ContentProvider o Broadcasts .
Espero que ayude.
Sugerencias:
- Retire el trabajo pesado del hilo principal de la interfaz de usuario, excutes en los otros hilos.
2. Cuando los otros hilos terminan el trabajo pesado, si necesita actualizar la interfaz de usuario, utilice Handler para enviar el msg al subproceso principal de UI y, a continuación, actualice la interfaz de usuario en el subproceso principal.