reactNative 화이트 화면 최적화

9939 단어
ReactNative 안드로이드 첫 화면 화이트 최적화
문제 설명
회사의 기존 app 중 일부 모듈은reactnative 개발을 사용하고 실시하는 과정에서 rn의 좋은 호환성, 좋은 탑재, 애니메이션 성능을 사용하여 우리의 개발, 테스트 효율을 향상시키고 사용자 체험을 향상시켰다.
그러나android에서 어떤 rn모듈의 입구 버튼을 누르면 rn의activity가 rn의 페이지에 나타나는 과정에서 뚜렷한 백스크린 현상이 나타난다. 서로 다른 기종이 다르고(cpu가 좋은 백스크린 시간이 짧음) 약 1s에서 2s의 시간이 걸린다.
이 현상은 실제 기기에서만 발생할 수 있으며, 시뮬레이터에서는 전혀 초로 켜지지 않습니다.ios에서도 초읽기 테스트의 최저 버전은 ios7, 아이폰4s다.
reactnative 버전 0.20.0.
jsbundle 파일 크기 717kb.
최적화 효과
대량의 원본 코드 읽기와 인터넷 자료 검색을 통해 최종적으로 이 문제를 완벽하게 해결했다. 어떤 기종이든 초읽기에 도달할 수 있다. 그림(아래 그림은 아날로그의 캡처이지만 실제 기기의 효과는 기본적으로 같다).
최적화 과정
시간 분포
일반적으로 속도 최적화 문제는 먼저 시간 분포를 찾은 다음에 28원칙에 따라 가장 오래 걸리는 부분을 최적화해야 한다.
android 통합rn은 공식적으로 제공된ReactActivity를 계승합니다
public class MainActivity extends ReactActivity {

그리고 자신의 activity에서만 설정 항목을 덮어씁니다.
공식 ReactActivity의 onCreate 방법에서
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
      // Get permission to show redbox in dev builds.
      if (!Settings.canDrawOverlays(this)) {
        Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        startActivity(serviceIntent);
        FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
        Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
      }
    }

    mReactInstanceManager = createReactInstanceManager();
    ReactRootView mReactRootView = createRootView();
    mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
    setContentView(mReactRootView);
  }

가장 느린 것은 이 두 줄 코드로 90퍼센트 이상을 차지한다.
ReactRootView mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());

이 두 줄 코드는 jsbundle 파일을 메모리에 읽고 실행한 다음 대상을 초기화하는 것입니다.
최적화 사고방식---메모리 교환 시간
앱이 시작될 때 mReactRootView를 초기화하고 캐시합니다. 사용할 때 setContentView(mReactRootView)를 직접 사용해서 초 간격에 도달합니다.
단계 1 캐시 루트 뷰 관리자
캐시 루트 view 관리자는 주로 캐시 루트 view 대상을 초기화하고 캐시하는 데 사용됩니다.
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewParent;

import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactRootView;

import java.lang.reflect.Field;

/**
 *   view  
 */
public class RNCacheViewManager {

    private static ReactRootView mRootView = null;
    private static ReactInstanceManager mManager = null;
    private static AbsRnInfo mRnInfo = null;

    //   
    public static void init(Activity act, AbsRnInfo rnInfo) {
        init(act, rnInfo, null);
    }

    public static void init(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) {
        if (mManager == null) {
            updateCache(act, rnInfo, launchOptions);
        }
    }

    public static void updateCache(Activity act, AbsRnInfo rnInfo) {
        updateCache(act, rnInfo, null);
    }

