2018年2月13日 星期二

將React Native0.53與現有的iOS整合

版本

  • xcode 9.2(9C40b)
  • react-native 0.53.0
  • react 16.0.0-beta.5

參考

前言

這版本在與原專案集成的坑真的不是普通的多...希望下一版能全部修復

步驟

  1. 新建一個iOS single view app,如果已經有iOS專案,可以跳到第三步

  2. 初始化pod

    > pod init
    
  3. 新增一個資料夾,然後在裡面新建一個ios資料夾,把iOS專案拉進來,這時目錄應該是這樣

    - RNFloder
        - ios
            - iosProjectName
                - ...
                - Assets.xcassets
                - ...
            - iosProjectName.xcodeproj
            - iosProjectName.xcworkspace
            - ... 
    
  4. 在React Native根目錄初始化npm,npm是js的CocoaPods,一般只要一直按enter就好了,他會在你的目錄下多出一個package.json檔案,作用等同於Podfile

    > npm init
    
  5. 接著安裝React Native相關的js lib,根據官方文件,必須要以下react版本,因為rn對react版本很敏感

    > npm install --save react@16.0.0-beta.5 react-native
    
  6. 在Podfile中將React Native lib引入到專案

    target 'ReactNativeiOSHybrid' do
              use_frameworks!
      # 'node_modules'目錄一般位於根目錄中
      # 但是如果你的結構不同,那你就要根據實際路徑修改下面的`:path`
      pod 'React', :path => '../node_modules/react-native', :subspecs => [
        'Core',
        'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
        'RCTText',
        'RCTNetwork',
        'RCTWebSocket', # needed for debugging
        'CxxBridge',
        # Add any other subspecs you want to use in your project
      ]
      # Explicitly include Yoga if you are using RN >= 0.42.0
      pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
    
      # Third party deps podspec link
      pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
      pod 'GLog', :podspec => '../node_modules/react-native/third-party-podspecs/GLog.podspec'
      pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
    
    end
    
    # 這裡要注意,如果CocoaPods在install的時候出了問題,記得下pod cache clean --all,不然會有緩存導致之後改動Podfile還是會install失敗
    
  7. CD到React Native目錄下的iOS目錄,安裝相關iOS lib

    > pod install
    
  8. 啟動Xcode,run app

  9. 這時候會發現有錯誤

    Yoga-internal.h:11:10 : fatal error: 'algorithm' file not found: #include 
    
  10. 這是因為react native(或是yoga?反正都是facebook)官方podspec沒配置好

  11. 接著按照github有一個還沒過的PR改動,打開以下文件

    > cd RNProject/node_modules/react-native/ReactCommon/yoga
    > vim yoga.podspec
    
  12. 在最後面補上

        ...
        ...
      # Set this environment variable when not using the `:path` option to install the pod.
      # E.g. when publishing this spec to a spec repo.
      source_files = 'yoga/**/*.{cpp,h}'
      source_files = File.join('ReactCommon/yoga', source_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION']
      spec.source_files = source_files
    
      # 補上以下兩句
      spec.public_header_files = 'yoga/Yoga.h', 'yoga/YGEnums.h', 'yoga/YGMacros.h'
    
    end
    
  13. 這樣就解決了algorithm.h找不到的問題,問題解決,想了解更多可以看一下這個issue:React Native iOS Pod issues: fatal error: 'algorithm' file not found

  14. 接著運行,還會報一個fishhook/fishhook.h頭文件找不到的問題

  15. 找到該報錯文件,將報錯的import改成以下

    #import fishhook/fishhook.h -> #import fishhook.h
    
  16. 問題解決,想了解更多可以看一下這個issue:React Native iOS issues: Fishhook error

  17. 然後在React Native根目錄新增一個index.js文件,這個是用來測試的React Native頁面

    import React from 'react';
    import { AppRegistry, StyleSheet, Text, View } from 'react-native';
    
    class RNHighScores extends React.Component {
      render() {
        var contents = this.props['scores'].map((score) => (
          <Text key={score.name}>
            {score.name}:{score.value}
            {'\n'}
          </Text>
        ));
        return (
          <View style={styles.container}>
            <Text style={styles.highScoresTitle}>2048 High Scores!</Text>
            <Text style={styles.scores}>{contents}</Text>
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#FFFFFF',
      },
      highScoresTitle: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
      scores: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
      },
    });
    
    // Module name
    AppRegistry.registerComponent('RNHighScores', () => RNHighScores);
    
  18. 然後在React Native根目錄執行以下指令,他會在local端開啟一個server,供React Native讀取我們開發中的Reat Native文件,他會自動打包成bundle

    > npm start 
    
  19. 然後執行Xcode->run iOS專案,或是在根目錄

    > react-native run-ios
    
  20. 如果你使用的是0.53.0版的React Native,你會出現以下錯誤

    No component found for view with name "RCTText"
    
  21. 這是由於我們facebook工程師一個美妙的錯誤,詳情可以看以下issue:React Native iOS issue: No component found for view with name "RCTText"

  22. 依照以上issue的解決方案,打開./node_modules/react-native/React.podspec

      s.subspec "RCTText" do |ss|
        ss.dependency             "React/Core"
    -   ss.source_files         = "Libraries/Text/*.{h,m}"
    +   ss.source_files         = "Libraries/Text/**/*.{h,m}"
      end
    
  23. 在iOS目錄下重新 pod install

  24. OK,畫面出現,歷經了千辛萬苦,終於可以愉快地使用React Native了


