• Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Package structure / AlertDialog problem

 
Jan Seer
Greenhorn
Posts: 17
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello,

I am currently working on a small game. Android should be the main platform but I want to keep the possibility port it to Windows (for desktop / laptop) and maybe iOS and Windows Phone.

Therefore, I currently use three packages in my project:
  • The basic package contains all classes that use only plain Java, for example the game logic.
  • The package with the Android views
  • The third package is for the system specific functions, e.g. the load & save paths


  • My idea is that I can use the basic package on every system and only have to rewrite the GUI and the system specific package.

    My problem concerning the AlertDialog is now, that I want to use a generic Error class in my basic package. Something like this:

    'showErrorMessage' should be a static method of the Class 'ErrorManagement' in the system specific package. So I can always call it from my basic package, without having to know on which system I am working. All the basic package sees is the return value of the function which shows which button was selected.

    The specific Android problem is now, that I cannot open an AlertDialog from another package than the views.
    My current code in 'ErrorManagement':


    I cannot support the 'context' variable the Builder needs. I know that I can get the context in a view class by using 'getContext()', but that does not work when I use it in the 'ErrorManagement' class or if I want to send it as additional input value from the basic package. It simply does not know the method.
    I also tried to have a static Context variable in 'ErrorManagement', which is filled from my main view during its 'onCreate()' method. That does not work either. It seems that Context variables cannot be send to other packages. At least I get an error message saying that new AlertDialog.Builder(context) failed due to the missing context.

    Has anybody an idea how to solve this problem? I know it can be a bit tricky to understand my thoughts, that is why I also presented my package structure. Maybe you have some additional suggestions for packaging, too?
     
    Steve Luke
    Bartender
    Posts: 4181
    22
    IntelliJ IDE Java Python
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    It is really hard to tell what the problem is, because we know nothing about the code which does any of the work, nor do we know the error you are getting. So any suggestions are a shot in the dark. It most likely is either different instances/class loaders being used when setting the context versus using it, or a timing issue, where the error message is sent before the code which sets the context. To fix that, try putting break points at key points in your code, and run the application in debug mode, looking for order of execution and the values of key objects. If you don't know how to do that, then pepper your code with identifying LogCat messages so you know when things occur by watching the LogCat output.

    Here is a scheme that should work - no details just an outline of how things might be put together, such that your shared package (which could be used as a library rather than just a package) would have access to the ErrorManager and need no knowledge about anything platform specific.

    First, lets do some architecture. The first thing we will need is an ErrorManagement construct. If there is not going to be shared code among the different platform implementations, then you minds well make it an interface. If there will be shared code, use an abstract class:

    We will use a factory to distribute ErrorManagement instances to anyone who wants one. We want the factory to be generic, not platform specific, so we will create another interface which has a method which will deliver platform-specific ErrorManagement implementations to the factory (the factory is a single, go-to location anyone can reach to get an ErrorManagement instance, this interface will be used to generate platform-specific implementations):

    Now we have everything we need for the factory that can be used to get ErrorManagement instances upon demand:

    Any code in any package would use the following code to get an ErrorManagement instance and call its showErrorMessage() method:

    Now we need the Android specific stuff. First, we need an Android specific ErrorManagement implementation that knows how to build the AlertDialog...

    Then we need to create an ErrorManagementSource implementation to create and deliver the AndroidErrorManagement to the factory upon need. Since AndroidErrorManagement needs a Context, we will need to make sure it is created in a location that has access to an Android context. We also need to make sure it is created before any chance of needing to use it. In a desktop application, that might be the main() method or the constructor of your main class. But for Android, the Application instance's onCreate() method is a good place to start:


    Now if you setup your manifest properly to use the Application class you created above as the app's Application scope, the AndroidErrorManagement instance will be created, an ErrorManagementSource which delivers that instance to all askers will be created and passed to ErrorManagementFactory, and the ErrorManagementFactory will be ready to distribute the AndroidErrorManagement instance to anyone who calls ErrorManagementFactory.getErrorManagement() as soon as the application is created (barring any typos or other errors).
     
    Jan Seer
    Greenhorn
    Posts: 17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Wow, thank you for the really detailed answer. I am pretty busy at my job currently, but I will try to implement it as soon as possible.
     
    Jan Seer
    Greenhorn
    Posts: 17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Hello, I tried to implement it in a test project. I have a button which should open an AlertDialog when it is clicked. The app starts and I have no compiler error but when I click the button, the app collapses and the LogCat gives me the following error: java.lang.IllegalStateException: Could not execute method of the activity

    I found and corrected some smaller flaws in the code you provided (e.g. somwhere was a return value type missing), but I do not understand the problem I encountered now. Could you help me again, please?

    Here is my code:

    Basic package




    Android specific package


    Activity

    Manifest file
     
    Steve Luke
    Bartender
    Posts: 4181
    22
    IntelliJ IDE Java Python
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Can you post the full stack trace of the error message? The message says there is a problem executing a method on the activity, but you need to find out which method is being called and from where (and from that you can hopefully figure out the why it is being called and what is making it fail)
     
    Jan Seer
    Greenhorn
    Posts: 17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Here is the LogCat data:

    08-27 12:30:44.629: D/libEGL(2032): loaded /system/lib/egl/libEGL_emulation.so
    08-27 12:30:44.629: D/(2032): HostConnection::get() New Host Connection established 0xb8314410, tid 2032
    08-27 12:30:44.659: D/libEGL(2032): loaded /system/lib/egl/libGLESv1_CM_emulation.so
    08-27 12:30:44.689: D/libEGL(2032): loaded /system/lib/egl/libGLESv2_emulation.so
    08-27 12:30:44.739: W/EGL_emulation(2032): eglSurfaceAttrib not implemented
    08-27 12:30:44.749: D/OpenGLRenderer(2032): Enabling debug mode 0
    08-27 12:31:02.230: D/dalvikvm(2032): GC_FOR_ALLOC freed 104K, 6% free 2679K/2840K, paused 59ms, total 63ms
    08-27 12:31:02.400: D/AndroidRuntime(2032): Shutting down VM
    08-27 12:31:02.400: W/dalvikvm(2032): threadid=1: thread exiting with uncaught exception (group=0xb1eb2648)
    08-27 12:31:02.410: E/AndroidRuntime(2032): FATAL EXCEPTION: main
    08-27 12:31:02.410: E/AndroidRuntime(2032): java.lang.IllegalStateException: Could not execute method of the activity
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.View$1.onClick(View.java:3633)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.View.performClick(View.java:4240)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.View$PerformClick.run(View.java:17721)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.os.Handler.handleCallback(Handler.java:730)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.os.Handler.dispatchMessage(Handler.java:92)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.os.Looper.loop(Looper.java:137)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.app.ActivityThread.main(ActivityThread.java:5103)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at java.lang.reflect.Method.invokeNative(Native Method)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at java.lang.reflect.Method.invoke(Method.java:525)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at dalvik.system.NativeStart.main(Native Method)
    08-27 12:31:02.410: E/AndroidRuntime(2032): Caused by: java.lang.reflect.InvocationTargetException
    08-27 12:31:02.410: E/AndroidRuntime(2032): at java.lang.reflect.Method.invokeNative(Native Method)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at java.lang.reflect.Method.invoke(Method.java:525)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.View$1.onClick(View.java:3628)
    08-27 12:31:02.410: E/AndroidRuntime(2032): ... 11 more
    08-27 12:31:02.410: E/AndroidRuntime(2032): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.ViewRootImpl.setView(ViewRootImpl.java:563)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:269)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.app.Dialog.show(Dialog.java:281)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at systemSpecificPackage.AndroidErrorManagement.showErrorMessage(AndroidErrorManagement.java:50)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at com.example.alertdialogtest.MainActivity.displayErrorMessage(MainActivity.java:46)
    08-27 12:31:02.410: E/AndroidRuntime(2032): ... 14 more


    I also tried if I made a mistake when I call the method in the activity. Therefore I used the following code. Everything worked as it should. So the error seems to come from the line which is commented out in this code fragment:

     
    Steve Luke
    Bartender
    Posts: 4181
    22
    IntelliJ IDE Java Python
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Jan Seer wrote:
    08-27 12:31:02.410: E/AndroidRuntime(2032): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.ViewRootImpl.setView(ViewRootImpl.java:563)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:269)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at android.app.Dialog.show(Dialog.java:281)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at systemSpecificPackage.AndroidErrorManagement.showErrorMessage(AndroidErrorManagement.java:50)
    08-27 12:31:02.410: E/AndroidRuntime(2032): at com.example.alertdialogtest.MainActivity.displayErrorMessage(MainActivity.java:46)
    08-27 12:31:02.410: E/AndroidRuntime(2032): ... 14 more


    Okay, from this part of the error message, it looks like the Application as a Context doesn't provide the correct display context required to display an alert. try to change the AndroidErrorManagement class to below(ish):


    so this takes the application context that you pass in, and, when the display is needed, uses the System Service to get the current WindowManager, which has access to the Display, which can be used to create a proper DisplayContext. I isolated the new work into its own method, and changed the initial context name to be more descriptive (uiContext doesn't make sense now, since we know it can't be used for UI). Also note that I intentionally create the context for the display at the time of making the dialog because we don't know how the display may have changed from construction of the class to the dialog request, or from one request to another. So this makes sure the dialog request keeps up with the display (I think)
     
    Jan Seer
    Greenhorn
    Posts: 17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Does not work either... The error message looks quite the same:
    LogCat wrote:
    08-29 09:47:01.761: E/AndroidRuntime(1964): FATAL EXCEPTION: main
    08-29 09:47:01.761: E/AndroidRuntime(1964): java.lang.IllegalStateException: Could not execute method of the activity
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.view.View$1.onClick(View.java:3633)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.view.View.performClick(View.java:4240)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.view.View$PerformClick.run(View.java:17721)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.os.Handler.handleCallback(Handler.java:730)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.os.Handler.dispatchMessage(Handler.java:92)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.os.Looper.loop(Looper.java:137)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.app.ActivityThread.main(ActivityThread.java:5103)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at java.lang.reflect.Method.invokeNative(Native Method)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at java.lang.reflect.Method.invoke(Method.java:525)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at dalvik.system.NativeStart.main(Native Method)
    08-29 09:47:01.761: E/AndroidRuntime(1964): Caused by: java.lang.reflect.InvocationTargetException
    08-29 09:47:01.761: E/AndroidRuntime(1964): at java.lang.reflect.Method.invokeNative(Native Method)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at java.lang.reflect.Method.invoke(Method.java:525)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.view.View$1.onClick(View.java:3628)
    08-29 09:47:01.761: E/AndroidRuntime(1964): ... 11 more
    08-29 09:47:01.761: E/AndroidRuntime(1964): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.view.ViewRootImpl.setView(ViewRootImpl.java:563)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:269)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at android.app.Dialog.show(Dialog.java:281)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at systemSpecificPackage.AndroidErrorManagement.showErrorMessage(AndroidErrorManagement.java:55)
    08-29 09:47:01.761: E/AndroidRuntime(1964): at com.example.alertdialogtest.MainActivity.displayErrorMessage(MainActivity.java:46)
    08-29 09:47:01.761: E/AndroidRuntime(1964): ... 14 more


    I looked a bit around on the internet and it seems as AlertDialog needs a DisplayContext rather than an ApplicationContext. That is strange, because as far as I understand, the Android documentation says it should work with both.
    Unfortunately, I found many people who complain about not being able to obtain a DisplayContext but I did not find a solution yet.

    Concerning the current problem, I think that the creation of a WindowManager instance also needs an DisplayContext, rather than an Application context. I am not completely sure, but the documentation sounds a bit like this for me:
    WindowManager documentation wrote:public interface
    WindowManager
    implements ViewManager

    android.view.WindowManager

    Class Overview
    The interface that apps use to talk to the window manager.
    Use Context.getSystemService(Context.WINDOW_SERVICE) to get one of these.
    Each window manager instance is bound to a particular Display. To obtain a WindowManager for a different display, use createDisplayContext(Display) to obtain a Context for that display, then use Context.getSystemService(Context.WINDOW_SERVICE) to get the WindowManager.

    The simplest way to show a window on another display is to create a Presentation. The presentation will automatically obtain a WindowManager and Context for that display.


    So I think these two lines do not work, because the Display is required to create the WindowManager:
     
    • Post Reply
    • Bookmark Topic Watch Topic
    • New Topic