Intent putExtra bitmap issue - android

I have the following code:
((ImageButton)findViewById(R.id.fish)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(StartupActivity.this, GameActivity.class);
intent.putExtra(NAME_EXTRA, ((EditText)findViewById(R.id.name)).getText().toString().trim());
intent.putExtra(TYPE_EXTRA, FishTypes.FISH.toString());
intent.putExtra(WORLD_TYPE_EXTRA, worldType);
intent.putExtra(LOGO_EXTRA, BitmapFactory.decodeResource(getResources(), R.drawable.logo));
startActivity(intent);
}
});
If I start activity and click on ImageButton it send me to the same Activity I was before (StartupActivity). However if I comment out the last putExtra like this:
//intent.putExtra(LOGO_EXTRA, BitmapFactory.decodeResource(getResources(), R.drawable.logo));
Then it works fine. It sends me to the GameActivity as I want. What could be the problem?
EDIT
My GameActivity looks like this:
public class GameActivity extends Activity {
public static GamePanel gamePanel;
public static String name;
public static FishTypes type;
public static String worldType;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(getWindow().FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
name = getIntent().getStringExtra(StartupActivity.NAME_EXTRA);
type = FishTypes.parse(getIntent().getStringExtra(StartupActivity.TYPE_EXTRA));
worldType = getIntent().getStringExtra(StartupActivity.WORLD_TYPE_EXTRA);
gamePanel = new GamePanel(this);
//gamePanel.bitmaps.put("logo", (Bitmap)getIntent().getParcelableExtra(StartupActivity.LOGO_EXTRA));
setContentView(gamePanel);
if(!StartupActivity.isNetworkAvailable()) {
Toast.makeText(StartupActivity.getInstance(), "You have no internet connection...", Toast.LENGTH_LONG).show();
finish();
}
}
#Override
protected void onDestroy() {
if(gamePanel.client != null)
gamePanel.client.disconnect();
StartupActivity.getInstance().reopen();
super.onDestroy();
}
}
What I want to achieve is preloading this bitmap in StartupActivity and then just send it to the GameActivity to the GamePanel and then draw it on the canvas as a loading image. I can't load this image in GameActivity because it will be late to show it. Do you understand?

So first of all, there is an Intent payload limit, as far as I know there is a limit of 1MB, but in some cases can be 500kb. If this limit is overreached the app will crash, in your case crashed and restarted.
Second of all, Bundle and Intent are used to send small amount of data to Activity and Fragments, usually some configs/params so that the activity/fragment will know how to build itself.
Third, is a really bad practice to pass around instances of bitmaps, you need just a second of distraction to create a huge leak in your app, that will cost you a lot of time to find and fix it.
Now the solution for you is really simple. You can pass the id of the bitmap you want to use in the next activity.
Intent intent = new Intent(StartupActivity.this, GameActivity.class);
intent.putExtra(NAME_EXTRA, ((EditText)findViewById(R.id.name)).getText().toString().trim());
intent.putExtra(TYPE_EXTRA, FishTypes.FISH.toString());
intent.putExtra(WORLD_TYPE_EXTRA, worldType);
intent.putExtra(LOGO_EXTRA, R.drawable.logo); // R.drawable.logo is actually an int.
startActivity(intent);
In your GameActivity
#Override
public void onCreate(Bundle savedInstanceState) {
setContentView(...)
int myAwesomeDrawable = getIntent().getExtra(LOGO_EXTRA, 0); // 0 is default value in case nothing is added to the key
if(myAwesomeDrawable != 0){ // safety check
imageView.setImageResource(myAwesomeDrawable);
// or do whatever you like with it.
}

Related

Android SavedState

I have tried every way possible to use SaveState in a prior post I was instructed to use finish() or use onBackPressed ok that saves the data on the MainActivity but I would like to understand how to use Bundle savedInstanceState below is my code arrangment Please comment what is wrong and offer suggested fixes My goal is to only restore one value in one of the EditText fields?
Yes I have looked at Adroid Developer and numerous other quality sites
public class MainActivity extends AppCompatActivity {
Button btnAdd;
Button brnNext;
EditText ETinput;
EditText ETans;
float X = (float) 10.0;
public final static String EXTRA = "com.dwight.thebigtest.pagetwo.MESSAGE";
public int sVarA;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
savedInstanceState.putInt("sVarA",sVarA);
btnAdd = (Button) findViewById(R.id.btnAdd);
brnNext = (Button) findViewById(R.id.btnNext);
ETans = (EditText) findViewById(R.id.ETans);
ETinput = (EditText) findViewById(R.id.ETinput);
addListenerOnButton_ADD();
}
#Override
protected void onRestoreInstanceState(Bundle InState) {
super.onRestoreInstanceState(InState);
if(InState != null) {
sVarA = InState.getInt("sVarA");
ETans.setText(String.valueOf(sVarA));
}
}
private void addListenerOnButton_ADD(){
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
float Y = Float.valueOf(ETinput.getText().toString().trim());
float Z = Y + X;
ETans.setText(String.valueOf(Z));
}
});
}
public void sendNext(View view) {
Intent intent = new Intent(MainActivity.this,PageTwo.class);
String message = ETans.getText().toString().trim();
intent.putExtra(EXTRA,message);
startActivity(intent);
}
}
This code makes the trip back from the secondActivity
private void addListenerOnButton_Back(){
btnBack.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(PageTwo.this,MainActivity.class);
startActivity(i);
//onBackPressed();// Less Screen Flash
}
});
}
}
This snippet caught my eye:
Intent i = new Intent(PageTwo.this,MainActivity.class);
startActivity(i);
//onBackPressed();// Less Screen Flash
You're making a new Intent to return to the MainActivity, but instead, it creates a new instance of MainActivity which is then added to the activity stack. When the new MainActivity is created, it has NO savedInstanceState: it's brand new.
onBackPressed and finish are the appropriate ways of finishing the current activity and returning to the previous one.
The purpose of savedInstanceState is to restore the state of an activity when it is destroyed by the system (for instance, due to lack of memory). If you simply return to the previous activity via the back button, savedInstanceState is not used because the activity is still in memory. No need to restore it.
If you enable "Don't keep activities" in your phone/emulator's Developer Options, Android will force-stop activities once abandoned. So when PageTwo is created and shown, since MainActivity is no longer in the foreground, it will be destroyed. This is where the instance state is saved. When you return to the same MainActivity (pressing back), the activity must be recreated using that instance state.
For more information, research about Android's activity lifecycle.

