apache / cordova-android

Apache Cordova Android
https://cordova.apache.org/
Apache License 2.0
3.65k stars 1.54k forks source link

Access onCreate() Lifecycle in Cordova plugin #1202

Open hug0b opened 3 years ago

hug0b commented 3 years ago

Feature Request

Motivation Behind Feature

Some plugins may require to perform actions inside the onCreate() Activity Lifecycle.

An example of logic that needs to be inside onCreate:

public class MainActivity extends AppCompatActivity {
  private Foo mService;
  private ServiceConnection mConnection;
  private boolean isConnect = false;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
     if(mConnection == null)
        mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName className, IBinder service) {
                mService = Foo.bar(service);
                isConnect = true;
            }
        };
  }

In my understanding onServiceConnected should have ran when onCreate has completed. This logic would not behave the same inside the available CordovaPlugin class methods like initialize() or onStart().

(My real world use case is to write a Cordova plugin for the MCOP SDK).

Feature Description

From a Cordova plugin, we should be able to hook inside Cordova's MainActivity onCreate. (We can currently inject code in Cordova's Activity but it can't be linked to plugin code afaik).

From my current Cordova knowledge I am unsure how it could be implemented.

Alternatives or Workarounds

I have not found any so far.

hug0b commented 3 years ago

As of now it seems that I don't actually need this feature after all (And also my sample code was not really a valid example of why accessing onCreate could be useful), so I guess this could be closed.

breautek commented 3 years ago

Alright then. In effort to not add things and require to maintain them (it's much more harder to remove features than it is to add them...) we'll close this feature request, especially since no one else expressed interest in this feature.

If things change, don't hesitate to open a new feature request, or comment on this one to reopen it.

j3k0 commented 1 year ago

I stumbled a case were this feature seemed to be required. The solution is quite simple though (setting onload in the plugin.xml as explained below). I'm writing it down here in case someone else stumble up this issue like I did.


An SDK is using androidx.lifecycle.LifecycleRegistry, which require initialization to happen:

  1. From the main thread.
  2. Before the activity is started.

When doing the SDK initialization in pluginInitialize, I was getting:

Method addObserver must be called on the main thread
        at androidx.lifecycle.LifecycleRegistry.enforceMainThreadIfNeeded(LifecycleRegistry.java:317)
        at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:172)

When running the initialization in a cordova.getActivity().runOnUiThread block, I get randomly:

MainActivity@bcee696 is attempting to register while current state is RESUMED.
LifecycleOwners must call register before they are STARTED.

Which I guess is because runOnUiThread might or might not happen before the activity is "started".


Reading through Cordova's code for onCreate, I found out about the onload parameter for plugins, which incidentally is documented here: https://cordova.apache.org/docs/en/latest/guide/platforms/android/plugin.html#plugin-initialization-and-lifetime

When onload is set, the plugin will be loaded during processing f the onCreate event. Not sure if this is 100% guaranteed to remain the case in the future, but this solves the issue with the current version of cordova-android.

dariosalvi78 commented 10 months ago

on Android 14 it seems that startActivityForResult has been abandoned and that we must use registerForActivityResult, but that function can only be called in the onCreate, I quote:

Note: You must call registerForActivityResult() before the fragment or activity is created, but you can't launch the ActivityResultLauncher until the fragment or activity's Lifecycle has reached CREATED.

breautek commented 10 months ago

Reopening this because it may be required for https://github.com/apache/cordova-android/issues/1504#issuecomment-1819745585

dariosalvi78 commented 10 months ago

thanks. I have used the solution suggested by @j3k0 , that is, to use <param name="onload" value="true" /> and the initialize() callback in the plugin. It has worked! But as he also mentions, if you decide to call plugins initializations out of the onCreate this won't work any longer. It may be better to make this more explicit, for example by simply renaming initialize to onCreate or smth like that.

Besides, for future versions of this platform supporting android 14, the whole startActivtyForResults and onActivityResults should be removed in favour of the new mechanism.

breautek commented 10 months ago

Yah I'd have to refresh my memory on how plugins gets loaded... onload parameter is used to initialize the plugin immediately which is probably during the onCreate call... But a plugin that's lazy loaded is only initialized when a plugin API is called I think. Like you said, the plugin initialization is not explicitly declared. If the onload path does work, it's probably working by accident, or by coincidence.

Edit:

the onload works because the plugin manager is constructed and initialized during the loadUrl call done by the app's MainActivity template and part of the plugin manager init is instantiating all plugins that has the onload flag.

Edit #2:

It may be better to make this more explicit, for example by simply renaming initialize to onCreate or smth like that.

I don't think we can change the name or anything like that, it would be too much of a breaking change, but I think using onload flag would be the route to take if you need to do something via onCreate.

I don't really see a good path of producing an onCreate hook when the plugin system is intended to be lazy loaded.

dariosalvi78 commented 10 months ago

I don't really see a good path of producing an onCreate hook when the plugin system is intended to be lazy loaded.

agreed, but as things are now, you would need to either

I am not sure if the plugins interface is something dealt with in this project or elsewhere, but support for the new permissions mechanism needs to be fixed before the following release of cordova-android, if you are planning to support Android 14.