I have a problem when my App is a long time in the background. After I click the App my activity opens empty /blank and the app crashes. How do I start my activity if the Android system keeps destroying my Activity ?
The onCreate() of my Activity
public new TableViewModel ViewModel
{
get { return (TableViewModel)base.ViewModel; }
set { base.ViewModel = value; }
}
protected override void OnViewModelSet()
{
base.OnViewModelSet();
SetContentView (Resource.Layout.SectorLayout);
}
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
//this.Intent.AddFlags (ActivityFlags.ClearTop | ActivityFlags.ClearTask | ActivityFlags.NewTask);
ActionBar.Title = Resources.GetString (Resource.String.tables);
_adapter = new MyPagerAdapter(SupportFragmentManager);
Pager = FindViewById<ViewPager> (Resource.Id.pager);
_tabs = FindViewById<PagerSlidingTabStrip> (Resource.Id.tabs);
Pager.Adapter = _adapter;
_tabs.SetViewPager (Pager);
var pageMargin = (int)TypedValue.ApplyDimension (ComplexUnitType.Dip, 4, Resources.DisplayMetrics);
Pager.PageMargin = pageMargin;
_tabs.OnTabReselectedListener = this;
}
You have to implement correct lifecycle for your activities/fragments, check.
For debugging purpose check developer option "Don't keep activities".
Related
I'm trying to unit test this Android Studio app with Robolectric4.0.1 that has multiple activities in it this particular activity is the login activity but it isn't the first activity launched from manifest.xml there are a few activities before it. Each activity passes a bundle over to the next one. The problem is that after debugging the Bundle object with all of the information I need to test it turns out to be null.
Here is my testing code in the Android test folder:
#RunWith(RobolectricTestRunner.class)
public class LoginActivityTest {
LoginActivity activity;
REdittext email;
REdittext password;
TextView signin;
String e;
String p;
Bundle bundle;
Info info;
#Before
public void setUp() {
activity = Robolectric.setupActivity(LoginActivity.class);
email = activity.findViewById(R.id.rtxtEmail);
password = activity.findViewById(R.id.rtxtPassword);
signin = activity.findViewById(R.id.lblSignIn);
bundle = activity.getIntent().getExtras();
if (bundle != null) {
this.info = bundle.getParcelable("Info");
this.info.bRemember = false;
this.info.helpWSURL = Common.HELP_WS_URL;
}
signin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
e = email.getText();
p = password.getText();
}
});
}
#Test
public void test_LoginActivity_ShouldNotBeNull()
{
assertNotNull(activity);
}
#Test
public void test_IncomingBundleInfo_NotNull()
{
assertNotNull(bundle);
}
This tutorial helped explain on how to do testing in Robolectric section
https://www.sitepoint.com/write-tests-android-development/
So I followed that example but certain objects aren't registering just yet such as bundle
My question was when do you actually run the test? Currently, I'm in Android Studio with that test selected and then the device I want to test on (Samsung SM-T290). Knowing that this isn't the first activity I'm wondering if bundle doesn't even populate with data yet.
Any help would appreciated, thank you.
I'm working on an alarm clock and I can't figure out how to sendEvent to React Native from MainActivity. This is what I managed to do so far:
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
ReactInstanceManager mReactInstanceManager = getReactNativeHost().getReactInstanceManager();
ReactApplicationContext context = (ReactApplicationContext) mReactInstanceManager.getCurrentReactContext();
if (context == null) {
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext context) {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // doesn't work. Should run after alarm manager starts app which previously had been killed
}
}
}
});
} else {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // works and sends event only when app was left open
}
}
}
super.onCreate(savedInstanceState);
}
The code works only If app is left open and alarm manager restarts app itself. If I close the app and alarm manager starts it then it seems that only startAlarm function (it has sound effect) is beeing triggered..
No matter what I do whether I put sendEvent function inside Mainactivity or elsewhere (e.g. external module) it simply won't send event if I close the app. I also tried getReactInstanceManager().getCurrentReactContext() combined with while from this question Send data from Android activity to React Native to no avail.
Also tried to create bolean beeing set to true onCreate and then send event onStart or onRestart. Also to no avail.
Any suggestions?
EDIT: Here is how sendEvent function looks like:
public final void sendEvent(String eventName, boolean isAlarmOn) {
getReactInstanceManager().getCurrentReactContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, isAlarmOn);
}
SOLUTION
Well I think that the answer is not to use sendEvent method onCreate because (I might be wrong) listener seems to be initialized after the event had been sent. So nothing is going to listen to this event.
It seems to work pretty well inside onStart, onRestart, onPause though.
What can we do? React Native provides ReactActivityDelegate with initial props. And it does the job!
ReactActivityDelegate in MainActivity should look as below:
public class ActivityDelegate extends ReactActivityDelegate {
private Bundle mInitialProps = null;
private final #Nullable Activity mActivity;
public ActivityDelegate(Activity activity, String mainComponentName) {
super(activity, mainComponentName);
this.mActivity = activity;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
mInitialProps.putBoolean("alarmOn", true);
}
}
super.onCreate(savedInstanceState);
}
#Override
protected Bundle getLaunchOptions() {
return mInitialProps;
}
};
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ActivityDelegate(this, getMainComponentName());
}
Then in your main app component (usually index.android.js) call your propTypes and use them to run your code:
static propTypes = {
alarmOn: PropTypes.boolean
}
componentDidMount() {
if (this.props.alarmOn === true) {
// your code
}
}
Voila!
You can find full example here: https://github.com/vasyl91/react-native-android-alarms
onStart()
I know that onStart() method is called after onCreate() ( via Activity Lifecycle documentation ), but in my LibGDX project this doesn't happen. I' ve this code:
#Override
protected void onStart()
{
super.onStart();
Gdx.app.debug(TAG, "onStart");
}
but the string in debug terminal appears only if I resume the app from background. I need to do stuff after the initialise of the activity, when it becomes visible.
EDIT: MORE CODE
public class AndroidLauncher extends AndroidApplication {
private final static String TAG = AndroidLauncher.class.getSimpleName();
GoogleResolver googleResolver;
GoogleSignInAccount acct;
private Preferences googlePrefs;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
googleResolver = new GoogleResolverAndroid();
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
config.useImmersiveMode = true;
config.useGyroscope = false;
config.useCompass = false;
config.useAccelerometer = false;
GoogleLoginHandler.getInstance().setContext(this.getContext());
GoogleLoginHandler.getInstance().startApiClient();
GameManager.getInstance().listener = googleResolver;
initialize(new MainCrucy(), config);
googlePrefs = Gdx.app.getPreferences(GOOGLE_PREF);
GoogleLoginHandler.getInstance().mGooglePrefs = Gdx.app.getPreferences(GOOGLE_PREF);
}
#Override
protected void onStart()
{
super.onStart();
Gdx.app.debug(TAG, "onStart");
OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(GoogleLoginHandler.getInstance().getGoogleApiClient());
if (opr.isDone())
{
Gdx.app.debug(TAG, "Loggato");
GoogleSignInResult result = opr.get();
handleSignInResult(result);
} else {
opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
#Override
public void onResult(GoogleSignInResult googleSignInResult) {
handleSignInResult(googleSignInResult);
}
});
}
}
This is what I do. But onStart() does anything
How long do you wait after launching you application?
You have to remember that your app can take time to Start. If what you say is true than you wouldn't see Gdx debug - it's still fires at onStart().
So I assume:
you launch an app
you don't want to wait so you minimize that
you open it and onStart() ends and you see debug logs
By the way, could you show more code?
In the meantime look at the life cycle of Android app.
Android lifecycle
You can't use Gdx.app.debug() before the Libgdx application has had a chance to start up. I'm not positive if this happens before onStart() because libgdx doesn't run on the UI thread. Also, you must also use Gdx.app.setLogLevel(Application.LOG_DEBUG) first or calls to Gdx.app.debug() will do nothing.
But you can just use Android's Log.d() instead.
I want to make a cloud synchronization everytime my app is brought to front and a second time if the app disappears in background.
So I overwrote the onStart and onStop event methods of my activity:
#Override
protected void onStart() {
super.onStart();
doSync();
}
#Override
protected void onStop() {
doSync();
super.onStop();
}
Ok, that works fine for me but I found out that these methods are also called if I start a new activity (f.e. SettingsActivity.class) within my app (onStop) and come back to the main activity (onStart).
Is there a good way to ignore the calls of my own activities and only react on calls from "outside", f.e. I only want to synchronize if the user stops the app by pressing the home button and I also want to synchronize only if the user returns to the app by starting it from the app dreawer or app switcher?
+++ SOLUTION +++
Now I found a solution for my problem and I want to share it. Maybe it's not the best way because it's no SDK-based functionality but it works and it's quite simple.
I declared a flag, set it to false when the activity is created. Everytime I start another activity in the same app, I will set the flag to true and check its state in onPause and onResume.
public class MainActivity extends Activity {
private boolean transition;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
transition = false;
}
private void startSettingsActivity() {
transition = true;
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
}
private void doSync() {
// all steps for the cloud synchronization
}
#Override
protected void onResume() {
super.onResume();
if (!transition) {
// this is the case the user returns from
// the app drawer or app switcher or starts
// the app for the first time, so do sync
doSync();
} else {
// this is the case the user returns from another
// activity, so don't sync but reset the flag
transition = false;
}
}
#Override
protected void onPause() {
if (!transition) {
// this is the case the user presses the home button or
// navigate back (leaves the app), so do final sync
doSync();
} else {
// this is the case the user starts another activity, but
// stays in the app, so do nothing
}
super.onPause();
}
}
I want to have a sensormanager on a fragment, which is only active when the fragment is active. If the user changes the fragment, the listener should be removed.
Adding and removing the listener is pretty simple. I'm not aware of any listeners / function on the fragment side, when the fragment appears / disappears. Also a problem was, that on almost all functions, this.getActivity() returned a null pointer.
That's my solution. I tried to cut it out of my Fragment. If there is anything wrong / syntax issues, please let me know.
public class MyFragment extends Fragment implements SensorEventListener {
private SensorManager mSensorManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSensorManager = (SensorManager) this.getActivity().getSystemService(Activity.SENSOR_SERVICE);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.mylayout, container, false);
return rootView;
}
#Override
public void onSensorChanged(SensorEvent event) {
float x = event.values[0], y = event.values[1];
}
#Override public void onAccuracyChanged(Sensor sensor, int accuracy) { }
#Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
// First starts (gets called before everything else)
if(mSensorManager == null) {
return;
}
if(menuVisible) {
this.registerSensorListener();
} else {
this.unregisterSensorListener();
}
}
#Override
public void onStart() {
super.onStart();
if(this.getUserVisibleHint()) {
this.registerSensorListener();
}
}
#Override
public void onStop() {
super.onStop();
this.unregisterSensorListener();
}
private void registerSensorListener() {
mSensorManager.registerListener(this, mSensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0), SensorManager.SENSOR_DELAY_FASTEST);
}
private void unregisterSensorListener() {
mSensorManager.unregisterListener(this);
}
}
Hold a reference of Activity in your fragment to handle that nullpointerexception.
Here is an example of a fragment.
public class YourFragment extends Fragment {
private Activity mActivity;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
#Override
public void onResume() {
super.onResume();
// BIND sensor here with mActivity,
// could also be done in other fragment lifecycle events,
// depends on how you handle configChanges
}
#Override
public void onPause() {
super.onPause();
// UNBIND sensor here from mActivity,
// could also be done in other fragment lifecycle events,
// depends on how you handle configChanges
}
}
Debug that code do determine if you should handle the binding there or in another method e.g. onCreate of a fragment. I have not tested this code for your purpose.
Edit:
This is indeed as commented below a dirty fix and could easily resolve into exceptions in some cases. I just wanted to show how you can use fragment lifecycle methods to bind and unbind sensors with a reference to activity. I'm currently learning fragments for quite some time but still don't understand them thoroughly. I advice you to take a look at the source of Fragment and other components involved. This is the only place were fragments are documented thoroughly hence the documentation on reference in my opinion isn't that explanatory.
Some of the options regarding null value Activity:
If you want to be completely sure that getActivity doesn't return null you should wait for onActivityCreated to be called. This method tells the fragment that its activity has
completed its own Activity.onCreate(). After this getActivity() will not return null until initState() gets called by the FragmentManager.
// Called by the fragment manager once this fragment has been removed,
// so that we don't have any left-over state if the application decides
// to re-use the instance. This only clears state that the framework
// internally manages, not things the application sets.
void initState() {
mIndex = -1;
mWho = null;
mAdded = false;
mRemoving = false;
mResumed = false;
mFromLayout = false;
mInLayout = false;
mRestored = false;
mBackStackNesting = 0;
mFragmentManager = null;
mActivity = null;
mFragmentId = 0;
mContainerId = 0;
mTag = null;
mHidden = false;
mDetached = false;
mRetaining = false;
mLoaderManager = null;
mLoadersStarted = false;
mCheckedForLoaderManager = false;
}
Before you call getActivity you can always check if activity isn't null by calling isAdded() method. As you can see below this method checks if mActivity isn't null. Optionally you can create a recursive function with Handler.postDelayed that tries to return an non null Activity in intervalls (you should add a max try counter). But this is also a dirty trick.
//Return true if the fragment is currently added to its activity.
final public boolean isAdded() {
return mActivity != null && mAdded;
}