2015-06-06 3 views
0

Если я позвоню следующий метод RoboVM с любым ненулевым аргумент:Действительно странно NullPointerException в RoboVM

public static void runOnUiThread(final Runnable runnable) { 
    System.out.println("Inside runOnUiThread():"); 
    System.out.println(" Null-check: "+(runnable==null)); 

    NSOperation operation = new NSOperation() { 

     @Override 
     public void main() { 
      System.out.println("Inside main():"); 
      System.out.println(" Null-check: "+(runnable==null)); 
      runnable.run();     // NullPointerException here?!? How??? 
      System.out.println(" main() completed"); 
     } 
    }; 

    NSOperationQueue.getMainQueue().addOperation(operation);   
} 

выводит:

Inside runOnUiThread(): 
    Null-check: false 
Inside main(): 
    Null-check: true 
java.lang.NullPointerException 
    at RoboVMTools$1.main(RoboVMTools.java) 
    at org.robovm.apple.foundation.NSOperation.$cb$main(NSOperation.java) 
    at org.robovm.apple.uikit.UIApplication.main(Native Method) 
    at org.robovm.apple.uikit.UIApplication.main(UIApplication.java) 
    at Main.main(Main.java) 

Что на земле происходит ??? И что еще более важно, как я могу обойти это?

  • Я пробовал добавить operation.addStrongRef(runnable); прямо сейчас NSOperationQueue.... Нет разницы.
  • Я также попытался переместить анонимный внутренний класс в свой класс, который имеет поле private final для хранения runnable, который передается в его конструктор. Тот же результат.

Я просто пропустил что-то совершенно очевидное ???

ответ

1

Вы имеете право на GC. Ваш экземпляр NSOperation - это сбор мусора, прежде чем операция будет вызвана со стороны Objective-C. Когда NSOperationQueue вызывается на сторону Java, будет создан новый экземпляр вашего анонимного класса NSOperation, который не имеет ссылки на экземпляр Runnable, а скорее null, и результат будет получен от NullPointerException.

, как вы решить его с помощью addStrongRef() является правильным, хотя только mainQueue.addStrongRef(operation) и соответствующие removeStrongRef() вызовы должны быть достаточно:

public static void runOnUiThread(final Runnable runnable) { 

    final NSOperationQueue mainQueue = NSOperationQueue.getMainQueue(); 

    NSOperation operation = new NSOperation() { 

     @Override 
     public void main() { 
      runnable.run(); 
      mainQueue.removeStrongRef(this); 
     } 
    }; 

    mainQueue.addStrongRef(operation); 
    mainQueue.addOperation(operation);  
} 

Это предотвратит operation экземпляра Java (и любые Java объекты достижимы из него подобно Runnable) от GCed до тех пор, пока экземпляр Objective-C NSOperationQueue не будет освобожден. Поскольку Очередь стороны Objective-C является одиночной, она не будет освобождена за время жизни приложения.

Класс RoboVM NSOperationQueue Java предоставляет версию метода addOperation(), который принимает Runnable. При использовании этого метода RoboVM позаботится о том, чтобы сохранить экземпляр Runnable, в то время как он необходим стороне Objective-C для вас. То же самое относится к любому методу, который принимает аннотированный параметр @Block типа Runnable или любой из интерфейсов org.robovm.objc.block.VoidBlock*10 или org.robovm.objc.block.Block*.

Используя этот метод addOperation() ваш код просто становится:

public static void runOnUiThread(Runnable runnable) { 
    NSOperationQueue.getMainQueue().addOperation(runnable);  
} 

PS. GC, используемый RoboVM, не имеет ничего общего с сборщиком мусора Apple, поэтому документы Apple не помогут вам понять такие проблемы.

+0

Это имеет смысл. Спасибо. +1 для 'addOperation (Runnable)'. Я полностью пропустил это! :) Кстати: RoboVM абсолютно потрясающий! Я очень рад, что мне не нужно иметь дело с Objective-C (yuck!)! Вы, ребята, потрясающие! Спасибо огромное! Лучший день в моей жизни будет, когда есть RoboVM-версия, которая может создавать приложения для Windows Phone! ;) Было бы трудно, учитывая инфраструктуру, которую у вас уже есть? Или вы уже работаете над этим? :) –

+0

Я рад слышать, что вам нравится RoboVM! Нет, мы не работаем над поддержкой Windows Phone. Недавно MS анонсировала [Project Astoria] (https://dev.windows.com/en-us/uwp-bridges/project-astoria), которая приведет Android-приложения к Windows Phone. Как только это произойдет, перенос RoboVM на Windows Phone не будет иметь большого смысла. Было бы интересной задачей, хотя ... :-) – ntherning

0

Ну ... Это фиксирует это:

public static void runOnUiThread(final Runnable runnable) { 

    final NSOperationQueue mainQueue = NSOperationQueue.getMainQueue(); 

    NSOperation operation = new NSOperation() { 

     @Override 
     public void main() { 
      runnable.run(); 

      mainQueue.removeStrongRef(runnable); 
      mainQueue.removeStrongRef(this ); 
     } 
    }; 

    mainQueue.addStrongRef(runnable); 
    mainQueue.addStrongRef(operation); 

    mainQueue.addOperation(operation);  
} 

Но не спрашивайте мой, почему это необходимо. Apple docs говорит "In garbage-collected applications, the queue strongly references the operation object." Итак, operation.addStrongRef(runnable);, как я и пытался раньше, должен был быть достаточным, так как объект операции должен ссылаться на очередь в любом случае. Но я думаю, что мир не всегда работает так, как я интерпретирую документацию.

Смежные вопросы