my dear friends. Could you help me to figure out where do I have memory leak in my application.
There are two activities.
In first activity I'v got a rather big static ArrayList consists of bitmaps and another needed information. I made it static, because I need to take it from another activity, and It's loading takes to much time.
In another activity, I also have one static field, using as a key for getting data which present in an Intent from previous activity.
If I try to start one activity from another and go back several times, I get shutting down VM.
To be more clear, here is a snippet of my code:
public class MoviesGallery extends Activity
{
...
private static ArrayList<Movie> films = new ArrayList<Movie>();
...
public void contextButtonsClickHandler(View v)
{
switch(v.getId())
{
case R.id.play_button:
Log.d("Context Button", "Play button has clicked");
mContextButtonHasPressed = true;
Intent filmData = new Intent(MoviesGallery.this, MovieInfo.class);
filmData.putExtra(MovieInfo.mPOS,mPOSITION_OF_CLICKED_ITEM);
startActivityForResult(filmData, 1);
break;
...
}
And this is another activity:
public class FilmInfo extends Activity
{
public static String mPOS = "pos";
private int mNumOfFilm = -1;
private LinearLayout mWall;
...
protected void onCreate(Bundle savedInstanceState)
{
Bundle extra = getIntent().getExtras();
mNumOfFilm=extra.getInt(mPOS);
...
Drawable d = new BitmapDrawable(MoviesGallery.getMoviesArray().get(mNumOfFilm).getWall()));
mWall = (LinearLayout) findViewById(R.id.Wall);
mWall.setBackgroundDrawable(d);
...
setResult(0);
}
I would suggest that potentially memory-leak point is in FilmInfo class member:
private LinearLayout mWall;
Layout has reference to Activity, which each time changes.
So to avoid keeping reference to dead Activity just use in FilmInfo class
#Override
public boolean onContextItemSelected(MenuItem item)
{
//blah-blah
mWall=null;
}
Anyway recipe is simple - try to avoid keeping objects which might contain references to Activity/Context objects.
Related
I have my code defined the way below. There are two crucial activities. Activity (1) shows some images in a ViewFlipper. It uses methods to load desired image directly. The onOptionsItemSelected() method fetches data from a menu defined within linked XML layout R.layout.browse. The other method, displaySelectedFlag(), gets a tag parameter passed from a different activity, let's call it activity (2).
Activity (1):
public class BrowserActivity extends AppCompatActivity implements SimpleGestureListener, View.OnClickListener {
public ViewFlipper vFlipper;
(...)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.browse);
vFlipper = (ViewFlipper) findViewById(R.id.viewFlipperBrowser);
(...)
} // onCreate() ends here
// this method below works fine:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
vFlipper.setDisplayedChild(item.getOrder());
return true;
}
// and this one doesn't:
public void displaySelectedFlag(int orderTag) {
vFlipper.setDisplayedChild(orderTag); // crashes here
}
}
Activity (2):
public class ListActivity extends Activity implements View.OnClickListener {
private BrowserActivity browserActivity = new BrowserActivity();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
ImageButton imageA = (ImageButton) findViewById(R.id.img_a);
imageA.setOnClickListener(this);
ImageButton imageB = (ImageButton) findViewById(R.id.img_b);
imageB.setOnClickListener(this);
}
public void displayImageInfo(View view) {
String tagValue = (String) view.getTag();
int tagId = Integer.parseInt(tagValue);
Intent intent = new Intent(this, BrowserActivity.class);
startActivity(intent);
browserActivity.displaySelectedImage(imageId);
}
#Override
public void onClick(View view) {
displayImageInfo(view);
}
}
As I checked, the method onClick() called in activity (2) fetches an ID of an ImageButton and passes it to activity (1). Unfortunately, I get a NullPointerException when calling the ViewFlipper (the line is marked in the code above, activity (1)).
Any idea why it happens?
You cannot reference one Activity from another activity. You must let the Android OS create the Activity object via the call to "startActivity". Allocating a local variable as an instance of an Activity doesn't actually mean anything (like your instantiation of the BrowserActivity). Apoorv's comment links to a decent article on the subject.
If you want to pass data from one Activity to another, you need to pass extras within the Intent's bundle. This post goes into detail: https://stackoverflow.com/a/819427/504252
I am developing a Quizz App in which an activity shows question and options from SQLite and on selecting option, another activity is showing result for 2000 ms(it has a timer)and then it calls First Activity via an Intent.
So, Most of the interaction is between 2 activities. But each time my MainActivity is called, it re-initializes all the variables again and again.
I am opening my database connection in onCreate() and also keeping a counter (that can count how many questions have been asked yet) whose value is not retained after the intent from Second Activity. I am worried on how to solve this.
I am a bit confused about the life cycle that is followed. Whether the call to First Activity from Second one starting with onCreate() or it's also initializing the instance variables again.
This is onCreate() method I wrote:
public class MainActivity extends Activity {
protected static final int SCORE_INCREMENT = 5;
TextView question;
Button score, opt1, opt2, opt3;
MyDatabaseManager dbManager;
QuizManager quizManager;
private int quiz_counter =1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbManager = new MyDatabaseManager(getApplicationContext());
dbManager.open();
quizManager = new QuizManager(MainActivity.this, dbManager);
Toast.makeText(MainActivity.this, "Asking The First Question", 0).show();
askQuestion();
}
}
Is there any difference between the above written code and the one I am writing now... if the activity is called again via an Intent
public class MainActivity extends Activity {
protected static final int SCORE_INCREMENT = 5;
TextView question;
Button score, opt1, opt2, opt3;
MyDatabaseManager dbManager = new MyDatabaseManager(getApplicationContext());
QuizManager quizManager = new QuizManager(this, dbManager);
private int quiz_counter =1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(MainActivity.this, "Asking The First Question", 0).show();
askQuestion();
}
}
This might be a silly question. But it's a bit confusing for me. Suggestions are welcome.
If you have variables that you want to maintain between changing activities, then you should either
Store them in SharedPreferences
or
Pass them between the Activites in the Intents (see Starting another activity)
First, I am an android rookie, so my solution ways can be found awkward, and i am open to suggestions.
I am trying to create a game manager object that handles all transitions between activities. And my purpose is that while in an activity, menuOut method will call the changeActivity method of GameManager object with nextActivity argument and changeActivity will start that Activity. I am getting errors consistently, and did not find a solution.
Here is my source codes:
GameManager:
public class GameManager{
public SplashScreen splash = new SplashScreen();
public MainScreen main = new MainScreen();
public LoadingScreen load = new LoadingScreen();
Context tempContext;
public GameManager(Context base) {
super();
tempContext = base;
}
public void start(){
createScreens();
Intent intent = new Intent(tempContext, splash.getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
tempContext.startActivity(intent);
}
public void createScreens() {
//here is the method that i am trying to find a solution
((SplashScreen)splash.getContext()).setGameClass(this);
((MainScreen)main.getContext()).setGameClass(this);
((LoadingScreen)load.getContext()).setGameClass(this);
}
public void changeMenu(MenuType nextMenu, MenuType previousMenu){
Intent intent2;
switch(nextMenu){
case MAIN_SC:
tempContext = main.getContext();
intent2.setClass(tempContext, main.getClass());
intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
tempContext.startActivity(intent2);
case GAME_LOADING_SC:
tempContext = load.getContext();
intent2.setClass(tempContext, load.getClass());
intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
tempContext.startActivity(intent2);
default:
break;
}
}
}
And here is SplashScreen activity:
public class SplashScreen extends Activity {
public Context context = this;
public GameManager gameman;
private static final int SPLASH_DURATION = 4000;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
splash();
menuOut();
}
public Context getContext() {
return this;
}
public void splash() {
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.HORIZONTAL);
ll.setBackgroundResource(R.drawable.game_loop_splash);
setContentView(ll);
Handler handler = new Handler();
// run a thread after 2 seconds to start the home screen
handler.postDelayed(new Runnable() {
#Override
public void run() {
finish();
}
}, SPLASH_DURATION);
}
public void setGameClass(GameManager game){
gameman = game;
}
private void menuOut(){
gameman.changeMenu(MenuType.GAME_LOADING_SC, MenuType.GAME_SPLASH_SC);
this.onDestroy();
}
}
I can not return to the GameManager and call the changeMenu method.
I am very exhausted to get null pointer exceptions.
Any idea?
From what I read, you are trying to implement a singleton pattern. There are two ways I'd recommend to do that on android:
Extend the Application class, register your class in the manifest and use getApplication() in your activities to get access to it:
// In MyApplicationSubclass.java:
public final class MyApplicationSubclass extends Application {
/* ... */
public void myMethod() {
// insert some code here
}
/* ... */
}
// From your Activity:
((MyApplicationSubclass) this.getApplication()).myMethod();
Use a "normal" java singleton pattern, e.g. use a private constructor and keep one static instance within your GameManager class (this is the way the Android docs recommend, but I personally prefer the first way when having something that is logically bound to the Application).
Also, if you're only using your central class to do static stuff, you can just mark all its method as static and access them directly. Transfer Context objects as parameters to these methods, and you should be able to start activities without any static variables (which are sometimes hard to implement properly in Android, as your VM might get restarted from time to time).
I simply have not found a solution to share a real-time data between the activitys. My first activity receives real-time object (type double, a random numbers). And i want to pas this numbers to second activity. It all works, only the second Activity shows only one time the data. I have to refresh the activity by going back to first activity and only then the second activity show the latest data. I implemented a Singelton pattern:
public class FirstActivity extends Activity{
public double xAxis;
public double yAxis;
public static FirstView instance;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.device_view);
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
int data1 = msg.arg1;
xAxis = (double) data1;
dataX.setText(String.valueOf(xAxis));
int data2 = msg.arg2;
yAxis = (double) data2;
dataY.setText(String.valueOf(yAxis));
}
};
secondview.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent nextScreen = new Intent(getApplicationContext(),
SecondActivity.class);
startActivity(nextScreen);
}
});
}
public static void initInstance(){
if(instance == null)
{
instance = new FirstActivity();
}
}
public static FirstActivity getInstance(){
return instance;
}
}
SecondView class
public class SecondActivity extends Activity{
private double valueX;
private double valueY;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.linegraph);
valueX = FirstActivity.getInstance().xAxis;
valueY = FirstActivity.getInstance().yAxis;
}
}
Application class
package com.bluetoothcomm;
import android.app.Application;
public class MyApplication extends Application {
#Override
public void onCreate(){
super.onCreate();
initSingeltons();
}
public void initSingeltons(){
FirstActivity.initInstance();
}
}
You may implement a background service capable of providing the real time data to Activity1 and also to Activity2. I am guessing that your problem ocurrs if you are passing data from Activity1 to Activity2 through an Intent with putExtras, on this way it will only do this at the moment you start Activity2.
I have found my problem and the solution is to use Application. Only this dose not solves my problem. The problem is that the static variable instance public static FirstView instance is bound to the class loader, the first class that initilize that. So when the static variable inside any class has been initilized by an Activity and when the second Activity is started the first Activity is destroyed, so this means the static variable is also uninitilized. Thats why the SecondActivity dose not gets the up to date data or real time data, it catches only static constant data.
I changed my code a littele bit with the combination of Singelton and Application, couse this way the static variable should never be uninitilized when SecondActivity is activated. But i still get the same results, the static variable instance is uninitilized when i swtich to Second Activity. I am doing somethink wrong, does any one sees it. I added my code.
#Max Rasguido, #Orabig
You should use the intent process. docs
How is your data supposed to change when activity2 is shown, if you say that it's received by activity1 ?
However, I would use a preference, or an attribute of your application class (which is a singleton itself), but you give too little informations to fully understand your needs...
I have one activity representing a screen that is used in two variations. The only difference is that in one case it's used to handle numbers in the other for colors. This is how it is declared:
> public class MainScreen extends Activity {
/** Called when the activity is first created. */
private Integer activityCode;
private static final int ACTIVITY_NUMBER = 0;
private static final int ACTIVITY_COLOR = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityCode = savedInstanceState != null ? savedInstanceState
.getInt("Task") : null;
if (activityCode == null) {
Bundle extras = getIntent().getExtras();
activityCode = extras != null ? extras.getInt("Task") : null;
}
do stuff depending on which activity is actually chosen
}
And this is how it is called
FROM WITHIN ITSELF
:
> #Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent i;
switch (item.getItemId()) {
case OTHER_PAGE_ID:
i = new Intent(this, MainScreen.class);
if (activityCode == ACTIVITY_NUMBER) {
i.putExtra("Task", ACTIVITY_COLOR);
startActivityForResult(i, ACTIVITY_COLOR);
finish();
} else {
i.putExtra("Task", ACTIVITY_NUMBER);
startActivityForResult(i, ACTIVITY_NUMBER);
finish();
}
return true;
....
}
Is this way of re-using the same class actually OK? I use the same class for very similar screens and want to switch back and forth depending on the user selection.
BUT the class calls itself everytime a different screen is selected between NUMBERS <-> COLOR.
The problem is, that when I go from NUMBERS to COLORS and then press back-arrow the app quits. However, when I go from NUMBERS to another screen and press back, it goes back to NUMBERS again.
Why doesn't going back to where I come from work in the case where the class calls itself? I would assume it just puts each call on the stack and comes back to it.
Isn't this just a mini recursion where NUMBER calls itself as COLOR and when finished appears again?
I hope I could make myself clear. Thanks for your help
That makes my head spin. Why not just use two Activities? If it's for code reuse, just have one base class with all your common code, then extend it for your Colour and Number classes:
public class Base extends Activity {
// common code here
}
public class Colour extends Base {
// colour specific code here
}
public class Number extends Base {
// number specific code here
}