简体   繁体   English

如何重构从 React Native 调用的原生 Android Java 方法?

[英]How to refactor native Android Java method to be called from React Native?

I currently have the following activity which is created when my app starts and is declared as an activity in my AndroidManifest.xml:我目前有以下活动,它是在我的应用程序启动时创建的,并在我的 AndroidManifest.xml 中声明为活动:

AndroidManifest.xml: AndroidManifest.xml:

<activity android:name=".IncomingActivity"></activity>

IncomingActivity.java:传入Activity.java:

package com.xyz;

import android.os.Build;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.WindowManager;

public class IncomingActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            setShowWhenLocked(true);
            setTurnScreenOn(true);
        }
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);

        setContentView(R.layout.activity_incoming);
    }
}

I now want to be able to use this functionality on demand from React Native.我现在希望能够从 React Native 按需使用这个功能。 I have refactored it like the below so far but when this native method is called, I just get a blank white screen and I think the problem is this line: activity.setContentView(mReactRootView);到目前为止,我已经像下面这样重构了它,但是当调用这个本机方法时,我只是得到一个空白的白屏,我认为问题出在这一行: activity.setContentView(mReactRootView);

Updated class:更新了 class:

public class UnlockDevice extends ReactContextBaseJavaModule {

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

        private ReactContext mReactContext;
        private ReactRootView mReactRootView;

    public UnlockDevice(ReactApplicationContext reactContext) {
        super(reactContext);
                mReactContext = reactContext;
                mReactRootView = new ReactRootView(reactContext);
    }

    /* React Methods */
   @ReactMethod
    public void Unlock() {
                    Activity activity = mReactContext.getCurrentActivity();

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
                    activity.setShowWhenLocked(true);
                    activity.setTurnScreenOn(true);
                }
                
                activity.getWindow().addFlags(
                        WindowManager.LayoutParams.FLAG_FULLSCREEN 
                        | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 
                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);

                activity.setContentView(mReactRootView);
    }
}

What is the correct way to be able to call this method to toggle the functionality but with the main activity instead of starting a new one?能够调用此方法来切换功能但使用主要活动而不是开始新活动的正确方法是什么?

Explanation解释

Basically, you missed 2 things - you didn't register your UnlockDevice in a ReactPackage , and you didn't add your ReactPackage to the list of packages in the application.基本上,您错过了两件事 - 您没有在UnlockDevice中注册ReactPackage ,并且您没有将ReactPackage添加到应用程序的包列表中。

Also, don't forget that your native method will not be executed on the UI thread with React .另外,不要忘记您的本地方法不会在带有React的 UI 线程上执行。 So, it's your responsibility to run it there.所以,在那里运行它是你的责任。 For example, by using runOnUiThread() as below:例如,通过使用runOnUiThread()如下:

...
    @ReactMethod
    public void Unlock() {
        Activity activity = mReactContext.getCurrentActivity();

        activity.runOnUiThread(() -> {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
                activity.setShowWhenLocked(true);
                activity.setTurnScreenOn(true);
            }

            activity.getWindow().addFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN
                        | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
            activity.setContentView(mReactRootView);
        });
   }
...

So, first create a ReactPackage class to register your module.所以,首先创建一个ReactPackage class 来注册你的模块。

MainReactPackage.java MainReactPackage.java

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

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

       modules.add(new UnlockDevice(reactContext));

       return modules;
   }
}

Then, add the latter to the list of packages in your Application class.然后,将后者添加到Application class 中的包列表中。

YourApplication.java YourApplication.java

 ...

 private final ReactNativeHost mReactNativeHost =
      new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          packages.add(new MainReactPackage());
          return packages;
        }

        @Override
        protected String getJSMainModuleName() {
          return "index";
        }
      };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }
...

Full code完整代码

For a reference, I created a sample project similar to yours that works just fine.作为参考,我创建了一个与您的类似的示例项目,它工作得很好。 Below is relevant code.下面是相关代码。

MainActivity.java MainActivity.java

package com.reacttest;

import android.os.Bundle;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript. This is used to schedule
     * rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "ReactTest";
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
    }
}

