I am trying to work out how I can simulate pausing an activity for debugging my app. I want onPause to be called but NOT onStop. I just want to try a pause resume cycle and am looking for some code i can call (e.g. after a button press) to trigger this.
Any ideas how?
I have seen people suggest pressing the home button in other threads but when I do this is stops the app and calls onStop as well as onPause so it isn't quite what I was looking for.
Taken from this link: The easiest is to add a semitransparent activity on top of your activity. I did the test myself and onStop is not called indeed:
The transparent activity:
public class TransparentActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.transparent_layout);
}
}
any simple layout can be used for transparent_layout, but the tricky part is in the Manifest:
<activity
android:name=".TransparentActivity"
android:theme="#style/Theme.Transparent" >
</activity>
where in styles.xml:
<style name="Theme.Transparent" parent="android:Theme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
Then in starter activity:
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnNext).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, TransparentActivity.class));
}
});
}
#Override
protected void onPause() {
super.onPause();
Log.d("TSTAct", "#onPause");
}
#Override
protected void onStop() {
super.onStop();
Log.d("TSTAct", "#onStop");
}
#Override
protected void onResume() {
super.onResume();
Log.d("TSTAct", "#onResume");
}
#Override
protected void onStart() {
super.onStart();
Log.d("TSTAct", "#onStart");
}
}
When opening the TransparentActivity I can see in the Logcat only:
07-10 23:35:28.323: D/TSTAct(27180): #onPause
no onStop call.
Simple way, From Mainactivity i will call another activity using Intent. In Manifest i will define as
<activity android:name=".AnotherActivity"
android:theme="#style/Theme.AppCompat.Dialog" >
</activity>
Means for another activity i will add style as "Theme.AppCompat.Dialog" means this will looks as Dialog.
From Main activity if you call this using Intent , then "AnotherActivity" will show as Dialog , It will come in on top of Main activity, that time main activity will be in onPause state (It will not call onStop state of MainActivity)
1) define a method.
public static Instrumentation callLifeCycleMethod()
{
return new Instrumentation();
}
2) call method.
callLifeCycleMethod().callActivityOnDestroy(MainActivity.this);
or
callLifeCycleMethod().callActivityOnPause(MainActivity.this);
Another way to debug what happens with onPause and onResume is to make use of the Android test framework. In an activity test you can get an Instrumentation object and then trigger pause and resume:
import android.app.Instrumentation;
import android.test.ActivityInstrumentationTestCase2;
public class LaunchActivityTest extends ActivityInstrumentationTestCase2<LaunchActivity> {
private LaunchActivity launchActivity;
public LaunchActivityTest() {
super(LaunchActivity.class);
// TODO Auto-generated constructor stub
}
#Override
protected void setUp() throws Exception {
super.setUp();
launchActivity = getActivity();
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
}
public void testPause() {
Instrumentation ins = getInstrumentation();
ins.callActivityOnPause(launchActivity);
assertEquals(true, true);
ins.callActivityOnResume(launchActivity);
}
}
Note that I am just using a dummy assert here. As an aside I am not yet sure how i can assert that the methods were called in Java
Another option might be to call the private method performPause in Activity using reflection.
Something like this should work in principle:
Method method = myactivity.getClass().getSuperclass().getDeclaredMethod("performPause");
method.setAccessible(true);
method.invoke(myactivity);
Note I have yet to test it
Related
As the title says, I want to get the reference of currently visible activity or you can say activity which is at the top of backstack from a class, I don't want to send activity reference to that class, because I am using that class from many activities and if I do, I have to pass activity reference from every activity which is a long process.
I already have seen many answers which are typecasting context reference to activity but it is not working.
If anyone has the idea of how to do that in a short way, then please share.
I don't know what you mean by "long process".
Normally, if the Activity is delegating work to another class, it needs to pass itself as a reference so that the delegate knows how to call back the Activity to report progress, etc. This is standard Android stuff. AsyncTask works like this (as an example).
However, if all you want to do is display a Dialog, then you can, instead, start an Activity that looks like a Dialog. This is also a pretty common Android solution. There are themes that you can apply to an Activity that make it look just like a Dialog.
I have found a way via we can do this, In your application class add: registerActivityLifecycleCallbacks(this will listen to all activity lifecycle methods), like this:
public class MyApplication extends Application {
public static Activity currentActivity=null;
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(#NonNull Activity activity, #Nullable Bundle bundle) {
}
#Override
public void onActivityStarted(#NonNull Activity activity) {
currentActivity=activity;
}
#Override
public void onActivityResumed(#NonNull Activity activity) {
}
#Override
public void onActivityPaused(#NonNull Activity activity) {
}
#Override
public void onActivityStopped(#NonNull Activity activity) {
}
#Override
public void onActivitySaveInstanceState(#NonNull Activity activity, #NonNull Bundle bundle) {
}
#Override
public void onActivityDestroyed(#NonNull Activity activity) {
}
});
}
}
And then use it like:
if (MyApplication.currentActivity!=null){
// your code here
}
Don't forget to add your application class to manifest:
<application
android:name=".MyApplication"
.../>
Problem: I need to run some code at every start before my app is ready to be used.
At first, I tried doing it in a dedicated activity.
AndroidManifest.xml
<activity android:name=".MainActivity" />
<activity android:name=".StarterActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
AppLoader.java
public class AppLoader {
private static Object someInstance;
public static void load(Runnable onCompleteCallback) {
try {
someInstance = new Object();
//potentially long operation to initialize the app
Thread.sleep(5000);
onCompleteCallback.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void checkInitialized() {
if (someInstance == null) {
throw new RuntimeException("Not initialized");
}
}
}
StarterActivity.java
public class StarterActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppLoader.load(() -> {
MainActivity.start(this);
finish();
});
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static void start(Context context) {
Intent starter = new Intent(context, MainActivity.class);
context.startActivity(starter);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppLoader.checkInitialized();
}
}
This works fine if the app is cold started via the launcher icon but crashes in all other cases. Simple way to reproduce the issue:
Go to developer settings on your device and set "Background process limit" to "No background process"
Open the app
Open some other app
Open the app again. Result: it crashes.
Here's an article describing a similar problem: Android process death — and the (big) implications for your app
Possible solutions:
Lazy loading/reactive approach. I try to use it as much as possible but there is always some code I need to run in a blocking way before user can interact with the app so this is not enough.
Putting all of that code in App.onCreate(). This would probably work for small apps but I've seen large apps that take 5-10 seconds to initialize, and I doubt they use onCreate() for that. Possible downsides: ANR and/or excessive startup time in Android Vitals?
Checking if the app is initialized in a BaseActivity, but that would require either blocking onCreate or managing lifecycle callbacks manually which doesn't sound like a good idea.
So, what's the proper way to run some code every time the app is launched?
Note: Normally StarterActivity would be a splash screen, AppLoader would be injected, etc, but I left that out for simplicity.
AndroidManifest.xml
<application
android:name=".AppLoader"
AppLoader.java
public class AppLoader extends Application {
private static Object someInstance;
#Override
public void onCreate() {
super.onCreate();
// DO YOUR STUFF
}
}
Update
- Use Handler with splash screen.
public class StarterActivity extends AppCompatActivity {
private Handler handler;
private Runnable myStuffRunnable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler();
myStuffRunnable = new Runnable(){
public void run(){
// DO MY STUFF
MainActivity.start(this);
}
};
}
#Override
protected void onPause() {
handler.removeCallbacks(myStuffRunnable);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
handler.post(myStuffRunnable);
}
#Override
protected void onDestroy() {
handler.removeCallbacks(myStuffRunnable);
super.onDestroy();
}
}
Your app is throwing the RuntimeException you set in AppLoader.checkInitialized() method, because your someInstance object is losing it's state when the app goes to background and gets killed by the system ('cause you have set your device to hold zero background threads). So, when you try to reopen the app, the system launches MainActivity directly (and not StarterActivity) because it is trying to restore it's previous state. But variables are not restored, not even static variables.
So, if you need the Object someInstance on your MainActivity, you should integrate it's instantiation into MainActivitie's lifecycle, overriding methods like onSavedInstanceState, onRestoreInstanceState, etc, to properly handle and reaload this object if your app gets killed by the system.
Take a look on this https://developer.android.com/guide/components/activities/activity-lifecycle
If anyone's interested, I ended up just redirecting the user to StarterActivity if needed to make sure the necessary code is executed at every start.
public abstract class BaseActivity extends AppCompatActivity {
private boolean isCreated;
#Override
protected final void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!appLoader.isLoaded()) {
StarterActivity.start(this);
finish();
return;
}
onCreateActivity(savedInstanceState);
isCreated = true;
}
protected void onCreateActivity(#Nullable Bundle savedInstanceState) {
}
#Override
protected final void onDestroy() {
super.onDestroy();
if (isCreated) {
onDestroyActivity();
}
}
protected void onDestroyActivity() {
}
}
All activities extend BaseActivity (except StarterActivity) and override onCreateActivity/onDestroyActivity instead of onCreate/onDestroy.
I want to display splash screen in android app. But I want to execute onCreate() method of MainActivity behind the splash screen. because i am doing huge work in this method. Anyone can tel me how to do that.
Basically you want to do some work in background while user is shown some splash screen, right ? What you need is an Async Task or a Loader kind of thing.
Step 1: Display the splash screen.
Step 2: Start an Async task and do all your heavy processing in the doInBackground method of Async Task
Step 3: Update the UI using the onPostExecute method of Async Task. In this method, first close the timer to splash screen. Then send the intent to start another screen with the data of the heavy processed result of Async task. Display it on UI thread.
Only Showing Splash screen is very simple. This code creates a splash screen of 3 seconds and then sends an Intent to another activity.
public class SplashScreenActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
CountDownTimer cdt1 = new CountDownTimer(3000, 1000) {
Boolean checkInternetConnection = false;
#Override
public void onTick(long l) {
}
#Override
public void onFinish() {
//Send Intent here
Intent i = new Intent(getApplicationContext(),
anotherActivity.class);
startActivity(i);
}
}.start();
}
PS- Dont forget to make the activity with this code as launcher activity from manifest file.
You can try this for Splash Screen...
public class SplashScreen extends Activity {
//Further Needed Declarations
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
/**
* Showing splashscreen while making network calls to download necessary
* data before launching the app Will use AsyncTask to make http call
*/
new PrefetchData().execute();
}
}
This is exactly the same way you wanted it.
create file in drawable
<item
android:drawable="#color/gray"/>
<item>
<bitmap
android:gravity="center"
android:src="#mipmap/ic_launcher"/>
</item>
</layer-list>
Styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">#drawable/background_splash</item>
</style>
</resources>
Activity:
public class SplashActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
}
}
You can add this in your onCreate Method
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// going to next activity
Intent i=new Intent(SplashScreenActivity.this,MainActivity.class);
startActivity(i);
finish();
}
},time);
And initialize your time value in milliseconds as yo want...
private static int time=5000;
for more detail download full code from this link...
https://github.com/Mr-Perfectt/Splash-Screen
I can't for the life of me figure out how to manage dialogs without using configChanges to specify that you want to manually handle orientation changes.
So lets say you have this AndroidManifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testandroid"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Take this MainActivity.java:
package com.example.testandroid;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
private final static String TAG = "MainActivity";
Dialog mDialog = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(R.layout.activity_main);
}
public void doShowDialog(View b) {
Log.d(TAG, "doShowDialog");
showDialog(1);
}
private void tryDismiss() {
Log.d(TAG, "tryDismiss");
try {
dismissDialog(1);
removeDialog(1);
mDialog.dismiss();
} catch(IllegalArgumentException ex) {
Log.e(TAG, ex.getMessage());
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
protected void onPause() {
tryDismiss();
super.onPause();
Log.d(TAG, "onPause");
}
#Override
protected Dialog onCreateDialog(int dialog) {
Builder b = new AlertDialog.Builder(this);
b.setTitle("Hello").setMessage("Waiting..");
mDialog = b.create();
return mDialog;
}
}
and this layout (main.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Open Dialog"
android:onClick="doShowDialog"
/>
</LinearLayout>
It doesn't seem to matter if you call from onDestroy or onPause, the dialog shows back up after the orientation switches. But why? I told it to go away. If call removeDialog/dismissDialog it does nothing when called before the orientation changes. I can't figure out for the life of me why this is. The only way to get rid of this that I know of is to handle the orientation change yourself by using
android:configChanges="keyboardHidden|orientation"
I know the new way of working is to use the FragmentDialog stuff which I have not upgraded to yet and am not ready to rewrite my whole app for that. Just seems strange that this doesn't work.
This is just an example of a real world problem I'm having in my app where the user can request some data be pulled from a remote server (to update a spinner's data), and if they switch orientation the loading dialog will never go away and there seems to be no fix for this besides handling the orientation change with the android:configChanges option. Which I can do but it seems ridiculous to me to have to do that.
-- Update --
Removed the button to dismiss the dialog as it's not necessary and you can't click it anyways since the dialog is on top.
To reproduce just start the app, click the button that opens the dialog, and then rotate your phone.
Your dialog is saved in onSaveInstanceState, so you might try dismissing it before it's launched:
#Override
protected void onSaveInstanceState(Bundle state)
{
tryDismiss();
super.onSaveInstanceState(state);
}
Also I don't really understand why do you use Activity's onCreateDialog to manage dialogs. The reason it was designed was to handle orientation changes automatically. If you want to handle it manually, why don't you just use dialog's functions? Instead of using showDialog(id) and onCreateDialog(id) just launch it directly, it won't reappear after rotating the screen.
Builder b = new AlertDialog.Builder(this);
b.setTitle("Hello").setMessage("Waiting..");
Dialog mDialog = b.create();
mDialog.show(); // <-----
Sebastian got it but I'd like to explain a bit more about the situation and my findings on dialogs since it can be quite confusing:
As sebastian put it, you must call dismissDialog/removeDialog from
onSaveInstanceState if you want the dialog gone before the rotation.
While you can create a dialog from onCreate, if you don't dismiss it before the orientation
change you won't be able to dismiss in the onCreate method when the activity restarts. You must call dismissDialog from onPostCreate
Calling dismissDialog didn't work in onRestoreInstanceState after orientation change as well. I tried both before and after calling super.onRestoreInstanceState and neither worked (thought it would since dimissing happens in onSaveInstanceState)
Even more important than this is that I learned if you are doing some Asynchronous task, such as an HTTP call and you have an inner class which contains a callback function which will run when the task is complete, you need to be aware that that inner class method will contain a reference to the original instance of the outer Activity class if the screen is rotated. This was not at all obvious to me as and I wasn't actually using AsyncTask as many others have had issues with (I was using an asynchronous http library).
Taking a small example similar to my real code:
public class MyActivity extends Activity {
private static MyActivity sThis;
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
sThis = this;
doAsyncWork();
}
#Override
public void onDestroy() {
super.onDestroy();
sThis = null;
}
private void doAsyncWork() {
showDialog(LOADING_DIALOG);
ExampleAsyncWorker.work(new AsyncWorkerCallback() {
#Override
public void onWorkComplete() {
dismissDialog(LOADING_DIALOG); //doesn't work if orientation change happened.
}
});
}
}
The above code, through the CategoryManager, connects to an external server, downloads a list of categories, and once it is complete calls onCategoriesObtained and then onFetchComplete (also there are some error handling callback function removed for brevity). If an orientation change happens between the fetchCategories call and onFetchComplete then the call to dismissDialog in onFetchComplete will never work. The reason is that this inner class has an implicit reference to the original instance of the Activity class which was created before the orientation change. Thus, when you call dismissDialog you are calling it on the original instance not the new one, which will cause the dismissDialog to fail with the message: "no dialog with id 1 was ever shown via Activity#showDialog". I did figure out a way around this but its a bit of hack:
In your Activity class, include a static reference to the this reference and set it in onCreate, and null it in onDestroy, and use that reference from your inner class like so:
public class MyActivity extends Activity {
private static MyActivity sThis;
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
sThis = this;
doAsyncWork();
}
#Override
public void onDestroy() {
super.onDestroy();
sThis = null;
}
private void doAsyncWork() {
showDialog(LOADING_DIALOG);
ExampleAsyncWorker.work(new AsyncWorkerCallback() {
#Override
public void onWorkComplete() {
sThis.dismissDialog(LOADING_DIALOG);
}
});
}
}
Note that I'm not sure this is a great practice but it worked for me. I know there can be problems with inner classes in activities that refer to the outer class (leaking the context) so there may be better ways of solving this problem.
AFAIK , This window leaked can be handled in two ways.
#Override
public void onDestroy(){
super.onDestroy();
if ( Dialog!=null && Dialog.isShowing() ){
Dialog.dismiss();
}
}
Or
if(getActivity()!= null && !getActivity().isFinishing()){
Dialog.show();
}
Here Dailog is your progress /alert dailog
for creating your app without the savedinstace you can use super.onCreate(null);
I have an app where I used default slide in/out effects on standards activities and fade in/out on a few specific activities.
I've noticed that the new Android 4.X (ICS) has a different default, which is exactly fade in/out.
How can i define on my standard activities my slide in/out effect?
I've been trying to define it with:
#Override
public void onCreate(Bundle savedInstanceState)
{
this.overridePendingTransition(0, android.R.anim.slide_in_left);
super.onCreate(savedInstanceState);
}
#Override
public void onPause()
{
super.onPause();
overridePendingTransition(android.R.anim.slide_out_right, 0);
}
To reproduce the fade in / out effect i'm using:
#Override
public void onCreate(Bundle savedInstanceState)
{
this.overridePendingTransition(0, android.R.anim.fade_in);
super.onCreate(savedInstanceState);
}
#Override
public void onPause()
{
super.onPause();
overridePendingTransition(android.R.anim.fade_in, 0);
}
but haven't managed to successfully reproduce it.
Also, I'm using this code on onCreate and onPause, is this correct? Or is there a better place to put it?
Thanks
PS: I've seen this New Android 4.0 Screen Transitions Between Activities but now answer has been provided.
overridePendingTransition() has to be called immediately after starting the new activity, so you would call it after startActivity(intent), for example.
To make a default you can create a class that extends Activity and override startActivity, onBackPressed(), etc. to override the transition. For example:
public class MyActivity extends Activity {
#Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(R.anim.slide_in_transition,R.anim.slide_out_transition);
}
#Override
public void startActivity(Intent intent) {
super.startActivity(intent);
overridePendingTransition(R.anim.slide_in_transition,R.anim.slide_out_transition);
}
#Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.slide_in_transition,R.anim.slide_out_transition);
}
}
Then, just extend MyActivity instead of Activity in your activities and the transitions should all be sliding by default.