author Iml1s

email ImL1s@outlook.com

若我的文章有幫助到你,可以考慮請我喝杯咖啡:D

2018年2月8日 星期四

React Native CodePush集成

author: ImL1s

email: ImL1s@outlook.com

github: 專案

參考

版本

  • react-native-cli: 2.0.1
  • react-native: 0.53.0
  • code-push: 2.1.6

前置作業

註冊CodePush

  1. 安裝CodePush CLI

    > npm install -g code-push-cli
    > code-push -v
    2.1.6 // 有顯示版本號代表安裝成功
    
  2. 向CodePush註冊App

    > code-push app add CodePushIntergradation ios react-native
    ┌────────────┬──────────────────────────────────────────────────────────────────┐
    │ Name       │ Deployment Key                                                   │
    ├────────────┼──────────────────────────────────────────────────────────────────┤
    │ Production │ xxxxs2KwnRds65xxxxbp2GpYF78h3bxxxx1f-xxxx-xxxx-bba3-5a79beaxxxxd │
    ├────────────┼──────────────────────────────────────────────────────────────────┤
    │ Staging    │ xxxxgs9s-QBRsxxxxGxxxxGGxxhxxxx467xf-xxx3-430a-bba3-5a7xxxx95xxx │
    └────────────┴──────────────────────────────────────────────────────────────────┘
    

