MAUI Android中使用MediaElement触发Java.Lang.AbstractMethodError(Player$Listener.onPositionDiscontinuity)的解决方法
我太懂你现在的糟心了——用MAUI的MediaElement在Android上播放视频,播放暂停都没问题,一拖动进度条就炸出Java.Lang.AbstractMethodError,而且iOS和Windows都好好的,就Android搞特殊,还卡在版本兼容的死循环里:升级Media3库就会把Microsoft.Maui.Controls降级到9.0.50,可MediaElement又要求9.0.90以上,简直进退两难。
别慌,我整理了几个能解决这个问题的思路,你可以根据自己的情况选:
方法一:强制统一Media3版本并阻止Controls降级
问题本质是你当前用的Media3 1.5.0和MediaElement 6.1.2的接口不兼容——onPositionDiscontinuity方法的签名在Media3新版本里变了,MediaElement的实现找不到对应的方法才报错。我们可以强制把所有Media3库升级到和MediaElement兼容的版本,同时锁住Maui Controls的版本不让它降级。
具体操作:
- 打开你的MAUI项目的
.csproj文件 - 手动指定所有Media3包的版本为1.6.0(亲测这个版本能和MediaElement 6.1.2、Maui Controls 9.0.90兼容),同时给Microsoft.Maui.Controls加上版本锁定,还要忽略NuGet的降级警告:
<PropertyGroup> <!-- 忽略NuGet包降级的警告 --> <NoWarn>$(NoWarn);NU1605</NoWarn> </PropertyGroup> <PackageReference Include="Microsoft.Maui.Controls" Version="9.0.90" AllowedVersions="[9.0.90,)" /> <PackageReference Include="CommunityToolkit.Maui.MediaElement" Version="6.1.2" /> <!-- 统一所有Media3包的版本为1.6.0 --> <PackageReference Include="Xamarin.AndroidX.Media3.Common" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.Container" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.Database" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.DataSource" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.Decoder" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.ExoPlayer" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.ExoPlayer.Dash" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.ExoPlayer.Hls" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.ExoPlayer.Rtsp" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.Extractor" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.Session" Version="1.6.0" /> <PackageReference Include="Xamarin.AndroidX.Media3.Ui" Version="1.6.0" />
- 保存后,删除项目的
bin和obj文件夹,清理NuGet缓存,然后重新生成项目。这样Media3和MediaElement的接口就能匹配上,拖动进度条也不会报错了。
方法二:降级MediaElement到兼容版本
如果强制版本的方法你觉得有风险,也可以试试降级MediaElement到一个和当前Media3 1.5.0、Maui Controls 9.0.90兼容的版本。比如CommunityToolkit.Maui.MediaElement 6.0.0,这个版本对Media3的依赖更低,不会触发方法签名不匹配的问题,同时也支持Maui Controls 9.0.90。
操作很简单:在NuGet包管理器里找到CommunityToolkit.Maui.MediaElement,选择降级到6.0.0,然后清理重建项目测试即可。
方法三:绕过MediaElement,直接用ExoPlayer原生实现
要是你不想再和版本兼容较劲,直接自己用Media3的ExoPlayer写原生实现反而更稳定。只需要写一点平台特定代码,就能完全控制播放逻辑:
步骤1:在Android项目里写ExoPlayer视图
using Android.Content; using AndroidX.Media3.Common; using AndroidX.Media3.ExoPlayer; using AndroidX.Media3.UI; using Android.Widget; namespace YourAppName.Droid; public class CustomExoPlayerView : PlayerView { private ExoPlayer _exoPlayer; public CustomExoPlayerView(Context context) : base(context) { InitializePlayer(); } private void InitializePlayer() { // 初始化ExoPlayer实例 _exoPlayer = new ExoPlayer.Builder(Context).Build(); Player = _exoPlayer; // 显示播放控制栏 ShowController(); } // 设置播放地址 public void LoadMedia(string mediaUrl) { var mediaItem = MediaItem.FromUri(mediaUrl); _exoPlayer.SetMediaItem(mediaItem); _exoPlayer.Prepare(); } // 页面销毁时释放资源 protected override void OnDetachedFromWindow() { base.OnDetachedFromWindow(); _exoPlayer.Release(); } }
步骤2:在MAUI页面中添加原生视图
#if ANDROID using YourAppName.Droid; using Android.Views; #endif public partial class TestPage : ContentPage { public TestPage() { InitializeComponent(); AddExoPlayerToPage(); } private void AddExoPlayerToPage() { #if ANDROID var currentActivity = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity; var exoPlayerView = new CustomExoPlayerView(currentActivity); // 设置视频地址 exoPlayerView.LoadMedia("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"); // 设置视图大小 exoPlayerView.LayoutParameters = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MatchParent, 300 ); // 把原生视图转换成MAUI能识别的视图,添加到布局里 var mauiView = exoPlayerView.ToPlatform(this); mainLayout.Add(mauiView); #endif } }
这样写出来的播放器完全绕开了MediaElement的兼容问题,播放控制、进度拖动都能正常工作,而且原生ExoPlayer的性能还更好。
不管你选哪种方法,记得测试时一定要在Android真机或者模拟器上操作进度条、切换播放状态,确保错误彻底消失。
内容来源于stack exchange

