在现有的Android项目中,我遇到了以下代码(我插入了调试垃圾)
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();
Android开发的新手,并且已经google了一下,我明白这是在不阻塞主(UI)线程的情况下执行操作的方法,同时仍然在解码后在UI线程上设置图像。 (at least according to android-developers)(我已通过在各个地方记录Thread.currentThread().getName()
进行了验证)
现在有时图像没有出现,stdout只说
I/System.out( 8066): bitmap: android.graphics.Bitmap@432f3ee8 img: android.widget.ImageView@4339d698
I/System.out( 8066): runnable posted
没有来自Runnable的消息的痕迹。因此,尽管run()
返回img.post()
,但Runnable并不true
。在onCreate()
中拉取ImageView并将其声明为final
也无济于事。
我很无能为力。简单地设置位图,同时阻止UI线程,确实可以解决问题,但我想把事情弄清楚。有人知道这里发生了什么吗?
(ps。这是在Android 1.6手机和android-3 sdk上观察到的)
答案 0 :(得分:56)
如果您查看View.post
的文档,则会提供一些相关信息:
此方法只能在UI线程外部调用 此视图附加到窗口。
由于您是在onCreate
中执行此操作,因此有时您的View
可能尚未附加到窗口。您可以通过覆盖onAttachedToWindow
并在日志中放置内容并在发布时进行日志记录来验证这一点。您会看到,当帖子失败时,帖子调用会在onAttachedToWindow
之前发生。
正如其他人所提到的,您可以使用Activity.runOnUiThread
或提供自己的处理程序。但是,如果您想直接从View
本身执行此操作,则只需获取View
的处理程序:
view.getHandler().post(...);
如果您的自定义视图包含某种背景加载,这将非常有用。还有一个额外的好处就是不必创建新的单独处理程序。
答案 1 :(得分:10)
我扩展了ImageView
课来解决这个问题。我收集传递给帖子的runnables,而视图没有附加到窗口和onAttachedToWindow
帖子收集的runnable。
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;
}
}
答案 2 :(得分:7)
我认为问题是你是用一个单独的线程更新UI(ImageView),而不是UI线程。 UI只能由UI线程更新。
您可以使用Handler:
解决此问题Handler uiHandler;
public void onCreate(){
...
uiHandler = new Handler(); // This makes the handler attached to UI Thread
...
}
然后替换你的:
if ( !img.post(new Runnable() {
与
uiHandler.post(new Runnable() {
确保在UI线程中更新imageview。
Handler是一个相当令人困惑的概念,我还花了几个小时的研究来真正理解这个;)
答案 3 :(得分:6)
我没有看到你在那里有什么明显的错误;调用View.post()应该使它在UI线程上运行。如果您的Activity消失了(可能是通过屏幕旋转),那么您的ImageView将不会更新,但我仍然希望日志条目说“设置位图...”,即使您看不到它。 / p>
我建议尝试以下操作,看看它是否有所作为:
1)使用Log.d(标准Android记录器)而不是System.out
2)将Runnable传递给Activity.runOnUiThread()而不是View.post()
答案 4 :(得分:3)
使用以下代码,可以随时随地将代码发布到MainThread,但不取决于任何Context
或Activity
。这可以防止view.getHandler()
失败或繁琐的onAttachedToWindow()
内容等。
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//TODO
}
});
答案 5 :(得分:1)
我遇到了同样的问题,并且使用view.getHandler()也失败了,因为处理程序不存在。 runOnUiThread()解决了这个问题。据推测,在UI准备好之前,这确实会排队等待。
我的原因是在基类中调用图标加载任务并返回结果 很快,主类没有建立视图(片段中的getView())。
我有点怀疑它可能会在某个时候虚假地失败。 但我现在准备好了!谢谢你们。