Android Studio中Google Maps API v2 Marker动画报错求助
解决Marker动画线程的IllegalMonitorStateException问题
这个错误的根源其实挺典型的,我来拆解一下问题,再给你修正后的代码方案:
为什么会抛出IllegalMonitorStateException?
你遇到的这个异常,核心问题有两个:
- 锁的持有线程不匹配:你的
wait(500)调用放在了map.runOnUiThread()的Runnable里面——这个Runnable是在UI线程执行的,但你代码里的synchronized(this)是在你的MarkerAnimationThread线程中,UI线程根本没有持有this对象的锁,直接调用wait()当然会触发异常。 - 阻塞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; } } } } }
额外的优化建议
- 线程终止逻辑:当你不需要动画的时候,记得调用
thread.interrupt()来终止这个线程,避免内存泄漏。 - 图标预加载:如果你的图标比较大,可以提前把BitmapDescriptor加载好,放在成员变量里,避免每次切换图标都去读取资源,提升性能。
- 更简洁的替代方案:其实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
相关产品推荐
相关产品推荐

