├── .gitignore ├── AaptBuild.md ├── And-Fix-Solution.md ├── Android 5 尝鲜.md ├── Android HWUI硬件加速模块浅析.md ├── Android NDK C++ 开发利器:Android Studio.md ├── Android_7_DayDreamVR.md ├── Android_7_Vulkan.md ├── Android下高斯模糊的多种实现.md ├── Circular_Camera_Implement.md ├── Fyuse Implementation.md ├── JIT_Introduction.md ├── LICENSE ├── Neon_Programming.md ├── README.md ├── React Native On Android.md ├── React_Native_V8.md ├── V8_Build_Guide.md ├── Vulkan in 30 Minutes.md └── images ├── a5_art_compiler_driver.png ├── a5_art_file.png ├── a5_art_view.png ├── a5_documented_app.png ├── a5_heads_up_notification.png ├── an_daydream.jpg ├── an_daydream.png ├── an_daydream_controller.png ├── an_daydream_devkit.png ├── an_daydream_gvrsdk.png ├── an_daydream_option.png ├── an_daydream_paint.png ├── an_vulkan.png ├── as_jni_debug.png ├── as_jni_debug_config.png ├── as_jni_editor.png ├── as_ndk_debug.png ├── as_ndk_native_debug.png ├── as_ndk_native_debug2.png ├── circular_camera_sample_image.png ├── hwui_atlas.png ├── hwui_batch_draw.gif ├── hwui_deferred_displaylist.png ├── hwui_displaylist_drawop.png ├── hwui_font_render.png ├── hwui_font_renderer.png ├── hwui_gfx_system.png ├── hwui_oglrenderer_rendernode.png ├── hwui_opengl_renderer.png ├── hwui_threadedrenderer.png ├── hwui_threadedrenderer.svg ├── hwui_view_renderer.png ├── react_2_code_activity.png ├── react_2_code_manager.png ├── react_2_lifecycle.png ├── react_2_sample.png ├── react_2_uml.png ├── rn-client-classes.png ├── rn-cs-inter.png ├── rn-init-sequ.png └── rn_v8.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.gradle/ 2 | *.idea/ 3 | *build/ 4 | *.iml -------------------------------------------------------------------------------- /AaptBuild.md: -------------------------------------------------------------------------------- 1 | # 在WSL2下构建Android ARM AAPT 2 | 3 | --- 4 | 5 | - 下载源码(清华源) 6 | 7 | curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo -o repo 8 | chmod +x repo 9 | echo "export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'" >> ~/.bashrc 10 | source ~/.bashrc 11 | repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-10.0.0_r36 # 下载android10源码 12 | repo sync -c 13 | source build/envsetup.sh 14 | lunch sdk-eng # 设置成sdk构建 15 | 16 | - 修改**Aapt**构建文件`Android.bp` 17 | - 修改`androidfw`的构建文件,让它支持`静态库`构建, `frameworks\base\libs\androidfw\Android.bp`,target.android.static设置成true 18 | - 修改Aapt构建文件`frameworks\base\tools\aapt\Android.bp`,增加: 19 | ```gn 20 | cc_library_shared { 21 | name: "libaapt_shared", 22 | 23 | // 系统不允许链接或者缺失函数符号的库改成静态链接 24 | static_libs: [ 25 | "libandroidfw", 26 | "libexpat", 27 | "libziparchive", 28 | "libbase", 29 | "libpng", 30 | ], 31 | 32 | // 大多数系统自带动态库 33 | shared_libs: [ 34 | "libz", 35 | "liblog", 36 | "libcutils", 37 | "libutils", 38 | ], 39 | cflags: [ 40 | "-Wno-format-y2k", 41 | "-Wno-error=implicit-fallthrough", 42 | ], 43 | 44 | srcs: [ 45 | "AaptAssets.cpp", 46 | "AaptConfig.cpp", 47 | "AaptUtil.cpp", 48 | "AaptXml.cpp", 49 | "ApkBuilder.cpp", 50 | "Command.cpp", 51 | "CrunchCache.cpp", 52 | "FileFinder.cpp", 53 | "Images.cpp", 54 | "Package.cpp", 55 | "pseudolocalize.cpp", 56 | "Resource.cpp", 57 | "ResourceFilter.cpp", 58 | "ResourceIdCache.cpp", 59 | "ResourceTable.cpp", 60 | "SourcePos.cpp", 61 | "StringPool.cpp", 62 | "WorkQueue.cpp", 63 | "XMLNode.cpp", 64 | "ZipEntry.cpp", 65 | "ZipFile.cpp", 66 | ], 67 | } 68 | 69 | cc_binary { 70 | name: "aapt_andr", 71 | 72 | srcs: ["Main.cpp"], 73 | use_version_lib: true, 74 | 75 | static_libs: [ 76 | "libandroidfw", 77 | "libexpat", 78 | "libziparchive", 79 | "libbase", 80 | "libpng", 81 | ], 82 | 83 | shared_libs: [ 84 | "libaapt_shared", 85 | "libz", 86 | "liblog", 87 | "libcutils", 88 | "libutils", 89 | ], 90 | } 91 | ``` 92 | - 构建 `make aapt_andr` 93 | - 构建其它CPU架构的可执行文件 -------------------------------------------------------------------------------- /And-Fix-Solution.md: -------------------------------------------------------------------------------- 1 | # Android HotFix(And-Fix方案实现分析) 2 | 3 | -------------------------------------------------------------------------------- /Android 5 尝鲜.md: -------------------------------------------------------------------------------- 1 | # Android 5 新特性尝鲜 2 | 3 | ------- 4 | 5 | # Android Runtime 6 | 7 | --- 8 | 9 | ## Ahead-of-time(AOT) Compilation 10 | 11 | ART模式中引入了能够改善性能的预编译方式。 12 | 13 | > 在安装的过程中,ART使用dex2oat工具对应用进行预编译。这个工具会把dex文件转换成目标设备上的可执行程序。 14 | 15 | ![ART Compiler](images/a5_art_view.png) 16 | 17 | 从上图可以看出Davik和ART的区别,Davik运行的过程中有JIT(对比AOT)的步骤,而ART是直接把dex通过工具转换成native的ELF可执行文件格式。 18 | 再看下图,ART镜像文件的只读数据段包含了dex的数据和类的一些元数据,代码段是dex编译之后的方法 19 | (method,直接映射到框架的具体调用/native code)代码,这样就加快了运行速度。 20 | 21 | > 编译出来的ART镜像文件格式: 22 | 23 | ![ART](images/a5_art_file.png) 24 | 25 | 具体的编译过程如下图所示: 26 | 27 | ![ART Compiler Driver](images/a5_art_compiler_driver.png) 28 | 29 | ## ART编译器带来的好处 30 | 31 | ### 聚焦于面向对象程序的优化上 32 | 33 | * 固化方法的调用 34 | * 更快的接口调用 35 | * 避免类初始化的检查 36 | * 消除了异常的检测 37 | 38 | ### AOT同样利于 39 | 40 | * 电池续航 - 编译一次,更快的运行 41 | * 顺滑的内存曲线 - 更加的多任务执行 42 | 43 | 44 | 45 | ``` cpp 46 | static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader, jstring javaName) { 47 | ScopedFastNativeObjectAccess soa(env); 48 | mirror::ClassLoader* loader = soa.Decode(javaLoader); 49 | ScopedUtfChars name(env, javaName); 50 | if (name.c_str() == NULL) { 51 | return NULL; 52 | } 53 | ClassLinker* cl = Runtime::Current()->GetClassLinker(); 54 | std::string descriptor(DotToDescriptor(name.c_str())); 55 | mirror::Class* c = cl->LookupClass(descriptor.c_str(), loader); 56 | if (c != NULL && c->IsResolved()) { 57 | return soa.AddLocalReference(c); 58 | } 59 | if (loader != nullptr) { 60 | // Try the common case. 61 | StackHandleScope<1> hs(soa.Self()); 62 | c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), hs.NewHandle(loader)); 63 | if (c != nullptr) { 64 | return soa.AddLocalReference(c); 65 | } 66 | } 67 | // Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into 68 | // the regular loadClass code. 69 | return NULL; 70 | } 71 | ``` 72 | 73 | ## GC 74 | 75 | # 通知和多任务 76 | 77 | --- 78 | 79 | ## Heads-Up Notification 80 | 81 | ![Heads Up Notification](images/a5_heads_up_notification.png) 82 | 83 | **上图是Android 5新的通知方式。** 84 | 85 | ### 实现代码 86 | 87 | --- 88 | 89 | ``` java 90 | Notification createNotification(boolean makeHeadsUpNotification) { 91 | Notification.Builder notificationBuilder = new Notification.Builder(getActivity()) 92 | .setSmallIcon(R.drawable.ic_launcher) 93 | .setPriority(Notification.PRIORITY_DEFAULT) 94 | .setCategory(Notification.CATEGORY_MESSAGE) 95 | .setContentTitle("Sample Notification") 96 | .setContentText("This is a normal notification."); 97 | if (makeHeadsUpNotification) { 98 | Intent push = new Intent(); 99 | push.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 100 | push.setClass(getActivity(), LNotificationActivity.class); 101 | 102 | PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(getActivity(), 0, 103 | push, PendingIntent.FLAG_CANCEL_CURRENT); 104 | notificationBuilder 105 | .setContentText("Heads-Up Notification on Android L or above.") 106 | .setFullScreenIntent(fullScreenPendingIntent, true); //FullScreen Heads-Up Notification 107 | } 108 | return notificationBuilder.build(); 109 | } 110 | ``` 111 | 112 | **Heads-Up通知**是通过下面这个方法的调用实现的: 113 | 114 | public Notification.Builder setFullScreenIntent (PendingIntent intent, boolean highPriority) 115 | 116 | > 如果用户正在使用这个设备时,调用这个方法后,系统UI可能会弹出Heads-Up通知,就像有电话打过来一样。 117 | 118 | 119 | ## 多任务视图 120 | --- 121 | 122 | **Android 5.0 新增了Document-centric apps特性,它能够让同一个应用的多个Activity在最近使用的视图中看到,同时新增了AppTask类,用于辅助Activity的管理。** 123 | 124 | ### 在最近使用中(Recents)显示Activity 125 | --- 126 | 127 | ![Document-Centered App](images/a5_documented_app.png) 128 | 129 | > Android 5.0还支持**`文档式的多任务`**视图,如果要使用该样式,可以在启动Activity的时候加入以下代码: 130 | 131 | 132 | ``` java 133 | Intent intent = ... 134 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); 135 | startActivity(intent); 136 | ``` 137 | 138 | > 这样就可以做到在同一个APP里进行多个Activity的切换。 139 | 140 | 141 | ### 新增的类(ActivityManager.AppTask) 142 | --- 143 | 144 | > Android 5在ActivityManager增加了新的接口`getAppTasks()`,它可以获得当前的任务列表(List),开发者可以直接通过AppTask将任务转至前台、从Recents中挪开或者新启一个Activity。 145 | 146 | | AppTask | Public Methods | 147 | | ------------- |:-------------:| 148 | | void | finishAndRemoveTask() | 149 | | ActivityManager.RecentTaskInfo | getTaskInfo() | 150 | | void | setExcludeFromRecents(boolean exclude) | 151 | | void | startActivity(Context context, Intent intent, Bundle options) | 152 | | void | moveToFront() | 153 | 154 | 155 | # 高性能图形 156 | 157 | --- 158 | 159 | Android 5.0的新特性包括了OpenGL ES 3.1和AEP扩展包。 160 | 161 | ## OpenGL ES 3.1 & AEP 162 | 163 | * OpenGL ES 3.1相比3.0支持了`compute shader(通用计算着色器)`、`stencil texture(模板纹理)`等等。 164 | * AEP提供桌面级GPU支持的特性(`曲面细分`和`几何着色器`、`ASTC纹理压缩`等等)。 165 | 166 | 167 | ## 支持ES 3.1的移动GPU系列 168 | 169 | * 高通Adreno 420、430+ 170 | * ARM Mali T860+ 171 | * NVIDIA Tegra K1、X1 172 | 173 | 174 | ## 使用Compute Shader实现GPGPU应用 175 | 176 | 177 | ## OpenGL ES 3.1在3D图形后处理的应用 178 | 179 | 180 | ## 参考 181 | 182 | * [Google I/O 2014 - The ART runtime][1] 183 | * [ART Source Link][2] 184 | * [Android 5.0 API changes][3] 185 | 186 | 187 | [1]:https://www.youtube.com/watch?v=EBlTzQsUoOw 188 | [2]:https://android.googlesource.com/platform/art/ 189 | [3]:http://developer.android.com/about/versions/android-5.0.html -------------------------------------------------------------------------------- /Android HWUI硬件加速模块浅析.md: -------------------------------------------------------------------------------- 1 | # Android HWUI硬件加速模块浅析 2 | 3 | 关键词:RenderNode,ThreadedRenderer,DisplayList,Atlas,FontRenderer 4 | 5 | --- 6 | 7 | ## 什么是硬件加速(What) 8 | 9 | 传统软件的UI绘制是依靠CPU来完成的,硬件加速就是将绘制任务交由GPU来执行。Android系统负责硬件加速的模块主要是**HWUI**,如下图所示: 10 | 11 | ![](images/hwui_gfx_system.png) 12 | 13 | --- 14 | 15 | ## 为什么要硬件加速(Why) 16 | 17 | Android HWUI硬件加速的底层实现是基于`OpenGL ES`接口向GPU提交指令来完成绘制的。硬件加速的优势在于: 18 | 19 | * 在高屏幕分辨率环境下(尤其对于4K高清电视而言),GPU UI绘制的帧率要高于CPU,同时能减轻CPU的负担,如果只使用CPU进行软绘制的话,效费比太低。 20 | * GPU相比CPU更加适合完成光栅化、动画变换等耗时任务,在移动设备上比起使用CPU来完成这些任务,GPU会更加省电,带来的用户体验也会更佳。 21 | * 现代移动GPU支持可编程管线,可以高效地开发应用界面的一些特效(吸入、渐变、模糊、阴影)。 22 | 23 | ## 硬件加速的实现(How) 24 | 25 | 实现的源码位于目录[android/platform/framework/base/libs/hwui][3] 26 | 27 | --- 28 | 29 | ### 独立的渲染线程 30 | 31 | 32 | ![](images/hwui_threadedrenderer.png) 33 | 34 | 在Android 5上,[ThreadedRenderer](https://android.googlesource.com/platform/frameworks/base/+/android-5.1.1_r2/core/java/android/view/ThreadedRenderer.java)的出现减轻了主线程的负担,可以更快的响应用户的操作。 35 | 36 | > 下面是一段Android5硬件加速的一个crash堆栈,从这个调用关系中,我们可以大致知道从View到RenderNode的调用流程。 37 | 38 | ``` 39 | android.view.GLES20Canvas.nDrawRenderNode(Native Method) 40 | android.view.GLES20Canvas.drawRenderNode(GLES20Canvas.java:233) 41 | android.view.View.draw(View.java:15041) 42 | android.view.ViewGroup.drawChild(ViewGroup.java:3405) 43 | android.view.ViewGroup.dispatchDraw(ViewGroup.java:3199) 44 | android.view.View.updateDisplayListIfDirty(View.java:14057) 45 | android.view.View.getDisplayList(View.java:14085) 46 | android.view.View.draw(View.java:14852) 47 | android.view.ViewGroup.drawChild(ViewGroup.java:3405) 48 | android.view.ViewGroup.dispatchDraw(ViewGroup.java:3199) 49 | android.view.View.updateDisplayListIfDirty(View.java:14057) 50 | android.view.View.getDisplayList(View.java:14085) 51 | android.view.View.draw(View.java:14852) 52 | android.view.ViewGroup.drawChild(ViewGroup.java:3405) 53 | ``` 54 | 55 | > 再看下nDrawRenderNode函数,在DisplayListRenderer的drawRenderNode方法里把RendeNode的DrawOp记录在了DisplayListData里。 56 | 57 | ```cpp 58 | static jint android_view_GLES20Canvas_drawRenderNode(JNIEnv* env, 59 | jobject clazz, jlong rendererPtr, jlong renderNodePtr, 60 | jobject dirty, jint flags) { 61 | DisplayListRenderer* renderer = reinterpret_cast(rendererPtr); 62 | RenderNode* renderNode = reinterpret_cast(renderNodePtr); 63 | ... 64 | status_t status = renderer->drawRenderNode(renderNode, bounds, flags); 65 | ... 66 | return status; 67 | } 68 | ``` 69 | 70 | > 下图是DisplayList里记录的DrawOp: 71 | 72 | ![](images/hwui_displaylist_drawop.png) 73 | 74 | --- 75 | 76 | ### 显示列表和批次渲染(Display List & Batch Rendering) 77 | 78 | > 下图是Android 5.1的OpenGL Renderer执行显示列表的流程: 79 | 80 | ![](images/hwui_oglrenderer_rendernode.png) 81 | 82 | > OpenGLRenderer类主要负责GL API的执行以及渲染状态的设置更新。 83 | 84 | 85 | ``` cpp 86 | enum OpBatchId { 87 | kOpBatch_None = 0, // Don't batch 88 | kOpBatch_Bitmap, 89 | kOpBatch_Patch, 90 | kOpBatch_AlphaVertices, 91 | kOpBatch_Vertices, 92 | kOpBatch_AlphaMaskTexture, 93 | kOpBatch_Text, 94 | kOpBatch_ColorText, 95 | kOpBatch_Count, // Add other batch ids before this 96 | }; 97 | ``` 98 | 99 | > 如上面代码所示,绘制的批次按**文本、图片资源、几何图形**等进行分类,分批绘制的效果如下图所示。 100 | 101 | ![HWUI Batch-Draw](images/hwui_batch_draw.gif) 102 | 103 | **那么HWUI又是如何对各个DrawOp进行合并的呢?** 104 | 105 | 106 | > Android 4.4之后,HWUI模块通过Deferred Display List剔除过绘制的区域,完成DrawOp的分批和合并。 107 | 108 | 我们来看下***位于DeferredDisplayList.cpp中MergingDrawBatch类的canMergeWith方法***: 109 | 110 | ``` cpp 111 | bool canMergeWith(const DrawOp* op, const DeferredDisplayState* state) { 112 | bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text || 113 | getBatchId() == DeferredDisplayList::kOpBatch_ColorText; 114 | if (!isTextBatch || op->hasTextShadow()) { 115 | //如果不是文本或者操作包含阴影,并且和当前的渲染状态的包围盒相交,则不能合并 116 | if (intersects(state->mBounds)) return false; 117 | } 118 | const DeferredDisplayState* lhs = state; 119 | const DeferredDisplayState* rhs = mOps[0].state; 120 | //Alpha值(透明度)不相等,同样不能合并 121 | if (!MathUtils::areEqual(lhs->mAlpha, rhs->mAlpha)) return false; 122 | ... 123 | // 操作绑定的着色器不一样,也不能合并 124 | if (op->mPaint && mOps[0].op->mPaint && 125 | op->mPaint->getShader() != mOps[0].op->mPaint->getShader()) { 126 | return false; 127 | } 128 | ... 129 | return true; 130 | } 131 | ``` 132 | 133 | DrawOp合并与否取决于DrawOp对应渲染管线的状态是否一致,一般是把相同状态的DrawOp进行合并,达到减少渲染状态切换的效果。 134 | 135 | HWUI模块中,DrawOp批次合并的实现主要在**DeferredDisplayList::addDrawOp方法**,如下面片段所示: 136 | 137 | ``` cpp 138 | void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { 139 | ... 140 | // 获取渲染状态 141 | DeferInfo deferInfo; 142 | op->onDefer(renderer, deferInfo, *state); 143 | ... 144 | // 剔除过绘制批次 145 | if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() && 146 | state->mClipSideFlags != kClipSide_ConservativeFull && 147 | deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) { 148 | // avoid overdraw by resetting drawing state + discarding drawing ops 149 | discardDrawingBatches(mBatches.size() - 1); 150 | resetBatchingState(); 151 | } 152 | ... 153 | // 将新的DrawOp合并至现有批次中 154 | // 首先,先找到现有批次中可以合并的Batch,我们希望这个批次是最新的 155 | DrawBatch* targetBatch = NULL; 156 | // insertion point of a new batch, will hopefully be immediately after similar batch 157 | // (eventually, should be similar shader) 158 | int insertBatchIndex = mBatches.size(); 159 | if (!mBatches.isEmpty()) { 160 | if (state->mBounds.isEmpty()) { 161 | // don't know the bounds for op, so add to last batch and start from scratch on next op 162 | DrawBatch* b = new DrawBatch(deferInfo); 163 | b->add(op, state, deferInfo.opaqueOverBounds); 164 | mBatches.add(b); 165 | resetBatchingState(); 166 | return; 167 | } 168 | if (deferInfo.mergeable) { 169 | // Try to merge with any existing batch with same mergeId. 170 | if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) { 171 | if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) { 172 | targetBatch = NULL; 173 | } 174 | } 175 | } else { 176 | // join with similar, non-merging batch 177 | targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId]; 178 | } 179 | if (targetBatch || deferInfo.mergeable) { 180 | // iterate back toward target to see if anything drawn since should overlap the new op 181 | // if no target, merging ops still interate to find similar batch to insert after 182 | for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) { 183 | DrawBatch* overBatch = (DrawBatch*)mBatches[i]; 184 | if (overBatch == targetBatch) break; 185 | // TODO: also consider shader shared between batch types 186 | if (deferInfo.batchId == overBatch->getBatchId()) { 187 | insertBatchIndex = i + 1; 188 | if (!targetBatch) break; // found insert position, quit 189 | } 190 | if (overBatch->intersects(state->mBounds)) { 191 | // NOTE: it may be possible to optimize for special cases where two operations 192 | // of the same batch/paint could swap order, such as with a non-mergeable 193 | // (clipped) and a mergeable text operation 194 | targetBatch = NULL; 195 | break; 196 | } 197 | } 198 | } 199 | } 200 | // 现有Batch中没有找到合适的Batch,我们只能创建新的批次并插入批次列表中 201 | if (!targetBatch) { 202 | if (deferInfo.mergeable) { 203 | targetBatch = new MergingDrawBatch(deferInfo, 204 | renderer.getViewportWidth(), renderer.getViewportHeight()); 205 | mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch); 206 | } else { 207 | targetBatch = new DrawBatch(deferInfo); 208 | mBatchLookup[deferInfo.batchId] = targetBatch; 209 | } 210 | DEFER_LOGD("creating %singBatch %p, bid %x, at %d", 211 | deferInfo.mergeable ? "Merg" : "Draw", 212 | targetBatch, deferInfo.batchId, insertBatchIndex); 213 | mBatches.insertAt(targetBatch, insertBatchIndex); 214 | } 215 | targetBatch->add(op, state, deferInfo.opaqueOverBounds); 216 | } 217 | ``` 218 | 219 | > DisplayList记录了绘制操作和状态,主线程将它们提交至OpenGL渲染器,由渲染器执行最终的DrawCall。 220 | 221 | 222 | 分批次渲染以及合并DrawOp带来的好处是显而易见的,**它大幅度的减少了OpenGL渲染状态的切换的开销(由于OpenGL ES的驱动实现的复杂性,每个GL的绑定调用以及GL状态的切换都会引起GL状态的检查以及同步操作)**。 223 | 224 | --- 225 | 226 | ### 创建纹理集 227 | 228 | ![HWUI ATLAS](images/hwui_atlas.png) 229 | 230 | > AssetAtlasService对资源的加载在做了专门的管理,它为了减少图片资源上传至GPU的操作,对对各资源进行合并,打包(Atlas算法实现在[graphics/java/android/graphics/Atlas.java](https://android.googlesource.com/platform/frameworks/base/+/android-5.1.1_r2/graphics/java/android/graphics/Atlas.java)类里)成单张纹理进行上传,通过**UvMapper重新映射纹理坐标至[0,1]区间**,使UI依然能够正确绘制。 231 | 232 | 233 | 由于App的进程是从Zygonte进程fork出来的,所以AssetAtlasService加载的纹理资源可以共享给App。 234 | 235 | ATLAS的使用减少了GPU显存的消耗,以及纹理单元(Texture Unit)绑定调用(Resource Binding)的开销。 236 | 237 | --- 238 | 239 | ### 字体绘制 240 | 241 | ![](images/hwui_font_render.png) 242 | 243 | 文字的渲染也是一个令人头疼的问题,APP的文字渲染不像游戏中那样的简单,通常游戏发布的时候可以预先将固定的文字转换成单张的纹理,渲染文字时直接映射纹理坐标即可。Android底层对文字字形纹理数据进行了Cache。Android使用了Skia和第三方开源字体库(FreeType)对字体进行栅格化。 244 | 245 | ![HWUI Font Renderer](images/hwui_font_renderer.png) 246 | 247 | 如果设备支持OpenGL ES 3.0,Android会使用**PixelBufferObject绑定**完成字体纹理的异步上传,这样做的好处在于通过映射方式(glMapBuffer)更加高效的上传纹理数据至GPU,具体实现在[libs/hwui/PixelBuffer.cpp](https://android.googlesource.com/platform/frameworks/base/+/android-5.1.1_r2/libs/hwui/PixelBuffer.cpp): 248 | 249 | ```c 250 | GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height): 251 | PixelBuffer(format, width, height), mMappedPointer(0), mCaches(Caches::getInstance()) { 252 | glGenBuffers(1, &mBuffer); 253 | mCaches.bindPixelBuffer(mBuffer); 254 | glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), NULL, GL_DYNAMIC_DRAW); 255 | mCaches.unbindPixelBuffer(); 256 | } 257 | 258 | void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { 259 | // If the buffer is not mapped, unmap() will not bind it 260 | mCaches.bindPixelBuffer(mBuffer); 261 | unmap(); 262 | glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, 263 | GL_UNSIGNED_BYTE, reinterpret_cast(offset)); 264 | } 265 | 266 | ``` 267 | 通过PBO减少字体纹理资源上传的等待时间,提高了界面流畅度。 268 | 269 | 270 | --- 271 | 272 | ### 应用场景 273 | 274 | > 在我们平常的需求开发中会遇到一些场景可以通过使用硬件加速来完成,比如对复杂的布局执行Alpha渐变的动画时,我们可以直接设置父布局LayerType为`LAYER_TYPE_HARDWARE`。 275 | 276 | ```java 277 | public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { 278 | ...... 279 | public void buildLayer() { 280 | ...... 281 | final AttachInfo attachInfo = mAttachInfo; 282 | ...... 283 | switch (mLayerType) { 284 | case LAYER_TYPE_HARDWARE: 285 | updateDisplayListIfDirty(); 286 | if (attachInfo.mHardwareRenderer != null && mRenderNode.isValid()) { 287 | attachInfo.mHardwareRenderer.buildLayer(mRenderNode); 288 | } 289 | break; 290 | case LAYER_TYPE_SOFTWARE: 291 | buildDrawingCache(true); 292 | break; 293 | } 294 | } 295 | ...... 296 | } 297 | ``` 298 | 299 | 当View的LayerType设置为HARDWARE时,View就会绑定到一个OpenGL ES的FrameBufferObject上去,如果要对这个View进行Animation(Alpha,Rotation,etc.),会将这个FrameBufferObject渲染至Texture(RTT,Render To Texture),然后再进行动画的渲染,但是这里要注意到内存的占用会增加。 300 | 301 | 302 | ## 硬件加速的改进 303 | 304 | 优化的点可以从API角度和GPU硬件特性展开: 305 | 306 | * 参考iOS 8之后引入了Metal,原生支持多线程GPU渲染,Android由于GL驱动以及GPU厂商实现的差异无法很好地榨干GPU的机能,但是在下一代的图形API([Vulkan][7])普及之后,仍然有较大的优化空间,UI流畅性可以进一步提升。 307 | * 大部分移动设备的GPU是基于分块渲染(Tile-based Rendering)的,批绘制的算法可以考虑依据Tile进行分批。 308 | 309 | > 在今年的SIGGRAPH上,[Google已经公开支持Vulkan][8],它将成为下一代Android设备上的主要图形接口 310 | 311 | --- 312 | 313 | ## 参考 314 | 315 | 1. [Efficient text rendering with OpenGL ES][1] 316 | 2. [Rendering Text in Metal with Signed-Distance Fields][2] 317 | 3. [老罗的Android之旅][4] 318 | 4. [How about some Android graphics true facts?][5] 319 | 5. [Introduction to the Android Graphics Pipeline][6] 320 | 6. [Gnomes per second in Vulkan and OpenGL ES][9] 321 | 322 | --- 323 | [1]:https://medium.com/@romainguy/androids-font-renderer-c368bbde87d9 324 | [2]:http://metalbyexample.com/rendering-text-in-metal-with-signed-distance-fields/ 325 | [3]:https://android.googlesource.com/platform/frameworks/base/+/android-5.1.1_r2/libs/hwui/ 326 | [4]:http://blog.csdn.net/Luoshengyang/ 327 | [5]:https://plus.google.com/105051985738280261832/posts/2FXDCz8x93s 328 | [6]:http://mathias-garbe.de/files/introduction-android-graphics.pdf 329 | [7]:https://www.khronos.org/vulkan 330 | [8]:http://android-developers.blogspot.com/2015/08/low-overhead-rendering-with-vulkan.html 331 | [9]:http://blog.imgtec.com/powervr/gnomes-per-second-in-vulkan-and-opengl-es -------------------------------------------------------------------------------- /Android NDK C++ 开发利器:Android Studio.md: -------------------------------------------------------------------------------- 1 | # Android NDK C++ 开发利器:Android Studio 2 | 3 | ---- 4 | 5 | 在今年的Google IO大会上,Google宣布Android Studio开始支持NDK开发。通过和JetBrains的合作,将CLion整合进了[Android Studio 1.3][1],并免费支持NDK C++开发。 6 | 7 | 8 | ![NDK Debug](images/as_ndk_debug.png) 9 | 10 | 11 | ## 使用Gradle编写C++项目脚本 12 | 13 | 下面这段工程脚本来自Google提供的[Sample][2]。 14 | 15 | ``` gradle 16 | 17 | apply plugin: 'com.android.model.application' 18 | 19 | model { 20 | android { 21 | compileSdkVersion = 22 22 | buildToolsVersion = "22.0.1" 23 | 24 | defaultConfig.with { 25 | minSdkVersion.apiLevel = 9 26 | targetSdkVersion.apiLevel = 22 27 | versionCode = 1 28 | versionName = "1.0" 29 | } 30 | } 31 | android.ndk { 32 | moduleName = "game" 33 | cppFlags += "-I${file("src/main/jni/native_app_glue")}".toString() 34 | cppFlags += "-I${file("src/main/jni")}".toString() 35 | cppFlags += "-I${file("src/main/jni/data")}".toString() 36 | ldLibs += ["android", "EGL", "GLESv2", "OpenSLES", "log"] 37 | stl = "stlport_static" 38 | } 39 | android.lintOptions { 40 | abortOnError = false 41 | } 42 | 43 | android.buildTypes { 44 | release { 45 | isMinifyEnabled = true 46 | } 47 | } 48 | 49 | android.productFlavors { 50 | create ("arm7") { 51 | ndk.abiFilters += "armeabi-v7a" 52 | } 53 | create ("arm8") { 54 | ndk.abiFilters += "arm64-v8a" 55 | } 56 | create ("x86-32") { 57 | ndk.abiFilters += "x86" 58 | } 59 | // for detailed abiFilter descriptions, refer to "Supported ABIs" @ 60 | // https://developer.android.com/ndk/guides/abis.html#sa 61 | 62 | // build one including all cpu architectures 63 | create("all") 64 | } 65 | } 66 | 67 | dependencies { 68 | compile fileTree(dir: 'libs', include: ['*.jar']) 69 | compile 'com.android.support:appcompat-v7:22.1.1' 70 | } 71 | ``` 72 | 73 | NDK工程的编译配置脚本主要是下面几行: 74 | > 75 | android.ndk { 76 | moduleName = `"game"` 77 | cppFlags += "-I${file("src/main/jni/native_app_glue")}".toString() 78 | cppFlags += "-I${file("src/main/jni")}".toString() 79 | cppFlags += "-I${file("src/main/jni/data")}".toString() 80 | ldLibs += ["android", "EGL", "GLESv2", "OpenSLES", "log"] 81 | stl = "stlport_static" 82 | } 83 | 84 | 85 | ---- 86 | 87 | ## 调试C++项目 88 | 89 | 调试项目之前,需要创建Native的调试配置。打开菜单Run->Edit Configurations..,然后选择Native配置类型,如下图所示: 90 | 91 | ![Natvie Debug](images/as_ndk_native_debug.png) 92 | ![Natvie Debug](images/as_jni_debug_config.png) 93 | 94 | 配置好后,选择运行的模块就行了。如果LLDB调试器挂了,可以换用GDB进行调试工作。 95 | 96 | ![Natvie Debug](images/as_ndk_native_debug2.png) 97 | 98 | 调试的体验和Visual Studio差不多,基本的功能都有,比Eclipse调试Native层可能要方便些。 99 | 100 | ![Natvie Debug](images/as_jni_debug.png) 101 | 102 | ---- 103 | 104 | ## 代码编辑器支持的特性 105 | 106 | * 支持Native函数和Java native方法的跳转和方法查找 107 | * 支持C++代码高亮和代码补全 108 | 109 | ![Editor](images/as_jni_editor.png) 110 | 111 | 112 | 113 | ---- 114 | [1]:http://tools.android.com/download/studio/canary/latest 115 | [2]:https://github.com/googlesamples/android-ndk -------------------------------------------------------------------------------- /Android_7_DayDreamVR.md: -------------------------------------------------------------------------------- 1 | # Android 7 Nougat :DayDream VR 2 | 3 | ![daydream](images/an_daydream.jpg) 4 | 5 | > DayDreamVR 是Android 7引进来的新特性,这个版本提供了App的VR模式,同时定义了**VR头盔以及控制器**的一些接口标准,可以让手机制造商提供兼容该平台的VR设备。 6 | 7 | --- 8 | 9 | ## DayDream VR开发设置 10 | 11 | 当前Android VR的开发目前需要**Nexus 6P**来支持,目前市面上暂时没有手机能满足`DayDreamVR`的要求。 12 | 13 | ![dev-kit](images/an_daydream_devkit.png) 14 | 15 | 不过我们可以使用**另外一台手机**作为**DayDreamVR的手柄**,在Nexus 6P需要打开DayDreamVR的**开发者选项**通过蓝牙和手柄配对即可使用。 16 | 17 | ![option](images/an_daydream_option.png) 18 | 19 | **下面就是Google提供的VR Sample:ControllerPaint运行截图:** 20 | 21 | ![paint](images/an_daydream_paint.png) 22 | 23 | > 我们可以使用手柄在场景中拖动绘画,或者点击触控板点缀场景。 24 | 25 | 26 | --- 27 | 28 | ## DayDream Controller支持的能力 29 | 30 | * 提供手柄朝向的数据 31 | * 用于基于Raycast算法实现射线与平面相交检测 32 | * 支持连续的操作(拖动) 33 | * 更多地依赖陀螺仪提供的数据,可以支持**倾斜**、**转动**等连续性的操作 34 | * 同样支持离散的操作 35 | * 依靠加速度计提供的数据,使得冲击性的动作易于侦测,比如**投掷**、**敲击** 36 | * 支持双手操作 37 | * 触控板操作(TouchPad) 38 | * 这个触控板类似笔记本上的触摸板,可以控制2D平面上的方向,以及触摸点击操作 39 | * 视角回正(Recenter) 40 | * 依赖手柄上的按钮 41 | 42 | --- 43 | 44 | ## DayDream Controller开发入手 45 | 46 | ![](images/an_daydream_gvrsdk.png) 47 | 48 | 目前,我们可以使用**Google VR SDK**来进行Android上的VR APP开发。 49 | 50 | ![DDC](images/an_daydream_controller.png) 51 | 52 | 控制器的数据全部由`gvr::ControllerApi`提供,它可以提供**角速度,加速度,朝向,触控板触摸位置,手势**等信息。 53 | 54 | ```c 55 | typedef struct gvr_controller_state { 56 | gvr_controller_api_status api_status; 57 | gvr_controller_connection_state connection_state; 58 | 59 | gvr_quatf orientation; //四元数表示手柄的朝向 60 | gvr_vec3f gyro; //陀螺仪角速度数据 61 | gvr_vec3f accel; //加速度读取 62 | bool is_touching; //是否触摸了触控板 63 | gvr_vec2f touch_pos; // 触摸点 64 | 65 | bool touch_down; 66 | bool touch_up; 67 | ... 68 | } gvr_controller_state; 69 | ``` 70 | 71 | GvrController的状态全部封装在结构体`gvr_controller_state`里,读取的时候调`ControllerApi::ReadState(&controller_state_)`方法即可; 72 | 73 | 头盔的信息则由`gvr::GvrApi`提供,我们可以通过它获取**头盔的姿态**,以及**左右眼的观察矩阵**。 74 | 75 | ```cpp 76 | //获取头盔姿态和左右眼ViewMatrix 77 | const gvr::HeadPose head_pose = gvr_api_->GetHeadPoseInStartSpace(pred_time); 78 | const gvr::Mat4f left_eye_view_matrix = 79 | Utils::MatrixMul(gvr_api_->GetEyeFromHeadMatrix(GVR_LEFT_EYE), 80 | head_pose.object_from_reference_matrix); 81 | ``` 82 | 83 | --- 84 | 85 | ## 渲染 86 | 87 | > 目前Google VR SDK的渲染是通过分别在左右眼的Viewport进行重复的绘制和畸变处理来完成渲染的。 88 | 89 | **Google SDK VR的渲染流程大致如下:** 90 | 91 | * 新建用于场景渲染的离屏FrameBufferObject(FBO) 92 | * 绑定离屏FBO,将左右眼的场景渲染到这份FBO上 93 | * 将上面的FBO作为纹理传输至下一个Pass做畸变矫正,输出至屏幕显示 94 | 95 | --- 96 | 97 | ## 参考 98 | 99 | 1. [Designing & Developing for the Daydream Controller - Google I/O 2016][1] 100 | 2. [Set up a Daydream Development Kit][2] 101 | 3. [Sample: ControllerPaint][3] 102 | 103 | [1]:https://www.youtube.com/watch?v=l9OfmWnqR0M 104 | [2]:https://developers.google.com/vr/concepts/dev-kit-setup 105 | [3]:https://github.com/googlevr/gvr-android-sdk/tree/master/ndk-beta/demos/controllerpaint -------------------------------------------------------------------------------- /Android_7_Vulkan.md: -------------------------------------------------------------------------------- 1 | # Android 7 Nougat 新特性调研:Vulkan 2 | 3 | ## Vulkan 4 | 5 | ![vk](images/an_vulkan.png) 6 | 7 | Android N的另一大特性就是支持新的图形接口`Vulkan`,它也是一种薄驱动设计理念的跨平台(Windows,Android,Linux)图形函数接口。它统一了桌面和移动平台的图形接口,实现了一次编码即可运行在多个目标平台。 8 | -------------------------------------------------------------------------------- /Android下高斯模糊的多种实现.md: -------------------------------------------------------------------------------- 1 | # Android下高斯模糊的多种实现 2 | 3 | -------------------------------------------------------------------------------- /Circular_Camera_Implement.md: -------------------------------------------------------------------------------- 1 | # Android 圆形 Camera 实现 2 | 3 | 最近遇到这样的功能需求:`在某个界面上唤起一个背景透明的圆形Camera`。 4 | 5 | ![Sample Image](images/circular_camera_sample_image.png) 6 | 7 | 8 | 9 | 首先的想法是,把相机预览界面绘制到Canvas上,然后用canvas做裁剪,但是会有性能问题,于是就想到 10 | 利用GLSurfaceView配合GLSL处理来展示圆形的Camera。 11 | 12 | 我的方案是: 13 | 14 | * 第一步:将Camera预览界面绘制到正方形的网格上面 15 | 16 | * 第二步:获取到Camera预览的纹理并将它贴到正方形网格,这时看到的是压扁了的图像 17 | 18 | * 第三步:利用OpenGL ES & GLSL处理预览的纹理。 19 | 20 | **在第三步,我们要处理几个问题:** 21 | 22 | * 图像变形 23 | * 背景透明 24 | * 圆形边缘锯齿 25 | 26 | >对于第一个问题,直接想到的办法是对预览的图像做正方形截取,但是效率太低,直接pass, 27 | 所以只能从Shader入手了,第一次尝试是将纹理的y放大1.33倍(图像大小是640x480)采样, 28 | 得到的图像比例终于正常了,但是会有瑕疵,图像边缘会有横条, 29 | 再换个思路,按0.75倍的x轴坐标采样纹理,这次终于没有横条纹了,简直完美! 30 | 31 | 于是,Shader就写成了这样: 32 | 33 | ```java 34 | private final String fragmentShaderCode = 35 | "#extension GL_OES_EGL_image_external : require\n"+ 36 | "precision mediump float;" + 37 | "varying vec2 texCoord;\n" + 38 | "uniform samplerExternalOES s_texture;\n" + 39 | "void main() {\n" + 40 | "vec2 coord = texCoord - vec2(0.5, 0.5);\n" + 41 | "float radius = length(coord);\n"+ 42 | "vec4 color = texture2D( s_texture, vec2(0.75*texCoord.x,texCoord.y) );\n"+ 43 | "float factor = 1.0-step(0.5, radius);\n"+ 44 | "gl_FragColor = color*factor;\n" + 45 | "}"; 46 | ``` 47 | 48 | >但是处女座的同学又会发现圆形图像的边缘并不光滑,存在一些锯齿。于是我采取了这样的策略,对于图像边缘的锯齿做Alpha透明的渐变处理,完美地解决掉了这个问题。 49 | 50 | 这是**最终版Shader:** 51 | 52 | ```java 53 | private final String fragmentShaderCode = 54 | "#extension GL_OES_EGL_image_external : require\n"+ 55 | "precision mediump float;" + 56 | "varying vec2 texCoord;\n" + 57 | "uniform samplerExternalOES s_texture;\n" + 58 | "void main() {\n" + 59 | "vec2 coord = texCoord - vec2(0.5, 0.5);\n" + 60 | "float factor=0.49;\n"+ 61 | "float scale = 1.0/(0.5-factor);\n"+ 62 | "float radius = length(coord);\n"+ 63 | "vec4 color = texture2D( s_texture, vec2(0.75*texCoord.x,texCoord.y) );\n"+ 64 | "float stepA = 1.0-step(0.5, radius);\n"+ 65 | "float stepB = 1.0-step(factor, radius);\n"+ 66 | "vec4 innerColor = stepB * color;\n"+ 67 | "vec4 midColor = (stepA-stepB) * (1.0-(radius-factor) * scale) * color;\n"+ 68 | "gl_FragColor = innerColor + midColor;\n" + 69 | "}"; 70 | ``` 71 | 72 | 点击[此处查看Demo源码](https://github.com/TsinStudio/AndroidDevSample/tree/master/CircularCamera) 73 | -------------------------------------------------------------------------------- /Fyuse Implementation.md: -------------------------------------------------------------------------------- 1 | # Fyuse 3D 照片实现简析 2 | 3 | 4 | 5 | > 觉得平面照、全景照、 Live Photo 都已经无法满足你的要求了?自带社交平台的摄影软件「立体摄影:Fyuse」将给你提供一个新选择。应用使用原理说来与全景相机非常相近,也是要用户手拿着设备平行挪动,进行拍摄。只不过它拍出来的成品不是全景照,而是一段不仅具备 Live Photo 的特性,还会随着我们摇晃设备而改变视角,可触摸互动的 3D 动图。用户只需用邮箱注册个帐号,就能立刻体验软件。 6 | 7 | --- 8 | 9 | ## 实现原理猜测 10 | 11 | Fyuse App在录像的同时会记录每一帧的手机姿态,完成录像后进行帧间矩阵变换运算(获取图像特征点->匹配帧间特征点->利用最小二乘算法计算出帧间的变换矩阵),这样就得到了从n帧到n+1帧的变换矩阵,在照片上叠加物件就可以通过这个矩阵实现。 -------------------------------------------------------------------------------- /JIT_Introduction.md: -------------------------------------------------------------------------------- 1 | # JIT是如何工作的 2 | 3 | ## 定义 4 | 5 | JIT就是Just In Time的缩写,但在编程中,JIT是这样的一个玩意:当一个程序运行的同时,生产一些程序本体之外可执行的代码,并执行它们,这就是JIT。 6 | 7 | ## JIT:生产机器码,然后运行它 8 | 9 | JIT一般包含两个步骤: 10 | 11 | * 程序运行时编译和生产机器码 12 | * 程序运行时执行机器码 13 | 14 | ## 执行动态生成的代码 15 | 16 | 这里只阐述**Unix**下JIT的实现。 17 | 18 | 第一步:分配可执行代码内存 19 | 20 | ``` cpp 21 | // 分配一段可读写可执行的内存 22 | void* alloc_executable_memory(size_t size) { 23 | void* ptr = mmap(0, size, 24 | PROT_READ | PROT_WRITE | PROT_EXEC, 25 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 26 | if (ptr == (void*)-1) { 27 | perror("mmap"); 28 | return NULL; 29 | } 30 | return ptr; 31 | } 32 | // 给分配的内存设置上可执行的标志位,内存必须在同一片内存页 33 | int make_memory_executable(void* m, size_t size) { 34 | if (mprotect(m, size, PROT_READ | PROT_EXEC) == -1) { 35 | perror("mprotect"); 36 | return -1; 37 | } 38 | return 0; 39 | } 40 | ``` 41 | 42 | 第二步:生成机器码,将代码拷贝至**`可执行`**内存 43 | 44 | ```cpp 45 | void emit_code_into_memory(unsigned char* m) { 46 | unsigned char code[] = { 47 | 0x48, 0x89, 0xf8, // mov %rdi, %rax 48 | 0x48, 0x83, 0xc0, 0x04, // add $4, %rax 49 | 0xc3 // ret 50 | }; 51 | memcpy(m, code, sizeof(code)); 52 | } 53 | ``` 54 | 55 | 第三步:执行该段代码 56 | 57 | ```cpp 58 | const size_t SIZE = 1024; 59 | typedef long (*JittedFunc)(long); 60 | //执行机器码 61 | void run_from_rwx() { 62 | void* m = alloc_executable_memory(SIZE); 63 | emit_code_into_memory(m); 64 | make_memory_executable(m, SIZE); 65 | 66 | JittedFunc func = m; 67 | int result = func(2); 68 | printf("result = %d\n", result); 69 | } 70 | ``` 71 | 72 | 看到这里,原理很简单吧 ^_^ 。 73 | 74 | > 原文:[How to JIT - an introduction](http://eli.thegreenplace.net/2013/11/05/how-to-jit-an-introduction) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | 118 | -------------------------------------------------------------------------------- /Neon_Programming.md: -------------------------------------------------------------------------------- 1 | # 高性能Android编程:Neon 2 | 3 | NEON指令是ARM上的SIMD指令集,它用于高性能的多媒体计算以及矩阵向量计算。 4 | 5 | GCC工具链提供了ARM-NEON的生成器以及arm_neon的头文件,便于使用,免去了直接编写NEON汇编代码的痛苦。 6 | 7 | arm_neon支持的指令可以参照 [GCC Neon文档](https://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/ARM-NEON-Intrinsics.html) ,neon接口包含了几大类操作:装载,存储,算术运算,移位,倒数,位运算,比较等等。 8 | 9 | 命名规则是`v{[qual|dual]operation}_{datatype}`。比如vld1q_dup_s32,v代表vector,ld是load的缩写,1q表示加载一个四元组,dup是duplicate复制,s32是signed 32bit,代表每个元素是32位整形,很好理解吧。 10 | 11 | 以下代码示例是说明用neon指令优化数组反转的算法,通过在三星S4手机上的测试,neon效率是一般实现的1.8倍,优化效果显著。 12 | 13 | ``` cpp 14 | const int DATA_SIZE = 1920*1080; 15 | 16 | void test(JNIEnv * env, jobject jRoot, jobject jObj) { 17 | int *testSet1 = (int*)malloc(sizeof(int)*DATA_SIZE); 18 | for(uint32_t i = 0; iFindClass("com/tencent/helloneon/BenchListener"); 45 | jmethodID method = env->GetMethodID(clasz, "onResult", "(Ljava/lang/String;)V"); 46 | 47 | std::stringstream out; 48 | out << "benchResult:" << 1.f*cost2/cost1; 49 | env->CallVoidMethod(jObj, method, env->NewStringUTF(out.str().c_str())); 50 | } 51 | ``` 52 | 53 | 完整代码在Github [SampleCode](https://github.com/TsinStudio/AndroidDevSample) 上。 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Development Posts 2 | 3 | -------------------------------------------------------------------------------- /React Native On Android.md: -------------------------------------------------------------------------------- 1 | # React Native On Android 介绍 2 | 3 | 近期,Facebook将**`React Native`**的**Andorid**版本也开源了,React Native是一个用于本地UI开发的javascript框架,目前FB通过JavascriptCore解释器将Andorid的框架层调用和JS绑定实现跨语言的调用。 4 | 5 | --- 6 | 7 | ## React Native初体验 8 | 9 | > 目前React Native Android的开发只适用于Mac系统,开发前的准备,除了安装完基本的Android SDK/NDK环境后,还需要安装brew(Mac下的软件包管理器,类似apt)、npm(node包管理器)等。详细的安装步骤见**[Getting Started](https://facebook.github.io/react-native/docs/getting-started.html)**,安装过程中遇到的大部分问题在React的**[Github issues](https://github.com/facebook/react-native/issues)**中都有解决。 10 | 11 | --- 12 | 13 | ## JSX 14 | 15 | 16 | ``` javascript 17 | var HelloMessage = React.createClass({ 18 | render: function() { 19 | return