公有雲的CodePush集成到新的iOS專案(OC)

  1. 新建一個React Native專案

    react-native init codePushIntergradation
    
  2. 在新建的React Native目錄下,使用npm安裝CodePush

    npm install --save react-native-code-push
    
  3. 再run一次安裝

    npm install
    
  4. 執行以下指令,會打開瀏覽器訪問M$的app center,按照步驟登入或是註冊

    > code-push register
    
  5. 上面的步驟完畢,在瀏覽器中可以得到一串金鑰

    fxxxdd9caxxxxxxadxxxc3xxxc9904xxxd5xxx1x
    
  6. 將金鑰輸入到終端機中,他會將session文件存在~/.code-push.config

    Successfully logged-in. 
    Your session file was written to /Users/userName/.code-push.config. 
    You can run the code-push logout command at any time to delete this file and terminate your session.
    
  7. 接著輸入以下指令,證明你已經登入了

    > code-push login
    [Error]  You are already logged in from this machine.
    
  8. 接著安裝幫原生的iOS/Android專案安裝CodePush的lib,deployment key先不用輸入,按Enter就好

    > react-native link react-native-code-push
    
    Scanning folders for symlinks in /Users/UserName/Project/IOS/CodePushIntergradation/node_modules (18ms)
    ? What is your CodePush deployment key for Android (hit <ENTER> to ignore) 
    rnpm-install info Linking react-native-code-push android dependency 
    rnpm-install info Android module react-native-code-push has been successfully linked 
    rnpm-install info Linking react-native-code-push ios dependency 
    rnpm-install info iOS module react-native-code-push has been successfully linked 
    Running ios postlink script
    ? What is your CodePush deployment key for iOS (hit <ENTER> to ignore) 
    Running android postlink script
    
  9. 在iOS專案中,使用Source Code方式打開info.plist,在裡面新增or更改CodePushDeploymentKey這個key的值為向CodePush註冊的Staging key(在上面前置作業申請的)

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        ...
        ...
        <key>CodePushDeploymentKey</key>
        <string>iGexxx9s-Qxxx1xxxGkxxxGxxxhF3xxx67xx-xxxx-43xx-xxxx-xxxxxxx9xxxd</string>
    </dict>
    </plist>
    
  10. 接著打開AppDelegate.m,可以看到以下程式碼,cli工具已經幫我們把RCTRootView(React Native JS的運行容器)更新的Code都寫好了

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        ...
        ...
        #ifdef DEBUG
            jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
        #else
            jsCodeLocation = [CodePush bundleURL];
        #endif
        RCTRootView *rootView = 
        [[RCTRootView alloc] 
        initWithBundleURL:jsCodeLocation
            moduleName:@"codePushIntergradation"
            initialProperties:nil
            launchOptions:launchOptions];
       ...
       ...
    }
    
  11. 接著我們還要設定iOS的http訪問權限,打開info.plist,把CodePush的Url加入進去

    <plist version="1.0">
      <dict>
        <!-- ...other configs... -->
    
        <key>NSAppTransportSecurity</key>
        <dict>
          <key>NSExceptionDomains</key>
          <dict>
            <key>codepush.azurewebsites.net</key>
            <dict><!-- read the ATS Apple Docs for available options --></dict>
          </dict>
        </dict>
    
        <!-- ...other configs... -->
      </dict>
    </plist>
    
  12. 將React Native的index.js改成以下

    import { AppRegistry } from 'react-native';
    import App from './App';
    
    import codePush from "react-native-code-push";
    
    AppRegistry.registerComponent('codePushIntergradation', () => codePush(App));
    
  13. 接著用Release模式Run,一定要Release喔,不然他只會讀local的js

  14. xcode怎麼找到最新的React Native js的呢?因為Xcode裡,react-native cli工具幫我們配置了一個run script,可以切到以下地方,找到一個.sh的script,這個script會在Copy Bundle Resources之後將打包好的js放到ipa中(可以參考開頭參考的連結)

    點擊項目 -> TARGETS -> {{porject name}} -> BuildPhases ->Bundle React Native code and images
    
  15. 接著修改React Native的app.js

    export default class App extends Component<Props> {
      render() {
        return (
          <View style={styles.container}>
            <Text style={styles.welcome}>
             Hello code push!!
            </Text>
            <Text style={styles.instructions}>
              To get started, edit App.js
            </Text>
            <Text style={styles.instructions}>
              {instructions}
            </Text>
          </View>
        );
      }
    }
    
  16. 接著要把React Native專案下的package.json版本號更新,不然不給上傳

    {
      "name": "codePushIntergradation",
      "version": "0.0.2",
      "private": true,
      "scripts": {
        "start": "node node_modules/react-native/local-cli/cli.js start",
        "test": "jest"
      },
      "dependencies": {
        "react": "16.2.0",
        "react-native": "0.53.0",
        "react-native-code-push": "^5.2.1"
      },
      "devDependencies": {
        "babel-jest": "22.2.0",
        "babel-preset-react-native": "4.0.0",
        "jest": "22.2.1",
        "react-test-renderer": "16.2.0"
      },
      "jest": {
        "preset": "react-native"
      }
    }
    
  17. 接著將寫好的js打包,並且發布到遠端Server上,這句command做兩個動作,打包React Native專案並且上傳到Code push

    > code-push release-react {{Your project name}} ios 
    
  18. 重新打開app兩次(滑掉重開),然後可就可以看到更新,至於為什麼要兩次呢...?我猜是為了用戶體驗,第一次檢查到更新先存著,等到下一次再更新