MainActivityModule主活动模块

package com.reacttest;

import android.app.Activity;
import android.os.Build;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;

import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

public class MainActivityModule extends ReactContextBaseJavaModule {

    private ReactContext mReactContext;
    private ReactRootView mReactRootView;

    public MainActivityModule(ReactApplicationContext reactContext) {
        super(reactContext);
        mReactContext = reactContext;
        mReactRootView = new ReactRootView(reactContext);
    }

    @ReactMethod
    public void helloFromAndroid() {
        Activity activity = mReactContext.getCurrentActivity();
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
                    activity.setShowWhenLocked(true);
                    activity.setTurnScreenOn(true);
                }

                activity.getWindow().addFlags(
                        WindowManager.LayoutParams.FLAG_FULLSCREEN
                                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
                activity.setContentView(mReactRootView);
                Toast.makeText(activity, "Hello from Android", Toast.LENGTH_LONG).show();
            }
        });
    }

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

MainActivityPackage.java MainActivityPackage.java

package com.reacttest;

import androidx.annotation.NonNull;

import com.facebook.react.ReactPackage;
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 MainReactPackage implements ReactPackage {
    @Override
    @NonNull
    public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

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

        modules.add(new MainActivityModule(reactContext));

        return modules;
    }
}

MainApplication.java主要应用.java

package com.reacttest;

import android.app.Application;
import android.content.Context;

import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost =
            new ReactNativeHost(this) {
                @Override
                public boolean getUseDeveloperSupport() {
                    return BuildConfig.DEBUG;
                }

                @Override
                protected List<ReactPackage> getPackages() {
                    @SuppressWarnings("UnnecessaryLocalVariable")
                    List<ReactPackage> packages = new PackageList(this).getPackages();
                    packages.add(new MainReactPackage());
                    return packages;
                }

                @Override
                protected String getJSMainModuleName() {
                    return "index";
                }
            };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
        initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
    }

    /**
     * Loads Flipper in React Native templates. Call this in the onCreate method with something like
     * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
     *
     * @param context
     * @param reactInstanceManager
     */
    private static void initializeFlipper(
            Context context, ReactInstanceManager reactInstanceManager) {
        if (BuildConfig.DEBUG) {
            try {
        /*
         We use reflection here to pick up the class that initializes Flipper,
        since Flipper library is not available in release mode
        */
                Class<?> aClass = Class.forName("com.reacttest.ReactNativeFlipper");
                aClass
                        .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
                        .invoke(null, context, reactInstanceManager);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

App.js应用程序.js

import {NativeModules} from 'react-native';

var mainActivityModule = NativeModules.MainActivityModule;
mainActivityModule.unlockScreen();

Result结果

在此处输入图像描述

As you can see, the Toast from the Activity is shown.如您所见,显示了来自Activity的 Toast。 That's it!而已!

Have you already registered your Native module with your React Native?你已经用你的 React Native 注册了你的 Native 模块了吗? Reference: https://reactnative.dev/docs/native-modules-android#register-the-module-android-specific参考: https://reactnative.dev/docs/native-modules-android#register-the-module-android-specific

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将Java方法的回调放入React Native中的文本中 - How to put callback from Java method into text in React Native 从 Java React Native 调用 JavaScript 方法 - Call JavaScript method from Java React Native 在 Javascript 中从 React Native 调用 Native Java 方法 - Calling Native Java method from React Native in Javascript React Native如何将数据从JavaScript传递到Java Android - React Native How to pass data from javascript to java android 如何在React-Native中运行Java方法 - How to run java method in react-native React Native Android:显示来自Java的活动 - React Native Android: Showing an Activity from Java 从Android中的Native Java Activity打开React Native组件 - Open React Native component from Native Java Activity in Android 如何找到本机 c++ function 谁在 android 应用程序和哪个库中调用了 java 方法? - how to find the native c++ function who called a java method in a android app and in which library? React Native-从React Native屏幕导航到本机Java(Android Studio)屏幕 - React Native - Navigating from a React Native screen to a native Java (Android Studio) screen React Native:如何从模块调用原生Android布局? - React Native: How to invoke native Android layout from module?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM