黑马程序员技术交流社区

标题: 【阳哥专栏】黑马Android教程Android-JNI-02NDK&JNI入门 [打印本页]

作者: 王震阳老师    时间: 2015-4-21 11:08
标题: 【阳哥专栏】黑马Android教程Android-JNI-02NDK&JNI入门
本帖最后由 王震阳老师 于 2015-4-21 11:09 编辑










1. NDK简介(★★

1.1 NDK产生的背景
 Android平台从诞生起,就已经支持C、C++开发。众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三方应用都必须使用Java语言。但这并不等同于“第三方应用只能使用Java”。在Android SDK首次发布时,Google就宣称其虚拟机Dalvik支持JNI编程方式,也就是第三方应用完全可以通过JNI调用自己的C动态库,即在Android平台上,“Java+C”的编程方式是一直都可以实现的。
  不过,Google也表示,使用原生SDK编程相比Dalvik虚拟机也有一些劣势,Android SDK文档里,找不到任何JNI方面的帮助。即使第三方应用开发者使用JNI完成了自己的C动态链接库(so)开发,但是so如何和应用程序一起打包成apk并发布?这里面也存在技术障碍。比如程序更加复杂,兼容性难以保障,无法访问Framework API,Debug难度更大等。开发者需要自行斟酌使用。
  于是NDK就应运而生了。NDK全称是Native Development Kit。
  NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式。NDK将是Android平台支持C开发的开端。
1.2 为什么使用NDK
1.代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反编译难度较大。
   2.可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
   3.提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
   4.便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
1.3 NDK简介
1.NDK是一系列工具的集合
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
2.NDK提供了一份稳定、功能有限的API头文件声明
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API支持的功能非常有限,包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。
1.4 NDK的安装
1.NDK的下载
NDK的官方下载地址http://developer.android.com/tools/sdk/ndk/index.html,由于官方网址在国外,国内访问不了,必须得翻墙。因此我提供了下载好的NDK工具放在百度网盘上供大家下载。http://pan.baidu.com/s/1jGpCDKi
2.将NDK解压到一个不包含空格和中文的目录下
本人将NDK解压在D:\software\ndkr9\android-ndk-r9b中。
1.5 NDK目录结构说明
build:该目录存放的使用NDK的mk脚本,mk脚本指定了编译参数
docs:该目录存放的是NDK的使用帮助文档
platforms:这里面存放的是与各个Android版本相关的平台(x86,arm,mips)相关C语言库和头文件
prebuilt:预编译工作目录
samples:存放的是演示程序
sources:存放的是NDK工具链的C语言源码
tests:测试相关的文件
toolchains:工具链,存放了三种架构的静态库等文件
ndk-build.cmd:Window平台使用NDK的命令
ndk-build:Linux平台使用NDK的命令
2. JNI入门(★★★
下面通过一个简单的JNI案例来演示如何使用JNI编程。
创建一个新的Android工程《JNI入门》,工程的最终目录结构如下图所示。
在MainActivity.java类中定义一个native方法
在工程跟目录下创建一个文件夹jni,该目录名称是约定(约定优于配置)好的,不能是其他名字。
在jni目录下创建hello.c源文件,文件名可以按照见名知意的规则来创建。hello.c代码清单如下。
:上面的代码虽然简单但是关于jni.h头文件和方法名必须单独说明。
1)jni.h头文件位于NDK安装目录下/platforms/android-*/(某平台)/usr/include目录中,如下图
上面的某平台指CPU的三种架构如下图。我们选择任意一架构皆可,但是对于手机来说CPU用arm架构的最多,x86次之,mips架构最少。
2)JNI中C源文件方法名的命名规则
这里的命名规则指用于跟java文件中native方法对应的C语言方法,而C语言中的其他方法命名只要符合C语言规则就行。jstring Java_com_itheima_jnihello_MainActivity_helloC(JNIEnv* env, jobject obj) 中,jstring是方法返回值类型,我们可以把jstring看成是java中String跟C语言中char*类型的一个中间转换类型,java跟C语言的数据类型是不一样的,他们之间要想互相调用就必须通过一种中介来实现,这个中介就是在jni.h头文件中定义的。关于更多的转换类型,在本文档的第2章会有更详细的说明。
方法名第一个字母必须是Java,首单词大写,然后下划线_,然后是将该方法所在的包、类、方法用“_”连接起来,比如com.itheima.jnihello.MainActivity类中的helloC方法,转变成C语言中的方法名为Java_com_itheima_jnihello_MainActivity_helloC。
方法的形参有两个是必须的也就是不管java中的方法是否有形参,但是C语言中对应的方法必须有JNIEnv* env,和jobject obj,如果java方法中还用其他形参,那么在C语言中严格按照顺序排在jobject obj参数的后面即可。
上面的env代表指向JVM的指针,obj是调用该方法的java对象。
使用NDK工具将hello.c编译成hello.so文件
为了方便直接在控制台中使用NDK工具的ndk-build.cmd命令,我们首先将ndk-build.cmd所在的目录设置成系统环境变量。环境变量配置好以后,在命令行中输入ndk-build.cmd会有如下提示:
将当前目录切换到hello.c所在的工程目录
这时候如果直接输入ndk-build.cmd那么会出现如下异常:
出现这种错误时因为我们并没有告诉ndk我们要将那个C语言源代码编译成目标文件。为了告诉ndk要将那个C源文件编译成目标文件,我们需要在工程中的jni目录中添加Android.mk配置文件。
在当前工程的jni目录下添加Android.mk配置文件,该配置文件可以从ndk安装目录的实例代码中拷贝,然后修改。
Android.mk文件清单如下,我们只需要修改LOCAL_MODULE和LOCAL_SRC_FILES两个参数即可。LOCAL_MODULE参数是指定编译后的目标文件的名称,其实编译好的目标文件名为libhello.so,LOCAL_SRC_FILES指定了要编译的源文件。
在cmd中,将当前目录切换到hello.c所在目录,然后重新执行ndk-build.cmd命令,这次成功编译,cmd显示效果如下图所示。
查看项目目录结构,发现在libs目录中多了两个文件夹armeabi和x86,这两个文件夹下分别包含了一个libhello.so动态链接库。这也代表着当前工程中的动态库支持arm架构和x86架构的cpu。
:可能你的并没有同时生成这两个文件,是因为我的工程中引入了Application.mk文件,因此你需要引入该文件。
Application.mk文件清单:
该清单其实只有一行内容,第一行是注释。APP_ABI参数指定要生成的目标文件支持的平台都有哪些,默认是armeabi如果想支持多个平台只需要空一格然后写出其他平台名字即可。
在MainActivity.java中调用C语言
运行上面工程,效果如下:
:如果我们编译的arm平台的so文件,但是却部署到了x86平台的模拟器上,那么运行的时候会报找不到libhello.so的异常。
3. JNI规范(3.1 JNI数据类型和数据结构
1)基本数据类型
JNI基本类型和本地等效类型的对应表格如下:
Java 类型
本地 C 类型
实际表示的 C 类型
Win32
说明
boolean
jboolean
unsigned char
无符号,8
byte
jbyte
signed char
有符号,8
char
jchar
unsigned short
无符号,16
short
jshort
short
有符号,16
int
jint
long
有符号,32
long
jlong
__int64
有符号,64
float
jfloat
float
32
double
jdouble
double
64
void
void
N/A
N/A
2)引用类型,JNI还包含了很对对应于不同Java对象的引用类型,JNI引用类型的组织层次如下图所示:
3.2 JNI接口函数命名方式
1)类型签名
Java虚拟机的类型签名如下:
类型签名
Java类型
Z
boolean
B
byte
C
char
S
short
I
int
J
long
F
float
D
double
Lfully-qulitied-class;
全限定类
[type
type[] 数组
(argtypes)rettype
方法类型
例如,Java方法int feet(int n, String s,int [] arr)的类型签名如下:
(ILJava/lang/String;[I)I
圆括号里面为参数,I表示第一个参数int型,LJava/lang/String;表示第二个参数为全限定Java.lang.String类型,[I表示第三个参数为int型的数组,圆括号后面为返回值类型,I表示返回值为int型。
2)一般函数的JNI接口函数命名方式
一般JNI接口函数命名如下:
Java_包名_类名_方法名。
例如:某工程下com/itheima包下MainActivity类的int getIntFromC()方法的C语言实现函数命名如下:
jint Java_com_itheima_MainActivity_getIntFromC(JNIEnv* env,jobject obj)
其中,包名所包含的“/”应全部以下划线替代,其本地实现的参数和返回值也应转换为JNI类型。
3) 重载函数的JNI接口函数命名方式
重载函数的JNI实现在一般函数的JNI实现之外,还应添加上类型签名以作为同名函数之间的区别,其接口函数命名如下:Java_包名_类名_方法名_参数签名。
例如:某工程下com/itheima包下MainActivity类的int getIntFromC(int n, String s,int [] arr)方法的C语言实现函数命名如下:
jint Java_com_itheima_MainActivity_getIntFromC_ILJava_lang_String_2_3I
(JNIEnv* env, jobject obj, jint n, jstring s, jintarray arr)。
JNI在函数命名时采用名字扰乱方案,以保证所有的Unicode字符都能
转换为有效的C函数名,所有的“/”,无论是包名中的还是全限定类名中的,均使用“_”代替,用_0,„,_9来代替转义字符,如下:
转义字符序列
表示
_0XXXX
Unicode字符XXXX
_1
字符“_
_2
签名中的字符“;”
_3
签名中的字符“[
3.3 JNI函数与API
在本文档中我们所主要需要关心的是C/C++数据类型与JNI本地类型之间的转化过程,这个过程某些数据的转换需要使用JNIEnv对象的一系列方法来完成。
1)jstring转换为C风格字符串
char* test = (char*)(*env)->GetStringUTFChars(env,jstring,NULL);
使用完毕后,应调用:
(*env)->ReleaseStringUTFChars(env, jstring, test);
释放资源。
2)C风格字符串转换为jstring
char charStr[50];
jstring jstr;
jstr = env -> NewStringUTF(charStr);
3)C语言中获取的一段char*的buffer传递给Java
在jni中new一个byte数组,然后使用
(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer) 操作将buffer拷贝到数组中。
这种方式主要是针对buffer中存在“\0”的情况,如果以C风格字符串的方式读入,就会损失“\0”之后的字符。
4)数组操作
JNI函数
功能
GetArrayLength
返回数组中的元素数
NewObjectArray
创建一个指定长度的原始数据类型数组
GetObjectArrayElement
返回Object数组的元素
SetObjectArrayElement
设置Object数组的元素
GetObjectArrayRegion
将原始数据类型数组中的内容拷贝到预先分配好的内存缓存中
SetObjectArrayRegion
设置缓存中数组的值
ReleaseObjectArrayRegion
释放GetObjectArrayRegion分配的内存
:对int,char等基本数据类型的数组操作,将相关Object名称替换为对应基本数据类型名称即为相关函数。
数组操作的方法选择基于使用者的需求而定,如果使用者需要在内存中拷贝数组并对其进行操作那么一般使用GetObjectArrayRegion和SetObjectArrayRegion函数,否则一般使用SetObjectArrayElement和GetObjectArrayElement函数。