Hello {this.props.name}

; 20 | } 21 | }); 22 | 23 | ReactDOM.render( 24 | , 25 | document.getElementById('example') 26 | ); 27 | ``` 28 | 29 | 30 | > JSX是一种类似XML语法的脚本扩展语法,并不能直接被JS引擎直接解释执行,它需要利用其他JS脚本把JSX翻译成JS来完成执行。 31 | 32 | 33 | --- 34 | 35 | ## React Native(Android)实现原理 36 | 37 | ![React Native intercom](images/rn-cs-inter.png) 38 | 39 | --- 40 | 41 | ### Node服务端 42 | 43 | ### Android客户端 44 | 45 | ![Core Class](images/rn-client-classes.png) 46 | 47 | 初始化 48 | 49 | ![](images/rn-init-sequ.png) 50 | 51 | 模块注册 52 | 53 | Java-JS通信 54 | 55 | 56 | --- 57 | 58 | ## React Native框架使用指南 59 | 60 | ### 定制React UI组件 61 | 62 | ``` java 63 | public class MyCustomViewManager extends SimpleViewManager { 64 | 65 | private static final String REACT_CLASS = "MyCustomView"; 66 | 67 | @UIProp(UIProp.Type.STRING) 68 | public static final String PROP_MY_CUSTOM_PROPERTY = "myCustomProperty"; 69 | 70 | @Override 71 | public String getName() { 72 | return REACT_CLASS; 73 | } 74 | 75 | @Override 76 | protected MyCustomView createViewInstance(ThemedReactContext reactContext) { 77 | return new MyCustomView(reactContext); 78 | } 79 | 80 | @Override 81 | public void updateView(MyCustomView view, CatalystStylesDiffMap props) { 82 | super.updateView(view, props); 83 | 84 | if (props.hasKey(PROP_MY_CUSTOM_PROPERTY)) { 85 | view.setMyCustomProperty(props.getString(PROP_MY_CUSTOM_PROPERTY)); 86 | } 87 | } 88 | } 89 | ``` 90 | 91 | ``` javascript 92 | var React = require('react-native'); 93 | var { requireNativeComponent } = React; 94 | 95 | class MyCustomView extends React.Component { 96 | render() { 97 | return ; 98 | } 99 | } 100 | MyCustomView.propTypes = { 101 | myCustomProperty: React.PropTypes.oneOf(['a', 'b']), 102 | }; 103 | 104 | var NativeMyCustomView = requireNativeComponent('MyCustomView', MyCustomView); 105 | module.exports = MyCustomView; 106 | ``` 107 | 108 | 109 | --- 110 | 111 | ### 实现Native模块 112 | 113 | 在Android平台下创建一个简单的Java Native模块,只需要继承**ReactContextBaseJavaModule**该类,并在需要导出至JS的函数加上注解`@ReactMethod`即可。 114 | 115 | > 另外你需要将这个模块需要注册到**ReactPackage**。 116 | 117 | 118 | ``` java 119 | public class MyCustomModule extends ReactContextBaseJavaModule { 120 | 121 | // Available as NativeModules.MyCustomModule.processString 122 | @ReactMethod 123 | public void processString(String input, Callback callback) { 124 | callback.invoke(input.replace("Goodbye", "Hello")); 125 | } 126 | } 127 | 128 | ``` 129 | 130 | 下面是js调用native的代码: 131 | 132 | ``` javascript 133 | var React = require('react-native'); 134 | var { NativeModules, Text } = React; 135 | var Message = React.createClass({ 136 | getInitialState() { 137 | return { text: 'Goodbye World.' }; 138 | }, 139 | componentDidMount() { 140 | NativeModules.MyCustomModule.processString(this.state.text, (text) => { 141 | this.setState({text}); 142 | }); 143 | }, 144 | render: function() { 145 | return ( 146 | {this.state.text} 147 | ); 148 | } 149 | }); 150 | 151 | ``` 152 | 153 | --- 154 | 155 | ### 性能跟踪与调试 156 | 157 | 158 | --- 159 | 160 | ## 扩展 161 | 162 | 163 | --- 164 | 165 | ## 参考 166 | 167 | 1. [React: a javascript library for building user interfaces.][1] 168 | 2. [React Native: A FRAMEWORK FOR BUILDING NATIVE APPS USING REACT][2] 169 | 170 | [1]:https://facebook.github.io/react/ 171 | [2]:https://facebook.github.io/react-native/ -------------------------------------------------------------------------------- /React_Native_V8.md: -------------------------------------------------------------------------------- 1 | #React Native Android V8接入 2 | 3 | ## 背景 4 | 5 | 当前官方提供的RN存在一些难解的底层Crash,部分发生在JavaScriptCore引擎底层,由于官方未提供JSC源码导致问题无法定位,所以我们需要寻求可控的JS引擎,这里我们找到了Google提供的V8引擎。 6 | 7 | 8 | V8包含了一个高效的JavaScript [JIT][1],运行效率在众多JS引擎中脱颖而出,这也是我们选择它的原因之一。 9 | 10 | ## 方案概览 11 | 12 | ReactNative JNI层设计之初就考虑了JS后端的扩展性,所以采用了**抽象工厂模式**实现了JSExecutor。 13 | 14 | ![rn v8](images/rn_v8.png) 15 | 16 | 这里我们适配的工作量少了许多,我们只需要提供V8的一个后端实现就可以兼容原有逻辑。而且ReactNative不存在多的 17 | `Native-JS`绑定接口,我们需要实现的接口也就很少了。 18 | 19 | > 注意:由于早期的V8使用了stlport提供C++ STL库,它与ReactNative使用的GNUSTL并不兼容,因此我们需要将V8的接口全部编译进单独的共享库(不能是静态库),然后编译reactnativejni时再链接这个动态库,这样就可以完成STL的隔离。 20 | 21 | 22 | 23 | ## 实现细节 24 | 25 | 26 | V8支持多种机器指令的JIT,由于我们适配的是Android ARM平台,这里我们使用支持ARM以及ARM64JIT的V8。 27 | 28 | --- 29 | 30 | ### 编译 31 | 32 | V8的编译有两套选择GYP和GN。 33 | 由于笔者使用的是Mac,所以只能采用GYP来构建V8。 34 | 详细的构建方法见[链接][2]。 35 | 36 | > 图省事的话,也可以直接使用笔者编译的[libv8rt.so][4],这个库是基于[v8-5.5.1][5]构建的。 37 | 38 | --- 39 | 40 | ### V8 RN适配 41 | 42 | 1. Native接口绑定 43 | 44 | 这里我们适配的是ReactNative0.17-stable和V8-5.5.1。我们先来看JSCExecutor的实现: 45 | 46 | ```cpp 47 | JSCExecutor::JSCExecutor(FlushImmediateCallback cb) : 48 | m_flushImmediateCallback(cb) { 49 | m_context = JSGlobalContextCreateInGroup(nullptr, nullptr); 50 | s_globalContextRefToJSCExecutor[m_context] = this; 51 | installGlobalFunction(m_context, "nativeFlushQueueImmediate", nativeFlushQueueImmediate); 52 | installGlobalFunction(m_context, "nativeLoggingHook", nativeLoggingHook); 53 | ... 54 | } 55 | 56 | static JSValueRef nativeFlushQueueImmediate( 57 | JSContextRef ctx, 58 | JSObjectRef function, 59 | JSObjectRef thisObject, 60 | size_t argumentCount, 61 | const JSValueRef arguments[], 62 | JSValueRef *exception) { 63 | ... 64 | } 65 | 66 | static JSValueRef nativeLoggingHook( 67 | JSContextRef ctx, 68 | JSObjectRef function, 69 | JSObjectRef thisObject, 70 | size_t argumentCount, 71 | const JSValueRef arguments[], JSValueRef *exception) { 72 | ... 73 | } 74 | ``` 75 | 76 | 从JSCExecutor的构造函数中看出,native暴露给Javascript的接口只有flush和log的接口,那么在V8Executor里我们同样需要实现相同的绑定。 77 | 78 | ```cpp 79 | namespace v8 { 80 | 81 | Handle CreateShellContext(Isolate* isolate) { 82 | Handle global = ObjectTemplate::New(isolate); 83 | /** 84 | * ObjectTemplate用于包装C++的对象成v8::Object, 85 | * 它也提供内部存储空间(internal field),相当于C++的成员变量, 86 | * 我们可以通过SetInternalFieldCount设定内部储存多少个变量, 87 | * 这里我们用来绑定RN用到的基本接口。 88 | */ 89 | auto functionTemplate = FunctionTemplate::New(isolate, nativeFlushQueueImmediate); 90 | /** 91 | * FunctionTemplate用来包装C++的方法,这个方法的签名必须符合 92 | * function(const FunctionCallbackInfo& args)->void的形式, 93 | * 返回值可以通过args.GetReturnValue().Set(xxx)调用返回给js 94 | */ 95 | functionTemplate->RemovePrototype(); 96 | global->Set(String::NewFromUtf8(isolate, "nativeFlushQueueImmediate"), functionTemplate); 97 | 98 | functionTemplate = FunctionTemplate::New(isolate, nativeLoggingHook); 99 | functionTemplate->RemovePrototype(); 100 | global->Set(String::NewFromUtf8(isolate, "nativeLoggingHook"), functionTemplate); 101 | 102 | return Context::New(isolate, NULL, global); 103 | } 104 | 105 | static void nativeLoggingHook(const FunctionCallbackInfo& args) 106 | { 107 | ... 108 | } 109 | 110 | static void nativeFlushQueueImmediate(const FunctionCallbackInfo& args) 111 | { 112 | ... 113 | } 114 | 115 | } 116 | ``` 117 | 118 | > 上面就用V8的接口实现了JSC相同的Native-JS绑定。 119 | 120 | 121 | 2. 引擎的初始化 122 | 123 | ```cpp 124 | bool initV8 { 125 | // 1.初始化国际化字符库 126 | V8::InitializeICU(); 127 | // 2.初始化platform库 128 | V8::InitializePlatform(g_platform); 129 | V8::Initialize(); 130 | 131 | Isolate::CreateParams create_params; 132 | create_params.array_buffer_allocator = 133 | v8::ArrayBuffer::Allocator::NewDefaultAllocator(); 134 | // 3.创建Isolate 135 | Isolate* m_isolate = Isolate::New(create_params); 136 | s_globalContextRefToV8Executor[m_isolate] = this; 137 | // 增加计数,防止数据重新初始化 138 | v8::HandleScope handle_scope(m_isolate); 139 | /** 140 | * 创建JS执行上下文 141 | */ 142 | v8::Handle context = CreateShellContext(m_isolate); 143 | if (context.IsEmpty()) { 144 | throwNewJavaException("com/facebook/react/bridge/JSExecutionException", "error init v8 engine!!"); 145 | return false; 146 | } 147 | /** 148 | * 这里需要将刚创建的Context持久化,否则就会被销毁 149 | */ 150 | m_context.Reset(m_isolate, context); 151 | return true; 152 | } 153 | ``` 154 | 155 | 3. V8执行Javascript脚本 156 | 157 | ```cpp 158 | bool ExecuteScript(Local script) { 159 | HandleScope handle_scope(GetIsolate()); 160 | TryCatch try_catch(GetIsolate()); 161 | 162 | Local context(GetIsolate()->GetCurrentContext()); 163 | 164 | Local