Android app crashes when retrieving int from an intent in another activity

I am trying to make a button in one activity (SetupMenu) that, when pressed, puts an int into the intent and carries that over to the next activity (IntroActivity) where a textView will retrieve the int and display it.
Problem is, when the app runs and I get to the activity and press the button, the app crashes and my emulator tells me that "Unfortunately [my app] has stopped working."
I feel like I've tested every possible angle to get this to work. I should note that the button has worked fine, the textview has worked fine, everything else is working smoothly - I only run into issues when I try retrieving the intent and displaying it in textView. I tried passing through a String instead of an Int and also had issues (my string would not appear). Any pointers?
SetupMenu activity (here I put an int into my intent):
public class SetupMenu extends Activity {
public final static String extra_progress_key = "com.example.angelsanddemons.track_players";
public int track_players = 0;
public void to_intro(View view) {
Intent intent = new Intent(this, IntroActivity.class);
intent.putExtra(extra_progress_key, track_players);
startActivity(intent);
}
IntroActivity activity (here I try to retrieve the int from the intent):
public class IntroActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
int temp = intent.getIntExtra(SetupMenu.extra_progress_key, 0 );
TextView textView = new TextView(this);
textView.setText(temp);
setContentView(textView);
}
}
One problem is that you can't set a TextView's text to an int; you'll need to first convert it to an string. It's also not a good idea to be manipulating views before you've inflated them, so perhaps your onCreate() should be:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
int temp = intent.getIntExtra(SetupMenu.extra_progress_key, 0 );
TextView textView = new TextView(this);
setContentView(textView);
textView.setText(String.valueof(temp));
}
I see nothing that ensure that SetupMenu activity is created and in memory when IntroActivity is launched. To make sure, don't pass the variable, but the string itself and check if it work:
int temp = intent.getIntExtra("com.example.angelsanddemons.track_players", 0 );

Changing activity in android clears the memory needed for the previous activities?

I am developing a multi level game, where each level is a new activity.
I want to know, if i change the activity like
Intent myIntent = new Intent(getBaseContext(), Level3.class);
startActivity(myIntent);
The memory used for Level 1 and 2 is cleared?
If not, how can I clear everything from previous level activities so the phone uses just the memory just for the current activity ?
You need to call finish() for the activity (or activities) that you no longer want to be active. You can simply call it right after starting the new activity:
Intent myIntent = new Intent(getBaseContext(), Level3.class);
startActivity(myIntent);
finish();
Otherwise, the previous activity will remain on the activity stack.
since you are using an activity/level design, just add a check if your activity is finishing in your onPause method, and null all your references to the current level, that way the GC will know that your level object should be released, and it will release it as soon as possible.
#Override
public void onPause(){
super.onPause();
if (isFinishing()){
levelObject = null;
}
}
I would not recommend you to create Activity for each of your game level. Would be better to create some Controller that will initializate you game levels in one Activity. And of cource it must have some methods to clear memmory from last stage, something like this :
class StageManager
{
public Stage curStage;
public initStage(Stage stage)
{
//init stage here
curStage = stage;
stage.init();
}
public clearStage()
{
//do some clearing staff
curStage .clear();
}
}
abstract class Stage
{
public abstract init();
public abstract clear();
}
abstract class FirstStage extends Stage
{
//....
}
abstract class SecondStage extends Stage
{
//....
}
In Activity :
StageManager stageManager = new StageManager();
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.your_view);
stageManager.init(new FirstStage());
}
#Override
public void onClick(View theView)
{
int id = theView.getId();
if (id == R.id.btnNextLevel) {
stageManager.clear();
stageManager.init(new SecondStage());
}
}
Instead of your custom manager, you can use fragmets :
http://developer.android.com/training/basics/fragments/creating.html
http://developer.android.com/training/basics/fragments/fragment-ui.html
In both ways - fragments or yout own manager you will seperate different stages logic to different classes.
Youd don't need to create another Activity to seperate yours 1000+ lines code. Just use one of Stage or Stratagy desing patters.
And if you still want to use Activities just do something like this :
Intent myIntent = new Intent(getBaseContext(), Level3.class);
startActivity(myIntent);
finish();
and in onDestroy() :
#Override
protected void onDestroy()
{
//here you must clear all data that were used in this Stage (Activity) like this :
clearMemmory();
super.onDestroy();
}
private void clearMemmory()
{
if(stageData!=null)
{
stageData.clear();
stageData =null;
}
}
or clear memmory directly before opening another Stage, something like :
clearMemmory();
Intent myIntent = new Intent(getBaseContext(), Level3.class);
startActivity(myIntent);
finish();
Best wishes.

