java - thread - Runnable se publica correctamente pero no se ejecuta
runnable java 8 (6)
Creo que el problema es que está actualizando la interfaz de usuario (ImageView) con un subproceso independiente, que no es el subproceso de la interfaz de usuario. La interfaz de usuario solo puede actualizarse mediante el subproceso de la interfaz de usuario.
Puedes resolver esto usando Handler :
Handler uiHandler;
public void onCreate(){
...
uiHandler = new Handler(); // This makes the handler attached to UI Thread
...
}
Luego reemplace su
if ( !img.post(new Runnable() {
con
uiHandler.post(new Runnable() {
para asegurarse de que la vista de imagen se actualice en el hilo de la interfaz de usuario.
Handler es un concepto bastante confuso, también tomé horas de investigación para entender esto;)
En un proyecto de Android existente, encontré el siguiente fragmento de código (donde inserté la litera de depuración)
ImageView img = null;
public void onCreate(...) {
img = (ImageView)findViewById(R.id.image);
new Thread() {
public void run() {
final Bitmap bmp = BitmapFactory.decodeFile("/sdcard/someImage.jpg");
System.out.println("bitmap: "+bmp.toString()+" img: "+img.toString());
if ( !img.post(new Runnable() {
public void run() {
System.out.println("setting bitmap...");
img.setImageBitmap(bmp);
System.out.println("bitmap set.");
}
}) ) System.out.println("Runnable won''t run!");
System.out.println("runnable posted");
}
}.start();
Como novedad en el desarrollo de Android, y habiendo buscado en Google, entiendo que esta es la forma de hacer cosas sin bloquear el hilo principal (UI), mientras se configura la imagen en el hilo UI después de la descodificación. (al menos de acuerdo con los desarrolladores de Android) (que he verificado mediante el registro de Thread.currentThread().getName()
en varios lugares)
Ahora, a veces la imagen simplemente no aparece, y stdout solo dice
I/System.out( 8066): bitmap: android.graphics.Bitmap@432f3ee8 img: android.widget.ImageView@4339d698
I/System.out( 8066): runnable posted
Sin rastro de los mensajes del Runnable. Entonces, aparentemente, el Runnable no se run()
, aunque img.post()
devuelve true
. Tirar de ImageView en onCreate()
y declararlo final
no ayuda.
No tengo ni idea Simplemente configurando el mapa de bits directamente, mientras se bloquea el subproceso de la interfaz de usuario, corrige las cosas, pero quiero hacer las cosas bien. ¿Alguien entiende lo que está pasando aquí?
(ps. todo esto se observó en un teléfono Android 1.6 y Android-3 SDK)
No veo nada obviamente malo con lo que tienes allí; llamar a View.post () debería hacer que se ejecute en el subproceso de la interfaz de usuario. Si su Actividad desapareció (quizás a través de una rotación de pantalla), entonces su ImageView no se actualizaría, pero aún así esperaría que una entrada de registro diga "configurando mapa de bits ...", incluso si no pudiera verlo.
Sugiero intentar lo siguiente y ver si hace una diferencia:
1) Use Log.d (el registrador estándar de Android) en lugar de System.out
2) Pase su Runnable a Activity.runOnUiThread () en lugar de View.post ()
Si miras los documentos de View.post
hay información relevante:
Este método puede invocarse desde fuera del subproceso de la interfaz de usuario solo cuando esta vista se adjunta a una ventana.
Ya que está haciendo esto en onCreate
, es probable que algunas veces su View
no esté adjunta a la ventana. Puede verificar esto anulando onAttachedToWindow
y poniendo algo en los registros y también onAttachedToWindow
sesión cuando publique. Verás que cuando falla la publicación, la llamada posterior ocurre antes de onAttachedToWindow
.
Como los demás han mencionado, puede usar Activity.runOnUiThread
o proporcionar su propio controlador. Sin embargo, si desea hacerlo directamente desde la propia View
, simplemente puede obtener el controlador de la View
:
view.getHandler().post(...);
Esto es especialmente útil si tiene una vista personalizada que incluye algún tipo de carga de fondo. También está la ventaja adicional de no tener que crear un nuevo controlador separado.
Tuve el mismo problema, y โโel uso de view.getHandler () también falló porque el controlador no estaba presente. runOnUiThread () resolvió el problema. Es de suponer que esto realmente hace algunas colas hasta que la interfaz de usuario está lista.
La causa para mí era llamar a la tarea de carga de iconos en una clase base y el resultado se estaba devolviendo tan rápidamente que la clase principal no había establecido la vista (getView () en el fragmento).
Estoy un poco sospechoso de que pueda fallar falsamente en algún momento. ¡Pero ahora estoy listo para ello! Gracias chicos.
Utilice el siguiente código, puede publicar su código en MainThread en cualquier momento, en cualquier lugar, pero no depende de ningún Context
o Activity
. Eso puede prevenir el view.getHandler()
o tedioso en los onAttachedToWindow()
etc.
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//TODO
}
});
ImageView
clase ImageView
para resolver este problema. onAttachedToWindow
pasados โโa la publicación mientras que la vista no está adjunta a la ventana y en onAttachedToWindow
publicación recopilada es ejecutable.
public class ImageView extends android.widget.ImageView
{
List<Runnable> postQueue = new ArrayList<Runnable>();
boolean attached;
public ImageView(Context context)
{
super(context);
}
public ImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
attached = true;
for (Iterator<Runnable> posts = postQueue.iterator(); posts.hasNext();)
{
super.post(posts.next());
posts.remove();
}
}
@Override
protected void onDetachedFromWindow()
{
attached = false;
super.onDetachedFromWindow();
}
@Override
public boolean post(Runnable action)
{
if (attached) return super.post(action);
else postQueue.add(action);
return true;
}
}