Simple...

catch react native IllegalViewOperationException

0x00 React Native Default Exception Handler React Native官方支持在生成ReactInstanceManager时使用NativeModuleCallExceptionHandler接口来设置自己的异常处理器,防止js/react-native代码异常导致native端直接崩溃,样例代码如下: ReactInstanceManagerBuilder builder = ReactInstanceManager.builder() .setApplication(context.getApplicationContext()) .addPackage(new MainReactPackage()) .setInitialLifecycleState(LifecycleState.BEFORE_CREATE) .setNativeModuleCallExceptionHandler(new NativeModuleCallExceptionHandler { @Override public void handleException(Exception e) { // 自己处理react-native异常. } }); 0x01 com.facebook.react.uimanager.IllegalViewOperationException app发布到线上之后,仍然出现了少部分未能捕获的异常,堆栈信息如下: com.facebook.react.uimanager.IllegalViewOperationException Trying to add unknown view tag: 500 detail: View tag:496 children(2): [ 497,498, ], viewsToAdd(1): [ [2,500], ], 1 com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren(NativeViewHierarchyManager.java:387) 2 com.facebook.react.uimanager.UIViewOperationQueue$ManageChildrenOperation.execute(UIViewOperationQueue.java:179) 3 com.facebook.react.uimanager.UIViewOperationQueue$2.run(UIViewOperationQueue.java:787) 4 com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:843) 5 com.facebook.react.uimanager.UIViewOperationQueue.access$1600(UIViewOperationQueue.java:48) 6 com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:889) 7 com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:31) 8 com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:129) 9 com.

upgrade android support library to 25.4.0

Android升级Support库版本至25.4.0 [TOC] 0x00 背景 support库是google官方提供的支持库,其主要功能在于兼容android高/低版本系统(如:support-v4、support-v7库),同时伴随着support库的不同版本,也会发布同版本的各种UI组件库(如Design、RecyclerView等)。support库的每次更新升级,一般都会包括bug修复、性能优化以及功能改进等等。 目前我们App使用的版本是23.0.1,截止目前google发布的最新稳定版本是25.4.0,中间相差了将近20个版本,其中包含大量的bug修复;同时又因为Design、RecyclerView等UI库的很多新组件也依赖于support库的升级,所以我们计划将support-v4、support-v7、Design、RecyclerView等库统一升级至25.4.0版本。 0x01 打包环境的影响及解决方案 1. 升级JDK & Android SDK 升级JDK到1.8 升级Android SDK到25 2. 升级CentOS或GLIBC 由于我们的jenkins打包服务器的系统是CentOS 6.5,其默认支持的GLIBC库版本最高为2.12,而android sdk高版本需要的GLIBC版本为2.14,所以要么升级系统CentOS到7.x版本,要么升级GLIBC版本到2.14. 当Android SDK需要的GLIBC版本不匹配时,出错信息大致如下: [exec] /data0/android-tools/android-sdk-linux/build-tools/24.0.2/aapt: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /data0/android-tools/android-sdk-linux/build-tools/24.0.2/aapt) [exec] /data0/android-tools/android-sdk-linux/build-tools/24.0.2/aapt: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /data0/android-tools/android-sdk-linux/build-tools/24.0.2/lib64/libc++.so) 在CentOS系统中,使用strings /lib64/libc.so.6 | grep GLIBC命令可以查看其支持的GLIBC库的版本。 3. 指定gradle使用jdk1.8 由于android sdk需要在jdk1.8环境才能正常编译、打包,所以要么在系统中配置java环境变量为jdk1.8版本;要么在执行gradle时指定其运行环境为jdk1.8。 指定gradle使用jdk1.8,具体配置如下: gradlew clean -Dorg.gradle.java.home=/usr/java/jdk1.8 也可以直接在gradle.properties中指定jdk版本,如下所示: org.gradle.java.home=/usr/java/jdk1.8 4. support库与build-tools版本 由于我们app原本使用的build-tools版本是22.0.1,当保持build-tools版本不变,只升级support库版本时,在HUAWEI P7-L00 4.4.2上面运行时,会出现如下错误,推测是因为support库版本和build-tools主版本号不一致导致,所以如果升级support库版本,最好保持和build-tools主版本号一致,防止各种诡异问题。 E/AndroidRuntime: FATAL EXCEPTION: main Caused by: android.

android fetch add custom cookie

[TOC] 0x00 为React Native的网络请求添加公共Cookie 本文分析基于React Native 0.44版本分析。 由于我们在使用React Native编写应用时,内部的网络请求均使用了fetch函数,所以下面我们主要分析一下fetch函数的整个调用流程。 0x01 Fetch追根溯源 从React Native的源代码我们可以知道fetch函数最终也是由native端的NetworkingModule.java(Android)或RCTNetworking.mm(iOS)实现。 1. 其调用流程如下 2. 代码分析如下 1. 将fetch函数添加到全局变量 在react-native/Libraries/Core/InitializeCore.js的全部变量global中定义了fetch函数。 // Set up XHR // The native XMLHttpRequest in Chrome dev tools is CORS aware and won't // let you fetch anything from the internet defineProperty(global, 'XMLHttpRequest', () => require('XMLHttpRequest')); defineProperty(global, 'FormData', () => require('FormData')); defineProperty(global, 'fetch', () => require('fetch').fetch); defineProperty(global, 'Headers', () => require('fetch').Headers); defineProperty(global, 'Request', () => require('fetch').

React Native Bundle Split

使用rn-packager拆分react-native的jsbundle(core.android.bundle + business.android.bundle),然后在程序启动时分步加载拆分后的bundle,以达到热更新目的,注:本文档使用的react native版本为0.43。 0x00 分步加载jsbundle 将rn-packager打包生成的jsbundle+图片资源统一放到assets目录中,应用程序启动时,复制到files目录,只要保持目录结构不变,js就可以正常访问图片资源。故而,如果需要热更新jsbundle和图片资源时,只需要直接更新files目录中的图片和jsbundle文件即可,具体可以看packager-bundle-split。 加载core.android.bundle ReactInstanceManagerBuilder builder = ReactInstanceManager.builder() .setApplication(mApplication) .setJSMainModuleName(getJSMainModuleName()) .setUseDeveloperSupport(getUseDeveloperSupport()) .setRedBoxHandler(getRedBoxHandler()) .setUIImplementationProvider(getUIImplementationProvider()) .setInitialLifecycleState(LifecycleState.BEFORE_CREATE); for (ReactPackage reactPackage : getPackages()) { builder.addPackage(reactPackage); } String jsBundleFile = getJSBundleFile(); if (jsBundleFile != null) { builder.setJSBundleFile(jsBundleFile); } else { builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName())); } File coreBundleFile = new File(mApplication.getFilesDir(), "rn/core.android.bundle"); if (!coreBundleFile.exists()) { Log.e("ReactNativeHost", "copy assets://core.android.bundle to " + coreBundleFile); AssetsUtils.copyFile(mApplication, "core.android.bundle", coreBundleFile.getAbsolutePath()); } // 加载core.android.bundle builder.setJSBundleLoader(JSBundleLoader.createFileLoader(coreBundleFile.getAbsolutePath())); Log.e("ReactNativeHost", "set core bundle"); return builder.

