Sometimes when my Xamarin.Android app gets killed and the application needs to recreate its main activity some layouts are not available and the screen displays no views.
By pressing the device back button and then reopening the application, its main activity gets correctly instantiated and displayed.
This is the code responsible for loading the used layouts:
// Returns null whenever trying to recreate views right after the application got killed by Android (Due to some multi-tasking reasons). Otherwise, it returns valid instances.
var layout = DroidResources.LoadLayout (context, parent, layoutName);
See next screenshots (texts were omitted):
Before/after the system automatically recreates the activity.
I am using:
Xamarin.Android 4.10
MvvmCross 3.0.13 - Android.Dialog
May some of you point me a direction about how can I fix this issue?
I am new to both described technologies. Any help will be appreciated.
Logs retrieved from the DDMS application:
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): ViewElement: Failed to load resource: MenuSeparator
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): LoadLayout failed: You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
E/Android.Dialog(9171): ViewElement: Failed to load resource: MenuSeparator
I would like to understand why the DroidResources class was initialized when normally opening the application but it is not initialized when the application is just returning from the background. I guess during the background time Android forced its instance to be garbage collected but not the entire application.
I also think I may be wrongly initializing it, that is my Setup.cs file:
/// <summary>
/// MvvmCross setup class.
/// Allows Android.Dialog usage during application lifecycle
/// </summary>
public class Setup : MvxAndroidDialogSetup
{
public Setup (Context applicationContext)
: base (applicationContext)
{
}
protected override IMvxApplication CreateApp ()
{
return new MvvmCrossApplication ();
}
}
// MvvmCross internal application.
public class MvvmCrossApplication : MvxApplication
{
public override void Initialize ()
{
// Setup initial application view which will be loaded as soon as the MvvmCross framework
// initialization has been completed.
RegisterAppStart<ApplicationViewModel>();
}
}
//
// These next classes ApplicationViewModel and ApplicationView are auxiliary classes
// needed by the MvvmCross framework to ensure that others application classes are loaded
// only after the MvvmCross initilization has been completed.
public class ApplicationViewModel : MvxViewModel
{
}
[Activity (Theme = "#style/Theme.Transparent", NoHistory = true)]
public class ApplicationView : MvxActivity
{
protected override void OnResume ()
{
base.OnResume ();
// Start the application main activity.
StartActivity (typeof(ApplicationMainActivity));
}
}
Until now I am just using the Android.Dialog dlls from the MvvmCross library. But I am going to start accordingly using ViewModels as soon as possible.
Here follows the ApplicationMainActivity source code:
/// <summary>
/// Application activity.
/// </summary>
/// <remarks>A transparent activity used to recreate current application screens workflow.</remarks>
[Activity (Theme = "#style/Theme.Transparent", NoHistory = true)]
public class ApplicationMainActivity : ClientUserActivity
{
#region Lifecycle methods
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
if (ClientUser.IsLoggedIn ()) {
if (ClientUser.ShouldShowWizard ()) {
// Show the wizard screen
StartActivity (wizardActivity);
} else {
// Show the main menu screen
StartActivity (typeof(NavigationDrawerActivity));
}
} else {
// Show the welcome screen
StartActivity (typeof(WelcomeActivity));
}
}
...
As you post layer-upon-layer of code in this question, I'm getting increasingly more confused about whether you are actually using MvvmCross.
If you are just using the MvvmCross Android.Dialog port - and not using MvvmCross - then you need to find a way to do for what the error message is asking you to do:
You must call DroidResources.Initialize(Resource.Layout) before using Dialogs
In a "normal" MvvmCross application, this problem is avoided because any MvxActivity will ensure Setup is created and initialised before OnCreate is complete - and Setup includes:
protected virtual void InitializeDialogBinding()
{
var finder = Mvx.Resolve<IMvxAppResourceTypeFinder>();
var resourceType = finder.Find();
var nestedResourceType = resourceType.GetNestedTypes().FirstOrDefault(x => x.Name == "Layout");
DroidResources.Initialize(nestedResourceType);
}
see https://github.com/MvvmCross/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Dialog.Droid/MvxAndroidDialogSetup.cs#L40
If you want to implement non-MvxActivity Activities, then you'll need to find some other way to ensure setup is started and/or droid resources are loaded.
Related
This problem is driving me crazy.
In principle the code below works correctly.
The authenticator that is called is a class defined in its own module (Authenticator.swift).
The code below is a method of the viewModel that conforms ObservableObject protocol.
The observed properties self.error and self.goToHomeView are properties published from the viewModel.
The authenticator method authenticates with FaceID. If authentication succeeds, error is nil, then the observed variable goToHomeView is set to true to switch to the Home view.
If error is not nil, then self.error is assigned which is of type LocalizedError and that triggers an alert in the view.
The problem is that an annoying warning appears all the time saying: "Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.”
I would like to know if anyone knows a technically valid way to modify values from the main thread from a secondary thread running in background.
Task {
await authenticator.authenticate { error in
if let error = error {
self.error = error
} else {
self.goToHomeView = true
}
}
}
I have two different View Models with their own fragments, and I want my arrayListB in ViewModelB have the same value of my arrayListA in ViewModelA.
I am sure this may help you to do that!
Architecture Components provides ViewModel helper class for the UI controller that is responsible for preparing data for the UI. ViewModel objects are automatically retained during configuration changes so that data they hold is immediately available to the next activity or fragment instance. For example, if you need to display a list of users in your app, make sure to assign responsibility to acquire and keep the list of users to a ViewModel, instead of an activity or fragment, as illustrated by the following sample code:
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData<List<User>>().also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
for more information see this :
(https://developer.android.com/topic/libraries/architecture/viewmodel?msclkid=bf393d12ce9011eca291710e2d69c5af)
I would like to send a binary file via an appended MVCResourceCommand I coded for the native journal portlet. But the program is unable to use the OutputStream provided by the resource request.
IOUtils.copy( input, response.getPortletOutputStream() );
Considering:
The code works perfectly on StrutsActions
In custom portlets, it also works
In StrutsActions:
IOUtils.copy( input, response.getOutputStream() );
However, the code throws an IllegalStateException, saying that the writer is being used when I call response.getOutputStream().
I know we can not mix these two
The code is not attempting to do so
I wonder if Liferay is doing something with that request before it reaches my extension of BaseMVCResourceCommand, this is specifically for that native portlet.
I checked the preview feature for a webcontect item, but its URL is for the view mode.
The URL is created from a portlet:resourceURL tag inserted through a JSP fragment and the command is in its own OSGi module.
For sure, the URL is correct and the command logs that it was hit, but the exception is thrown afterwards.
The portlet I am trying to change is the:
"com_liferay_journal_web_portlet_JournalPortlet"
Any thoughts?
PS: I know about the Servlet and Portlet ResponseUtils. but they also eventually try getting the stream, leadin to the same exception.
#Component( immediate = true,
property = {
"javax.portlet.name=" + JOURNAL, "mvc.command.name=/command"
},
service = MVCResourceCommand.class )
public class Resource extends BaseMVCResourceCommand {
#Override
public void doServeResource( ResourceRequest request, ResourceResponse response ) throws PortletException {
try {
response.getPortletOutputStream();
}
catch ( Exception e ) {
throw new PortletException( e );
}
}
}
Caused by: java.lang.IllegalStateException: Unable to obtain OutputStream because Writer is already in use
at com.liferay.portlet.MimeResponseImpl.getPortletOutputStream(MimeResponseImpl.java:75)
Update:
It seems this is the source of my issues (PortletURLImpl), still looking for a solution though:
if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
_copyCurrentRenderParameters = true;
}
When the URL is created it comes with all sources of garbage from the render phase. Including an MVCPath
I'm working with the revit api, and one of its problems is that it locks the .dll once the command's run. You have to exit revit before the command can be rebuilt, very time consuming.
After some research, I came across this post on GitHub, that streams the command .dll into memory, thus hiding it from Revit. Letting you rebuild the VS project as much as you like.
The AutoReload Class impliments the revit IExteneralCommand Class which is the link into the Revit Program.
But the AutoReload class hides the actual source DLL from revit. So revit can't lock the DLL and lets one rebuilt the source file.
Only problem is I cant figure out how to implement it, and have revit execute the command. I guess my C# general knowledge is still too limited.
I created an entry in the RevitAddin.addin manifest that points to the AutoReload Method command, but nothing happens.
I've tried to follow all the comments in the posted code, but nothing seems to work; and no luck finding a contact for the developer.
Found at: https://gist.github.com/6084730.git
using System;
namespace Mine
{
// helper class
public class PluginData
{
public DateTime _creation_time;
public Autodesk.Revit.UI.IExternalCommand _instance;
public PluginData(Autodesk.Revit.UI.IExternalCommand instance)
{
_instance = instance;
}
}
//
// Base class for auto-reloading external commands that reside in other dll's
// (that Revit never knows about, and therefore cannot lock)
//
public class AutoReload : Autodesk.Revit.UI.IExternalCommand
{
// keep a static dictionary of loaded modules (so the data persists between calls to Execute)
static System.Collections.Generic.Dictionary<string, PluginData> _dictionary;
String _path; // to the dll
String _class_full_name;
public AutoReload(String path, String class_full_name)
{
if (_dictionary == null)
{
_dictionary = new System.Collections.Generic.Dictionary<string, PluginData>();
}
if (!_dictionary.ContainsKey(class_full_name))
{
PluginData data = new PluginData(null);
_dictionary.Add(class_full_name, data);
}
_path = path;
_class_full_name = class_full_name;
}
public Autodesk.Revit.UI.Result Execute(
Autodesk.Revit.UI.ExternalCommandData commandData,
ref string message,
Autodesk.Revit.DB.ElementSet elements)
{
PluginData data = _dictionary[_class_full_name];
DateTime creation_time = new System.IO.FileInfo(_path).LastWriteTime;
if (creation_time.CompareTo(data._creation_time) > 0)
{
// dll file has been modified, or this is the first time we execute this command.
data._creation_time = creation_time;
byte[] assembly_bytes = System.IO.File.ReadAllBytes(_path);
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assembly_bytes);
foreach (Type type in assembly.GetTypes())
{
if (type.IsClass && type.FullName == _class_full_name)
{
data._instance = Activator.CreateInstance(type) as Autodesk.Revit.UI.IExternalCommand;
break;
}
}
}
// now actually call the command
return data._instance.Execute(commandData, ref message, elements);
}
}
//
// Derive a class from AutoReload for every auto-reloadable command. Hardcode the path
// to the dll and the full name of the IExternalCommand class in the constructor of the base class.
//
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
public class AutoReloadExample : AutoReload
{
public AutoReloadExample()
: base("C:\\revit2014plugins\\ExampleCommand.dll", "Mine.ExampleCommand")
{
}
}
}
There is an easier approach: Add-in Manager
Go to Revit Developer Center and download the Revit SDK, unzip/install it, the check at \Revit 2016 SDK\Add-In Manager folder. With this tool you can load/reload DLLs without having to modify your code.
There is also some additional information at this blog post.
this is how you can use the above code:
Create a new VS class project; name it anything (eg. AutoLoad)
Copy&Paste the above code in-between the namespace region
reference revitapi.dll & revitapiui.dll
Scroll down to AutoReloadExample class and replace the path to point
your dll
Replace "Mine.ExampleCommand" with your plugins namespace.mainclass
Build the solution
Create an .addin manifest to point this new loader (eg.
AutoLoad.dll)
your .addin should include "FullClassName" AutoLoad.AutoReloadExample
This method uses reflection to create an instance of your plugin and prevent Revit to lock your dll file! You can add more of your commands just by adding new classes like AutoReloadExample and point them with seperate .addin files.
Cheers
So, here's my code (for simplicity I put it all into one file):
#include <afxwin.h>
#include "resource.h"
class CMainWnd : public CFrameWnd
{
};
class CApp : public CWinApp
{
public:
virtual BOOL InitInstance()
{
CMainWnd* wnd = new CMainWnd();
if (!wnd->Create(0, _T("test"))) return FALSE;
m_pMainWnd = wnd;
wnd->ShowWindow(SW_SHOW);
wnd->UpdateWindow();
return TRUE;
}
};
CApp app;
It creates simple window with default parameters and title "test". Works perfectly. But then, I want to load my window from resources, so I can put something to it. I replace:
if (!wnd->Create(0, _T("test"))) return FALSE;
with
if (!wnd->LoadFrame(IDD_CLIENTWINDOW)) return FALSE;
(IDD_CLIENTWINDOW is ID of my dialog in resources). LoadFrame returns FALSE, and program exits. There's debug message in output:
Warning: failed to load menu for CFrameWnd.
But there's no menu in dialog IDD_CLIENTWINDOW that I created. How do I load frame correctly? What am I missing?
What you are trying won't work. LoadFrame() with the ID of a dialog won't load a dialog. If you want to use a dialog, make CWnd derive from CDialog or use a view derived from CFormView. Your call to LoadFrame is failing because you don't have a menu resource with the correct ID. But, you're not really trying to do that.
I recommend you use the AppWizard to generate a new application that is either dialog based or CFormView based and see what kind of code is generated. You can look at the code to see what you really want to do.