Android开发如何不声明变量为final即可启动非UI新线程
问题解决方法
报错的根本原因是Java语法规则限制:匿名内部类(你代码里的Runnable实例)访问外部方法的局部变量时,变量必须被final修饰,或者实际没有被二次赋值(Java 8及以上支持隐式final判定)。你给变量加final后出现崩溃和final本身无关,大概率是子线程直接操作UI控件导致的,Android系统禁止非UI线程访问、更新UI相关组件。
以下是不需要修改原方法参数为final的实现方案:
方案1:使用隐式final规则(Java 8+适用)
只要position、rowImpact、takenPawn三个参数在方法内没有被重新赋值,在支持Java 8的编译环境下不需要显式加final也不会报错,你只需要在模块的build.gradle中添加Java 8支持配置即可:
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // 如果项目用了kotlin再加下面配置 kotlinOptions { jvmTarget = "1.8" } }
如果参数后续会被修改,可使用下面两种方案。
方案2:局部变量拷贝
在方法内声明临时final变量存储参数值,不需要修改原参数的修饰符:
public void checkUpTaking(int position, int rowImpact, int takenPawn) { //taken pawn: -1 = brown, 1 = white // 拷贝参数到临时final变量 final int finalPos = position; final int finalRowImp = rowImpact; final int finalTakenPawn = takenPawn; new Thread(new Runnable() { @Override public void run() { if (finalPos > 10) { //if you'll try to take from the last row - array out of bounds if (finalPos % 5 != 0 && playableTile[finalPos - 1 - 4 - finalRowImp].getIsTaken() == finalTakenPawn && playableTile[finalPos - 1 - 9].getIsTaken() == 0) { checkTreeNodes(finalPos, finalPos - 9, finalPos-4-finalRowImp); //checking possible mandatory moves before clicking pawn } if ((finalPos - 1) % 5 != 0 && playableTile[finalPos - 1 - 5 - finalRowImp].getIsTaken() == finalTakenPawn && playableTile[finalPos - 1 - 11].getIsTaken() == 0) { checkTreeNodes(finalPos, finalPos - 11, finalPos-5-finalRowImp); } } } }).start(); }
方案3:自定义Runnable实现类(最稳妥)
编写静态内部类实现Runnable接口,通过构造方法传入需要的参数,完全不依赖外部方法的变量:
// 替换PlayableTile为你项目中playableTile的实际类型 private static class CheckTask implements Runnable{ private final int position; private final int rowImpact; private final int takenPawn; private final PlayableTile[] tiles; private final MainActivity activity; // 替换为你当前类的类名,用来调用外部类方法 public CheckTask(int position, int rowImpact, int takenPawn, PlayableTile[] tiles, MainActivity activity) { this.position = position; this.rowImpact = rowImpact; this.takenPawn = takenPawn; this.tiles = tiles; this.activity = activity; } @Override public void run() { if (position > 10) { if (position % 5 != 0 && tiles[position - 1 - 4 - rowImpact].getIsTaken() == takenPawn && tiles[position - 1 - 9].getIsTaken() == 0) { activity.checkTreeNodes(position, position - 9, position-4-rowImpact); } if ((position - 1) % 5 != 0 && tiles[position - 1 - 5 - rowImpact].getIsTaken() == takenPawn && tiles[position - 1 - 11].getIsTaken() == 0) { activity.checkTreeNodes(position, position - 11, position-5-rowImpact); } } } } // 调用处代码 public void checkUpTaking(int position, int rowImpact, int takenPawn) { new Thread(new CheckTask(position, rowImpact, takenPawn, playableTile, this)).start(); }
崩溃问题修复提示
如果加final后应用崩溃,优先检查以下两点:
- 如果
playableTile是UI控件,不允许在子线程中读取它的属性 - 如果
checkTreeNodes方法包含更新UI的逻辑,必须切回UI线程执行,可以用runOnUiThread包裹UI更新代码:
runOnUiThread(new Runnable() { @Override public void run() { // 此处写更新UI的逻辑 } });
内容的提问来源于stack exchange,提问作者Franklyn Oreben
相关产品推荐
相关产品推荐

