Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add kotlin examples in new-architecture-app-modules-android.md #3241

Merged
178 changes: 175 additions & 3 deletions docs/new-architecture-app-modules-android.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ title: Enabling TurboModule on Android
---

import NewArchitectureWarning from './\_markdown-new-architecture-warning.mdx';
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
import constants from '@site/core/TabsConstants';

<NewArchitectureWarning/>

Expand Down Expand Up @@ -135,11 +137,14 @@ You can now verify that everything works correctly by running your android app:
yarn react-native run-android
```

## 2. Java - Provide a `ReactPackageTurboModuleManagerDelegate`
## 2. Java/Kotlin - Provide a `ReactPackageTurboModuleManagerDelegate`

Now is time to actually use the TurboModule.
First, we will need to create a `ReactPackageTurboModuleManagerDelegate` subclass, like the following:

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
package com.awesomeproject;

Expand Down Expand Up @@ -179,6 +184,52 @@ public class MyApplicationTurboModuleManagerDelegate extends ReactPackageTurboMo
}
```

</TabItem>

<TabItem value="kotlin">

```kotlin
package com.awesomeproject

import com.facebook.jni.HybridData
import com.facebook.react.ReactPackage
import com.facebook.react.ReactPackageTurboModuleManagerDelegate
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.soloader.SoLoader

class MyApplicationTurboModuleManagerDelegate
protected constructor(
reactApplicationContext: ReactApplicationContext,
packages: List<ReactPackage>
) : ReactPackageTurboModuleManagerDelegate(reactApplicationContext, packages) {
override protected external fun initHybrid(): HybridData?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we add new lines around this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

class Builder : ReactPackageTurboModuleManagerDelegate.Builder() {
override protected fun build(
context: ReactApplicationContext,
packages: List<ReactPackage>
): MyApplicationTurboModuleManagerDelegate {
return MyApplicationTurboModuleManagerDelegate(context, packages)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
): MyApplicationTurboModuleManagerDelegate {
return MyApplicationTurboModuleManagerDelegate(context, packages)
}
) = MyApplicationTurboModuleManagerDelegate(context, packages)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

}

@Synchronized
override protected fun maybeLoadOtherSoLibraries() {
// Prevents issues with initializer interruptions.
if (!sIsSoLibraryLoaded) {
SoLoader.loadLibrary("myapplication_appmodules")
sIsSoLibraryLoaded = true
}
}

companion object {
@Volatile private var sIsSoLibraryLoaded = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Volatile private var sIsSoLibraryLoaded = false
@Volatile private var isSoLibraryLoaded = false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

}
}
```

</TabItem>
</Tabs>