    //  cache,           cache
    public static void updateCache(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) {
        mRnInfo = rnInfo;
        mManager = createReactInstanceManager(act);
        mRootView = new ReactRootView(act);
        mRootView.startReactApplication(mManager, rnInfo.getMainComponentName(), launchOptions);
    }

//      ,   private,        
    public static void setModuleName(String moduleName) {
        try {
            Field field = ReactRootView.class.getDeclaredField("mJSModuleName");
            field.setAccessible(true);
            field.set(getReactRootView(), moduleName);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

//      ,   private,        
    public static void setLaunchOptions(Bundle launchOptions) {
        try {
            Field field = ReactRootView.class.getDeclaredField("mLaunchOptions");
            field.setAccessible(true);
            field.set(getReactRootView(), launchOptions);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static ReactRootView getReactRootView() {
        if(mRootView==null){
            throw new RuntimeException("  view        !");
        }
        return mRootView;
    }

    public static ReactInstanceManager getReactInstanceManager() {
        if(mManager==null){
            throw new RuntimeException("  view        !");
        }
        return mManager;
    }

    public static AbsRnInfo getRnInfo() {
        if(mRnInfo==null){
            throw  new RuntimeException("  view        !");
        }
        return mRnInfo;
    }

    public static void onDestroy() {
        try {
            ViewParent parent = getReactRootView().getParent();
            if (parent != null)
                ((android.view.ViewGroup) parent).removeView(getReactRootView());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static void clear() {
        try {
            if (mManager != null) {
                mManager.onDestroy();
                mManager = null;
            }
            if (mRootView != null) {
                onDestroy();
                mRootView = null;
            }
            mRnInfo = null;
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private static ReactInstanceManager createReactInstanceManager(Activity act) {

        ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
                .setApplication(act.getApplication())
                .setJSMainModuleName(getRnInfo().getJSMainModuleName())
                .setUseDeveloperSupport(getRnInfo().getUseDeveloperSupport())
                .setInitialLifecycleState(LifecycleState.BEFORE_RESUME);

        for (ReactPackage reactPackage : getRnInfo().getPackages()) {
            builder.addPackage(reactPackage);
        }

        String jsBundleFile = getRnInfo().getJSBundleFile();

        if (jsBundleFile != null) {
            builder.setJSBundleFile(jsBundleFile);
        } else {
            builder.setBundleAssetName(getRnInfo().getBundleAssetName());
        }
        return builder.build();
    }
}


2단계ReactActivity 재작성
공식 ReactActivity를 붙여서 두 가지 방법을 다시 쓰십시오. onCreate와 onDestroy. 나머지 코드는 움직이지 않습니다.
onCreate 방법에서 캐시 루트 view 관리자를 사용하여 루트 view 대상을 다시 만들지 않고 가져옵니다.
이 클래스를 다시 쓰는 것이 아니라 ReactActivity를 계승하려고 시도했지만, 하위 클래스가 onCreate 방법을 덮어쓸 때 슈퍼를 호출해야 합니다.onCreate, 그렇지 않으면 컴파일링이 잘못될 수 있지만, 슈퍼.onCreate 방법은 루트 뷰를 다시 만들 수 있기 때문에 도저히 돌아갈 수 없습니다.
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (RNCacheViewManager.getRnInfo().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
            // Get permission to show redbox in dev builds.
            if (!Settings.canDrawOverlays(this)) {
                Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                startActivity(serviceIntent);
                FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
                Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
            }
        }

        mReactInstanceManager = RNCacheViewManager.getReactInstanceManager();
        ReactRootView mReactRootView = RNCacheViewManager.getReactRootView();
        setContentView(mReactRootView);
    }

onDestroy 방법에서 원래의 mReactInstanceManager를 다시 호출할 수 없습니다.Destroy () 방법입니다. 그렇지 않으면 rn이 초기화한 대상은 소각되고 다음에는 사용할 수 없습니다.루트 뷰의parent 대상을 마운트 해제해야 합니다. 그렇지 않으면 다음에 set Content View에서 오류가 발생합니다.
protected void onDestroy() {
        RNCacheViewManager.onDestroy();
        super.onDestroy();
    }

RNCacheViewManager.onDestroy 방법:
public static void onDestroy() {
        try {
            ViewParent parent = getReactRootView().getParent();
            if (parent != null)
                ((android.view.ViewGroup) parent).removeView(getReactRootView());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

3단계 app 시작 시 캐시 루트 뷰 관리자 초기화
RNCacheViewManager.init((Activity) context, new RnInfo(moduleName, launchOptions));

여기서 RnInfo는 다음과 같습니다.
public class RnInfo extends AbsRnInfo {

    private String mModuleName;
    private Bundle mLaunchOptions;

    public RnInfo(String moduleName) {
        this.mModuleName = moduleName;
    }

    public RnInfo(String moduleName, Bundle launchOptions) {
        this.mModuleName = moduleName;
        this.mLaunchOptions = launchOptions;
    }

    @Nullable
    @Override
    public Bundle getLaunchOptions() {
        return mLaunchOptions;
    }

    @Override
    public String getMainComponentName() {
        return mModuleName;
    }

    @Override
    public String getJSMainModuleName() {
        return RNKeys.Default.DEf_JS_MAIN_MODULE_NAME;
    }

    @Nullable
    @Override
    public String getJSBundleFile() {
        return RNManager.getJsBundlePath();
    }

    @Override
    public boolean getUseDeveloperSupport() {
        return true;
    }

    @Override
    public List<ReactPackage> getPackages() {
        return Arrays.asList(
                new MainReactPackage(),
                new BBReactPackage()
        );
    }
}

결어
이 문서가 비슷한 문제에 부딪힌 친구들을 도울 수 있기를 바랍니다.
reactnative는 은탄이 아니지만 현재 모바일 브라우저의 호환성이 약해진 상황에서 개발 테스트 효율을 크게 향상시킬 수 있고 성능도 매우 좋아서 rn의 미래를 전망합니다.
참고 문장
http://zhuanlan.zhihu.com/magilu/20587485
http://zhuanlan.zhihu.com/magilu/20259704
https://yq.aliyun.com/articles/3208?spm=5176.100239.yqblog1.39.t2g49u&utm_source=tuicool&utm_medium=referral

좋은 웹페이지 즐겨찾기