常用指令

初始化階段:
1:npm install -g code-push-cli 安裝客戶端
2:code-push -v 查看是否安裝成功
3:code-push register 在codepush注冊賬號
4:code-push login
5:code-push app add <appName> <android/ios> react-native 添加app
例如
code-push app add test android react-native

6:code-push app list 列出app列表
code-push deployment ls <appName> -k 查看APP的key
code-push deployment history <appName> Porduction/Staging
例如:
code-push deployment history test Production

7:yarn add react-native-code-push 在rn項目下安裝codepush
8:react-native link react-native-code-push 鏈接codepush

2018年2月2日 星期五

React Native與Android交互1-Activity引用React Native Component

Android引用React Native Component

步驟

  1. 新建一個Android專案
  2. 在Android專案根目錄下執行以下指令,提示大部分按enter就好了

    npm init

  3. 繼續執行

    npm install react react-native --save

  4. 這時Android的目錄會是以下的樣子

    -YourProjectName
    |-.gradle
    |-.idea
    |-app
    |-build
    |-gradle
    |-node_modules
    |-.gitignore
    |-YourProjectName.iml
    |-build.gradle
    |-gradle.properties
    |-gradlew
    |-gradlew.bat
    |-local.properties
    |-npm-debug.log
    |-package.json
    |-settings.gradle
    |-yarn.lock
    
  5. package.json的內容應該為下,react和react-native版本會隨著時間推移改變

    {
      "name": "YourProjectName",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "react": "^16.2.0",
        "react-native": "^0.52.2"
      }
    }
    
  6. 接著開啟Android專案的root gradle,將剛剛npm下載的react native
    Android library放到repositories中

    allprojects {
    repositories {
        jcenter()
        maven { url 'https://maven.google.com' }
        // 將node_modules中Android相關的lib放到maven引用中
        maven {
            // All of React Native (JS, Android binaries) is installed from npm
            url "$rootDir/node_modules/react-native/android"
        }
    }
    

    }

  7. 接著到app的gradle中,將react native相關的lib引入

    dependencies {
     ... ...
    compile "com.facebook.react:react-native:+" // From node_modules.
    

    }

  8. 配置完成,打開MainActivity,在onCraete中

     //檢查權限:讓使用者打開懸浮視窗權限以便開發中的紅屏錯誤能正確顯示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, 154);
            }
        }
    
  9. Android端的作業做得差不多了,現在來寫一下react native的部分吧,在Android專案根目錄新增一個目錄js,在裡面新增一個index.js,這個就是等等Activity要用來顯示的react native component

    import React from 'react';
    import {
      AppRegistry,
      StyleSheet,
      Text,
      View
    } from 'react-native';
    
    class HelloWorld extends React.Component {
      render() {
        return (
          <View style={styles.container}>
            <Text style={styles.text}>I'm React Native Text</Text>
          </View>
        )
      }
    }
    var styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
      },
      text: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
    });
    
    AppRegistry.registerComponent('MyReactNativeApp', () => HelloWorld);
    
  10. 接下來在Activity中

    class MainActivity extends AppCompatActivity {
    
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                // 在asset文件夾中,打包過的react native js文件名稱
                .setBundleAssetName("index.android.bundle")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
    
        // 第二個參數"MyReactNativeApp"為React Native中的AppRegistry.registerComponent的第一個參數
        mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);
    
        setContentView(mReactRootView);
    }
    

    }

  11. 將剛剛寫好的react native component打包成bundle,給ReactInstanceManager使用,bundle會放在Android的asset下

    react-native bundle --platform {{平台}} --entry-file {{入口文件,一般命名index.js}} --bundle-output {{打包好的bundle存放路徑}} --assets-dest {{react native引用的資源文件置放路徑}}
    
    react-native bundle --platform android --entry-file ./app/js/index.js --bundle-output app/src/main/assets/index.android.bundle --assets-dest app/src/main/res/
    
  12. 恭喜!執行App,看到React Native的介面