4. 案例-银行登录系统(★★
需求:假设银行的登陆模块是用C语言来编写的,但是我们的Android应用想登陆银行系统,那么就需要通过JNI来实现了。
创建一个新Android工程《建行客户端》,工程目录结构如下图。
在工程中创建jni文件夹,然后将jni.h、Android.mk、Application.mk从JNI入门工程拷贝进去。
在jni目录下创建login.c文件,在该文件中实现登录业务逻辑。代码清单如下。
是用ndk工具,将login.c编译成动态库文件。编译前修改Android.mk文件的LOCAL_SRC_FILES := login.c
编写在MainActivity.java类
布局文件比较简单,这里就不再给出。
运行上面的代码,运行结果如下:

5. CDT插件的安装(5.1 CDT简介
CDT 项目致力于为 Eclipse 平台提供功能完全的 C/C++ 集成开发环境(Integrated Development Environment,IDE)。CDT 是完全用 Java 实现的开放源码项目(根据 Common Public License 特许的),它作为 Eclipse SDK 平台的一组插件。这些插件将 C/C++ 透视图添加到 Eclipse 工作台(Workbench)中,现在后者可以用许多视图和向导以及高级编辑和调试支持来支持 C/C++ 开发。
5.2 CDT的下载
CDT插件可以通过eclipse的在线安装,但是受限于跨国家网络访问,一般不是很好用。因此这里我主要给大家说的是如何离线安装。
下载CDT离线安装包。针对不同版本eclipse的cdt安装包如下,大家可以从我的百度网盘上直接下载。考虑到我们大部分都用的最新的ADT因此建议选择8.5.0版本的CDT。
cdt-8.5.0-for-Eclipse-Luna [color=rgb(87, 137, 220) !important]http://pan.baidu.com/s/1c0m1k0w
cdt-8.3.0-for-Eclipse-Kepler [color=rgb(87, 137, 220) !important]http://pan.baidu.com/s/1kT21QOf
cdt-8.1.2-for-Eclipse-Juno [color=rgb(87, 137, 220) !important]http://pan.baidu.com/s/1qWAzjBI
选择eclipse的Help->Install New Software...,弹出如下对话框
点击Add按钮,在弹出的对话框中输入Name。在Location栏如果输入一个http地址是让eclipse自动从网络上下载安装,这里我们点击Archive按钮找到我们事先下载好的离线安装包。然后点击OK。
将CDT所有的插件勾选上,同时将最下面的联网检查更新去掉勾选,然后点击Next,直到Finish。
安装好以后在File->New->Other中会有C/C++选项,如下图。
在Open Perspective中也多了C/C++视图可选项,如下图。
安装好以后,我们就可以在eclipse中开发我们的C/C++工程了。不过对我们Android开发人员来说用到的机会不是很多。就算是开发C/C++工程,大多数程序员也不会选择在eclipse平台上进行开发。Eclipse更多的是专注于Java语言项目的开发,比如JavaEE、Android。















至此,本文档完!

2015年1月28日 星期三 21:37:10
北京市海淀区东北旺中路东馨园小区



作者: 突然世界晴    时间: 2015-4-21 11:14
沙发妥妥的,大声告诉我是不是一楼
作者: czhmike    时间: 2015-4-21 13:37
有点意思
作者: li子文    时间: 2015-4-21 18:06
我又来了,哈哈哈哈,谢谢分享
作者: 前进的途中    时间: 2015-4-21 19:18
不错,赞一下
作者: itheima_llt    时间: 2015-4-21 21:31
真不错,赞一个,继续努力啊
作者: ~谢绝勾引~    时间: 2015-4-21 21:31
好东西 收藏啊
作者: godrick007    时间: 2015-4-21 21:46
阳哥威武
作者: Stars    时间: 2015-4-21 22:29
顶一个!!!
作者: itheima_llt    时间: 2015-4-21 23:16
回复一下吧
作者: 鸡脑壳    时间: 2015-4-21 23:40
这是提前学习了 0.0
作者: 王震阳老师    时间: 2015-4-21 23:56
鸡脑壳 发表于 2015-4-21 23:40
这是提前学习了 0.0

先睹为快。
作者: Hosing    时间: 2015-4-22 01:16
Thanks for YangGe's Share!
作者: 古典牧童    时间: 2015-4-22 10:53
mark,好东西
作者: smartisan    时间: 2015-4-22 15:50
mark very good~~~
作者: 你猜猜22    时间: 2015-4-22 20:24
好东西  值得收藏
作者: 誓_″___訁、    时间: 2015-4-23 19:48
感谢阳哥!
作者: Oh.Ba    时间: 2015-4-23 21:07
支持个,沙发吗?
作者: panbingqi    时间: 2015-4-23 21:26
好东西!
作者: CZTTZ    时间: 2015-4-23 21:54
真的只是安卓的基础吗???
作者: lixiaominls    时间: 2015-4-23 21:56
mark下~  给以后的自己留着
作者: xuelanghu120    时间: 2015-4-23 22:06
好高深啊,还没接触过这些,学java弄安卓已经开始落伍了?????? 求解
作者: 王震阳老师    时间: 2015-4-23 22:11
xuelanghu120 发表于 2015-4-23 22:06
好高深啊,还没接触过这些,学java弄安卓已经开始落伍了?????? 求解

再过几十年再谈落伍还差不多,当下比较流行
作者: xuelanghu120    时间: 2015-4-23 22:55
王震阳老师 发表于 2015-4-23 22:11
再过几十年再谈落伍还差不多,当下比较流行

看着这个新工具  java和c都可以并行  很高大上的
作者: 优质码农    时间: 2015-4-24 16:16
顶起,加油啊

作者: 安日成    时间: 2015-4-24 19:48
好的  你真的很棒
作者: 跳得更远    时间: 2015-4-24 21:32
阳哥 牛 顶起来
作者: se7en    时间: 2015-4-24 23:36
好东西 收藏啊
作者: 赶路要紧    时间: 2015-4-25 00:04
好东西。。
作者: 刘斌斌    时间: 2015-4-25 11:54
赞一个~!
作者: 莫里亚蒂    时间: 2015-4-25 14:01
谢谢王震阳老师的分享啊,很有用啊,我刚刚看看认真看了一遍,了解了很多知识,O(∩_∩)O~
作者: chenxin2015    时间: 2015-4-25 21:22
好东西,收藏一下
作者: Wilsoncyf    时间: 2015-4-26 09:48
嗯,不错。
作者: 付长云    时间: 2015-4-26 10:51
顶~~~~~~~~~~~~~~·
作者: l_z    时间: 2015-4-26 13:33
涨姿势!学习学习。
作者: 崔小可    时间: 2015-4-26 22:12
阳哥威武
作者: guoyuan    时间: 2015-4-26 23:11
多谢分享,好东西已收下。
作者: 黑马94那么拽    时间: 2015-4-26 23:29
赞。。。。。。。。。。。。。
作者: 奋斗的黑马    时间: 2015-4-27 09:02
老师,怎么不把这些基础的只是,录制成视频呢?视频讲解的话,估计会更好些,,嘿嘿
作者: Tauruszzy    时间: 2015-4-27 11:15
赞一个!
作者: 19期1910    时间: 2015-4-27 21:36
顶一个。
作者: 乘梦而飞    时间: 2015-4-27 22:06
感谢楼主分享
作者: youngzk    时间: 2015-4-27 22:13
不明觉历 啊  
作者: mxdeheima    时间: 2015-4-27 22:38
支持支持
作者: 赵桂勇    时间: 2015-4-27 22:44
帅气啊,东西不错,有用啊。
作者: andioid-张先生    时间: 2015-4-27 23:01
看来啊 我要先把安卓学好了 才能来学习更多的啊
作者: 陈捷旋    时间: 2015-4-27 23:06
大神威武,全是技术
作者: 真正黑马    时间: 2015-4-28 12:53
很详细,谢谢分享
作者: 静心明德    时间: 2015-4-28 15:55
好给力,整理的太好了,收藏了,正缺这个。
作者: 雾以泪聚丶    时间: 2015-4-28 21:05
写的太好了,我正向着这个方向前行!!
作者: 马雄鹿    时间: 2015-4-28 21:23
看不到,还没学到这里!!!!!
作者: 13317121995    时间: 2015-4-28 21:47
对于初学的我来说 感觉好深奥啊
作者: 下海的鱼儿    时间: 2015-4-28 22:07
谢谢阳哥,收藏啦
作者: wyq512208105    时间: 2015-4-28 22:19
听着就高大上
作者: GXJ1236987450    时间: 2015-4-29 00:17
好经典的帖子
作者: 水货-年华    时间: 2015-4-30 23:30
这事个改变命运的机会,相信努力就会收获。努力让自己成为优秀的程序员,未来不至于站在社会最底层,一无是处
作者: zy18692243338    时间: 2015-4-30 23:39
虽然还没有学到,但是想想都觉得精彩!!!!
作者: 木风雪林    时间: 2015-5-1 22:05
真是好东西,虽然我现在在基础班,还用不上,但是收藏着,我相信我一定能进就业班的!到时就可以用上了。
作者: 黛玉之殇    时间: 2015-5-3 13:48
留名观看
作者: zuoyou    时间: 2015-5-3 15:33
好东西!
作者: 焦博    时间: 2015-5-3 18:48
顶一个!!!!!!!!!!!!
作者: 时间都去哪了    时间: 2015-5-6 08:13
好东西,感谢杨哥分享
作者: 木风雪林    时间: 2015-5-9 21:33
好东西,值得收藏!
作者: gzp123    时间: 2015-5-9 21:45
这么多,太复杂了
作者: lostaloneesk    时间: 2015-5-12 15:42
一直想学习下NDK,谢谢老师
作者: dianxiaoer    时间: 2015-5-12 16:08
mark very good~~~
作者: 云瑶grace    时间: 2015-5-12 16:13
好复杂的样子
作者: w401634075    时间: 2015-5-12 16:30
马克一下,虽然还在学java基础
作者: 嘉Ming    时间: 2015-5-12 16:43
很用心,顶一个,长见识了。
作者: 赵旗    时间: 2015-5-12 17:03
java快学完了
作者: 横看成岭侧成锋    时间: 2015-5-12 17:11
申请入学测试

作者: 立志转行    时间: 2015-5-12 17:47
真有心了!
作者: 814326663    时间: 2015-5-12 17:53
very good   哈哈
作者: 黑马94那么拽    时间: 2015-5-12 18:31
赞一个。。。。。。。。
作者: 18631147315    时间: 2015-5-12 18:52
赞,,赞,,赞,,超赞。。
作者: junshan    时间: 2015-5-12 19:46
这是什么情况《,,,,
作者: 韩侠    时间: 2015-5-12 20:00
群主好人呀
作者: 南朝小和尚    时间: 2015-5-12 20:49
赞一个!
作者: 菜鸟的求学路    时间: 2015-5-12 20:54
哈哈   给力   赞一个
作者: Misa    时间: 2015-5-12 21:06
太无私了,给王老师赞一个。。。
作者: shentan000    时间: 2015-5-12 21:09
支持一个~~
作者: xiao7181jun    时间: 2015-5-12 21:15
赞一个~~~~~

作者: T-fra    时间: 2015-5-12 21:21
赞一个。。。。
作者: 韩侠    时间: 2015-5-12 21:34
支持一个
作者: xicheng26    时间: 2015-5-12 21:35
来了支持
作者: LANC    时间: 2015-5-12 21:43
支持~~~~!!!
作者: 浪死歌    时间: 2015-5-12 21:46
顶~~~~~~~

作者: 笑爷    时间: 2015-5-12 21:56
支持!!!!
作者: yinping    时间: 2015-5-12 22:17
支持发的发给对方
作者: Richard926    时间: 2015-5-12 22:34
好东西,必须收藏了。正好过几天看是学安卓。
作者: zy18692243338    时间: 2015-5-12 22:36
6666666666666666666666666666666666666666666666666
作者: youngzk    时间: 2015-5-12 22:52
真的是好东西
作者: 高海峰186    时间: 2015-5-12 22:58
何时我也能向楼主一样
作者: 我干阿衰    时间: 2015-5-12 23:09
我也想去上海,超赞!!!!!!!!!!!
作者: kolen.j    时间: 2015-5-12 23:16
顶顶顶。。。。。。。。。。。。。
作者: 火云邪神    时间: 2015-5-12 23:32
顶顶顶。。。。。。。。。。。。。
作者: qinhaihang    时间: 2015-5-12 23:56
支持个,要好好学习
作者: zhubingg    时间: 2015-5-13 00:01
好好学习 ,高薪就业
作者: xicheng26    时间: 2015-5-13 00:09
好好学习,天天向上
作者: Troy-Fu    时间: 2015-5-13 00:12
喜欢好东西,先收藏了:D




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2