Implementing Restart App Functionality in React Native with Native Modules (Java, Objective-C) new Architecture

Steve Blue
3 min readFeb 28, 2024

--

https://reactnative.dev/architecture/xplat-implementation

Android Native module:

Exposing Java Methods: to make specific Java methods callable from JavaScript, you use the @ReactMethod annotation. This annotation tells React Native to treat the annotated method as a callable function from the JavaScript side.

Methods Invoked from JavaScript: functions marked with @ReactMethod can be called directly from JavaScript using the module instance on the JavaScript side.

  1. Create a ‘restart’ module folder at: android/src/main/java/com/your-app/app.
https://reactnative.dev/docs/native-modules-android
  1. RestartModule.java
package com."your-app".app.restart;

import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;

public class RestartModule extends ReactContextBaseJavaModule {
private static String restartReason = null;

private LifecycleEventListener mLifecycleEventListener = null;

public RestartModule(ReactApplicationContext reactContext) {
super(reactContext);
}

private void loadBundleLegacy() {
final Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
return;
}

currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
currentActivity.recreate();
}
});
}

private void loadBundle() {
clearLifecycleEventListener();
try {
final ReactInstanceManager instanceManager = resolveInstanceManager();
if (instanceManager == null) {
return;
}

new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
try {
instanceManager.recreateReactContextInBackground();
} catch (Throwable t) {
loadBundleLegacy();
}
}
});

} catch (Throwable t) {
loadBundleLegacy();
}
}

private static ReactInstanceHolder mReactInstanceHolder;

static ReactInstanceManager getReactInstanceManager() {
if (mReactInstanceHolder == null) {
return null;
}
return mReactInstanceHolder.getReactInstanceManager();
}

private ReactInstanceManager resolveInstanceManager() throws NoSuchFieldException, IllegalAccessException {
ReactInstanceManager instanceManager = getReactInstanceManager();
if (instanceManager != null) {
return instanceManager;
}

final Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
return null;
}

ReactApplication reactApplication = (ReactApplication) currentActivity.getApplication();
instanceManager = reactApplication.getReactNativeHost().getReactInstanceManager();

return instanceManager;
}


private void clearLifecycleEventListener() {
if (mLifecycleEventListener != null) {
getReactApplicationContext().removeLifecycleEventListener(mLifecycleEventListener);
mLifecycleEventListener = null;
}
}

@ReactMethod
public void restart(String reason) {
restartReason = reason;
loadBundle();
}

@ReactMethod
public void getReason(Promise promise) {
try {
promise.resolve(restartReason);
} catch(Exception e) {
promise.reject("Create Event Error", e);
}
}


@Override
public String getName() {
return "RNRestart";
}
}

3. ReactInstanceHolder.java

package com."your-app".app.restart;

import com.facebook.react.ReactInstanceManager;

public interface ReactInstanceHolder {
ReactInstanceManager getReactInstanceManager();
}

4. RestartPackage.java

package com."your-app".app.restart;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RestartPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();

modules.add(new RestartModule(reactContext));

return modules;
}
}

5. MainApplication.java

import com."your-app".app.restart.RestartPackage;

@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new RestartPackage()); // <-- add here
return packages;
}

iOS Native module

RCT_EXPORT_MODULE is used to expose a native module to the JavaScript environment. Native modules in React Native are used to provide a bridge between JavaScript and native code.

RCT_EXPORT_METHOD is used to expose specific methods of a native module, making them accessible from JavaScript.

  1. Create a custom native module file in Xcode: Restart.h (Header File) and Restart.m (Objective-C File).
https://reactnative.dev/docs/native-modules-ios

2. Restart.h


#import <React/RCTBridgeModule.h>
#import <React/RCTRootView.h>
#import <React/RCTReloadCommand.h>

@interface Restart : NSObject <RCTBridgeModule>

@end

3. Restart.m


#import "Restart.h"

@implementation Restart

RCT_EXPORT_MODULE(RNRestart)
NSString *restartReason = nil;

- (void)loadBundle
{
RCTTriggerReloadCommandListeners(@"react-native-restart: Restart");
}

RCT_EXPORT_METHOD(restart: (NSString *)reason) {
restartReason = reason;
if ([NSThread isMainThread]) {
[self loadBundle];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[self loadBundle];
});
}
return;
}

@end

Now, use it in JS

// RNRestartModule.ts 

import {NativeModules} from 'react-native';

interface RNRestartInterface {
restart(reason: string): void;
}

export const RNRestart = NativeModules.RNRestart as RNRestartInterface;

In component

import { RNRestart } from "@utils"

<Button onPress={() => RNRestart.restart("")} />

--

--

Steve Blue

Experienced Mobile Application Developer with a demonstrated history of working in the computer software industry.