Android 呼叫 React Native Component

  1. 新增一個implement ReactContextBaseJavaModule的class,這個class是最終與react native通信的類

    class ToastAndroidModule(private val reactContext: ReactApplicationContext) :
        ReactContextBaseJavaModule(reactContext) {
    
        /**
         * react native call android時的模組名稱
         */
        override fun getName(): String {
            return "ToastAndroidModule"
        }
    
        /**
         * react native call(->) android
         */
        @ReactMethod // 此註解代表要expose給react native的方法
        fun HandleMessage(message: String) {
            Toast.makeText(reactContext, message, Toast.LENGTH_LONG).show()
        }
    
        /**
         * android call(->) react native
         */
        fun sendMessage(params: String) {
            reactContext
                    .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
                    .emit("mEventName", params)
        }
    }
    
  2. 新增一個implement ReactPackage的Package,此類底下會有許多NativeModule(就是第一步驟寫的),必須將此類放入react native API,才能進行溝通

    class AndroidWidgetPackage : ReactPackage {
    
        private var nativeModules: MutableList<NativeModule>? = null
    
        override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
            // 在這裡將需要溝通原生模組放入list並回傳,這樣就等於將原生註冊到react native
            nativeModules = ArrayList()
            nativeModules!!.add(0, ToastAndroidModule(reactContext))
            return nativeModules as ArrayList<NativeModule>
        }
    
        override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
            return Collections.emptyList()
        }
    
        /**
         * 方便取得AndroidModule的方法
         */
        fun getModule(index: Int): NativeModule? {
            return if (nativeModules == null) null else nativeModules!![index]
        }
    
        fun getToastModule(): ToastAndroidModule {
            return getModule(0) as ToastAndroidModule
        }
    }
    
  3. 撰寫React Native的Component,此Component功能很簡單,點擊按鈕呼叫第一部撰寫的ToastAndroidModule中的HandleMessage,然後就會看到Toast出現

     import React from 'react';
     import {
       AppRegistry,
       StyleSheet,
       Text,
       View,
       TouchableOpacity,
       Dimensions,
       NativeModules,
       ToastAndroid,
       DeviceEventEmitter
     } from 'react-native';
    
    export default class Communication3 extends React.Component {
    
          constructor(){
            super();
            this.state = {
                info : "我是React Native寫的內容"
            }
          }
    
        componentWillMount(){
          DeviceEventEmitter.addListener('mEventName',
                               this.rnMethod.bind(this));
        }
    
        rnMethod(params){
          this.setState({info:params});
        }
    
       render() {
         return (
           <TouchableOpacity style={styles.container}>
              <View style={{width:Dimensions.get('window').width,height:50,margin:10,
                  backgroundColor:'#dfd',alignItems:'center',justifyContent:'center'}}>
                    <Text style={styles.hello}>{this.state.info}</Text>
              </View>
           </TouchableOpacity>
         )
       }
     }
     var styles = StyleSheet.create({
       container: {
         flex: 1,
         justifyContent: 'center',
       }
    });
    
  4. 將剛剛寫好的react native component打包成bundle,給ReactInstanceManager使用,bundle會放在Android的asset下

    react-native bundle --platform {{平台}} --entry-file {{入口文件,一般命名index.js}} --bundle-output {{打包好的bundle存放路徑}} --assets-dest {{react native引用的資源文件置放路徑}}
    
    react-native bundle --platform android --entry-file ./app/js/index.js --bundle-output app/src/main/assets/index.android.bundle --assets-dest app/src/main/res/
    
  5. 接下來撰寫Activity的View

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/root_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <Button
            android:id="@+id/native_btn"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_margin="10dp"
            android:background="#ddf"
            android:text="我是原生按鈕點擊我調用React Native方法" />
    
        <com.facebook.react.ReactRootView
            android:layout_weight="1"
            android:id="@+id/react_root_view1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    
  6. 最後就是要將ReactPackage註冊到React Native API中了

    class ReactCommunicationActivity : AppCompatActivity() {
    
        private var mReactRootView: ReactRootView? = null
        private var mReactInstanceManager: ReactInstanceManager? = null
        private var reactPackage: AndroidWidgetPackage? = null
        private var mClickTime = 0
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_react_comunucatuin)
    
            mReactRootView = ReactRootView(this)
            reactPackage = AndroidWidgetPackage()
            mReactInstanceManager = ReactInstanceManager.builder()
                    .setApplication(application)
                    .setBundleAssetName("index.android.bundle")
                    .setJSMainModulePath("index")
                    .addPackage(MainReactPackage())
                    .addPackage(reactPackage)   //加入AndroidModule
                    .setUseDeveloperSupport(BuildConfig.DEBUG)
                    .setInitialLifecycleState(LifecycleState.RESUMED)
                    .build()
    
            // 注意這裡的MyReactNativeApp必須對應“index.android.js”中的
            // “AppRegistry.registerComponent()”的第一個參數
            react_root_view1.startReactApplication(mReactInstanceManager, "Communication2", null)
    
            //添加本地按鈕的點擊事件
            native_btn.setOnClickListener {
                reactPackage!!.getToastModule().sendMessage("這是一條Android發送給React的消息${mClickTime++}")
            }
        }
    }
    
  7. 點擊按鈕,成功調用原生的Toast方法