常用命令

1. golang跨平台编译 // 如: env GOOS=linux GOARCH=amd64 go build *.go 2. ssh ssh -p port username@host 3. scp scp -P port local_file username@host:remote_file scp -P port -r local_dir username@host:remote_dir 4. github pull request # clone git clone https://github.com/coofee/tinker.git # 添加tinker git remote add tinker https://github.com/Tencent/tinker # 从tinker的dev分支创建功能分支 git branch compat-gradle-plugin-3.x-dev tinker/dev git checkout compat-gradle-plugin-3.x-dev # push分支到origin git push -u origin compat-gradle-plugin-3.x-dev # 合并tinker的dev分支代码 git pull tinker dev # 删除远程分支 git push origin --delete compat-gradle-plugin-3.

android jni bspatch

[TOC] 1. 下载并配置NDK: https://developer.android.com/ndk/downloads/index.html 2. 编译Bsdiff 2.1 编写java类,创建native方法 package com.commons.utils; public class Bspatch { static { System.loadLibrary("bspatch"); } public static native int applyPatch(String oldFile, String newFile, String patchFile); } 2.2 生成.h文件 使用javac编写Bspatch或者在ide里面编译,这里我在android studio中编写并执行gradle的compileDebugJavaWithJavac任务进行编译,编译完成后,切换到classes目录。 使用javah生成.h文件 // 切换到编译好的类目录 cd app/build/intermediates/classes // 生成.h文件 javah -d h -classpath debug com.commons.utils.Bspatch 2.3 编译c/c++代码. 复制生成的.h文件到c/c++文件目录. 在存放c/c++代码的目录中添加Android.mk、Application.mk文件。 其中Android.mk用于指定要编译的类,模块名称等; Application.mk用于指定要生成特定abi类型的so文件,如:x86,armeabi等。 目录结构如下: app build jni bspatch bzip2 Android.

