When running the activity the first time, Parte initialization (which is inside the onCreate method) goes well:
Parse.enableLocalDatastore(this);
Parse.initialize(this, "...", "...");
Then, If I press the back button and enter again in the activity, I get an error:
java.lang.IllegalStateException: `Parse#enableLocalDatastore(Context)` must be invoked before `Parse#initialize(Context)`
Which I do not really understand why, because the Parse.enableLocalDatastore(this); is before Parse.initialize(this, "...", "...");.
Well, Ok. Then I tried to retrieve when the enableLocalDatastore has finihed, with Parse.isInitialized() method, but it is private, so I can't use it (as well as some others Parse variables I could use).
After some time, I found that If I call both methods inside a new Thread it works.
I'd like to know why the error happens and why It was solved with the Thread. Also, is there any better way to do it?
Follows the code (trimmed for the important parts):
public class RegisterActivity extends Activity {
Button linkParse;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
linkParse = (Button)findViewById(R.id.linkparse);
linkParse.setOnClickListener(new LinkParse());
linkParse.setClickable(false);
try {
// Enable Local Datastore.
Parse.enableLocalDatastore(this);
Parse.initialize(this, "...", "...");
} catch(Exception e){
Toast.makeText(RegisterActivity.this, "Parse not started.", Toast.LENGTH_SHORT).show();
linkParse.setClickable(true);
}
}
class LinkParse implements View.OnClickListener{
#Override
public void onClick(View v) {
Thread thread = new Thread(new StartParse());
thread.start();
}
}
class StartParse implements Runnable{
#Override
public void run() {
try {
// Enable Local Datastore.
Parse.enableLocalDatastore(RegisterActivity.this);
Parse.initialize(RegisterActivity.this, "...", "...");
} catch(Exception e){
}
}
}
}
You should invoke these two lines of codes from the application class not from the activity
Parse.initialize(this, "....","....");
Parse.enableLocalDatatore(this);
There is no need to initialized this multiple times and global application state is meant to be in the application class.
Create a class and extend application and initialize your parse code there
public class MyApplication extends Application{
#Override
public void onCreate(){
super.onCreate()
Parse.initialize(this, "....", "....");
Parse.enableLocalDatastore(this)
}
}
After you have created the application class. Add it to your AndroidManifest.xml
<application android:icon="#drawable/icon"
android:label="#stringapp_name"
android:name="MyApplication">
Related
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 am trying to implement ShineButton in my project . I have successfully synced the library to the gradle and added shine button in the xml.
now when i am trying to write the java code
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button b1 = (Button)findViewById(R.id.button);
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "Kill bill", Toast.LENGTH_SHORT).show();
}
});
ShineButton shineButton = (ShineButton) findViewById(R.id.po_image2);
shineButton.init(context);
}
}
.init(activity); is showing cannot resolve symbol activity.
You don't literally copy the code verbatim, you read the documentation and object types supported by the method.
public void init(Activity activity) {
For example, I assume you are running that from an activity based on the usage of findViewById? Then you need "this instance of the Activity"
shineButton.init(this);
or an instance of an Activity if you were in a Fragment
shineButton.init(getActivity());
If the code is in Activity use shineButton.init(Activityname.this).
If it is in
fragment use shineButton.init(getActivity()).
Read this: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/ and this https://developer.android.com/training/index.html
Change:
shineButton.init(context);
To:
shineButton.init(MainActivity.this);
MainActivity.this holds the instance of MainActivity class and can be used to initialise the view.
I have an SplashActivity that create an ArrayList of custom CommerceObjects. This List will be used in the rest of the app, in diferent activities and fragments. The problem is that sometimes, when the app is stoped and then restarted, the objects List appear as not initialized. The solution is to check if the ArrayList is not null and in the case of being null force the SplashActivity launch again and remake the ArrayList. I tried to do this in the onRestart method in the rest of activities but is not working at all.
For example, this is the way that I'm checking in MainActivity if the ArrayList (created in SplashActivity) is null.
public class MainActivity extends AppCompatActivity {
...
#Override
protected void onRestart() {
// If the full list of commerces is null or is empty, launch the SplashActivity.
// Here check if the ArrayList of CommerceObjects is null
if (SplashActivity._commerces == null || SplashActivity._commerces.size() == 0) {
Intent mIntent = new Intent(MainActivity.this, SplashActivity.class);
startActivity(mIntent);
this.finish();
}
super.onRestart();
}
...
}
So, the array list to check is "_commerces". It's decalred as public static in SplashActivity. I need to check if is not null no mather what fragment or activity is currently in the front of the stack.
What I'm missing?
UPDATE
I recommend you to use onStart().
onRestart() is not called if App process is killed by Android OS.
https://developer.android.com/reference/android/app/Activity.html
ORIGINAL
The static variables will be initialized by Android OS.
see: static variable null when returning to the app
So I recommend you to avoid using static variables.
Make your Application class and Hold the CommerceObjects in your Application instance.
Following codes explains.
Make your Application class:
public class App extends Application {
private CommerceObjects mCommerces;
public void setCommerces(CommerceObjects commerces) {
mCommerces = commerces;
}
public CommerceObjects getCommerces() {
return mCommerces;
}
public static App get(Context context) {
return (App) context.getApplicationContext();
}
}
Set name of application in your AndroidManifest.xml:
<application
...
android:name=".App">
...
</application>
Initialize commerces in your SplashActivity:
public class SplashActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
initializeCommerces();
}
private void initializeCommerces() {
//do initialize tasks
...
CommerceObjects commerces = ...;
//set CommerceObjects to App
App.get(this).setCommerces(commerces);
//start other Activity. ex) MainActivity
}
}
Use commerces in anther Activity:
public class MainActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//use CommerceObjects
CommerceObjects commerces = App.get(this).getCommerces();
...
}
}
I am a fairly new to android development, and I don't understant how the main class works on Android Studio.
I'm trying to make my app have the Crashlytics and Parse services but I'm not sure where to put them. Currently I have the code on the OnCreate method in the Login Class:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fabric.with(this, new Crashlytics());
Parse.initialize(this, "CODE", "CODE");
ParseInstallation.getCurrentInstallation().saveInBackground();
}
But I heard that this code should go on the Application Class... That's because the Application Class is always started and its basically the main Class for the app... I am not sure about this, please correct me if I'm wrong.
If that's the case, how can I access the Application Class and where should I put the code?
Just create a class which should extends the Application , Using this you can initialize the parse installation
public class DemoClass extends Application {
#Override
public void onCreate() {
super.onCreate();
Fabric.with(this, new Crashlytics());
Parse.initialize(this, "CODE", "CODE");
ParseInstallation.getCurrentInstallation().saveInBackground();
}
}
Copy this above code
I am using social auth library for sharing and login with social media in android app.
I have logged in and authorize successfully with facebook.
But when I try to signout app crashed with nullpointer exception
adapter.signOut(getActivity(), Provider.FACEBOOK.toString());
Getting below error:
05-09 10:24:23.010: E/AndroidRuntime(19998): java.lang.NullPointerException
05-09 10:24:23.010: E/AndroidRuntime(19998): at org.brickred.socialauth.android.SocialAuthAdapter.signOut(SocialAuthAdapter.java:797)
I am using latest version. socialauth-4.4.jar and socialauth-android-3.2.jar
Please make sure to call from activity. Getactivity() from fragment not working after calling method from activity like this will work adapter.signOut(this, Provider.FACEBOOK);
I had a similar problem, this solution works for me:
https://code.google.com/p/socialauth-android/issues/detail?id=108#c16
basically there is a bug in the log out function, you have to log in before log out, otherwise you may get an NPE. the solution above is create a new SocialAuthManager when necessary.
I suggest to import that source code as a java module instead of using the jar file, so you can fix something by yourself, like change the dialog title text, etc...
In signOut function put these lines,
if (providerName != null) {
if (socialAuthManager == null) {
socialAuthManager = new SocialAuthManager();
try {
loadConfig(ctx);
} catch (Exception e) {
Log.d(" SocialAuthAdapter ", "Could not load configuration");
}
}
before
if (socialAuthManager.getConnectedProvidersIds().contains(providerName)) socialAuthManager.disconnectProvider(providerName);
This has solved the issue for me for now, in the class where you would like to sign out instantiate a new adapter if the application-wide adapter is null
//My Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
//Get the currently available adapter
myApp = (MyApplication) getApplication();
adapter = myApp.getSocialAuthAdapter();
//Adapter initialization if null
if (adapter==null){
adapter = new SocialAuthAdapter(new ResponseListener());
}
}
//ResponseListener Class
public final class ResponseListener implements DialogListener {
#Override
public void onComplete(Bundle values) {
String providerName = values.getString(SocialAuthAdapter.PROVIDER);
// Set a application wide reference to the social adapter here
myApp = (MyApplication) getApplication();
myApp.setSocialAuthAdapter(adapter);
}
#Override
public void onError(SocialAuthError error) {
Log.d("Custom-UI", "Error");
error.printStackTrace();
}
#Override
public void onCancel() {
Log.d("Custom-UI", "Cancelled");
}
#Override
public void onBack() {
Log.d("Custom-UI", "Dialog Closed by pressing Back Key");
}
}
//Code for application class
public class MyApplication extends Application {
// SocialAuth Component
private SocialAuthAdapter socialAuthAdpater;
public SocialAuthAdapter getSocialAuthAdapter() {
return socialAuthAdpater;
}
public void setSocialAuthAdapter(SocialAuthAdapter socialAuthAdapter) {
this.socialAuthAdpater = socialAuthAdapter;
}