React-Native系列Android源碼分析

使用了一段時間的react-native,它的UI設計方式真是酷炫,原來我是學android的,由於有android的開發基礎,所以我打算從java上學習react-native在原生代碼上的設計,並且寫下該文章筆記作爲以後的知識回顧。

  • react-native版本 0.33.0

AndroidManifest.xml

    ...
    <application
      android:name=".MainApplication" //使用自己的Application
      android:allowBackup="true"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:theme="@style/AppTheme">
      <activity
        android:name=".MainActivity" // 初始Activity
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"> // 添加配置監聽
        <intent-filter> // 隱式intent配置
            <action android:name="android.intent.action.MAIN" /> // 決定應用程序最先啓動的Activity
            <category android:name="android.intent.category.LAUNCHER" /> // 決定應用程序是否顯示在程序列表裏
        </intent-filter>
      </activity>
      <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> // debug模式下react-native的工具Activity
    </application>

</manifest>

MainApplication.java

// ReactApplication接口定義getReactNativeHost方法
public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    // 開發者模式開關
    @Override
    protected boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG; // 全局類BuildConfig,判斷是否爲debug模式
    }
    // 返回ReactPackage對象數組,
    // ReactPackage定義基本的原生模塊和視圖管理
    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage()
      );
    }
  };

  // ReactNativeHost控制一個ReactInstanceManager對象
  @Override
  public ReactNativeHost getReactNativeHost() {
      return mReactNativeHost;
  }
}

MainActivity繼承com.facebook.react.ReactActivity,ReactActivity實現DefaultHardwareBackBtnHandler(機器返回按鈕接口,觸發JS中返回按鈕監聽事件),PermissionAwareActivity兩個接口,使用ReactActivityDelegate封裝主幹代碼的

com.facebook.react.ReactActivityDelegate

/**
 * MainActivity的初始化
 */
public class ReactActivityDelegate {
  private static final String REDBOX_PERMISSION_MESSAGE =
    "Overlay permissions needs to be granted in order for react native apps to run in dev mode";

  private final @Nullable Activity mActivity;
  private final @Nullable FragmentActivity mFragmentActivity;
  private final @Nullable String mMainComponentName; // app名

  private @Nullable ReactRootView mReactRootView; // 根視圖
  private @Nullable DoubleTapReloadRecognizer  mDoubleTapReloadRecognizer; // 雙擊'R'鍵Reload JS
  private @Nullable PermissionListener mPermissionListener;
  ...
  protected void onCreate(Bundle savedInstanceState) {
    if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
      // Get permission to show redbox in dev builds.
      if (!Settings.canDrawOverlays(getContext())) {
        Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        getContext().startActivity(serviceIntent);
        FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
        Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
      }
    }

    if (mMainComponentName != null) {
      loadApp(mMainComponentName); // 創建ReactRootView
    }
    mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
  }

  protected void loadApp(String appKey) {
    if (mReactRootView != null) {
      throw new IllegalStateException("Cannot loadApp while app is already running.");
    }
    mReactRootView = createRootView();
    mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      appKey,
      getLaunchOptions());
    getPlainActivity().setContentView(mReactRootView);
  }
  ...
  private Context getContext() {
    if (mActivity != null) {
      return mActivity;
    }
    return Assertions.assertNotNull(mFragmentActivity);
  }

  // 返回Activity或FragmentActivity轉換後的Activity
  private Activity getPlainActivity() {
    return ((Activity) getContext());
  }

}

ReactRootView繼承SizeMonitoringFrameLayout,基礎父類爲FrameLayout。

ReactRootView

...
/**
 * 監聽窗口大小變化改變內部佈局,獲取觸摸事件遍歷子視圖觸發JSTouchDispatcher中的響應事件
 * 重寫ViewGroup方法:
 *   onInterceptTouchEvent
 *   requestDisallowInterceptTouchEvent
 * 重寫View方法:
 *   onTouchEvent
 */
public class ReactRootView extends SizeMonitoringFrameLayout implements RootView {
  ...
  /**
   * 獲取所有的屏幕觸摸事件
   */
  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    dispatchJSTouchEvent(ev);
    return super.onInterceptTouchEvent(ev);
  }

  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    dispatchJSTouchEvent(ev);
    super.onTouchEvent(ev);
    // 當沒有子類觸發時返回true
    // 從而允許後續的觸摸
    return true;
  }

  private void dispatchJSTouchEvent(MotionEvent event) {
    ...
    // 所有事件統一通過JSTouchDispatcher發送觸摸事件給JS
    mJSTouchDispatcher.handleTouchEvent(event, eventDispatcher);
  } 
  ...
} 

JSTouchDispatcher

/**
 * 觸摸事件和JS事件連接
 */
public class JSTouchDispatcher {
  ...
  // 找出觸發事件的視圖ID
  public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) {

    //MotionEvent.ACTION_POINTER_DOWN 多點按下
    int action = ev.getAction() & MotionEvent.ACTION_MASK; // 獲取action,添加&...支持多點觸摸
    if (action == MotionEvent.ACTION_DOWN) {
      // mTargetTag按鈕事件標識符,-1爲初始
      if (mTargetTag != -1) {
        FLog.e(
          ReactConstants.TAG,
          "Got DOWN touch before receiving UP or CANCEL from last gesture");
      }

      // 是否取消原生手勢
      mChildIsHandlingNativeGesture = false;
      // 查找觸發手勢的視圖ID
      mTargetTag = TouchTargetHelper.findTargetTagAndCoordinatesForTouch(
        ev.getX(),
        ev.getY(),
        mRootViewGroup,
        mTargetCoordinates, // 儲存ev的x,y
        null);
      // 處罰獲取到的子類視圖ID,處罰內部的JS事件
      eventDispatcher.dispatchEvent(
        TouchEvent.obtain(
          mTargetTag,
          TouchEventType.START, // 說明是按下事件
          ev,
          mTargetCoordinates[0],
          mTargetCoordinates[1],
          mTouchEventCoalescingKeyHelper));
    }  
    ...後面的結構基本相同,主要是通過eventDispatcher.dispatchEvent觸發不同的JS事件
  }
  ...
}

touch部分這裏只是簡單的介紹了下,詳細解析可以參考React-Native系列Android——Touch事件原理及狀態效果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章