Android Gradle插件升级填坑指南

[TOC] Android Gradle插件升级填坑指南 1. 引子 自打android开发环境从eclipse迁移到android studio之后,android项目的打包就从ant迁移到了gradle。自此只要是在打包过程中需要干扰代码生成或执行其他特殊处理,都需要通过gradle脚本完成,具体来说就是通过在打包过程中插入task或者给现用task添加hook。 对于我们的项目而言,在插件化和热修复技术,为了降低打包成本,我们编写了自己的gradle插件,用于支持app的插件化和热修复打包。 2. 由instant-run引发的血案 时间来到了16年,随着Instant-Run功能的逐渐完善,我们也升级android studio和gradle来体验强大的Instant-Run,刚一运行就崩溃了。。。 查看一下报错日志,发现proguardDebug任务找不到;瞅了一下打包产生的临时目录,发现intermediates/classes-proguard目录也找不到了,并且还多出来了intermediates/transforms这个奇怪的目录,赶紧google一下,发现google在gradle插件高版本中引入了transform-api(主要是给大家提供了一个操作代码的接口,比如可以注入代码什么的),并且在高版本的gradle插件中使用transformClassesAndResourcesWithProguardForDebug任务替换了低版本的proguardDebug任务。 3. 兼容Transform Api 知道了问题所在,那就让我们撸起袖子干起来吧,将原本需要在proguardDebug之后执行的代码迁移到transformClassesAndResourcesWithProguardForDebug之后,并稍作改动就可以了。大概代码如下: def proguardTaskName = "transformClassesAndResourcesWithProguardFor${flavor.capitalize()}${buildType.capitalize()}".toString() gradle.taskGraph.afterTask { Task task, TaskState state -> if (state.failure != null) { println "${task} error: ${state.failure}" state.failure.printStackTrace() state.rethrowFailure(); return; } if (task.name.equals(proguardTaskName)) { // 执行自己的代码,将先前的代码迁移至此,并稍作改动即可。 } } 4. 兼容不同版本 这里我们为了兼容各个版本的gradle插件,那么就出现了一个新问题如何区分某个gradle插件版本是否支持transform api? 查看官网,发现上面有这么一句话 > (The API existed in 1.4.0-beta2 but it’s been completely revamped in 1.5.0-beta1) 也就是说,这个api在1.4.0-beta2的时候就已经存在了,但是直到1.5.0-beta1版本的时候才改造完成。这么来看的话,通过版本来判断比较复杂,并且不一定靠谱,那么如何能够既简单又靠谱的判断呢? 答案很简单,直接判断project是否拥有transform的task即可,大概代码如下:

hugo help

Hugo 1.1 创建文件 # 切换到博客目录 $ hugo new post/xxxxx.md 1.2 发布到本地预览网站 $ hugo server --theme=greyshade --buildDrafts --watch 1.3 发布网站到github $ hugo --theme=greyshade --buildDrafts $ cd public $ git add * $ git commit -a -m 'add all' $ git push origin master 1.4 添加图片到文件 The images should be put in the static folder, which will be copied to the root of the website (so their url is www.

groovy replace method

[TOC] Groovy 动态代理即替换方法 我们知道在Java中可以使用以下几种方法替换方法。 JDK自带的动态代理只支持修改某个类所实现的接口的方法。java只支持单继承,所有的代理类都是Proxy的子类,所以只能覆盖接口的方法。 asm直接修改字节码,直接修改字节码,直接修改类的方法和所实现接口的方法。 javassist直接修改字节码,直接修改类的方法和所实现接口的方法。 cglib基于asm封装,直接修改类的方法和所实现接口的方法。 总的来说,使用jdk自带的方案有局限,使用第三方框架能够支持全部功能。下面我们就来看一下,groovy作为一门jvm语言如何更加方便的实现动态代理的。 1.1 演示类 class ReplaceMethod { // 用于演示替换实例方法 public List<String> getStreamInputs() { List<String> strings = "a, b, c, d".split(", "); return strings; } // 用于演示替换泛型方法,同时演示如何区分同名的方法。 public void genericMethod(List<String> strings) { println strings } public void genericMethod(List<String> strings, boolean num) { println strings } } 2 如何替换方法? 通过metaClass的pickMethod获取到原始的方法引用。 使用闭包替换metaClass上要替换的方法即可。 样例代码如下: def repalceMethod = new ReplaceMethod(); println repalceMethod.getStreamInputs(); def oldMethod = ReplaceMethod.

如何科学上网

1. 如何使用shadowsocks代理访问网络? 购买代理服务器,安装shadowsocks。 本机安装shadowsocks-gui,然后添加代理服务器上shadowsocks的配置,此时shadowsocks会在本机上面开启一个socks代理,其默认端口是: 1080。 打开shadowsocks,勾选代理服务器,同时勾选shadowsocks-gui的全局模式或者自动代理模式,然后在浏览器上面选择使用系统代理即可正常访问google。 2. 如何在mac的Terminal中使用socks代理? 使用homebrew安装proxychains4-ng 创建~/.proxychains/proxychains.conf文件,文件内容如下。 strict_chain proxy_dns remote_dns_subnet 224 tcp_read_time_out 15000 tcp_connect_time_out 8000 localnet 127.0.0.0/255.0.0.0 quiet_mode [ProxyList] socks5 127.0.0.1 1080 在Terminal中,使用proxychains4 [cmd]就可以使用代理服务器了。如: * 对于mac osx 10.11,由于apple新增了sip模式,需要进入到Recovery模式(**cmmand+R**),然后在terminal中执行`csrutil enable --without debug`命令,然后重启电脑,即可正常使用proxychains4-ng。 ## 3. 如何让socks代理支持http代理? * 安装polipo:`brew install polipo` * 查看配置参数:`polipo -v` * 启动http代理:`polipo socksParentProxy=localhost:1080`,其默认端口是8123。 * 此时其他应该程序就可以设置polipo的http代理:`localhost:8123` ## 4. 如何在Terminal中使用Http代理? * 在`~/.bash_profile`文件中添加以下代码,然后执行`source ~/.bash_profile`。 bash alias gaproxy=‘export http_proxy=127.0.0.1:8123 https_proxy=127.0.0.1:8123’ alias noproxy=‘unset http_proxy https_proxy’