当前位置: 首页 > news >正文

从 leanback 的npe崩溃谈起

最近接手了一个TV的应用,然后看到有些历史的崩溃。
其中top3有这样一个崩溃,但是被离职的前同事标记为不处理了。
崩溃是这样的:

java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.ViewGroup$LayoutParams android.view.View.getLayoutParams()' on a null object reference
at androidx.recyclerview.widget.OrientationHelper$2.getDecoratedStart(Proguard:403)
at androidx.leanback.widget.GridLayoutManager.getViewMin
at androidx.leanback.widget.GridLayoutManager$2.getEdge(Proguard:1812)
at androidx.leanback.widget.Grid.removeInvisibleItemsAtEnd
at androidx.leanback.widget.GridLayoutManager.removeInvisibleViewsAtEnd(Proguard:1906)
at androidx.leanback.widget.GridLayoutManager.scrollDirectionPrimary(Proguard:2545)
at androidx.leanback.widget.GridLayoutManager.scrollVerticallyBy(Proguard:2487)
at androidx.recyclerview.widget.RecyclerView.scrollStep(Proguard:1974)
at androidx.recyclerview.widget.RecyclerView$ViewFlinger.run(Proguard:5476)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:727)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7661)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

issuetracker

issuetracker上显示已经修复了,更新日志中也可以看到这个的信息

https://issuetracker.google.com/issues/177338150
https://android-review.googlesource.com/c/platform/frameworks/support/+/2808353
https://developer.android.com/jetpack/androidx/releases/leanback?hl=zh-cn
https://developer.android.com/jetpack/androidx/releases/leanback?hl=zh-cn#leanback-grid_version_100_2

Version 1.2.0-alpha04
November 15, 2023androidx.leanback:leanback:1.2.0-alpha04 and androidx.leanback:leanback-preference:1.2.0-alpha04 are released. Version 1.2.0-alpha04 contains these commits.Bug Fixes(I2c3a0, b/292114537)
https://android-review.googlesource.com/#/q/I2c3a0f7ae43f72bd6a1dbbe30c269148f824a885
https://issuetracker.google.com/issues/292114537
Dependency UpdateUpdate recyclerview requirement to 1.3.2 to fix a common crash in TV apps

修复方法

dependencies {
def leanback_version = "1.2.0"
implementation "androidx.leanback:leanback:$leanback_version"
implementation "androidx.leanback:leanback-tab:1.1.0"
}

注意到这个升级了recyclerview到1.3.2之后。
compile_sdk_version 也需要升级到35,即 compileSdk 35

1.2.0 leanback ui 紊乱

本来上个小节之后,这篇博客应该就结束了。
但是我自测发现升级之后,列表刷新之后,出现了ui紊乱,即列表叠在一起。

项目中是如何做列表刷新的?

在 Android TV 开发中,Leanback 库的 DiffCallback 用于高效比较数据集合并更新 ArrayObjectAdapter 的内容,通常结合 setItems(List, DiffCallback) 方法使用。以下是其核心用法:


1. 作用

  • 高效更新列表:通过比较新旧数据集差异,仅更新变化的项(支持动画效果)。
  • 避免全量刷新:减少不必要的 UI 重绘,提升性能。

2. 实现步骤

a. 创建 DiffCallback 子类

继承 DiffCallback 并实现两个关键方法:

public class MyDiffCallback extends DiffCallback<YourItemType> {@Override
public boolean areItemsTheSame(YourItemType oldItem, YourItemType newItem) {
// 判断是否为同一个项(如唯一ID相同)
return oldItem.getId() == newItem.getId();
}@Override
public boolean areContentsTheSame(YourItemType oldItem, YourItemType newItem) {
// 判断内容是否相同(如字段值完全一致)
return oldItem.equals(newItem);
}@Optional @Override
public Object getChangePayload(YourItemType oldItem, YourItemType newItem) {
// 可选:返回变化的部分数据,用于局部更新
return super.getChangePayload(oldItem, newItem);
}
}
b. 使用 DiffCallback 更新数据

通过 ArrayObjectAdaptersetItems() 方法触发差异计算:

ArrayObjectAdapter adapter = new ArrayObjectAdapter(presenter);
List<YourItemType> newItems = ...; // 新数据集// 更新数据并自动应用差异
adapter.setItems(newItems, new MyDiffCallback());

3. 示例场景

假设有一个 Movie 类作为数据项:

public class Movie {
private int id;
private String title;
private String description;// Getters & Setters...
}public class MovieDiffCallback extends DiffCallback<Movie> {
@Override
public boolean areItemsTheSame(Movie oldItem, Movie newItem) {
return oldItem.getId() == newItem.getId(); // ID 相同则为同一项
}@Override
public boolean areContentsTheSame(Movie oldItem, Movie newItem) {
return oldItem.getTitle().equals(newItem.getTitle())
&& oldItem.getDescription().equals(newItem.getDescription()); // 内容完全一致
}
}

4. 注意事项

  • 正确实现比较逻辑areItemsTheSame 应基于唯一标识符(如 ID),areContentsTheSame 判断数据是否变化。
  • 性能优化:避免在 areContentsTheSame 中进行耗时操作。
  • 支持局部更新:通过 getChangePayload 返回变化的部分数据,在 Presenter 中处理 onBindViewHolder 的局部更新。

UI错乱原因

项目中的 ConstraintLayout 中的view有点复杂,特别是在计算高度的时候,都是动态计算的。
在动画过程中,计算出来的结果出现了变化,所以导致了最终的效果是错乱的。

修复UI错乱的方法

  1. 和iOS一样,去掉动画,即 DiffCallback 传空进去。

  2. 重写 getChangePayload 和 public void onBindViewHolder(@NonNull ViewHolder viewHolder,@NonNull Object item,@NonNull List payloads) payloads变化的时候忽略 onBindViewHolder,然后定时400ms post一下,刷新列表。

  3. 固定View的高度、并且把高度依赖计算的view替换成 LinearLayout(或者其他容器)的子view

相关文章:

  • leetcode2221. 数组的三角和-medium
  • 榕壹云医疗服务系统:基于ThinkPHP+MySQL+UniApp的多门店医疗预约小程序解决方案
  • 【git stash切换】
  • MySQL事务及其原理
  • Bonjour
  • 7.3 Organizing data into training batches
  • 20250530-C#知识:String与StringBuilder
  • 算力租赁革命:弹性模式如何重构数字时代的创新门槛​
  • shadcn/ui
  • Python+requests+pytest接口自动化测试框架的搭建(全)
  • C# MySQL 实现多层级联数据迁移
  • 数据结构:导论
  • RK3399 Android7.1增加应用安装白名单机制
  • python进程hung住如何找到问题所在
  • 内存池学习(一)
  • 腾讯云开发者社区文章内容提取免费API接口教程
  • Hive的存储格式如何优化?
  • 计算机视觉入门:OpenCV与YOLO目标检测
  • CSS3前端入门(第三天)2D转换 transform
  • CAD多边形密堆积2D插件
  • 网站推广思路/广州网页制作
  • 厦门门户网站建设/昆山网站建设公司
  • 怎么做日本钓鱼网站吗/个人小白如何做手游代理
  • wordpress 自定义鼠标/优化排名软件
  • 网上翻译网站做译员/seo优化快速排名
  • 网络服务器与个人计算机的区别/网络优化培训