ASM4 使用指南
ASM4 使用指南
HashMap
ArrayList动态扩容机制
Android 组件架构之LifeCycle
Dagger2 使用
LocalBroadcastManager源码分析
Android应用开发基本都是手机应用作为客户端访问服务器。但是有些时候, 却需要将手机作为服务端对外提供服务。
目前,可用的嵌入式服务器有NanoHttpd和I-Jetty。
NanoHttpd的地址是https://github.com/NanoHttpd/nanohttpd,该框架的好处是简单,轻量,只有一个java文件。坏处也明显: 对于稍复杂的项目,支持力度就不太够,请求并发增多后,稳定性减弱。
I-Jetty是开源Web容器Jetty移植到Android的项目。I-Jetty本身就是一个标准的servlet容器,该框架的好处是拥有标准的ServletApi,也就意味着,我们基于标准的ServletApi开发war,这对于熟悉后台开发的同学来说更简便。如果将来业务变更,我们甚至可以无缝地将war部署到云端。同时,并发稳定性也稳定。
I-Jetty 项目地址:https://github.com/jetty-project/i-jetty。 原来是在googlecode的。
1 | git clone https://github.com/jetty-project/i-jetty.git |
i-jetty
该目录是i-jetty的核心,实现Android上运行jetty的Android项目。
我们称之为WebApp的容器。
example-webapps
该目录是一个WebApp,也就是war应用的demo项目。我们称之为WebApp项目。
console
该目录是一个控制台项目。是用来控制的。
首先配置环境,包括Android, maven等,这些网上很多,请读者自行配置。
编译容器项目
i-jetty 是使用maven构建Android应用的。对于Android开发者来说, 绝大部分基本只接触过ADT+Eclipse 和 Android Studio 这两种开发环境,
而使用maven构建现在已经很少,而且也不推荐使用了。所以,我们使用Android Studio也就是gradle来构建我们的容器应用。
创建空项目
使用Android Studio创建一个空的Android项目,创建时包名使用org.mortbay.ijetty。 创建后,删除res下的所有文件。如果有java代码也都删除。
拷贝java代码
进入i-jetty目录,拷贝i-jetty-server目录下src/main/java下 和i-jetty-ui目录下src/下的java源码到我们的源码目录中。
拷贝res代码
进入i-jetty-ui目录,拷贝res/下的资源文件到我们的资源res/目录中。
拷贝resource目录到项目中的src/main/目录下,和java/和res保持同一目录。
移植依赖
i-jetty项目的依赖是使用POM文件定义的,我们需要转为gradle方式。
1 | implementation 'javax.servlet:servlet-api:2.5' |
如果你使用的是旧版本的gradle, 需改为compile
1 | compile 'javax.servlet:servlet-api:2.5' |
移植AndroidManefest.xml
拷贝i-jetty-ui的AndroidManefest.xml文件,替换现在的AndroidManefest.xml文件。
处理旧版本API。
a. i-jetty-ui的IJettyService使用了Notification的setLatestEventInfo旧版API,我们使用新版的替换。
1 | // Notification notification = new Notification(R.drawable.ijetty_stat, text, System.currentTimeMillis()); |
到此,容器项目到此构建完成。
由于要往SD卡读写文件,如果是在api23以上系统运行,因为没有做动态的权限申请请先在设置-应用里给应用赋予SD卡权限。
到此,服务器已经运行起来了, 点击configure,可以配置我们的服务器。
例如: 端口,ssl, nio 等等。
通常Web应用都是由maven构建的。,我们直接进进入example-webapps/hello应用目录。执行
1 | mvn clean package |
提示
1 | [INFO] I-Jetty :: Example Webapps Parent .................. SUCCESS [ 0.148 s] |
原因:最初Android版本SDK里的dx.jar都是放在platform-tools/lib/dx.jar这的,现在都是在build-tools下的每个版本里都有自己的dx.jar.
解决:复制SDK里的dx.jar(最好是你构建使用的Android SDK版本)到hello项目的根目录(也就是example-webapps目录下)。修改hello目录下的POM文件,
1 | <argument>${project.build.directory}/../../dx.jar</argument> |
使用
.
再次执行构建,mvn clean package。
如果没问题,在target目录下,生成了我们的war包。
adb push target/hello-3.2-SNAPSHOT.war /sdcard/jetty/webapps/hello.war
将war push 到 i-jetty的部署目录并改名。重启i-jetty,使用浏览器访问 http://localhost:8080/hello。
访问http://localhost:8080/sayit/*
源码地址
https://github.com/davyjoneswang/AndroidIJetty
https://github.com/codefar/i-jetty
[TOC]
OpenGL(Open Graphics Library)是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。
OpenGL ES (OpenGL for Embedded Systems)是 OpenGL的子集,针对手机、PDA和游戏主机等嵌入式设备而设计的。OpenGL ES 是从 OpenGL 裁剪的定制而来的,去除了glBegin/glEnd,四边形(GL_QUADS)、多边形(GL_POLYGONS)等复杂图元等许多非绝对必要的特性。
顶点:
一个顶点就是一个代表几何对象的拐角的点,这个点有很多附加属性;最重要的属性就是位置,它代表了这个顶点在空间中的定位。
attribute:属性
vec4:
具有四个分量的向量Vector。
uniform
varying: 是一个特殊的变量,他把给它的那些值进行混合,并把这些混合后的值发送给片段着色器。
mat4:具有四个量的矩阵;
归一化设备坐标:在openGL里,我们要涫染的一切物体都要映射到x轴和y轴上一1, 1的范围内,对z轴也是一样的。这个范围内的坐标 被称为归一化设备坐标
在openGL里,我们要涫染的一切物体都要映射到x轴和y轴上一1, 1的范围内,对z轴也是一样的。这个范围内的坐标 被称为归一化设备坐标,其独立于屏幕实际的尺寸或 形状。
不幸的是.因为它们独立于实际的屏幕尺寸,如 果直接使用它们.我们就会遇到问题.例如在横屏模 式情况下被压扁的桌子G 假设实际的设备分辨率以像素为单位是1280×720. 这在新的Andro记设备上是一个常用的分辨率。为了 使讨论更加容易,让我们也暂时假定OpenGL占用整 个显示屏。 如果设备是在竖屏模式下,那么-1,1』的范围对 应1280像素高.却只有720像素宽。图像会在X轴显 得扁平,如果在横屏模式,同样的问题也会发生在y 轴上。 归一化设备坐标假定坐标空间是一个正方形.
在 OpenGL ES里,只能绘制点、直线以及三角形。
当我们定义三角形的时候,我们总是以逆时针的顺序排列顶点;这称为卷曲顺序(windingorder),因为在任何地方都使用这种一致的卷曲顺序,可以优化性能:使用卷曲顺序可以指出一个三角形属于任何给定物体的前面或者后面,OpenGL 可忽略那些无论如何都无法被看到的后面的三角形。
Open GL 绘制流程
1·顶点着色器(vertexshader)生成每个顶点的最终位置,针对每个顶点,它都会执行一次;一旦最终位置确定了,OpenGL 就可以把这些可见顶点的集合组装成点、直线以及三角形。
2,片段着色器(fragmentshader)为组成点、直线或者三角形的每个片段生成最终的颜色。片段着色器的主要目的就是告诉GPU每个片段的最终颜色是什么。针对每个图元,它都会执行一次,一个片段是一个小的、单一颜色的长方形区域,类似于计算机屏幕上的一个像素。
1 | attribute vec4 a_Position; |
顶点着色器里必须给gl_Position赋值,来保存顶点的坐标。
什么是片段着色器?片段着色器是怎么产生的?
OpenGL通过光栅化把每个点、直线及三角形分解成大量的小片段,他们可以映射到移动设备显示屏的像素上,从而生成一幅图像。这些片段类似显示屏上的像素,每一个都包含单一的纯色。为了表示颜色,每个片段都有四个分量:其中红色、绿色、蓝色、用来表示颜色,alpha用来表示透明度。
1 | precision mediump float; |
第一句用来定义浮点数据类型的精度。可选为lowp,mediump,highp.
顶点着色器的默认精度为highp。无需定义。
uniform: 我们使用uniform 来保存颜色。uniform相对于atrribute的不同,atrribute每个顶点都要设置一个,而uniform会让每个顶点都是用同一个值,除非我们再次改变他的值。
在片段着色器总,vec4代表了颜色的四个分量。而在顶点着色器中,vec4代表x ,y, z, w 四个坐标分量。
着色器代码就是一段文本程序。需要经过一定处理才能使用,就像我们写的程序,需要编译,链接、生成可执行文件一样。着色器也需要经过类似的步骤。
这个过程有着固定的步骤。
1 | final int shaderObjectId = glCreateShader(type); |
1 | // Pass in the shader source. |
1 | // Compile the shader. |
1 | // 获取编译状态 |
有了着色器,Open GL还不能直接工作。就像我们有了一些库的,必须有一个可执行的程序才能工作,同样,Open GL 也需要一个Program.
我们需要一个程序,把顶点着色器和片段着色器链接起来才能一起工作。步骤如下:
1 | // Create a new program object. |
1 | // Attach the vertex shader to the program. |
1 | // Link the two shaders together into a program. |
和着色器类似,我们也需要验证链接过程中没有出错,以确保使用时不会发生错误。
1 | // 获取链接状态 |
在使用程序之前,我们还需要验证程序对象,看看这个程序对于当前的Open GL 状态是不是有效。根据Open GL ES 2.0 的文档,它给OpenGL提供了一种方法让我们知道为什么当前的程序可能是低效率的、无法运行,等等、
1 | glValidateProgram(programObjectId); |
调用glValidateProgram(programObjectId)来验证程序的状态。然后使用GL_VALIDATE_STATUS作为参数来调用glGetProgramiv,获取检查结果,并打印一些程序信息。
1 | glUseProgram(program); |
首先,要使用glUseProgram告诉OpenGL,我们使用这个程序绘制.
现在我们可以真正使用这个程序了。
我们通过给着色器对象中的变量赋值。来让OpenGL绘制我们想要的东西。
OpenGL将着色器编译为一个程序的时候。他实际上用一个位置编号把片段着色器中定义的每一个uniform都关联起来.我们通过位置编号来给着色器发送数据。像uniform一样,在使用属性之前我们也要获得它们的位置。我们可以让 OpenGL 自动给这些属性分配位置编号,或者在着色器被链接到一起之前,可以通过调用glBindAttribLocation()由我们自己给它们分配位置编号。让OpenGL自动分配这些属性位置,使代码更容易管理。
要想给着色器对象中定义的变量赋值。我们需要获取到这些变量在程序中的位置。(这些变量的位置在程序编译完后,就固定了。)
获取uniform位置。
1 | uniformLocation = glGetUniformLocation(program, “uniform变量的名字”); |
在上面参数就是u_Color
获取attribute位置。
1 | attributLocation = glGetUniformLocation(program, “attribut变量的名字”); |
在上面参数就是a_Position.
给变量赋值。关联顶点数据,也就是给着色器赋值。
我们的数据都是放到FloatBuffer中的。
1 | vertexData.position(0); |
OpenGL的坐标到屏幕映射。OpenGL会把坐标映射到【-1,1】的范围内,也就是屏幕会被映射为-1,1、 1,1、 -1,-1、 1,-1. 这第四个点为屏幕四个角的坐标。0,0为屏幕中心。
刚才说过,OpenGL可以绘制点,线,三角形。
在每次绘制前,我们要给片段着色器赋值。
1 | //先给三角形赋值颜色。 |
第一个点与后面的每两个点构成一个三角形。
线段,三角形之间片段颜色采用顶线之间线性着色。
varying: 通过varying变量产生平滑着色。
顶点着色器定义varying变量来生成着色, 片段着色器定定义相同的varying变量,来接收顶点着色器的值。
如果一个片段属于一条直线,那么 OpenGL就会用构成那条直线的两个顶点计算其混合后的颜色。如果那个片段属于一个三角形,那 OpenGL 就会用构成那个三角形的三个顶点计算其混合后的颜色。
二维三维化原理:
就是透视。在一个想象中的消失点处,把并行线段聚合在一起,从而创建出三维的幻像。
透视原理
近大远小
一个顶点着色器上的顶gl_Postion点最终变换为屏幕坐标的过程
1 | graph LR |
两个变换,三种不同的坐标空间。
当顶点着色器把一个值写到gl_Position的时候,OpenGL会做裁剪。 OpenGL使用透视除法将坐标裁剪到裁剪空间中。
针对每个坐标,OpenGL将想x,y,z分量除以w. 这样都会位于
-w,w范围内。这样之后,较远的物体会被移动到距离渲染中心更近的地方。这个点就像是一个消失点。
(0,0,0)就是归一化坐标的渲染区域的中心。
因为透视除法,裁剪空间中的坐标被称为同质化坐标。不同的点可能会映射到相同的点。
OpenGL把归一化坐标的x,y 分量映射到屏幕的一个区域,被称为视口。
glViewport就是设置视口的。
归一化坐标默认使用的是左手坐标系。
在前面章节中,使用正交投影处理屏幕的宽高问题,它通过调整显示区域的宽高使之变换为归一化坐标。在使用正交投影时,我们假定一个包围整个场景的立方体。
使用透视投影,立方体的线延长后就会回合到一点,成为一个椎体,叫做视椎体。
视椎体只是一个立方体,它的近端比远端大,看起来就是一个金字塔。两端的大小差距越大,观察范围越广,能看的也就越多。
视椎体有大端和小端。
焦点:金字塔的顶。
焦距:焦点到小端的距离。影响大端和小端的比例。
使用模型矩阵来平移场景中的物体。因为默认z位置在0, 需要使用模型矩阵沿着负轴移动。
矩阵操作: OpenGL的Matrix提供了矩阵操作的方法,可以方便对矩阵进行相乘,平移,旋转等操作。
纹理:是图像,图片,上传到OpenGL。叫纹理
POT纹理: 图片的宽高都是2的幂的纹理。
每个二维的纹理都有其自己的坐标空间,其范围是从个拐角的(0,0)到另一个拐角的(11)。按照惯例,一个维度叫做 s,而另一个称为T。当我们想要把一个纹理应用于个三角形或一组三角形的时候,我们要为每个顶点指定一组 ST 纹理坐标,以便 OpenGL 知道需要用那个纹理的哪个部分画到每个三角形上。这些纹理坐标有时也会被称为 UV 纹理坐标,如图 7-3 所示。
1 | final int[] textureObjectIds = new int[1]; |
1 | //数量,存储数组、偏移 |
1 | glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]); |
GL_TEXTURE_2D 告诉OpenGL 将纹理看做是一个二维纹理,
textureObjectIds[0] 具体的纹理对象
最近邻过滤:OPenG为每个片段选择最近的纹理。放大时,会有细节丢失。产生锯齿。
双线性过滤:OPenG使用双线性插值平滑像素之间的过滤。使用四个邻接的纹理,并用一个线性插值算法做插值。双线性指的是它是沿着两个维度插值的。
MIP贴图
双线性过滤:因为每一帧都要选择不同纹理元素,这会引起噪音和移动中物体的闪数。
MIP贴图:MIP贴图可以用来生成一组优化的的不同大小的纹理。 当生成纹理时,OpenGL会使用所有的纹理元素生成每个级别的纹理,在渲染时,OpenGL会根据每个片段的纹理元素数量为每个片段选择最合适的级别。
三线性过滤:
最邻近的MIP贴图级别也要插值。
1 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
glTexParameteri设定过滤器
制定缩小模式时,使用三线性过滤
制定放大模式时,使用双线性过滤
1 | // 加载纹理到OpenGL |
至此,纹理贴图已经生成。
1 | uniform mat4 u_Matrix; |
a_TextureCoordinates 纹理坐标,有两个分量,S和T。
v_TextureCoordinates 我们将上面的坐标传给顶点着色器做插值。
1 | precision mediump float; |
OPenGL为每个片段调用片段着色器,并且用v_TextureCoordinates接受纹理坐标。
sampler2D u_TextureUnit: 片段着色器通过sampler2D接收实际的纹理数据
被插值的纹理坐标和纹理数据被传递给着色器函数 texture2D (),它会读入纹理中那个特定坐标处的颜色值。接着通过把结果赋值给 glFragColor 设置片段的颜色。
纹理绘制
当我们在 OpenGL 里使用纹理进行绘制时,我们不需要直接给着色器传递纹理。相反,我们使用纹理单元(textureunit)保存那个纹理。之所以这样做,是因为一个 GPU 只能同时绘制数量有限的纹理。它使用这些纹理单元表示当前正在被绘制的活动的纹理。
1 | //激活纹理单元0 |
纹理坐标 (ST)UV坐标:
图像坐标:
三角形带: 一个三角形带的前三个顶点定义了第一个三角形。这之后的每个额外的顶点与前两个顶点定义了另外的三角形。
三角形扇:
一个三角形扇的前三个顶点定义了第一个三角形。这之后的每两个顶点与第一个顶点定义了另外的三角形。
我们需要将触摸事件交给渲染器处理。
Android中使用setOntouchListener接受事件处理。
坐标转换。
Android中事件坐标为坐左上为0,0. 右下角为w,h。
OpenGL坐标为归一化坐标。需要把Android坐标转换为归一化坐标:
1 | final float x = ((float)event.getX() / (float)v.getWidth) * 2 - 1. |
Android的事件处理在主线程。而OpengGL在子线程,使用下面的方法将事件传递给Android的主线程。
1 | glSurfaceView.queueEvent(new Runnable); |
介绍如何在React Native中封装和调用Android原生UI组件
介绍在React Native中创建Android原生模块的方法,实现调用Android原生模块的功能