Crash when creating Activity

Good evening Stack !
I have started to learn Android development as a hobby and I am now trying to develop my first "real" application (I have made already only five simple applications from books).
In this application, I have two buttons that will "create" the same Activity but by using two different objects from the same base class, hence allowing me to customize the behavior of the application depending on the button that was clicked.
However, when I am trying to create the Intent instance, my application crashes.
Here is the code of the base Activity class
public class BaseDictionnaryActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.basedictionnary);
}
public void onDestroy()
{
super.onDestroy();
}
}
and here is the code that crashes. The line is the one creating the Intent object.
public class DictionnaryActivity extends Activity
{
private BaseDictionnaryActivity jlpt1;
private BaseDictionnaryActivity jlpt2;
private Button btjlpt1 = null;
private Button btjlpt2 = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.dictionnary);
jlpt2 = new BaseDictionnaryActivity();
jlpt1 = new BaseDictionnaryActivity();
btJLPT1 = (Button)findViewById(R.id.jlpt1);
btJLPT1.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Intent myIntent = new Intent(DictionnaryActivity.this,
jlpt1.getClass());
jlpt1.this.startActivity(myIntent);
}
});
btJLPT2 = (Button)findViewById(R.id.jlpt2);
btJLPT2.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Intent myIntent = new Intent(DictionnaryActivity.this,
jlpt2.getClass());
jlpt2.this.startActivity(myIntent);
}
});
}
public void onDestroy()
{
super.onDestroy();
}
}
Thank you for your help !
Just to make correction,
Intent myIntent = new Intent(DictionnaryActivity.this,
jlpt1.getClass());
In this the second argument must be, your target activity BaseDictionnaryActivity.class
So, it looks something like,
Intent myIntent = new Intent(DictionnaryActivity.this,
BaseDictionnaryActivity.class);
startActivity(myIntent);
Also please make sure there will be entry of BaseDictionnaryActivity in your Application's manifest file,
Which is look like,
<activity android:name=".BaseDictionnaryActivity"
....>
</activity>
Maybe:
Intent myIntent = new Intent(DictionnaryActivity.this,
BaseDictionnaryActivity.class);
startActivity(myIntent);
change this
Intent myIntent = new Intent(DictionnaryActivity.this,
NextActivity.class);
Intent myIntent = new Intent(DictionnaryActivity.this,
jlpt2.class);
^^^^^^^^^^^^
You need to provide next activity .class in second argument of Intent.
Replace jlpt1.getClass() with NameOfClassToBeLaunched.class
Also this is bad practice to create Activity instances in other activities.

Change tabhost tab title in a different activity

I have a main activity that hold the tabs and each tab start a new activity. May I know how can I change the tab title from the new activity? Thanks.
Although CommonsWare has pointed out that having Activities as Tab content is deprecated, if you still want to do it then one possibility is to use a nested BroadcastReceiver and have the content Activity send a broadcast Intent. I'm not sure if it will work but I would try something like the following...
public class MainActivity extends Activity {
bool tabMonitorIsRegistered = false;
TabMonitor tabMonitor = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code
tabMonitor = new TabMonitor();
}
#Override
protected void onResume() {
super.onResume();
if (!tabMonitorIsRegistered) {
registerReceiver(tabMonitor, new IntentFilter(Intent.com.mydomain.myapp.ACTION_TAB_CHANGE));
tabMonitorIsRegistered = true;
}
}
#Override
protected void onPause() {
super.onPause();
if (tabMonitorIsRegistered) {
unregisterReceiver(tabMonitor);
tabMonitorIsRegistered = false;
}
}
// Nested BroadcastReceiver
private class TabMonitor extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent arg1) {
// Process the Intent here to change the tab title
}
}
}
At this point it occurs to me that each 'content' Activity will need to tell the MainActivity (via the Intent it sends) 'who' it is. To do this, I would use an Intent extra when adding the tab content Activities identifying each as 'tab1', tab2' etc. When the 'content' Activities start, e.g., in onCreate(), they can store this string and use it in the Intent they send as the broadcast to the MainActivity.

Categories

Resources