My app was working fine until I started to click one specific button, the rate-button. Every time I click this button, the app crashes and I get this message in log cat:
22808/com.nileworx.guesstheplace E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.nileworx.guesstheplace, PID: 22808
java.lang.ClassCastException: com.nileworx.guesstheplace.GameActivity cannot be cast to com.nileworx.guesstheplace.MainActivity
at com.nileworx.guesstheplace.CustomDialog$11.onClick(CustomDialog.java:283)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
06-25 06:46:03.423 1642-2032/system_process W/ActivityManager: Force finishing activity com.nileworx.guesstheplace/.GameActivity
I believe there is a problem in the gameactivity, and there is just one error:
#Override
protected void onResume() {
super.onResume();
if (!mSharedPreferences.getString("placesNum", "0").equals("0")) {
String updatesDlgMsg = String.format(getResources().getString(R.string.updatesDlg), mSharedPreferences.getString("placesNum", "0"));
dialog.showDialog(R.layout.blue_dialog, "updatesDlg", updatesDlgMsg, mSharedPreferences.getString("placesJSON", ""));
e.putString("placesNum", "0");
e.commit();
}
}
The 6th line of code, has the problem with the following message:
Format string 'updatesDlg' is not a valid format string so it should
not be passed to String.format less... (Ctrl+F1) If a string contains
a '%' character, then the string may be a formatting string which will
be passed to String.format from Java code to replace each '%'
occurrence with specific values. This lint warning checks for two
related problems: (1) Formatting strings that are invalid, meaning
that String.format will throw exceptions at runtime when attempting to
use the format string. (2) Strings containing '%' that are not
formatting strings getting passed to a String.format call. In this
case the '%' will need to be escaped as '%%'. NOTE: Not all Strings
which look like formatting strings are intended for use by
String.format; for example, they may contain date formats intended for
android.text.format.Time#format(). Lint cannot always figure out that
a String is a date format, so you may get false warnings in those
scenarios. See the suppress help topic for information on how to
suppress errors in that case
I do not know how to fix this, and I have tried for over 2 hours to solve it.
My Rate Button Code
private void rateDlg(final Dialog dialog, final String marketLink) {
Button rateBtn = (Button) dialog.findViewById(R.id.rateBtn);
// if button is clicked, close the custom dialog
rateBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sou.playSound(R.raw.buttons);
dialog.dismiss();
MainActivity mainAct = (MainActivity) context;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("playstorelink, changed for stackoverflow"));
if (!mainAct.MyStartActivity(intent)) {
// Market (Google play) app seems not
// installed, let's try
// to open a webbrowser
intent.setData(Uri.parse("playstorelink, changed for stackoverflow"));
if (!mainAct.MyStartActivity(intent)) {
// Well if this also fails, we have run
// out of options,
// inform the user.
Toast.makeText(context, "Could not open Android market, please install the market app.", Toast.LENGTH_SHORT).show();
} else {
editor.putInt("usingNum", 100);
editor.commit();
}
} else {
editor.putInt("usingNum", 100);
editor.commit();
}
}
});
Button laterBtn = (Button) dialog.findViewById(R.id.laterBtn);
// if button is clicked, close the custom dialog
laterBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sou.playSound(R.raw.buttons);
editor.putInt("usingNum", 0);
editor.commit();
dialog.dismiss();
}
});
}
First check that R.string.updatesDlg exists in your Strings resource file.
Your other issue is due to a bad cast as stated by the error GameActivity cannot be cast to com.nileworx.guesstheplace.MainActivity, you are attempting to cast one class to another. Your button dialog code shows that here...
MainActivity mainAct = (MainActivity) context;
I'm pretty sure context in this case is not of type MainActivity. Check the type of context. The error says it's of type GameActivity.
With that said, I don't see why you need to reach out to the MainActivity, unless something special happens there. You can create a Helper class and include a method that will check if a package is installed. You pass in the context of your activity. Like this:
//MainActivity mainAct = (MainActivity) context;
//Intent intent = new Intent(Intent.ACTION_VIEW);
//intent.setData(Uri.parse("playstorelink, changed for stackoverflow"));
if (!isPackageInstalled(playstorelink)) {
// Market (Google play) app seems not
// installed, let's try
// to open a webbrowser
//intent.setData(Uri.parse("playstorelink, changed for stackoverflow"));
if (!isPackageInstalled(playstorelink)) {
Toast.makeText(context, "Could not open Android market, please install the market app.", Toast.LENGTH_SHORT).show();
} else {
editor.putInt("usingNum", 100);
editor.commit();
}
} else {
editor.putInt("usingNum", 100);
editor.commit();
}
private boolean isPackageInstalled(Context context, String packagename) {
try {
context.packageManager.getPackageInfo(packagename, 0);
return true;
} catch (NameNotFoundException e) {
return false;
}
}
On a side note, this line of code leads me to believe you have your activity's instance saved to a static variable:
if (!mainAct.MyStartActivity(intent)) {
This is very bad! I will quote from this answer -
Don't store the Context in a hard reference, ever. Or you will leak
memory. An activity has references to View and a multitude of other
things. If you store the Context, you're storing the activity and
things go bad from there. Learn to pass the Context around, use the
Application Context whenever possible and if you need to pass it
around, do it for very good reasons. Most of the time the App context
is enough for getting resources, strings, etc. If you're going to
store the Context, always store context.getApplicationContext(); Never
store a static activity context. You can google this too and
StackOverflow has some good answers.
Instead of storing context in a static variable, you can use an event system library such as EventBus to communicate between activities/fragments. You can also just pass the context from one place to another or use an intent putExtras to add data to the intent and then receive it in another activity/dialog...
Related
I just curious what makes a value inside of some variable become empty again or back to its initial value in the android life cycle.
First lets take a look at how i create a variable :
public class myData {
public static String myCode = "";
public static String getData(String Choice) {
String theData = "";
if ("Code".equals(Choice) {
theData = myCode;
}
return myCode;
}
public static void setData(String setData,String Choice) {
if ("Code".equals(Choice) {
myData.myCode = setData;
}
}
}
If I want to fill the variable, i usually do this :
myData.setData("value of variable","Code");
And if I want to get the value of the variable, I usually do this :
myData.getData("Code");
I just want to know what makes my variable gone inside of android lifecycle, of course excluding when the application is closed.
I have to try to Log and show the value in onstart , oncreate, onresume and onrestart. And all of them is still have the value inside of my variable intact without any problem.
My client always tells me that my application sometimes gets crash when they open some activity. I also ask if they did something while using my application,
some of them answer that the application get crashed after they got a phone call and when the phone call is ended, the application is started with a crash.
some of them also said that when they open the application and then idle the phone withouth closing the application until the phone become black screen, and when they open it again the application get crashed.
After I check the log, the problem was the variable become empty. which is why I want to know is there another possibilites that makes the value inside of the variable become empty?
As John Lord saying, on low-end device variables might back to its initial value again if there is not enough memory.
So for future reference, I use a shared preference to counter it, here is my structure for fetching the data :
public class myActivity extends AppCompatActivity {
String myCode = "";
protected void onCreate(Bundle savedInstanceState) {
....
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("myData", Context.MODE_PRIVATE);
myCode = sharedPreferences.getString("Code",null);
....
}
#Override
protected void onResume() {
super.onResume();
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("myData", Context.MODE_PRIVATE);
myCode = sharedPreferences.getString("Code",null);
}
}
And here is how i set the data :
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("myData",Context.MODE_PRIVATE);
sharedPreferences.edit().putString("Code","Hello World").apply();
I hope it will be helpful for those who want to search the same thing
Thank you every one for giving time to this question.
I have one editview that I need to append when I call appendToMessageHistory method from another Activity.
second Activity
EditText et;
Messaging msg = new Messaging(getApplicationContext(), et);
msg.appendToMessageHistory(username, messegestr);
on Messaging Activity.
private EditText messageHistoryText;
messageHistoryText = (EditText) findViewById(R.id.messageHistory);
private Context mCon;
public Messaging(Context applicationContext, EditText name) {
// TODO Auto-generated constructor stub
this.mCon = applicationContext;
this.messageHistoryText = name;
}
public Messaging() {
}//by default constructor
public void appendToMessageHistorysend(String username, String message) {
if (username != null && message != null) {
if (messageHistoryText != null) {
messageHistoryText.append(Html
.fromHtml(username1));
messageHistoryText.append(Html
.fromHtml(message1));
}
else {
Toast.makeText(mCon,
"hey yo not working value are null",
Toast.LENGTH_LONG).show();
}
}
}
}
}
I still get null for messageHistoryText.
and app crash
again thank you for your time
In your first block of code:
EditText et;
Messaging msg = new Messaging(getApplicationContext(), et);
msg.appendToMessageHistory(username, messegestr);
the object "et" is not initialized... it will crash if you try to access it.
Activity life-cycle is managed by the Android framework. Writing a custom constructor in an Activity is a big mistake (overriding an existing constructor is some rare cases useful).
When you develop for Android you should never call an Activity constructor in your own code. And when I say never I mean NEVER !!!
To instantiate an Activity : you MUST use an Intent and let Android do the job.
Before coding anything else, please be sure to understand the Android activity life-cycle
To answer precisely to your question:
messageHistoryText is null when you use it in your custom constructor because your activity Messaging don't contains any view when you initialize it.
The method findViewById will always return null (whatever the id is) if you call it before setting the content of your Activity.
The most usual way to set the content of an Activity is to call setContentView in the Activity.onCreate() method (but do it after the call to super.onCreate(bundle) otherwise your Activity will not be ready to set it's content)
I'm build the Fun Facts app on the Android Development Track. I decided to take a exploratory detour and try to create a very basic introductory message to the user. I changed the factTextView text to "You can click the button below to see a new fact!" and changed the showFactButton text to "Try it out!"
From there, I changed the final line onClick object (is that an object?) to the following:
public void onClick(View view) {
String fact = mFactBook.getFact();
// Update the label with our dynamic fact
factLabel.setText(fact);
// Set button text to new fact prompt
showFactButton.setText("Show another fun fact.");
This seems to work fine. However, I feel like "updating" the button text to the same new string on every press isn't always the best practice, even if it is easy and readable. I tried to add a boolean that will check the text of the button, and update it only if it has not already been updated. This is what I've come up with so far:
View.OnClickListener listener = new View.OnClickListener() {
#Override
public String launchText = getResources().getString(R.string.start_text);
public String nextText = getResources().getString(R.string.next_text);
public String buttonText = (String) showFactButton.getText();
public boolean updateLaunchText() {
if (buttonText.equals(launchText)) {
buttonText.replaceAll(launchText, nextText);
return true;
}
else {
return true;
}
}
public void onClick(View view) {
String fact = mFactBook.getFact();
// Update the label with our dynamic fact
factLabel.setText(fact);
}
};
With the following added to strings.xml:
<string name="start_text">Try it out!</string>
<string name="next_text">Show another Fun Fact!</string>
No errors, but the button text stays on "Try it out!" I'm sure that all the extra objects are totally unnecessary compared to the first, working method for the scope of this app, but I'd still like to figure it out since I don't really have any idea what I'm doing with the boolean.
Questions: 1) What am I missing in the longer boolean approach? 2) What's the actual most efficient approach to accomplish this task?
Did you connect the listener to the button object?Without that connection no logic is applied to a button click.It goes like this:
buttonName.setOnClickListener(...)
You'd have to initialize the button object first though :)
Where r u call to method updateLaunchText() ?
you should change the objects to global object (not to create the into the listener):
private String launchText = getResources().getString(R.string.start_text);
private String nextText = getResources().getString(R.string.next_text);
private String buttonText = (String) showFactButton.getText();
and take the method updateLaunchText() out of the listener too.
and then into the onClick(View view) call to updateLaunchText() like this:
public void onClick(View view) {
updateLaunchText();
String fact = mFactBook.getFact();
// Update the label with our dynamic fact
factLabel.setText(fact);
}
So I'm still working on my first little app here, new to Android and Java, so I'm stuck on a basic little problem here. Answers to my first questions were really helpful, so after researching and not coming up with anything, I thought I'd ask for some more help!
The idea is that on another screen the user makes a choice A, B, C, or D, and that choices is passed as a string through the intent. OnResume checks if the choice is not null and sets an integer that corresponds to that string. Later when the user pushes another button, some if else logic checks that int and performs and action based on which was chosen. The problem is that the App crashed at onResume.
I learned that I have to use equals(string) to compare string reference, but maybe the problem is that I am trying to compare a string in reference to a literal string? Any help would be greatly appreciated.
Thanks!
protected void onResume() {
super.onResume();
// Get the message from the intent
Intent intent = getIntent();
String choice = intent
.getStringExtra(ExtensionSetupSlector.TORQUE_SETUP);
// Create the text view
TextView displayChoice = (TextView) findViewById(R.id.displayChoice);
if (!choice.equals("")){
displayChoice.setText(choice);
if (choice.equals("A")) {
myChoice = 1;
}
if (choice.equals("B")) {
myChoice = 2;
}
if (choice.equals("C")) {
myChoice = 3;
}
if (choice.equals("D")) {
myChoice = 4;
}
}
}
myChoice is declare right after ...extends Activity{ Also I'm not quite sure If this should really be in onResume, but it was working before I started try to set myChoice in the onResume (when I was just displaying the choice). Thanks again!
Change if (!choice.equals("")) to check for null instead. Otherwise your app attempts to access an empty reference and crashes.
in my android application at some event in an activity I want to ask the user for a name (string). I know how to do this: call showDialog, create the dialog in the Activity.onCreateDialog method (I need to supply a string for the label) and handle the result in the onClick of the dialog. This works fine and to my satisfaction.
BUT this way I have three different places, where this simple task spreads throughout the code of my activity. I would much more prefer to keep this code together, to write some code like this:
string result;
if (showSimpleEditDialog(idForLabelString, result)==DIALOG_OK)
{
// do something with the result
}
or maybe with a class instance
SimpleEditDialog dlg = new SimpleEditDialog(idForLabelString);
if (dlg.showModal()==DIALOG_OK)
{
string result = dgl.getResult();
// do something with the result
}
(The "idForLabelString" would be some resource id for the label to use, DIALOG_OK would be some constant returned when the user clicks OK)
I know, I would have to write this methodes or this class. But for better readibility of my code I would do it. Any suggestions?
Thank you,
Gerhard
"BUT this way I have three different places, where this simple task spreads throughout the code"
So why don't you create a Method for this task? What you are talking about sounds like some sort of 'ActionListener' to me. This can be done in Java/Swing, but not in Android.
But, if you have three Dialogs, which all need to do the same when "YES" e.g. "NO" is pressed, you could define the 'DialogInterface.OnClickListener()' as a global inner-Class (or in a second class which extends the 'onClickListener') and then use it for all the Dialogs.
Now actually the problem with modal dialogs is mostly a problem with programm flow. You want to keep things together that belong together. You want to display a dialog that returns "ok" or "cancel" and additionaly e.g. a string that the user entered into one of the dialog widgets.
I do not want to write half of the code up to the line where I need the result of the dialog on one place and the rest of the code on another place namely the onClickListener of the dialog.
In some scenarios the first dialog might invoke a second dialog e.g. to specify a color which is not in the list of the first dialog's ListView.
Your code will be scattered all over the place (in each dialog's button onClickListener) and will be hard to read or to maintain.
Now after having written some unclear code like that I came up with the following solution which certainly respects the android design guides.
Instead of directly showing a dialog I create a Handler derived class which handles messages.
I send it a first message which creates and shows a dialog. It also forwards the handler to the dialog and the diaolg in it's onStop method sends another message to the handler, indicating the end of the dialog. There you can examine the dialogs properties, the contents of the edit fields or whether it was stopped with OK or CANCEL.
Now in the message handler all the logic of the task sits in different cases of the messages arg1 value.
Some cases might be skipped (e.g. the user selected a standard color and did not need a special color dialog).
The dialogs are independant of the scenario from which they are called and in their code only reflect their simple task (selecting from a list, some checkboxes etc.). They may be reused from other scenarios.
Following a kind of a template how to use this approach:
public class DoSomethingWithDialogs extends Handler
{
Context context; // from which it was called
final static int stepBegin = 0;
final static int stepNext = 1;
final static int stepSomethingElse = 2;
final static int stepLast = 3;
protected DoSomethingWithDialogs(Context context)
{
this.context = context;
}
public static void start(Context context)
{ // this is the main (only) entry point from outside
DoSomethingWithDialogs st = new DoSomethingWithDialogs(context);
st.sendMessage(st.obtainMessage(0, stepBegin, 0));
}
#Override
public void handleMessage(Message msg)
{
// step by step handling the task
switch (msg.arg1)
{
case stepBegin:
{
SomeDlg somedlg = new SomeDlg(context, this, stepNext);
// when the dialog closes, it sends a message to this with stepNext as arg1
somedlg.show();
}
break;
case stepNext:
{ // this message was send by the dialog when it finished
SomeDlg somedlg = (SomeDlg) msg.obj;
if (msg.arg2 == Dialog.BUTTON_NEGATIVE)
{
// has been canceled, nothing to do
} else
{
if (somedlg.someProperty)
{
} else
{
sendMessage(obtainMessage(0, stepSomethingElse, 0));
}
}
}
break;
case stepSomethingElse:
break;
}
}
}