React Native Component 呼叫 Activity

  1. 在上一部的基礎上,在View加上

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/root_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
    <Button
        android:id="@+id/native_btn"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="10dp"
        android:background="#ddf"
        android:text="我是原生按鈕點擊我調用React Native方法" />
    
    <com.facebook.react.ReactRootView
        android:layout_weight="1"
        android:id="@+id/react_root_view1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    
    <!--新增一個react view-->
    <com.facebook.react.ReactRootView
        android:layout_weight="1"
        android:id="@+id/react_root_view2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    
    </LinearLayout>
    
  2. 新增一個Component,此Componenet目的為顯示Android端點擊後,反應點擊

    import React from 'react';
    import {
       AppRegistry,
       StyleSheet,
       Text,
       View,
       TouchableOpacity,
       Dimensions,
       NativeModules,
       ToastAndroid
    } from 'react-native';
    
    export default class Communication extends React.Component {
    
      onPress = ()=> {
          // 這樣調用原生端方法,show出吐司
          NativeModules.ToastAndroidModule
          .HandleMessage("React Native 呼叫Native来吐司!!");
      }
    
       render() {
         return (
           <TouchableOpacity style={styles.container} 
           onPress = {this.onPress.bind(this)}>
              <View style={{
              width:Dimensions.get('window').width,
              height:50,
              backgroundColor:'#dfd',
              alignItems:'center',
              justifyContent:'center'
              }}>
                <Text style={styles.text}>
                這是一個React Native按鈕,點擊調用原生Toast方法
                </Text>
              </View>
           </TouchableOpacity>
         )
       }
     }
     var styles = StyleSheet.create({
       container: {
         flex: 1,
         justifyContent: 'center',
       },
       text:{
           fontSize: 20
       }
    });
    
  3. 打包js

    # react-native bundle --platform {{平台}} --entry-file {{入口文件,一般命名index.js}} --bundle-output {{打包好的bundle存放路徑}} --assets-dest {{react native引用的資源文件置放路徑}}
    
    react-native bundle --platform android --entry-file ./app/js/index.js --bundle-output app/src/main/assets/index.android.bundle --assets-dest app/src/main/res/
    
  4. 在Activity中,啟動該React Native Component的生命週期

    class ReactCommunicationActivity : AppCompatActivity() {    
        private var mReactRootView: ReactRootView? = null
        private var mReactInstanceManager: ReactInstanceManager? = null
        private var reactPackage: AndroidWidgetPackage? = null
        private var mClickTime = 0
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_react_comunucatuin)
    
            mReactRootView = ReactRootView(this)
            reactPackage = AndroidWidgetPackage()
            mReactInstanceManager = ReactInstanceManager.builder()
                    .setApplication(application)
                    .setBundleAssetName("index.android.bundle")
                    .setJSMainModulePath("index")
                    .addPackage(MainReactPackage())
                    .addPackage(reactPackage)   //加入AndroidModule
                    .setUseDeveloperSupport(BuildConfig.DEBUG)
                    .setInitialLifecycleState(LifecycleState.RESUMED)
                    .build()
    
            // 注意這裡的MyReactNativeApp必須對應“index.android.js”中的
            // “AppRegistry.registerComponent()”的第一個參數
    //        mReactRootView!!.startReactApplication(mReactInstanceManager, "Communication3", null)
            react_root_view1.startReactApplication(mReactInstanceManager, "Communication2", null)
            react_root_view2.startReactApplication(mReactInstanceManager, "Communication3", null)
    
            //添加本地按鈕的點擊事件
            native_btn.setOnClickListener {
                reactPackage!!.getToastModule().sendMessage("這是一條Android發送給React的消息${mClickTime++}")
            }
        }
    }
    
  5. 測試,完工!

