Using static methods in Android? - android

I already made some Apps in Android and noticed that I am often using static methods.
For example I have an Class which extends PreferenceFragment. In this PreferenceFragment I set an onClick event on a Button.
Button btn = new Button(getActivity().getApplicationContext());
btn.setText("Save");
v.addView(btn);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SettingsActivity.finishActivityWithResultOkey();
}
});
Then I'm calling a static method in my SettingsActivity which finishes this Activity. Is this a good way of doing what I want to do? Or is there a better solution?

In Android (in memory managed programming languages like Java, more precisely), static methods can lead to "memory leaks" if not used correctly. I've quoted "memory leaks" as they are not the pure definition of memory leaks, like in C++ where you forget to remove some items from memory and lose the reference to them (and thus cannot clear them later on - at least not easily), but more like keeping on to references when you no longer need them and preventing the GC from doing its job in clearing that memory for you. There are a lot of articles on the web that cover this (search for "Android static memory leak") and a lot of other questions on SO that have been asked about this (see [static][Android][memory-leaks] tags), so I won't go into this.
For your particular case, in order to avoid possible memory leaks, you can get the Activity from the Fragment object that your anonymous OnClickListener object is tied to by using MyFragmentClass.this.getActivity(), cast it to, say, SettingsActivity (as is the case here) and call .finishActivityWithResultOkey() on it. It's best that you guard your cast too.
Here's an example of how the code could look like:
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final Activity activity = MyFragmentClass.this.getActivity();
if(activity instanceof SettingsActivity)
((SettingsActivity)activity).finishActivityWithResultOkey();
else
throw new IllegalStateException("This OnClickListener requires a SettingsActivity to start the fragment containig it"); //helps with debugging
}
});
As a note: MyFragmentClass refers to the class of the fragment that you're putting this code in :)

You can use a Java property known as mirroring.
Below I use the property to finish the activity.
Button btn = new Button(getActivity().getApplicationContext());
btn.setText("Save");
v.addView(btn);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SettingsFragment.this.getActivity().finish();
}
});

Related

Pass reference of one Activity to another Activity

I know that I can pass some values between Activities using intent.
However, if I want to pass whole Activity to another Activity I think it is not good approach.
Is there another way to do that?
I have Settings Activity in which I am changing some Colors. So after I come back to my Main Activity I would like to apply those colors. To do this, I need access to MainActivity fields after I change Color value, so inside PreferenceActivity. In other words, I want to have access to Main activity fields from PreferenceActivity class. Any ideas?
You should be using a SharedPreference and then accessing that in your main activity. I recommend you reading up at http://developer.android.com/guide/topics/ui/settings.html because it seems like you are implementing your settings activity incorrectly. The part you might be specifically interested in is the "Read Preferences" section. However, I strongly suggest you read through the whole thing and then implement your settings the proper way.
Updated answer with the 3 different ways (that I can think of):
1) Start your preference activity using startActivityForResult(), then in your onActivityResult() access the SharedPreference and make your necessary changes. See here
2) Register a SharedPreferenceChangeListener with your MainActivity, which will be called when any changes happen to your SharedPreference. See here for a detailed discussion. Also see my initial response.
3) In your MainActivity's onResume(), access the SharedPreference and then make your changes there. I do not like this method because you will be cluttering onResume() with more logic and you will also probably have to have a variable that keeps track of the state of the variable you are interested in.
I would personally go with option 2 because the callback was created for this exact purpose.
I think you could pass the value by using method putExtra(name, value).
And after you start new activity you can get the value you pass before by using method getStringExtra(name).
Shared preferences can be used. If you want your changes to be reflected right away add listener. Refer to SharedPreferences.onSharedPreferenceChangeListener. Its an easy way to do.
If you want to lots of changes required in many activity from you change in any one.
And access last modify data from all Activity and modify also.
for example.
Constants.java
public class Constants
{
public static String name;
}
In your MainActivity you have an editText.
MainActivity.java
public class MainActivity extends Activity {
private EditText yourName;
private Button btn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
yourName = (EditText) findViewById(R.id.yourName);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
Constants.name = yourname.getText().toString();
Intent intent = new Intent(getApplicationContext(),Activity2.class);
startActivity(intent);
}
});
}
In your Activity2 you have an TextView and that getting value which you enter in MainActivity.java without pass in Intent.
Activity2.java
public class Activity2 extends Activity {
private TextView yourName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
yourName = (TextView) findViewById(R.id.tv_yourName);
// directly use ferom serializable class
yourname.setText(Constants.name);
}
like that you use many values from all activity and modify from all activity.

Android (student cw) in need of direction

