F8App-ReactNative项目源码分析3-Androi

作者: offbye西涛 | 来源:发表于2016-05-20 23:13 被阅读2198次

    近期开始研究Facebook f8app项目,目标是理解Facebook官方React Native f8app的整体技术架构,给公司目前几个的React Native项目开发提供官方经验借鉴,并对原生开发和React Native开发进行框架层面的融合。
    本文分析f8app android代码的结构和技术实现,阅读本文的前提是熟悉Android开发。

    f8app android代码结构分析

    ReactNative项目Android相关的代码都在android目录下,可以直接使用Android Studio打开这个目录方便查看和修改。android目录结构如下

    ├── F8v2.iml
    ├── app
    │   ├── app.iml
    │   ├── build.gradle
    │   ├── proguard-rules.pro
    │   ├── react.gradle
    │   └── src
    │       └── main
    │           ├── AndroidManifest.xml
    │           ├── java
    │           │   └── com
    │           │       └── facebook
    │           │           └── f8
    │           │               └── MainActivity.java
    │           └── res
    │               ├── mipmap-hdpi
    │               │   ├── ic_launcher.png
    │               │   └── ic_notification.png
    │               ├── mipmap-mdpi
    │               │   ├── ic_launcher.png
    │               │   └── ic_notification.png
    │               ├── mipmap-xhdpi
    │               │   ├── ic_launcher.png
    │               │   └── ic_notification.png
    │               ├── mipmap-xxhdpi
    │               │   ├── ic_launcher.png
    │               │   └── ic_notification.png
    │               ├── mipmap-xxxhdpi
    │               │   ├── ic_launcher.png
    │               │   └── ic_notification.png
    │               ├── raw
    │               │   └── third_party_notices.html
    │               └── values
    │                   ├── strings.xml
    │                   └── styles.xml
    ├── build.gradle
    ├── gradle
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradle.properties
    ├── gradlew
    ├── gradlew.bat
    ├── local.properties
    └── settings.gradle
    

    在android Studio下显示如下:


    as文件结构

    MainActivity 代码分析

    通过AndroidManifest.xml可以知道App的页面入口Activity是android/app/src/main/java/com/facebook/f8/MainActivity.java,代码比较简单,继承了ReactActivity,关键的方法是getJSBundleFile,可以看到使用了CodePush推送更新技术,返回了一个bundle文件,分析代码可知默认的bundle文件是index.android.bundle。
    CodePush是微软提供的一个开发者服务,支持对React Native和Cordova App在不重新安装App的前提下热更新App,是目前很流行的一种App热部署技术。
    getPackages方法初始化了CodePush,初始了ReactNativePushNotificationPackage推送服务,初始化了用到的一些RN第三方包:

    • FBSDKPackage是Android Facebook SDK的React Native封装,让React Native App能够使用Facebook的接口,提供了Facebook用户登录token,分享等功能;
    • LinearGradientPackage是一个渐变UI控件;
    • RNSharePackage是个分享插件,提供了通过Android Intent.ACTION_SEND分享文字和链接的简单分享功能;
    • RNSendIntentPackage是一个发送Android Intent的插件,提供了通过Intent拨打电话,发送短信,在系统日历添加事件提醒等功能。
    • ReactNativePushNotificationPackage是一个本地和远程通知推送插件,可以接受Google GCM推送的消息,并在状态栏上显示。
    public class MainActivity extends ReactActivity {
      private CodePush _codePush;
      private ReactNativePushNotificationPackage _pushNotification;
      private CallbackManager mCallbackManager;
    
        @Override
        protected String getJSBundleFile() {
            return this._codePush.getBundleUrl("index.android.bundle");
        }
    
        /**
         * Returns the name of the main component registered from JavaScript.
         * This is used to schedule rendering of the component.
         */
        @Override
        protected String getMainComponentName() {
            return "F8v2";
        }
    
        /**
         * Returns whether dev mode should be enabled.
         * This enables e.g. the dev menu.
         */
        @Override
        protected boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }
    
       /**
       * A list of packages used by the app. If the app uses additional views
       * or modules besides the default ones, add more packages here.
       */
        @Override
        protected List<ReactPackage> getPackages() {
          this._codePush = new CodePush("qwfkzzq7Y8cSrkiuU7aRCkIP7XYLEJ6b-AFoe", this, BuildConfig.DEBUG);
          this._pushNotification = new ReactNativePushNotificationPackage(this);
          mCallbackManager = new CallbackManager.Factory().create();
    
          return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new FBSDKPackage(mCallbackManager),
            new LinearGradientPackage(),
            new RNSharePackage(),
            new RNSendIntentPackage(),
            this._codePush.getReactPackage(),
            this._pushNotification
          );
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            FacebookSdk.sdkInitialize(getApplicationContext());
        }
    
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
          super.onActivityResult(requestCode, resultCode, data);
          mCallbackManager.onActivityResult(requestCode, resultCode, data);
        }
    
       @Override
       protected void onNewIntent (Intent intent) {
         super.onNewIntent(intent);
         _pushNotification.newIntent(intent);
       }
    
      @Override
      protected void onResume() {
          super.onResume();
          AppEventsLogger.activateApp(getApplicationContext());
      }
    
      @Override
      protected void onPause() {
          super.onPause();
          AppEventsLogger.deactivateApp(getApplicationContext());
      }
    
      @Override
      protected void onStop() {
          super.onStop();
          AppEventsLogger.onContextStop();
      }
    }
    
    

    可以查看settings.gradle,发现f8app Android引用了5个子工程,子工程源码放在../node_modules目录下。

    rootProject.name = 'F8v2'
    
    include ':app', ':react-native-linear-gradient'
    include ':app', ':react-native-code-push'
    include ':react-native-push-notification'
    include ':react-native-share', ':app'
    include ':react-native-fbsdk'
    include ':react-native-send-intent', ':app'
    project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
    project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
    project(':react-native-push-notification').projectDir = file('../node_modules/react-native-push-notification/RNPushNotificationAndroid')
    project(':react-native-share').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-share/android')
    project(':react-native-fbsdk').projectDir = new File(settingsDir, '../node_modules/react-native-fbsdk/Android')
    project(':react-native-send-intent').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-send-intent')
    
    

    可以看到,可以通在settings.gradle引入工程的方法增加新的自定义RN插件。实际上,这些插件是通过rnpm install命令安装的。React Native Package Manager (RNPM) 是一个简化React Native插件安装体验的工具。
    下面我们分析react-native-linear-gradient插件看看插件是如何实现的。

    React Native android 插件实现分析

    LinearGradientView是一个典型的原生UI插件,可以先看看官方的文档NativeComponentsAndroid, 我感觉这个文档有点过时,没有提到ReactPackage。ReactPackage接口是现在RN插件的主入口。LinearGradientPackage实现了ReactPackage接口
    ReactPackage接口代码如下:

    /**
     * Main interface for providing additional capabilities to the catalyst framework by couple of
     * different means:
     * 1) Registering new native modules
     * 2) Registering new JS modules that may be accessed from native modules or from other parts of the
     * native code (requiring JS modules from the package doesn't mean it will automatically be included
     * as a part of the JS bundle, so there should be a corresponding piece of code on JS side that will
     * require implementation of that JS module so that it gets bundled)
     * 3) Registering custom native views (view managers) and custom event types
     * 4) Registering natively packaged assets/resources (e.g. images) exposed to JS
     *
     * TODO(6788500, 6788507): Implement support for adding custom views, events and resources
     */
    public interface ReactPackage {
    
      /**
       * @param reactContext react application context that can be used to create modules
       * @return list of native modules to register with the newly created catalyst instance
       */
      List<NativeModule> createNativeModules(ReactApplicationContext reactContext);
    
      /**
       * @return list of JS modules to register with the newly created catalyst instance.
       *
       * IMPORTANT: Note that only modules that needs to be accessible from the native code should be
       * listed here. Also listing a native module here doesn't imply that the JS implementation of it
       * will be automatically included in the JS bundle.
       */
      List<Class<? extends JavaScriptModule>> createJSModules();
    
      /**
       * @return a list of view managers that should be registered with {@link UIManagerModule}
       */
      List<ViewManager> createViewManagers(ReactApplicationContext reactContext);
    }
    

    LinearGradientPackage类主要实现了createViewManagers方法,返回了一个LinearGradientManager实例,代码如下:

    public class LinearGradientPackage implements ReactPackage {
    
        @Override
        public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
            return Collections.emptyList();
        }
    
        @Override
        public List<Class<? extends JavaScriptModule>> createJSModules() {
            return Collections.emptyList();
        }
    
        @Override
        public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
            return Arrays.<ViewManager>asList(
                new LinearGradientManager());
        }
    }
    
    

    下面看LinearGradientManager类,包含了一个LinearGradientView实例,LinearGradientView是渐变View的具体实现,通过@ReactProp注解可以在js层设置渐变View的一些属性,例如渐变颜色,渐变开始和结束位置等的。代码如下:

    public class LinearGradientManager extends SimpleViewManager<LinearGradientView> {
    
        public static final String REACT_CLASS = "BVLinearGradient";
        public static final String PROP_COLORS = "colors";
        public static final String PROP_LOCATIONS = "locations";
        public static final String PROP_START_POS = "start";
        public static final String PROP_END_POS = "end";
        public static final String PROP_BORDER_RADIUS = "borderRadius";
    
        @Override
        public String getName() {
            return REACT_CLASS;
        }
    
        @Override
        protected LinearGradientView createViewInstance(ThemedReactContext context) {
            return new LinearGradientView(context);
        }
    
        @ReactProp(name=PROP_COLORS)
        public void setColors(LinearGradientView gradientView, ReadableArray colors) {
            gradientView.setColors(colors);
        }
    
        @ReactProp(name=PROP_LOCATIONS)
        public void setLocations(LinearGradientView gradientView, ReadableArray locations) {
            gradientView.setLocations(locations);
        }
    
        @ReactProp(name=PROP_START_POS)
        public void setStartPosition(LinearGradientView gradientView, ReadableArray startPos) {
            gradientView.setStartPosition(startPos);
        }
    
        @ReactProp(name=PROP_END_POS)
        public void setEndPosition(LinearGradientView gradientView, ReadableArray endPos) {
            gradientView.setEndPosition(endPos);
        }
    
        // temporary solution until following issue is resolved:
        // https://github.com/facebook/react-native/issues/3198
        @ReactProp(name=PROP_BORDER_RADIUS, defaultFloat = 0f)
        public void setBorderRadius(LinearGradientView gradientView, float borderRadius) {
            gradientView.setBorderRadius(PixelUtil.toPixelFromDIP(borderRadius));
        }
    }
    
    

    CodePush 热更新技术介绍

    f8app使用了CodePush技术热更新App,可以先看下微软官方的文档React Native Client SDK
    CodePush提供了对React Native App的热更新能力,目前只能对js bundle进行热更新,如果原生代码修改了是不能热更新的,只能通过重新安装App的方式升级。
    CodePush的一个问题是是对React Native版本的兼容性,由于React Native版本更新很快,CodePush是由微软而不是Facebook维护的,所以对React Native0.18以前的版本支持有各种兼容问题,具体参考文档
    CodePush的具体配置可以参考这篇文章,配置需要注意的事项还是挺多的,我参考这篇文章配置成功了,本文不做详细介绍。

    总结

    本文分析了f8app android代码结构,以react-native-linear-gradient插件为例子分析了React Native插件的结构,介绍了CodePush热更新技术,通过本文可以对f8app有比较初步的认识。
    下篇文章将分析React Native js代码,这块是f8app的核心实现,代码量比较大。

    相关文章

      网友评论

      本文标题: F8App-ReactNative项目源码分析3-Androi

      本文链接:https://www.haomeiwen.com/subject/gawnrttx.html