You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中Google Maps API v2 Marker动画报错求助

解决Marker动画线程的IllegalMonitorStateException问题

这个错误的根源其实挺典型的,我来拆解一下问题,再给你修正后的代码方案:

为什么会抛出IllegalMonitorStateException?

你遇到的这个异常,核心问题有两个:

  1. 锁的持有线程不匹配:你的wait(500)调用放在了map.runOnUiThread()的Runnable里面——这个Runnable是在UI线程执行的,但你代码里的synchronized(this)是在你的MarkerAnimationThread线程中,UI线程根本没有持有this对象的锁,直接调用wait()当然会触发异常。
  2. 阻塞UI线程的致命错误:退一步说,就算你在UI线程里加了锁,调用wait()会直接阻塞UI线程,很快就会导致ANR(应用无响应),这在Android开发里是绝对不能犯的错误。

另外,你的代码里MarkerAnimationThread thread = this这个变量完全多余,直接用this就可以了,没必要多此一举。

修正后的代码方案

我们需要把等待逻辑放在动画线程本身,而不是UI线程,同时保证线程安全和UI更新的正确性:

package com.example.?;
import android.util.Log;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;

public class MarkerAnimationThread extends Thread {
    Marker currentLocationMarker;
    GameMap map;
    // 用volatile保证线程间可见性,因为UI线程修改counter,动画线程读取
    volatile int counter = 0;

    public MarkerAnimationThread(Marker currentLocationMarker, GameMap map) {
        this.currentLocationMarker = currentLocationMarker;
        this.map = map;
    }

    @Override
    public void run() {
        while (!isInterrupted()) { // 用isInterrupted判断,方便终止线程
            // 切换到UI线程更新Marker图标,这部分必须在UI线程执行
            map.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    switch (counter) {
                        case 0:
                            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_1));
                            counter++;
                            break;
                        case 1:
                            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_2));
                            counter++;
                            break;
                        case 2:
                            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_3));
                            counter++;
                            break;
                        case 3:
                            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_4));
                            counter = 0;
                            break;
                        default:
                            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_1));
                            counter = 0;
                            break;
                    }
                }
            });

            // 在动画线程中等待,不会阻塞UI
            synchronized (this) {
                try {
                    wait(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 线程被中断时退出循环,避免无限运行
                    break;
                }
            }
        }
    }
}

额外的优化建议

  1. 线程终止逻辑:当你不需要动画的时候,记得调用thread.interrupt()来终止这个线程,避免内存泄漏。
  2. 图标预加载:如果你的图标比较大,可以提前把BitmapDescriptor加载好,放在成员变量里,避免每次切换图标都去读取资源,提升性能。
  3. 更简洁的替代方案:其实Android里还有更适合做这种帧动画的方式,比如用ValueAnimator来驱动,不需要手动管理线程,代码会更简洁,也更符合Android的生命周期规范。示例如下:
ValueAnimator animator = ValueAnimator.ofInt(0, 3);
animator.setDuration(2000); // 4帧,每帧500ms,总时长2000ms
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(animation -> {
    int frame = (int) animation.getAnimatedValue();
    switch (frame) {
        case 0:
            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_1));
            break;
        case 1:
            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_2));
            break;
        case 2:
            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_3));
            break;
        case 3:
            currentLocationMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.figure_4));
            break;
    }
});
animator.start();

这种方式不需要手动创建线程,系统会帮你处理UI线程的切换,也更容易控制动画的开始和停止。

内容的提问来源于stack exchange,提问作者Leon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:03:35