I'm developing an app which counts number of times the app goes to the background. It also retains the values when the orientation is changed. Though I have it working of all use cases, I have one use case which does not work.
Case : When I press the home button, change the phone's orientation and reopen the app, It does open in landscape mode but, the background count does not increase.
I have tried setting values in all the life cycle methods. It doesn't work. Hope somebody can help me with this.
`
public class MainActivity extends AppCompatActivity {
private int clickCount =0, backgroundCount = 0;
TextView tvClickCountValue, tvBackgroundCountValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if( savedInstanceState != null){
clickCount = savedInstanceState.getInt("COUNT");
backgroundCount = savedInstanceState.getInt("BGCOUNT");
}
setContentView(R.layout.activity_main);
tvClickCountValue = (TextView) this.findViewById(R.id.tvClickCountValue);
tvBackgroundCountValue = (TextView) this.findViewById(R.id.tvBackgroundCountValue);
setView(MainActivity.this);
}
public void onClick(View v){
clickCount += 1;
tvClickCountValue.setText(Integer.toString(clickCount));
}
public void setView(Context ctx){
tvClickCountValue.setText(Integer.toString(clickCount));
tvBackgroundCountValue.setText(Integer.toString(backgroundCount));
}
#Override
protected void onStop() {
super.onStop();
backgroundCount += 1;
}
#Override
protected void onResume() {
super.onResume();
tvClickCountValue.setText(Integer.toString(clickCount));
tvBackgroundCountValue.setText(Integer.toString(backgroundCount));
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("COUNT", clickCount);
outState.putInt("BGCOUNT", backgroundCount);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
clickCount = savedInstanceState.getInt("COUNT");
backgroundCount = savedInstanceState.getInt("BGCOUNT");
}
}
This article contains useful information: https://developer.android.com/guide/topics/resources/runtime-changes.html especially in the section about Handling The Change.
Try handling it on onConfigurationChanged() of you have disabled Activity restarts on orientation changes. Otherwise, probably through this article you will find which case applies to your scenario.
By reading the problem description, I am assuming that if you don't rotate the device the application works as intended.
you need to persist the count in the SharedPreferences. each time you reopen the app read the last value from the SharedPreferences. And increment and save to SharedPreferences each time the app is hidden.
Then you can count the with #kiran code:
public class BaseActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public static boolean isAppInFg = false;
public static boolean isScrInFg = false;
public static boolean isChangeScrFg = false;
#Override
protected void onStart() {
if (!isAppInFg) {
isAppInFg = true;
isChangeScrFg = false;
onAppStart();
}
else {
isChangeScrFg = true;
}
isScrInFg = true;
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
if (!isScrInFg || !isChangeScrFg) {
isAppInFg = false;
onAppPause();
}
isScrInFg = false;
}
public void onAppStart() {
// app in the foreground
// show the count here.
}
public void onAppPause() {
// app in the background
// start counting here.
}
}
Related
I`v been trying for a while to destroy activity when the home button is pressed or when the app is in background and I manage to do something with this code
#Override
protected void onPause() {
this.finish();
super.onPause();
}
and when the app runs it does not crash but after a while playing in the activity the app just goes background and crashes.
This is the activity that I`m talking about:
public class Playing extends AppCompatActivity implements View.OnClickListener {
final static long INTERVAL=1154;//1 second
final static long TIMEOUT=7000;
int progressValue = 0;
CountDownTimer mcountDown;
List<Question> questionPlay = new ArrayList<>();
DbHelper db;
int index=0 , score =0,thisQuestion=0,totalQuestion,CorrectAnswer;
String mode = "";
ProgressBar progressBar;
ImageView imageView;
Button btnA, btnB, btnC,btnD;
TextView txtScore,txtQuestion;
#Override
protected void onPause() {
this.finish();
super.onPause();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playing);
Bundle extra = getIntent().getExtras();
if(extra!=null)
mode =extra.getString("MODE");
db = new DbHelper(this);
txtScore = (TextView)findViewById(R.id.txtScore);
txtQuestion =(TextView)findViewById(R.id.txtQuestion);
progressBar=(ProgressBar) findViewById(R.id.progreessBar);
btnA = (Button) findViewById(R.id.btnAnswerA);
btnB =(Button)findViewById(R.id.btnAnswerB);
btnC = (Button)findViewById(R.id.btnAnswerC);
btnD = (Button)findViewById(R.id.btnAnswerD);
imageView = (ImageView)findViewById(R.id.question_bike);
btnA.setOnClickListener(this);
btnB.setOnClickListener(this);
btnC.setOnClickListener(this);
btnD.setOnClickListener(this);
}
#Override
protected void onResume() {
super.onResume();
questionPlay = db.getQuestionMode(mode);
totalQuestion = questionPlay.size();
mcountDown = new CountDownTimer(TIMEOUT, INTERVAL) {
#Override
public void onTick(long millisUntilFinished) {
progressBar.setProgress(progressValue);
progressValue++;
}
#Override
public void onFinish() {
mcountDown.cancel();
showQuestion(++index);
}
};
showQuestion(index);
}
private void showQuestion(int index) {
if (index < totalQuestion){
thisQuestion++;
txtQuestion.setText(String.format("%d %d",thisQuestion,totalQuestion));
progressBar.setProgress(0);
progressValue = 0;
int ImageID = this.getResources().getIdentifier(questionPlay.get(index).getImage().toLowerCase(),"drawable",getPackageName());
imageView.setBackgroundResource(ImageID);
btnA.setText(questionPlay.get(index).getAnswerA());
btnB.setText(questionPlay.get(index).getAnswerB());
btnC.setText(questionPlay.get(index).getAnswerC());
btnD.setText(questionPlay.get(index).getAnswerD());
mcountDown.start();
}
else {
Intent intent = new Intent(this,Done.class);
Bundle dataSend = new Bundle();
dataSend.putInt("SCORE", score);
dataSend.putInt("TOTAL",totalQuestion);
dataSend.putInt("CORRECT",CorrectAnswer);
intent.putExtras(dataSend);
startActivity(intent);
finish();
}
}
#Override
public void onClick(View v) {
mcountDown.cancel();
if (index < totalQuestion){
Button clickedButton = (Button)v;
if(clickedButton.getText().equals(questionPlay.get(index).getCorrectAnswer())){
score+=1;
CorrectAnswer++;
showQuestion(++index);
}
else showQuestion(++index);
txtScore.setText(String.format("S:%d",score));
}
}
#Override
public void onBackPressed() {
Intent intent = new Intent(Playing.this ,MainActivity.class) ;
startActivity(intent) ;
finish() ;
}
}
I`v tried and with this code
#Override
protected void onDestroy(){
super.onDestroy;
finish();
}
but then the second activity does not start and the app crashes again.
So what to do?
I am using a countdown timer which updates my progress bar which decreases from 100 (full) to 0(empty) but when the app is paused would like to pause the timer and then restart it upon resuming the app. I implement the count down timer like so:
public class MyCountDownTimer extends CountDownTimer {
public MyCountDownTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onTick(long millisUntilFinished) {
int progress = (int) (millisUntilFinished / 100);
progressBar.setProgress(progress);
}
#Override
public void onFinish() {
progressBar.setProgress(0);
Intent intent = new Intent(getApplicationContext(), GameOver.class);
startActivity(intent);
finish();
}
}
And in my on create:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_screen);
progressBar = (ProgressBar)findViewById(R.id.timerBar);
progressBar.setProgress(100);
myCountDownTimer = new MyCountDownTimer(10000, 10);
myCountDownTimer.start();
}
You can add a boolean in your MyCountDownTimer class that will change in the onPause and onResume methods of your Activity. Check the // ADDED comments to see the changes you have to make to make this work.
public class MyActivity extends Activity {
MyCountDownTimer myCountDownTimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_screen);
progressBar = (ProgressBar)findViewById(R.id.timerBar);
progressBar.setProgress(100);
myCountDownTimer = new MyCountDownTimer(10000, 10);
myCountDownTimer.start();
}
// ADDED
#Override
public void onPause() {
super.onPause(); // Always call the superclass method first
myCountDownTimer.pause();
}
// ADDED
#Override
public void onResume() {
super.onResume(); // Always call the superclass method first
myCountDownTimer.resume();
}
}
public class MyCountDownTimer extends CountDownTimer {
// ADDED
private boolean pause;
public MyCountDownTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
// ADDED
public void pause() {
pause = true;
}
// ADDED
public void resume() {
pause = false;
}
#Override
public void onTick(long millisUntilFinished) {
int progress = (int) (millisUntilFinished / 100);
// ADDED
if (!pause) {
progressBar.setProgress(progress);
}
}
#Override
public void onFinish() {
// ADDED
isRunning = false;
progressBar.setProgress(0);
Intent intent = new Intent(getApplicationContext(), GameOver.class);
startActivity(intent);
finish();
}
}
I'm new to Android Studio and currently working on my first app. Thanks to numerous examples on here i have managed to catch up on the basics very quickly. I now have a problem with my autocompletetextview, i want to get the text selected then use it in a if statement to determine what is shown on the "results" textview. The if statement works fine but only for the "else" part and i can't figure out where i went wrong.
I have commented out the onItemClickListener because the "testfoodph" method does the same.
Here is the activity code:
public class phTester extends AppCompatActivity {
Intent intent = getIntent();
AutoCompleteTextView text;
MultiAutoCompleteTextView text1;
TextView results;
String foodies;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ph_tester);
String[] foods = getResources().getStringArray(R.array.foodlist);
text=(AutoCompleteTextView)findViewById(R.id.autoCompleteTextView);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,foods);
text.setAdapter(adapter);
text.setThreshold(1);
results=(TextView)findViewById(R.id.phResults);
foodies = text.getText().toString();
text.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//if (foodies.equalsIgnoreCase("Mango")) {
//results.setText(getString(R.string.phAlk));
}
//else {
//results.setText(getString(R.string.Error));
//}
//}
});
}
public void TestFoodPH(View view) {
if (foodies.equalsIgnoreCase("Mango")) {
results.setText(getString(R.string.phAlk));
}
else {
results.setText(getString(R.string.Error));
}
}
I run my code and rotate the phone couple of times, then dump memory and analyze it.
Below is my code:
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
LogUtils.e("111");
}
};
private boolean mScanning = false;
private BluetoothManager bm;
private void scanLeDevice(final boolean enable) {
LogUtils.e(enable);
try {
if (enable) {
mScanning = true;
if(bm.getAdapter()!=null)bm.getAdapter().startLeScan(mLeScanCallback);
} else {
mScanning = false;
if(bm.getAdapter()!=null)bm.getAdapter().stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}catch (Throwable e){
}
}
#Override
protected void onDestroy() {
super.onDestroy();
scanLeDevice(false);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initStage();
}
#Override
protected void initStage() {
bm = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
scanLeDevice(true);
}
The java heap:
The LeScanCallback is holding a reference to the Activity. I just ran into this when testing the BluetoothLeGatt sample provided by Google. I copied the scanning code into my app and I suddenly found massive leaks occurring.
I solved it by wrapping the scan callback into a static class which then holds a weak reference to the activity. Much like Google reccomends when using a Handler. Like this:
private final BluetoothAdapter.LeScanCallback mLeScanCallback = new LeScanCallbackClass(this);
private static final class LeScanCallbackClass implements BluetoothAdapter.LeScanCallback {
private String TAG = makeLogTag(LeScanCallbackClass.class.getName());
private final WeakReference<TrackActivity> mAct;
public LeScanCallbackClass(TrackActivity act) {
mAct = new WeakReference<>(act);
}
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
LOGI(TAG, String.format("BLE LeScan Result: %s", device.getAddress()));
final TrackActivity act = mAct.get();
act.runOnUiThread(new Runnable() {
#Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
act.sendNotification();
}
}
I'm trying to create simple app which contains of ten activities. Each activity look pretty much the same, it has four buttons (different color) and when one particular button is clicked it's open next activity. OnCreate method of every activity has mediaplayer that play name of that activity. After some time I see in LogCat that gc freed some objects and at that time Activity don't play any sound and buttons are disabled.
Do you have any advice or suggestions how to resole that?
Here is code of one Activity:
public class Green extends Activity {
int buttonActive = 0;
int buttonWrong = 0;
MediaPlayer player;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_green);
buttonWrong = 0;
playSound();
Button btn = (Button)findViewById(R.id.button1);
Button btn2 = (Button)findViewById(R.id.button2);
Button btn3 = (Button)findViewById(R.id.button3);
Button btn4 = (Button)findViewById(R.id.button4);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(buttonActive == 1){
playSoundCorrect();
Intent intent = new Intent(v.getContext(),Blue.class);
startActivity(intent);
}
}
});
btn2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(buttonActive == 1 && buttonWrong == 0){
buttonWrong = 1;
playSoundWrong();
}
}
});
btn3.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(buttonActive == 1 && buttonWrong == 0){
buttonWrong = 1;
playSoundWrong();
}
}
});
btn4.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(buttonActive == 1 && buttonWrong == 0){
buttonWrong = 1;
playSoundWrong();
}
}
});
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
finish();
}
private void playSound(){
MediaPlayer player = MediaPlayer.create(this, R.raw.green);
player.start();
player.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
buttonActive = 1;
}
});
}
private void playSoundWrong(){
player = MediaPlayer.create(this, R.raw.wrong2);
if(!player.isPlaying()){
player.start();
}
player.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
buttonWrong = 0;
}
});
}
private void playSoundCorrect(){
MediaPlayer player = MediaPlayer.create(this, R.raw.correct);
player.start();
player.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
}
}
It looks like in private void playSound you're masking the class's player property. If you say player = MediaPlayer.create then the class's player property will be set to the new MediaPlayer, and the player will be reachable so long as the class is reachable. Instead you're saying MediaPlayer player = MediaPlayer.create, which is creating a new player variable that is scoped to the playSound method - once the method terminates the player is no longer reachable and will be garbage-collected, even if the class is still reachable.