Please note that the `SoLoader.loadLibrary` parameter (in this case `"myapplication_appmodules")` should be the same as the one specified for `LOCAL_MODULE :=` inside the `Android.mk` file you created before.

This class will then be responsible of loading the TurboModules and will take care of loading the native library build with the NDK at runtime.
Expand All @@ -189,10 +240,13 @@ Then, you can provide the class you created to your `ReactNativeHost`. You can l

Once you located it, you need to add the `getReactPackageTurboModuleManagerDelegateBuilder` method as from the snippet below:

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
public class MyApplication extends Application implements ReactApplication {

private final ReactNativeHost mReactNativeHost =
private final ReactNativeHost reactNativeHost =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I was not precise before. Let's use mReactNativeHost for Java and reactNativeHost for Kotlin.

That convention was in place for Java only:
https://source.android.com/setup/contribute/code-style#follow-field-naming-conventions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() { /* ... */ }
Expand All @@ -212,14 +266,49 @@ public class MyApplication extends Application implements ReactApplication {
}
```

</TabItem>
<TabItem value="kotlin">

```kotlin
class MyApplication : Application(), ReactApplication {
private val reactNativeHost: ReactNativeHost =
object : ReactNativeHost(this) {

override fun getUseDeveloperSupport(): Boolean {
/* ... */
}

override fun getPackages(): List<ReactPackage?>? {
/* ... */
}

override fun getJSMainModuleName(): String? {
/* ... */
}

@NonNull
override fun getReactPackageTurboModuleManagerDelegateBuilder():
ReactPackageTurboModuleManagerDelegate.Builder? {
return Builder()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
override fun getReactPackageTurboModuleManagerDelegateBuilder():
ReactPackageTurboModuleManagerDelegate.Builder? {
return Builder()
}
override fun getReactPackageTurboModuleManagerDelegateBuilder() =
ReactPackageTurboModuleManagerDelegate.Builder()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
}
```

</TabItem>
</Tabs>

## 4. Extend the `getPackages()` from your `ReactNativeHost` to use the TurboModule

Still on the `ReactNativeHost` , we need to extend the the `getPackages()` method to include the newly created TurboModule. Update the method to include the following:

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
public class MyApplication extends Application implements ReactApplication {

private final ReactNativeHost mReactNativeHost =
private final ReactNativeHost reactNativeHost =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() { /* ... */ }
Expand Down Expand Up @@ -272,8 +361,71 @@ public class MyApplication extends Application implements ReactApplication {
return new MyApplicationTurboModuleManagerDelegate.Builder();
}
};
}
```

</TabItem>
<TabItem value="kotlin">

```kotlin
class MyApplication() : Application(), ReactApplication {
private val reactNativeHost: ReactNativeHost =
object : ReactNativeHost(this) {
override fun getUseDeveloperSupport(): Boolean {
/* ... */
}

override protected fun getPackages(): List<ReactPackage>? {
val packages: MutableList<ReactPackage> = PackageList(this).getPackages()

// Add those lines
packages.add(
object : TurboReactPackage() {
@Nullable
override fun getModule(
name: String,
reactContext: ReactApplicationContext?
): NativeModule? =
if ((name == NativeAwesomeManager.NAME)) {
NativeAwesomeManager(reactContext)
} else {
null
}

override fun getReactModuleInfoProvider() =
mutableMapOf<String, ReactModuleInfo>(
NativeAwesomeManager.NAME,
ReactModuleInfo(
NativeAwesomeManager.NAME,
"NativeAwesomeManager",
false, // canOverrideExistingModule
false, // needsEagerInit
true, // hasConstants
false, // isCxxModule
true // isTurboModule
)
)
}
)
return packages
}

override protected fun getJSMainModuleName(): String? {
/* ... */
}

@NonNull
override protected fun getReactPackageTurboModuleManagerDelegateBuilder():
ReactPackageTurboModuleManagerDelegate.Builder? {
return Builder()
}
}
}
```

</TabItem>
</Tabs>

## 5. C++ Provide a native implementation for the methods in your `*TurboModuleDelegate` class

If you take a closer look at the class `MyApplicationTurboModuleManagerDelegate` that you created before, you will notice how some of the methods are `native`.
Expand Down Expand Up @@ -422,6 +574,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {

Now you can finally enable the `TurboModule `support in your Application. To do so, you need to turn on the `useTurboModule` flag inside your Application `onCreate` method.

<Tabs groupId="android-language" defaultValue={constants.defaultAndroidLanguage} values={constants.androidLanguages}>
<TabItem value="java">

```java
public class MyApplication extends Application implements ReactApplication {

Expand All @@ -430,8 +585,25 @@ public class MyApplication extends Application implements ReactApplication {
ReactFeatureFlags.useTurboModules = true;
//...
}
}
```

</TabItem>

<TabItem value="kotlin">

```kotlin
class MyApplication : Application(), ReactApplication {
override fun onCreate() {
ReactFeatureFlags.useTurboModules = true
// ...
}
}
```

</TabItem>
</Tabs>

It’s now time to run again your Android app to verify that everything works correctly:

```bash
Expand Down