I have an activity to play a game. The user may be resuming from a saved state or initiating a new game. If resuming, the identifier for the game to resume is passed to the activity in the bundle. If it is a new game, that part of the bundle isn't passed, and is therefore null. I currently implement this as follows...
Bundle bundle = this.getIntent().getExtras();
int GameNumberToResume;
boolean StartNewGame = false;
try
{
GameNumberToResume = bundle.getInt("GameToResume");
}
catch (Exception e)
{
System.out.println("Exception Catch, so we start a new game.");
StartNewGame = true;
System.out.println((e.toString()));
}
...and it works. StartNewGame drives the decision tree on if we are starting a new one or resuming a saved one, and if we are resuming, GameNumberToResume has the game ID to resume. However, Android Studio throws the soft warning...
Variable 'GameNumberToResume' is assigned but never accessed.
...because in the parts of the decision tree when I need the value of the game to resume, I pull it straight from the bundle via bundle.getInt("GameToResume").
So here's my question: What should I do different? I can make the warning go away by referencing the variable GameNumberToResume downstream instead of pulling it from the bundle, but it doesn't seem to me like that would change anything. The intent of the warning is to point out that I'm wasting memory, and if I do this, I still have two things in scope that both contain the same value.
Is there a way to detect the absence or presence of "GameToResume" in
the bundle without doing a variable assignment in a try/catch loop?
If I move the declaration inside the try part of the loop, then
fire off a System.gc(); after the catch portion of the loop,
would it free up what was used by the variable GameNumberToResume ?
I know in this specific case it probably doesn't matter, but it is simple enough to follow and illustrates a general hole in my understanding of how to efficiently code Android.
Checkout
bundle.containsKey ("GameToResume");
it will return whether this key is there or not in bundle.
Do not use the try-catch here. And you can initialise the game number like int GameNumberToResume=Integer.MAX_VALUE. Then remove the try-catch. Just use if(xx==Integer.MAX_VALUE).
In my opinion, try-catch can only use for the unknown error or unpredictable situation. Here, you know everything.
You can utilize getInt second parameter to put default value, like this.
Bundle bundle = this.getIntent().getExtras();
// Don't forget NullPointerException
if(bundle != null){
final int IMPOSSIBLE_VALUE = -1; // default value
boolean startNewGame = false;
int gameNumberToResume = bundle.getInt("GameToResume", IMPOSSIBLE_VALUE);
if(gameNumberToResume != IMPOSSIBLE_VALUE){
startNewGame = true;
// continue ..
}
}
Related
I need to save an int[][] into a Bundle so it could be saved during onSaveInstanceState() and restored on onCreate(). First, I've decided to make it straightforward and flatten a 2D array into 1D and deflatten on load. It all worked fine.
I've decided to find an easier way. I was told that 2D arrays are serializable, so I made it something like this:
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putSerializable("CELLS_STATE",universe.getUniverse());
super.onSaveInstanceState(savedInstanceState);
}//onSaveInstanceState
where universe.getUniverse() returns an int[][].
Reloading within onCreate():
if(savedInstanceState != null)
{//save data exists, reload it
universe = new Universe( (int[][])savedInstanceState.getSerializable("CELLS_STATE") );
}
Here's the catch. If I run the program and turn my phone so the screen changes orientation, it loads perfectly fine.
But, when I press the home button and then kill the app using an app killer (in my case it is Battery Doctor) and try to start it again from the menu, it crashes and I can launch it only from the second attempt, and the saved state is lost. The error that is given on that crash points to the line with getSerializable I've specified above, and it is:
java.lang.ClassCastException: java.lang.Object[] cannot be cast to int[][]
This confuses me. So, whenever screen orientation changes (and maybe something else), getSerializable returns something that can be cast to a 2D array (probably, Serializable). But whenever the app is killed, it returns something that cannot be cast. What causes such a strange behaviour and is there a way to bypass it?
What causes such a strange behavior...?
It appears that this is a bug in the Parcel's implementation, I recommend to read comment #6 which contains the very good explanation of bug's nature.
.. and is there a way to bypass it?
The following code should work:
if (savedInstanceState != null) {
final Object[] objects = savedInstanceState.getSerializable("CELLS_STATE");
final int[][] cells = new int[objects.length][];
for (int i = 0; i < objects.length; i++) {
cells[i] = (int[]) objects[i];
}
universe = new Universe(cells);
}
Found the issue on my payment Input control. There was a small computed Text field that was failing but not throwing an error. Just stopped the whole process. In any case removed the computedText and it now works. The compositeData formula that returns pItem to the custom control still fires way to often but I can't figure out how to stop that. It is all memory resident so it is probably not a major performance hit, but still.....
This question is a follow up to my previous question and I will try to refine the issue
defining an object property in a compositeData on a custom control
Here is a picture of what I am trying to do:
The repeat control is bound to an arrayList generated by the Java method Payments.getAllItems(LinkKey) and that works correctly. The button in the repeat is fairly simple it just setts the viewScope.vsShowPayment = true and vsRIndex to the repeat Index value so I know which element in the ArrayList we are working with. It then does a refresh of the panelPaymentContainer which hides the repeat and renders the custom control ccTestPayment.
ccTestPayment has a custom property called pItem of the type java.lang.Object with this code:
<xc:ccTestPaymentInput rendered="#{javascript:(viewScope.vsShowPayment)}">
<xc:this.pItem><![CDATA[#{javascript:try{
var debug:Boolean = true;
if (debug) print("Open existing row = " + viewScope.vsRIndex)
rIndex = parseInt(viewScope.vsRIndex.toString());
if (debug) print("rIndex = " + rIndex);
pItem = Payments.getItem(rIndex);
return pItem;
}catch(e){
print("Failure in Custom Prop of add item " + e.toString());
return null;
}}]]></xc:this.pItem>
</xc:ccTestPaymentInput>
the method in the class Payments Payments.getItem(rIndex) then returns the PaymentItem Object from the ArrayList of PaymentItems and displays them in custom control. the fields in the custom control are bound to compositeData.pItem.getPaymentDate etc and to this point everything is cool.
I can edit any of the fields on the custom control and that all works fine. However, when I press the "Save" button none of the code in it gets executed.
try{
print("Start Payment save");
var debug:Boolean = true;
var pos:Integer = parseInt(viewScope.vsRIndex.toString());
if (debug) print("Working with pos = " + pos + " Call saveThisItem");
if (Payments.saveThisItem(compositeData.pItem , pos)){
if (debug) print("save Payments Worked ");
}else{
if (debug) print("save Payments FAILED ");
}
}catch(e){
print("payment save Error " + e.tostring);
}finally{
viewScope.vsExpPayDate = null;
viewScope.vsShowPayment = false;
viewScope.remove("vsRIndex");
viewScope.remove("vsGotItem")
}
None of the print statements get fired. I suspect it has something to do how pItem gets defined. the code behind the custom property gets fired over and over again and I'm wondering if that is getting in the way.
The reason that the save was not working was that there was a computed Text field on the control that generated an error. The problem was that there was no error message reported on the client nor the console. After a lot of head scratching I noticed the text filed was no longer displaying the value it was supposed to. Deleted the field and the save and everything else started to work.
On the issue of the number of times the processes are called I think I have resolved many of them. I'm moving the control ccTestPaymentInput.xsp inside the repeat. It will now have direct access to the 'current' PaymentItem Object so I can access teh repeat var=pItem which is teh PaymentItem object I want to work with. Clean and far simpler than what I was doing. The only refreshes necessary are the ones related to the rpeat control and there is not much that I can do about that.
I have a non-sticky service that's called on a regular basis via a broadcastreceiver to start a thread that'll perform some tasks. While the thread is running an ongoing notification shows some progress information, and a button to bring up a status page.
This status page shows a lists of items curerntly being processed, this list is a static ArrayList used by both the thread and this activity. When the status Activity is started I have a null check:
if(Global.statusItems == null)
{
Global.statusItems = new ArrayList<StatusPageItem>();
}
The thread is still running, and has perfectly fine access to the ArrayList, but as soon as the Status Activity is brought up it'll recreate the ArrayList as if it were null.
So far I haven't been able solve the issue without saving the list using an ObjectOutputStream and reloading when the status page is started. Is there a more elegant solution I could use?
Regards,
Quint.
Is it possible that your service is running on a different process?
You need to make sure that the 2 lines of code (null test and creation of a new list) are atomic and that the allocation is visible from other threads.
The easiest way to do that is to synchronize that piece of code:
synchronized(Global.class) {
if(Global.statusItems == null) {
Global.statusItems = new ArrayList<StatusPageItem>();
}
}
However, if you need to read the list from one thread and write to it from another thread, you will need to add extra synchronization when adding/removing/iterating to make sure that both treads see the same list - if you don't, it is possible that the writing thread adds an item to the list but the reading thread does not see it.
The easiest way would be to use a thread safe implementation of list:
synchronized(Global.class) {
if(Global.statusItems == null) {
Global.statusItems = new CopyOnWriteArrayList<StatusPageItem>();
}
}
If memory / object creation is a concern (CopyOnWriteArrayList is not very efficient from that perspective), you can also use a synchronized collection instead:
synchronized(Global.class) {
if(Global.statusItems == null) {
Global.statusItems = Collections.synchronizedList(new ArrayList<StatusPageItem>());
}
}
In that case, make sure you lock on the collection when iterating:
synchronized(Global.statusItems) {
for (StatusPageItem item : Global.statusItems) {
}
}
In my j2me application i have to play a small sound file each times user click on an item. But the issues is when i play sound file multiple times like after 10-14 times it gives me
out of memory exception. Although i release the player each time i play the file but still it
gives out of memory exception : Here is the code snippet,
public void playSound(String soundFile) {
try{
if (player!=null) {
try {
player.deallocate(); //deallocate the unnecessary memory.
} catch (Exception ex) {
player=null;
System.gc();
}
}
player = Manager.createPlayer(getClass().getResourceAsStream(musicFolder + soundFile), "audio/mpeg");
// player = Manager.createPlayer(is, "audio/mpeg");
player.realize();
// get volume control for player and set volume to max
VolumeControl vc = (VolumeControl) player.getControl("VolumeControl");
if (vc != null) {
vc.setLevel(100);
}
player.prefetch();
player.start();
isException=false;
} catch (Exception e) {
isException=true;
}
}
Can someone tell me what is going wrong?
3 things to keep in mind
If you are going to play the same sound several times, you might want to keep one Player prefetched and simply start it multiple times.
When you want to properly cleanup a player, you should call Player.close()
You may want to use a media event listener to close and/or restart a player independently of user input.
I think you should also call
player.close()
right after after
player.deallocate();
According to documentation "When deallocate returns, the Player is in the UNREALIZED or REALIZED state." but close goes further... "When the method returns, the Player is in the CLOSED state and can no longer be used."
I'm not sure why the de-allocation isn't working. I guess it either takes longer to de-allocated than to create a new one, or the de-allocation fails for some reason. Is there a player.stop() to match the player.start()?
Another thing to try (if nothing else, for good form :) is not to create new player unless you need to/should. I.e. move the
if(player!=null){
So it also covers
player = Manager.createPlayer(getClass().getResourceAsStream(musicFolder + soundFile), "audio/mpeg");
HTH!
I'm currently doing Android development.
The class R (an automatically-generated class) has lots of 'global' values accessed from anywhere like so:
R.drawable.<file_name_ignoring_suffix>
I have lots of files named 'item_0.png', 'item_1.png', 'item_2.png' etc.
Is there an easy way to iterate through these items without putting them into an array first?
My current implementation:
int[] itemResources = { R.drawable.item_0, R.drawable.item_1, R.drawable.item_2,
R.drawable.item_3, R.drawable.item_4, R.drawable.item_5 };
... then iterating through the array.
This works well, but it isn't particularly maintainable, and gets laborious when I have many items.
Any ideas?
You could certainly use reflection to get the list of fields of R.drawable. It's kind of hacky, but it might work for you.
I think, there is no easy way to do it. Here is small code i tried to get the drawables.
R.drawable d = new R.drawable();
try {
for (Field fld : declaredFields) {
Integer id = fld.getInt(d);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), id);
if (bitmap != null)
Log.d("tag", "success");
else
Log.d("tag", "failed");
}
} catch (Exception e) {
Log.d("tag", "" + e);
}
In the above test, some of them failed. I dont know the reason as it need further investigation. But this will get you started.
Secondly, also look for this api from theme, if you want to pick the right png using the rules that android platform uses for find the suitable resource based on the orientation, locale etc.
How about putting them in a same directory, then automatically querying the directory content and have it create an array using the contents?