Normally, if I want to start a new activity I can use
StartActivity(typeof(foo));
This is fine.
I can also set an intent
Intent i = new Intent(this, typeof(foo));
StartActivity(i);
Problem is this. I have Activity A. This fires off Activity B. However, I need to fire off Activity B after using PutExtra on an intent. If I do
Intent i = new Intent(this, typeof(ActivityB));
monodroid gets rightly annoyed as I'm defining a new Intent from within an Activity.
Is there a way to do this
(psuedocode)
[Activity]
public partial class A
{
protected override void OnCreate(Bundle savedInstance)
{
SetContentView(Resource.Layout.layout);
Button btnClick = FindViewById<Button>(Resource.Id.btnClicky);
btnClick.Click += new EventHandler(button_click);
}
private void button_Click(object s, EventArgs e)
{
Intent i = new Intent(this, typeof(B)); // <- gets annoyed
i.PutExtra("foo", 1);
i.PutExtra("bar", true);
StartActivity(i);
}
}
Any help here would be appreciated.
PFJ
Looks like I needed to add a using which wasn't needed elsewhere.
Thanks for the answers and sorry for the delay in getting back :)
Promise that in future, I'll make sure things are clearer.
Related
MainView include InformationCOmponent:
#Push
#Route
public class MainView extends VerticalLayout {
InformationComponent infoComponent;
public MainView(#Autowired StudentRepository studentRepo, #Autowired Job jobImportCsv, #Autowired JobLauncher jobLauncher, #Value("${file.local-tmp-file}") String inputFile) {
[...] // some stuffs
infoComponent = new InformationComponent(studentRepo);
add(infoComponent);
}
//update when job process is over
private void uploadFileSuccceed() {
infoComponent.update(myUploadComponent.getFile());
}
InformationComponent:
public class InformationComponent extends HorizontalLayout {
StudentRepository studentRepo;
Label nbLineInFile = new Label();
VerticalLayout componentLeft = new VerticalLayout();;
VerticalLayout componentRight = new VerticalLayout();;
public InformationComponent(StudentRepository studentRepo) {
[...] // some init and style stuff
addLine("Nombre de lignes dans le fichier", nbLineInFile);
}
private void addLine(String label, Label value) {
componentLeft.add(new Label(label));
componentRight.add(value);
}
public void update(File file) {
try {
long nbLines = Files.lines(file.toPath(), Charset.defaultCharset()).count();
System.out.println("UPDATED! " +nbLines); // value is display in console ok!
UI.getCurrent().access(() -> nbLineInFile.setText(nbLines)); // UI is not updated!!
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
When I call InformationComponent from MainView the Label is not update in the browser.
UI.getCurrent().access(() -> nbLineInFile.setText(nbLines))
also try wwith #Push(PushMode.MANUAL) and ui.push(); but doesn't work either...
Complete source code is here: https://github.com/Tyvain/ProcessUploadedFile-Vaadin_SpringBatch/tree/push-not-working
I suspect the problem here is that uploadFileSuccceed() is run from a background thread, in which case UI.getCurrent() will return null. This would cause a NullPointerException that either kills the background thread or alternatively the exception is caught and silently ignored by the caller. Another alternative is that uploadFileSuccceed() happens through a different browser window and thus also a different UI instance, which means that the changes would be pushed in the context of the wrong UI.
For exactly these reasons, UI.getCurrent().access(...) is generally an anti pattern, even though it's unfortunately quite widely used in old examples.
You can check whether this is the cause of your problem by logging the value of UI.getCurrent() in the beginning of the update method, and comparing that to the value of UI.getCurrent() e.g. in the constructor of InformationComponent.
To properly fix the problem, you should pass the correct UI instance through the entire chain of events originating from whatever triggers the background processing to start. You should also note that it might be tempting to use the getUI() method that is available in any Component subclass, but that method is not thread safe and should thus be avoided in background threads.
As a final notice, I would recommend using the Span or Text component instead of Label in cases like this. In Vaadin 10, the Label component has been changed to use the <label> HTML element, which means that it's mainly intended to be used as the label of an input component.
Based on information provided by Leif you should do something like the following example.
At runtime, when this HorizontalLayout subclass object is attached to a parent UI object, its onAttach method is called. At that point we can remember the UI by storing its reference is a member variable named ui. Actually, an Optional<UI> is returned rather than a UI object, so we need to test for null, though it should never be null at point of onAttach.
public class InformationComponent extends HorizontalLayout {
UI ui;
StudentRepository studentRepo;
Label nbLineInFile = new Label();
VerticalLayout componentLeft = new VerticalLayout();;
VerticalLayout componentRight = new VerticalLayout();;
public InformationComponent(StudentRepository studentRepo) {
[...] // some init and style stuff
addLine("Nombre de lignes dans le fichier", nbLineInFile);
}
private void addLine(String label, Label value) {
componentLeft.add(new Label(label));
componentRight.add(value);
}
public void update(File file) {
try {
long nbLines = Files.lines(file.toPath(), Charset.defaultCharset()).count();
System.out.println("UPDATED! " +nbLines); // value is display in console ok!
this.ui.access(() -> nbLineInFile.setText(nbLines)); // UI is not updated!!
} catch (IOException e) {
throw new RuntimeException(e);
} catch (UIDetachedException e) {
// Do here what is needed to do if UI is no longer attached, user has closed the browser
}
#Override // Called when this component (this `HorizontalLayout`) is attached to a `UI` object.
public void onAttach() {
ui = this.getUI().orElseThrow( () -> new IllegalStateException("No UI found, which should be impossible at point of `onAttach` being called.") );
}
From the detailpage's view I try to push a new page on and I get this error: **
System.InvalidOperationException: Page must not already have a parent.
I keep trying different things but nothing works. Is there a way to push a page onto it, I mean, the detailpage is a navigationpage but it is a detailpage. Any and all help is much appreciated.
I am using xamarin forms labs ViewFactory.
//app.cs GetMainPage
var rootPage = ViewFactory.CreatePage<HomeVM>();
//in HomeView.xaml.cs, setting the detailpage to the list of messages
Detail = new NavigationPage(ViewFactory.CreatePage<MessagesVM>());
//This is in the MessagesView to show an individual message with a back button to the list of messages
Navigation.PushAsync(ViewFactory.CreatePage<MessageDetailVM>());
If you already have a NavigationPage, do not create another one to wrap your Detail instance in.
Detail = iewFactory.CreatePage<MessagesVM>();
Navigation.PushAsync(ViewFactory.CreatePage<MessageDetailVM>());
On my part, I also having the same error using MessagingCenter, but also solve it by unsubscribing/disposing after page closing/OnDisappearing.
Hope it helps.
public partial class MainPage : MasterDetailPage
{
public MainPage()
{
InitializeComponent();
MasterBehavior = MasterBehavior.Popover;
MessagingCenter.Subscribe<NavigationPage>(this, "Navigate", (pageItem) =>
{
Detail = pageItem;
IsPresented = false;
});
MessagingCenter.Subscribe<string>(this, "Logout", (s) =>
{
Application.Current.MainPage = new LoginPage("", "");
});
}
protected override void OnDisappearing()
{
MessagingCenter.Unsubscribe<NavigationPage>(this, "Navigate");
MessagingCenter.Unsubscribe<string>(this, "Logout");
base.OnDisappearing();
}
}
Unfortunately, I encountered this error again, and I solve it by using this setting the NavigationPage Parent property to null.
MessagingCenter.Subscribe<NavigationPage>(this, "Navigate", (pageItem) =>
{
pageItem.Parent = null; //solution
Detail = pageItem;
IsPresented = false;
});
Both answers before mine are pointing to the right direction, so this is just an addition. The key is in fact to not create the NavigationPage/NavigationView again.
In my project, I am using static objects for the MasterDetailPage, the NavigationView and the corresponding ViewModels in Xamarin Forms App class;
I am only creating an instance if their Value is null, which is likely to happen only if the app was closed before (no matter if closed by the user or the OS). If the app is still running (resumed), I am just using the already existing objects to restore the state.
This solved all these problems for me, and I hope it is helpful for someone else.
Try using the Navigation property of the Detail object like this:
Detail.Navigation.PushAsync(page);
To push a new page on my Detail I use the code below:
Note there I'm using a ListView with page options in MasterDetailPage
private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MainMenuItem;
if (item == null)
return;
var page = (Page)Activator.CreateInstance(item.TargetType);
//Detail = new NavigationPage(page);
Detail.Navigation.PushAsync(page);
IsPresented = false;
MasterPage.ListView.SelectedItem = null;
}
I have the following Codename One code for accessing a network resource. It is almost an exact copy of the Codename One tutorial for this use case.
public void executeRequest(){
String url = "http://www.random.net";
InfiniteProgress prog = new InfiniteProgress();
final Dialog dlg = prog.showInifiniteBlocking();
ConnectionRequest r = new ConnectionRequest() {
#Override
protected void postResponse() {
//handle changes to my form
}
#Override
protected void readResponse(InputStream input)
throws IOException {
//handle parsing data
}
#Override
protected void handleIOException(IOException err) {
super.handleIOException(err);
}
};
r.setUrl(url);
r.setPost(false);
r.addArgument("arg", "2");
r.setDuplicateSupported(true);
r.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueue(r);
}
The first time I run it - no problem. If I try to "refresh" my data by calling the same method over again, the app will hang up with the InfiniteProgress dialog spinning forever. Its almost like the first network request is not ever really completing, and then the second one kind of conflicts. Any ideas what I'm doing wrong?
By default duplicate requests to the exact same URL are disabled, try invoking setDuplicatesSuppotred(true) on the connection request.
For future reference, what fixed this for me was to use
NetworkManager.getInstance().addToQueueAndWait(r);
instead. That cleared up most of my problems.
I stucked with the same problem and none of solutions worked. However, I did it this way:
final NetworkManager nm = NetworkManager.getInstance();
nm.setTimeout(3000);
then
protected void postResponse() {
...
nm.shutdown();
}
and call was made as
nm.addToQueueAndWait(request);
Maybe the fact that NetworkManager was made final did the job, but I put "shutdown" just for sure. It worked for me
I've read several related posts and even posted and answer here but it seems like I was not able to solve the problem.
I have 3 Activities:
Act1 (main)
Act2
Act3
When going back and forth Act1->Act2 and Act2->Act1 I get no issues
When going Act2->Act3 I get no issues
When going Act3->Act2 I get occasional crashes with the following error: java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#.... This is a ListView cursor.
What I tried:
1. Adding stopManagingCursor(currentCursor);to the onPause() of Act2 so I stop managing the cursor when leaving Act2 to Act3
protected void onPause()
{
Log.i(getClass().getName() + ".onPause", "Hi!");
super.onPause();
saveState();
//Make sure you get rid of the cursor when leaving to another Activity
//Prevents: ...Unable to resume activity... trying to requery an already closed cursor
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
stopManagingCursor(currentCursor);
}
When returning back from Act3 to Act2 I do the following:
private void populateCompetitorsListView()
{
ListAdapter currentListAdapter = getListAdapter();
Cursor currentCursor = null;
Cursor tournamentStocksCursor = null;
if(currentListAdapter != null)
{
currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor();
if(currentCursor != null)
{
//might be redundant, not sure
stopManagingCursor(currentCursor);
// Get all of the stocks from the database and create the item list
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
((SimpleCursorAdapter)currentListAdapter).changeCursor(tournamentStocksCursor);
}
else
{
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
}
}
else
{
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
}
startManagingCursor(tournamentStocksCursor);
//Create an array to specify the fields we want to display in the list (only name)
String[] from = new String[] {StournamentConstants.TblStocks.COLUMN_NAME, StournamentConstants.TblTournamentsStocks.COLUMN_SCORE};
// and an array of the fields we want to bind those fields to (in this case just name)
int[] to = new int[]{R.id.competitor_name, R.id.competitor_score};
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter tournamentStocks = new SimpleCursorAdapter(this, R.layout.competitor_row, tournamentStocksCursor, from, to);
//tournamentStocks.convertToString(tournamentStocksCursor);
setListAdapter(tournamentStocks);
}
So I make sure I invalidate the cursor and use a different one. I found out that when I go Act3->Act2 the system will sometimes use the same cursor for the List View and sometimes it will have a different one.
This is hard to debug and I was never able to catch a crashing system while debugging. I suspect this has to do with the time it takes to debug (long) and the time it takes to run the app (much shorter, no pause due to breakpoints).
In Act2 I use the following Intent and expect no result:
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
Intent intent = new Intent(this, ActivityCompetitorDetails.class);
intent.putExtra(StournamentConstants.App.competitorId, id);
intent.putExtra(StournamentConstants.App.tournamentId, mTournamentRowId);
startActivity(intent);
}
Moving Act1->Act2 Act2->Act1 never gives me trouble. There I use startActivityForResult(intent, ACTIVITY_EDIT); and I am not sure - could this be the source of my trouble?
I would be grateful if anyone could shed some light on this subject. I am interested in learning some more about this subject.
Thanks,D.
I call this a 2 dimensional problem: two things were responsible for this crash:
1. I used startManagingCursor(mItemCursor); where I shouldn't have.
2. I forgot to initCursorAdapter() (for autocomplete) on onResume()
//#SuppressWarnings("deprecation")
private void initCursorAdapter()
{
mItemCursor = mDbHelper.getCompetitorsCursor("");
startManagingCursor(mItemCursor); //<= this is bad!
mCursorAdapter = new CompetitorAdapter(getApplicationContext(), mItemCursor);
initItemFilter();
}
Now it seems to work fine. I hope so...
Put this it may work for you:
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
orderCursor.requery();
}
This also works
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
startManagingCursor(Cursor);
}
I have the following test code (based on standard monodroid HelloWorld)
namespace TestGREF
{
[Activity (Label = "TestGREF", MainLauncher = true)]
public class Activity1 : Activity
{
int count = 1;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
Button button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate {
button.Text = string.Format ("{0} clicks!", count++);
for(int i=0;i<10000;i++){
new Java.Lang.Object(new System.IntPtr(i));
//...some stuff here. Instead of Java.Lang.Object may be
//something much more useful.
}
//If uncomment here, looks ok
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
};
}
}
}
If I click the button 5-6 times, application crashes.
I know this happens because of global refences (GREF) limit (described here, "Unexpected NullReferenceExceptions" section). The question is: what to do with it? What is the best practice? If possible, with code example please
If uncomment GC.Collect() call, all seems working, but calling GC too often is too exspensive for performance. Another popular design is to put new statement put of loop, but it is not always possible cause of program logic.
Any more ideas?
You need to release all unmanaged objects when they no longer needed. All classes that inherits from Android.Runtime.IJavaObject also inherits IDisposable so you need to dispose them.
Here is part from my project
private Spinner _spType;
private ArrayAdapter _arrayAdapter;
protected override void OnCreate(Android.OS.Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
_spType = FindViewById<Spinner>(Resource.Id.spinnerType);
_arrayAdapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleSpinnerItem, new[] {"1","2","3","4","5"});
_spType.Adapter = _arrayAdapter;
}
public override void Finish()
{
if (_spType != null)
_spType.Dispose();
if (_arrayAdapter != null)
_arrayAdapter.Dispose();
base.Finish();
}
for(int i=0;i<10000;i++){
var obj = new Java.Lang.Object(new System.IntPtr(i));
//...some stuff here. Instead of Java.Lang.Object may be
//something much more useful.
obj.Dispose(); //Deletes an object and GREF too.
//Cannot be used if object is still used in dalvik VM
}
If you cannot use Dispose() (for example, unmanaged object is a part of layout, which will be used by android lated, but not by C# code), use GC.Collect() wisely. GC.Collect() kills all the GREFs to variables, which are out of usage by Mono Environment and out of current scope.
Here is article about GC and memory management in monodroid. It can be helpful for you
http://docs.xamarin.com/android/advanced_topics/garbage_collection