將現有的Android專案整React Native

// TODO

在AWS-Ubuntu14.04上架設Java Web+MariaDB

參考鏈結

Linux Tomcat安裝

  1. 更新包管理器的軟體資料庫

    > sudo apt-get update
    
  2. 根據專案使用的JDK版本下載JDK

    // 這裡安裝的版本的是openJDK7
    > sudo apt-get install default-jdk
    
  3. 新增使用者群組

    > sudo groupadd tomcat
    
  4. 新增使用者

    // -s 使用者啟用的shell類型(註1.) | -g 使用者的群組 | -d 使用者的home(~)目錄
    > sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat
    
  5. 下載專案使用的tomcat版本,以下為8.0版本

    > cd ~
    > wget http://mirror.sdunix.com/apache/tomcat/tomcat-8/v8.0.23/bin/apache-tomcat-8.0.23.tar.gz
    
  6. 新建tomcat目錄,並且將tomcat壓縮檔放置該目錄

    > sudo mkdir /opt/tomcat
    > sudo tar xvf apache-tomcat-8*tar.gz -C /opt/tomcat --strip-components=1
    
  7. 變更tomcat目錄權限(註2.),conf為tomcat的配置檔案目錄

    > cd /opt/tomcat
    > sudo chgrp -R tomcat conf
    > sudo chmod g+rwx conf
    > sudo chmod g+r conf/*
    
  8. 變更文件的擁有者

    > sudo chown -R tomcat work/ temp/ logs/
    
  9. 顯示當前JDK路徑(註3.)

    > sudo update-alternatives --config java
    ubuntu@ip-172-31-40-55:/opt/tomcat$ sudo update-alternatives --config java
    There is only one alternative in link group java (providing /usr/bin/java): /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
    Nothing to configure.
    
    // 注意每個人的路徑不一定相同,我這裡是 /usr/lib/jvm/java-7-openjdk-amd64/jre
    
  10. 新增init配置upstart script(註4.)

    > sudo nano /etc/init/tomcat.conf
    
  11. 使用上上一步驟的JDK,配置upstart script(註4.)

    description "Tomcat Server"
    
      start on runlevel [2345]
      stop on runlevel [!2345]
      respawn
      respawn limit 10 5
    
      setuid tomcat
      setgid tomcat
    
      env JAVA_HOME={**上上一步驟的JDK,EX: /usr/lib/jvm/java-7-openjdk-amd64/jre**}
      env CATALINA_HOME=/opt/tomcat
    
      # Modify these options as needed
      env JAVA_OPTS="-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom"
      env CATALINA_OPTS="-Xms512M -Xmx1024M -server -XX:+UseParallelGC"
    
      exec $CATALINA_HOME/bin/catalina.sh run
    
      # cleanup temp directory after stop
      post-stop script
        rm -rf $CATALINA_HOME/temp/*
      end script
    
  12. 重新讀取upstart配置檔

    > sudo initctl reload-configuration
    
  13. tomcat配置完成,在瀏覽器中打開8080端口,看看有沒有tomcat預設頁面

    http://server_IP_address:8080
    

Linux MariaDB安裝

  1. 安裝mariaDB

    > sudo apt-get update
    > sudo apt-get install mariadb-server
    
  2. 安裝時會要求設定密碼,根據java專案中的設定

  3. 登入mysql

    > mysql -u root -p
    
  4. 接下來導入之前的db資料,首先新建一個db

    > CREATE DATABASE new_database;
    Query OK, 1 row affected (0.00 sec)
    
  5. 然後將之前的資料導入

    mysql -u username -p new_database < data-dump.sql
    
  6. 導入完成

AWS配置

  • 打開22 port(ssh使用的端口)
  • 打開8080 port(tomcat使用的端口)
  • 如何設定可以看看參考鏈結

步驟

  1. 將開發機上的java專案打包成war檔案
  2. 將開發機上的db資料export成sql檔案
  3. 將sql檔匯入Linux下的mariaDB
  4. 將java專案放入Linux下的tomcat/webapps/
  5. 打開AWS下的port
  6. 有時候可能要打開linux防火牆

註解

  1. useradd -s /bin/flase 所謂 /bin/false 就是指該使用者無法使用任何 Shell 功能,因此也就無法與系統進行溝通。如此一來,此使用者便無法使用 telnet 或 ftp 來登入系統,但仍可以收發電子郵件,這是一個企業中經常使用的方式,尤其是免費提供電子郵件信箱的組織中更為重要

  2. sudo chgrp -R {{群組名稱}} {{目錄名稱}} chgrp為改變文件or目錄所屬群組 -R為遞歸處理

  3. sudo update-alternatives --config java 如果當前jdk版本只有一個,那就會顯示目前java目錄

  4. upstart script,為upstart系統的腳本,upstart系統為Ubuntu上的一個新型的init系統

2016年5月21日 星期六

[Dll注入]簡單練習

此篇示範如何使用dll工具,簡單注入自己寫的dll


  1. 首先下載Dll注入工具,這裡我們用remotedll.

2016年5月16日 星期一

[C#]使用Activator動態創建物件

這篇文章只是簡略的介紹Activator的應用,如果想更了解反射(Reflection),請看這位大大寫的文章:點我


以下開始示範



使用Type來動態創建物件:


  using System;

namespace TestReflc
{
    class Program
    {
        static void Main(string[] args)
        {
            CreatePeopleByType();

            Console.ReadLine();
        }

        static void CreatePeopleByType()
        {
            Type a = typeof(People);

            // 動態創建物件,並且調用默認無參數的建構子(構造方法)
            // 如果沒有定義無參數的建構子,那麼將出錯
            var obj = Activator.CreateInstance(a);

            // 輸出結果: PeoPle construction
        }
    }


    public class People
    {
        public string Name;

        public People()
        {
            Console.WriteLine("PeoPle construction");
        }
    }
}

a

2016年1月15日 星期五

FSM有限狀態機01-介紹


名稱&別名:


名稱:Finite State Machine 或者是 Finite State Automate,常常被稱作FSM



 什麼是有限狀態機?


以Unity來說,Animator就是一個非常典型的例子(見下圖),每一個紅框處就是一個狀態(State),他會根據你設定的規則與條件做狀態的切換.