public class Menu extends Activity {
/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
//myIntent.setClassName("hello.World", "hello.World.mybuttonclick");
// myIntent.putExtra("com.android.samples.SpecialValue", "Hello, Joe!"); // key/value pair, where key needs current package prefix.
//startActivity(myIntent);
//Button myButton = (Button) findViewById(R.id.my_button);
super.onCreate(icicle);
setContentView(R.layout.main);
}
public void updateLayout(){
Intent myIntent = new Intent(Menu.this, mybuttonclick.class);
startActivity(myIntent);
// TextView sayHello = (TextView) findViewById(R.id.Hello);
}
}
Hey guys, I am a new android java student and we have to develop a simple hello world app.. I am finding some difficulty getting my onClick() activity to work, using android:Onclick in xml.. what i am trying to do is change the content view do display a simply a different layout and saying hello.. i am using setContentLayout to do this, every time i click said button tho the android app crashes out.. am i doing something wrong?
regards,
Stefan
When you set a click listener in xml you must have the method defined inside the activity you are clicking in. Lets say you set the onClick in xml to be "buttonClicked", you must create a method looking exactly like the one below.
public void buttonClicked(View view)
{
//Your code here
}
The thing to notice is that the method is a public void with only a single parameter of type View. XML defined click listeners must be like this to work. The view object in the example above is the view that was clicked.
You update layout function needs to read
public void updateLayout(View view)
In response to your question, there are a number of things that are issues causing the complication that you described. Let it first be said, that you don't have to do anything any particular way, provided that you make concessions for certain things. Android is a very flexible platform and Java, as an OOP language allows you to do things that many non OOP languages do not.
Whenever you create a "clickable" item, like a Button, if you want to have your program respond, you must have something "listen" to it. This is known as a Listener. In your case, you are looking for an OnClickListener. The OnClickListener does not have to be a part of the Activity necessarily. It just has to be a class that implements View.OnClickListener. Then, you have tell the setOnClickListener() method of the Button who its listener is. The following example shows what is necessary without your declaration in XML (but it is important).
class Menu extends Activity implements View.OnClickListener
{
public void onCreate(Bundle icicle)
{ setContentView(R.layout.main);
Button btn = (Button)findViewById(R.id.BUTTON_ID_AS_DEFINED_BY_YOUR_XML);
btn.setOnClickListener(this);
}
public void onClick(View view)
{ int id = view.getId();
if (id == R.id.BUTTON_ID_AS_DEFINED_BY_YOUR_XML)
updateLayout()//Do your Click event here
}
public void updateLayout()
{ //updateLayout code...
}
}
Something that needs to be noted is the OnClick() above. Every OnClickListener must use the same signature as theOnClick() That means itmust have the same return and same arguments even if it has a different name. For what you are trying to do (in XML), you have set your android:OnClick to updateLayout. This means that `updateLayout() must be declared as follows:
public void updateLayout(View view)
Now, getting the update method to actually work: While you provide your code, we don't actually know what errors you are getting. It is always much easier to solve a problem if we have a copy of the Logcat output that includes the error you are receiving. Once, we have that we can target your error specifically and I can edit my answer to include what you may additionally need.
FuzzicalLogic

Android: Listener Pattern within onCreate()

So, I am again, asking a very basic question. I apologize for my ineptness but I guess I read the given tutorials on these topics poorly. My question is as follows:
I would like to use a "listener" pattern to handle button presses on my GUI. I believe an onClickListener is what I need to use to handle these button presses. However, I'm not sure if I should be creating and handling events that occur after the GUI is created within an onCreate method. The following code is within my onCreate method for one of my Activities:
View.OnClickListener upDownListener = new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if(v == (upOneButton))
{
Log.d("OptionSelect", "Up One Button Pressed.");
ops.getOptionList().get(0).incrementProbability(4);
} . . .
This method being called updates some GUI text with a different number. It is being called, but the GUI isn't responding. I imagine this has to do with my attempt to use it within the onCreate method.
In short, what is a good and simple way to deal with user events within a GUI and where should this occur?
Thank you so much.
EDIT: Log.d() does in fact get called. Also, ops is an object of type OptionSelect which happens to be the type of the class in which the onCreate() call is made. Will that become an issue? Also, here is the method for incrementProbability():
public void incrementProbability(int numberOfOptions)
{
probability += (numberOfOptions - 1);
if(probability > 100)
{
Log.i("OptionSelect", "Exceeded Maximum by " + (probability - 100));
probability = 100;
}
}
Also, here is relevant code I should've included that is updating my GUI at the end of the onClick() method:
private void refreshDisplay(TextView a, TextView b, TextView c, TextView d)
{
a.setText(getOptionList().get(0).getProbability() + "");
b.setText(getOptionList().get(1).getProbability() + "");
c.setText(getOptionList().get(2).getProbability() + "");
d.setText(getOptionList().get(3).getProbability() + "");
a.invalidate();
b.invalidate();
c.invalidate();
d.invalidate();
}
Thanks for the help so far!
I personally prefer to have my Activities implement listener interfaces and add an onClick method to the Activity itself such as...
public class MyActivity extends Activity
implements View.OnClickListener {
...
#Override
public void onClick(View v) {
...
}
}
I then just use...
myGuiObject.setOnClickListener(this);
...whenever I want to set that method as the listener for any GUI object.

Is it OK to use "mContext" (initialized at onCreate)?

Is this a bad habit, and why if it is? So in every activity adding this right after onCreate...
mContext = this;
and then use it in all other cases where context is required? For example
Toast.makeText(mContext, mContext.getString(R.string.someString), Toast.LENGTH_LONG);
EDIT: What if I have something like this...how the context should be passed? Because this cannot be applied (because of the View.OnClickListener()).
someButton = (Button) findViewById(R.id.someButton);
someButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext, mContext.getString(R.string.warning), Toast.LENGTH_LONG).show();
}
});
Is this a bad habit, and why if it is?
Yes, it is a bad habit. It is a waste of code. this is shorter than mContext, and you have an extra line of code for setting the data member.
I disagree with Mr. Damiean's suggestion of always using getApplication(). Use getApplication() when you specifically need the Application object. You neither need nor want the Application object for creating a Toast -- your Activity is a perfectly suitable Context to use there. The Application object fails to work in many places, particularly when dealing with things involving the UI.
You can use this instead. Even in an OnClickListener or other subclasses you use ActivityName.this like this:
someButton = (Button) findViewById(R.id.someButton);
someButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(ActivityName.this, ActivityName.this.getString(R.string.warning), Toast.LENGTH_LONG).show();
}
});

Android AsyncTask context behavior

I've been working with AsyncTasks in Android and I am dealing with an issue.
Take a simple example, an Activity with one AsyncTask. The task on the background does not do anything spectacular, it just sleeps for 8 seconds.
At the end of the AsyncTask in the onPostExecute() method I am just setting a button visibility status to View.VISIBLE, only to verify my results.
Now, this works great until the user decides to change his phones orientation while the AsyncTask is working (within the 8 second sleep window).
I understand the Android activity life cycle and I know the activity gets destroyed and recreated.
This is where the problem comes in. The AsyncTask is referring to a button and apparently holds a reference to the context that started the AsyncTask in the first place.
I would expect, that this old context (since the user caused an orientation change) to either become null and the AsyncTask to throw an NPE for the reference to the button it is trying to make visible.
Instead, no NPE is thrown, the AsyncTask thinks that the button reference is not null, sets it to visible. The result? Nothing is happening on the screen!
Update: I have tackled this by keeping a WeakReference to the activity and switching when a configuration change happens. This is cumbersome.
Here's the code:
public class Main extends Activity {
private Button mButton = null;
private Button mTestButton = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button) findViewById(R.id.btnStart);
mButton.setOnClickListener(new OnClickListener () {
#Override
public void onClick(View v) {
new taskDoSomething().execute(0l);
}
});
mTestButton = (Button) findViewById(R.id.btnTest);
}
private class TaskDoSomething extends AsyncTask<Long, Integer, Integer>
{
#Override
protected Integer doInBackground(Long... params) {
Log.i("LOGGER", "Starting...");
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
#Override
protected void onPostExecute(Integer result) {
Log.i("LOGGER", "...Done");
mTestButton.setVisibility(View.VISIBLE);
}
}
}
Try executing it and while the AsyncTask is working change your phones orientation.
AsyncTask is not designed to be reused once an Activity has been torn down and restarted. The internal Handler object becomes stale, just like you stated. In the Shelves example by Romain Guy, he simple cancels any currently running AsyncTask's and then restarts new ones post-orientation change.
It is possible to hand off your Thread to the new Activity, but it adds a lot of plumbing. There is no generally agreed on way to do this, but you can read about my method here : http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html
If you only need a context and won't use it for ui stuff you can simply pass the ApplicationContext to your AsyncTask.You often need the context for system resources, for example.
Don't try to update the UI from an AsyncTask and try to avoid handling configuration changes yourself as it can get messy. In order to update the UI you could register a Broadcast receiver and send a Broadcast.
You should also have the AsyncTask as a separate public class from the activity as mentioned above, it makes testing a lot easier. Unfortunately Android programming often reinforces bad practices and the official examples are not helping.
This is the type of thing that leads me to always prevent my Activity from being destroyed/recreated on orientation change.
To do so add this to your <Activity> tag in your manifest file:
android:configChanges="orientation|keyboardHidden"
And override onConfigurationChanged in your Activity class:
#Override
public void onConfigurationChanged(final Configuration newConfig)
{
// Ignore orientation change to keep activity from restarting
super.onConfigurationChanged(newConfig);
}
To avoid this you can use the answer givin here: https://stackoverflow.com/a/2124731/327011
But if you need to destroy the activity (different layouts for portrait and landscape) you can make the AsyncTask a public class (Read here why it shouldn't be private Android: AsyncTask recommendations: private class or public class?) and then create a method setActivity to set the reference to the current activity whenever it is destroyed/created.
You can see an example here: Android AsyncTask in external class

Categories

Resources