分类目录归档:Android

.project文件

描述项目目录结构的文件,比如src位置、classes位置、lib位置等等。通常我们点击refresh,就是刷新.project文件,把项目结构变动在IDE中显示出来,并且触发语法检查,如果有错误会显示出来。

Dalvik

Dalvik是Google公司自己设计用于Android平台的Java虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一。它可以支持已转换为 .dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。
很长时间以来,Dalvik虚拟机一直被用户指责为拖慢安卓系统运行速度不如IOS的根源。
2014年6月25日,Android L 将正式亮相于召开的谷歌I/O大会,Android L 改动幅度较大,谷歌将直接删除Dalvik,代替它的是传闻已久的ART。

JNI详解

Java Native Interface,(JNI)是一个标准的 Java API,它支持将 Java 代码与使用其他编程语言编写的代码相集成。在这里主要就是JavaC++的交互。

    1java调用C++

    首先在java文件中对将要调用的方法做本地声明,关键字为native。且只需要声明,而不需要具体实现。如

    public native void loginSuccessNative(String p_qqId);

    public native static void setStateNative(int i);

然后我们需要在c++文件中实现这些方法,这些方法有特定的格式,我们可以使用javah命令来帮助生成这些方法的声明.调用javac编译我们的java类,获得class文件,然后javah yourClassName, 便可以得到一个.h文件:

 

#include

 

#ifndef _Included_com_test_Hello

#define _Included_com_test_Hello

#ifdef __cplusplus

    extern “C” {

#endif

 

JNIEXPORT void JNICALL Java_com_test_Hello_loginSuccessNative (JNIEnv *, jclass, jstring);

 

JNIEXPORT void JNICALL Java_com_test_setStateNative (JNIEnv *, jclass, jint);

 

#ifdef __cplusplus

}

#endif

#endif

 

    其实只要知道了这个格式,我们也可以自己手动来写。由于java直接回查找cpp里的方法,所以.h文件我们也可以不用声明。

    JNIEXPORT void JNICALL Java_com_test_setStateNative(JNIEnv *, jclass, jint state)

  {

     gameState = state;

 }

  最后我们需要将c++文件,编译成so然后加入java工程,并在java中导入。

    static {

     System.loadLibrary(“test”);

}//系统会自己判断后缀。

 

2:在C++中调用java方法。

C++中调用java会比较麻烦一些,因为需要在C++中获取java的运行环境,并寻找我们要用的类和方法。首先我们需要了解几个概念:

JavaVM:这个代表java的虚拟机。所有的工作都是从获取虚拟机的接口开始的,如何获取这个接口呢?我们之前导入C的组件时调用了:

   System.loadLibrary(“test”);

调用该方法时,java会先调用该组件的JNI_OnLoad()函数.其用途有二: 一是:告诉java VMC组件使用那一个JNI版本。如果你没有提供JNI_OnLoad()函数,VM会默认使用最老的JNI 1.1版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4java.nio.ByteBuffer,就必须藉由JNI_OnLoad()函数来告知VM

二是:由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),所以C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的初期值之设定,也就是获取JavaVM接口。

如:

jint JNI_OnLoad(JavaVM *vm, void *reserved)

{

   JniHelper::setJavaVM(vm);//获取JavaVM接口

   return JNI_VERSION_1_4;//告知java使用什么版本的VM

}

   JNIEnv: 它代表Java环境。通过这个JNIEnv*指针,就可以对Java端的代码进行操作。如,创建Java类得对象,调用Java对象的方法,获取Java对象的属性等。通过之前获得的JavVM我们可以获取JNIEnv

    static bool getEnv(JNIEnv **env)

    {

       bool bRet = false;

       do

       {

           if (JAVAVM->GetEnv((void**)env, JNI_VERSION_1_4) != JNI_OK)

           {

              LOGD(“Failed to get the environment using GetEnv()”);

              break;

           }

           if (JAVAVM->AttachCurrentThread(env, 0) < 0)

           {

              LOGD(“Failed to get the environment using AttachCurrentThread()”);

              break;

           }

           bRet = true;

       } while (0);     

       return bRet;

    }

有了上面的准备,下面我们就可以开始调用java的东西了:

一:获取对象的类id

我们只要知道类的名字就可以通过JNIEnv来获取classid

jclass classID = pEnv->FindClass(className);

二:获取要调用的方法id,包括静态和普通方法。

    jmethodID methodID = pEnv->GetStaticMethodID(classID, methodName, paramCode);

    jmethodID methodID = pEnv->GetMethodID(classID,methodName, paramCode);

    这其中的最后一个参数代表方法的参数信息,因为java支持多态,只有完整的参数信息才可以找到唯一的方法。这个参数有着特定的格式,见附录。 

 

三:调用方法,同样包括静态和普通。

    pEnv ->CallStatic***Method(classID,methodID);

         pEnv ->Call***Method(classID,methodID);

 不同的返回参数,调用不同的方法,如CallBooleanMethodCallIntMethodCallObjectMethodCallStaticIntMethod等等。

 

 

附:函数属性签名规则:

    在GetMethodID最后一个参数是签名字符串,用来标示java函数和成员的唯一性。因为java中存在重载函数,所以一个函数名不足以唯一指定一个函数,这时候就需要签名字符串来指定函数的参数列表和返回值类型了。
函数签名是一个字符串:“(M)N”,括号中的内容是函数的参数类型,括号后面表示函数的返回值。

    具体的每一个字符的对应关系如下
字符  Java类型     C类型
  void       void
  jboolean    boolean
  jint        int
  jlong      long
  jdouble     double
  jfloat       float
  jbyte        byte
  jchar        char
  jshort       short

数组则以”["开始,用两个字符表示
[I   jintArray    int[]
[F   jfloatArray  float[]
[B   jbyteArray   byte[]
[C   jcharArray   char[]
[S   jshortArray  short[]
[D   jdoubleArray double[]
[J   jlongArray   long[]
[Z  jbooleanArray boolean[]


  
 如果Java函数的参数是class,则以”L”开头,以”;”结尾,中间是用”/” 隔开的包及类名。而其对应的C函数名的参数则为jobject
  
 一个例外是String类,其对应的类为jstring
   Ljava/lang/String; String jstring
   Ljava/net/Socket; Socket jobject

Android ABI

ABI: Application Binary Interface 指应用程序基于哪种指令集来进行编译,一般就四种: armeabi armeabi-v7a x86 mips, 前两者最常用到。

在编译的时候你可以指定其中的一种或者几种,如果指定了几种,这时候打包到APK后这个APK被称为“胖二进制”,也就是它包含了几种ABI类型的lib

当APK被安装到设备上的时候,android系统有这样一个机制:

系统支持哪些ABI类型它自己是知道的,它们会将最适合机器性能发挥的ABI类型标记位’primary’ 把剩下的也支持的标记为‘secondary’

例如

CPU 工艺为 ARMv5TE-based 的CPU只有’primary’  为armeabi  没有’secondary’而类型为ARMv7-based的CPU ‘primary’  为armeabi-v7a  ‘secondary’为armeabi
应用程序安装的时候系统首先检查lib/<primary-abi>/libxx.so 如果有的话就将此处的lib随应用程序copy到/data/app/下面这个大家应该知道第三方应用的安装目录

如果没有的话,而机器有secondary ,就检查lib/<secondary-abi>/libxx.so

import-module用法及NDK_MODULE_PATH配置

import-module的功能
导入外部模块的.mk文件 ,和 include基本一样。
概念上的区别是include导入的是由我们自己写的.mk。而import-module导入的是外部库、外部模块提供的.mk。
用法上:include的路径是.mk文件的绝对路径。
而import是设置的路径指定到模块的.mk所在目录,是相对于NDK_MODULE_PATH中的路径列表的相对路径。
import-module的使用
$(call import-module,相对路径)
 
—————–场景重现—————————
比如我的当前模块要调用 cocosdenshion模块。
1\找到模块名字和路径
找到cocosdenshion模块的android.mk的位置。F:\cocos2d-x\CocosDenshion\android\android.mk
打开看到:
LOCAL_MODULE := cocosdenshion_shared
include $(BUILD_STATIC_LIBRARY)
那么cocosdenshion模块在我自己的android.mk中引用它是应该叫它cocosdenshion_shared。而且他是个静态库。
2\在Android.mk中引用模块
就像普通代码中引用头文件一样。
在android.mk的最后一行调用
$(call import-module,CocosDenshion/android)
来导入模块。
注意:我的NDK_MODULE_PATH=/cygdrive/f/cocos2d-x 是已经设置好了的。
如果引用的模块里面也有import-module,他的相对路径也要加到NDK_MODULE_PATH中。如果它没被加进去的话。
然后
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_jpeg_static
声明我这模块要引用该静态库模块。
——————————————————-
 
import-module的使用注意
1、设置路径时,注意与NDK_MODULE_PATH中的路径相互配合。
1、导入模块的.mk中如果也有import-module,则注意其相对路径也要在NDK_MODULE_PATH中。
2、上面说了import-module和include一样。如果import-module和Include包含了同一个.mk,会报重复包含的错误。
NDK_MODULE_PATH的配置
 
NDK_MODULE_PATH的作用
    NDK_MODULE_PATH是一个很重要的变量,当android.mk中使用了$(call import-module,XXX)函数引入外部库文件时会用到,用以指示该往哪里去找这个文件。
    如果NDK_MODULE_PATH 没有设置或者设置不正确。编译时都是报错 Are you sure your NDK_MODULE_PATH variable is properly defined。
NDK_MODULE_PATH的设置与格式
    NDK_MODULE_PATH 是一个环境变量,不是android.mk中设置的变量。
    NDK_MODULE_PATH多个路径用冒号分割。不是分号!且整个字符串中间不能有空格。格式不正确也会报错上面的错误的。
   设置NDK_MODULE_PATH的方法
    1、在系统环境里手动添加这个环境变量,
    2、在build_native.sh中 运行ndk-build之前使用export命令定义环境变量NDK_MODULE_PATH。
    如:export NDK_MODULE_PATH=路径1:路径2:路径3
    3、直接将NDK_MODULE_PATH=路径1:路径2 加到 ndk-build命令的参数后面。ndk-build的参数最终会直接传给make.
    如:$NDK_ROOT_LOCAL/ndk-build -C $HELLOWORLD_ROOT NDK_MODULE_PATH=路径1:路径2
(命令 make aaa=213 //在编译makefile之前将aaa当作环境变量设置为213.)
    
    4、还可以在android.mk中设置NDK_MODULE_PATH
    在import语句之前加入,
$(call import-add-path,$(LOCAL_PATH)/platform/third_party/android/prebuilt)
    将一个新的路径加入NDK_MODULE_PATH变量。
NDK_MODULE_PATH的注意
    感觉NDK_MODULE_PATH中路径中有那些,自己一定要时刻清楚。路径尽量设在模块某个共同的根目录下,不要舍得太乱,免得用起来乱。

windows下ANT安装配置

1. 下载ant: http://ant.apache.org/   解压放在c盘下即可。

2. 配置环境变量:

右击 我的电脑—-属性—–高级—-环境变量
新增环境变量,ANT_HOME:C:\apache-ant-1.7.1
往PATH变量内添加: %ANT_HOME%\bin

3. 测试安装是否成功
cmd中输入 ant -version,出现以下信息为成功,

ant_install_sucess

如果出现以下错误信息:

ant_install_fail

是由于JAVA_HOME找不到引起的,解决办法:

在系统环境变量中新建一个变量,变量名:JAVA_HOME,值:JDK安装目录(例如我的:C:\Program Files\Java\jdk1.6.0_24)

打开一个新的命令行窗口,继续ant -version,应该就成功了。

Android.mk语法详解

Android.mk文件功能: 向Android NDK描述c/c++源代码文件,是GNU Makefile的一小部分,会被编译一次或多次,文件可以把你的代码组织成模块(静态库或共享库)。

LOCAL_PATH := $(call my-dir)   

include $(CLEAR_VARS)

CLEAR_VARS由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等…),

除LOCAL_PATH 。

LOCAL_MODULE := helloworld

LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为’foo’的共享库模块,将会生成’libfoo.so’文件。

重要注意事项

如果你把库命名为‘libhelloworld’,编译系统将不会添加任何的lib前缀,也会生成libhelloworld.so,这是为了支持来源于Android平台的源代码的Android.mk文件,如果你确实需要这么做的话。

LOCAL_SRC_FILES := helloworld.c

LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,你不用在这里列出头文件和包含文件,因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。【注意,默认的C++源码文件的扩展名是’.cpp’. 指定一个不同的扩展名也是可能的,只要定义LOCAL_DEFAULT_CPP_EXTENSION变量,不要忘记开始的小圆点(也就是定义为‘.cxx’,而不是‘cxx’)(当然这一步我们一般不会去改它)】

include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY是编译系统提供的变量,指向一个GNU Makefile脚本(应该就是在build/core目录下的shared_library.mk),负责收集自从上次调用’include $(CLEAR_VARS)’以来,定义在LOCAL_XXX变量中的所有信息,并且决定编译什么,如何正确地去做。并根据其规则生成静态库。同理对于静态库。

Ant编译系统

简述:

java编译工具,类似于c中的make,但更健全(make有缺陷)。常用于自动化调用程序完成项目的编译,打包,测试等。

运行机制: 

通过基于xml的配置文件(默认叫build.xml),控制编译流程。通过调用target树,就可执行各种task。每个task由实现了一个特定Task接口的对象来运行。target标签功能说明:

1.<project>标签

每个构建文件对应一个项目。<project>标签时构建文件的根标签。它可以有多个内在属性,就如代码中所示,其各个属性的含义分别如下。
(1) default表示默认的运行目标,这个属性是必须的。
(2) basedir表示项目的基准目录。
(3) name表示项目名。
(4) description表示项目的描述。
每个构建文件都对应于一个项目,但是大型项目经常包含大量的子项目,每一个子项目都可以有自己的构建文件。
2.<target>标签
一个项目标签下可以有一个或多个target标签。一个target标签可以依赖其他的target标签。
例如,有一个target用于编译程序,另一个target用于生成可执行文件。在生成可执行文件之前必须先编译该文件,因此可执行文件的target依赖于编译程序的target。Target的所有属性如下。
(1).name表示标明,这个属性是必须的。
(2).depends表示依赖的目标。
(3)if表示仅当属性设置时才执行。
(4)unless表示当属性没有设置时才执行。
(5)description表示项目的描述。
Ant的depends属性指定了target的执行顺序。Ant会依照depends属性中target出现顺序依次执行每个target。在执行之前,首先需要执行它所依赖的target。程序中的名为run的target的depends属性compile,而名为compile的target的depends属性是prepare,所以这几个target执行的顺序是prepare->compile->run。
一个target只能被执行一次,即使有多个target依赖于它。如果没有if或unless属性,target总会被执行。
3.<mkdir>标签
该标签用于创建一个目录,它有一个属性dir用来指定所创建的目录名,其代码如下:
<mkdir dir=”${class.root}”/>
通过以上代码就创建了一个目录,这个目录已经被前面的property标签所指定。
4<jar>标签
该标签用来生成一个JAR文件,其属性如下。
(1) destfile表示JAR文件名。
(2) basedir表示被归档的文件名。
(3) includes表示被归档的文件模式。
(4) excludes表示被排除的文件模式。
5.<javac标签>
该标签用于编译一个或一组java文件,其属性如下。
(1).srcdir表示源程序的目录。
(2).destdir表示class文件的输出目录。
(3).include表示被编译的文件的模式。
(4).excludes表示被排除的文件的模式。
(5).classpath表示所使用的类路径
(6).debug表示包含的调试信息。
(7).optimize表示是否使用优化。
(8).verbose 表示提供详细的输出信息。
(9).fileonerror表示当碰到错误就自动停止。
6.<java>标签
该标签用来执行编译生成的.class文件,其属性如下。
(1).classname 表示将执行的类名。
(2).jar表示包含该类的JAR文件名。
(3).classpath所表示用到的类路径。
(4).fork表示在一个新的虚拟机中运行该类。
(5).failonerror表示当出现错误时自动停止。
(6).output 表示输出文件。
(7).append表示追加或者覆盖默认文件。
7.<delete>标签
该标签用于删除一个文件或一组文件,其属性如下。
(1)/file表示要删除的文件。
(2).dir表示要删除的目录。
(3).includeEmptyDirs 表示指定是否要删除空目录,默认值是删除。
(4).failonerror 表示指定当碰到错误是否停止,默认值是自动停止。
(5).verbose表示指定是否列出所删除的文件,默认值为不列出。
8.<copy>标签
该标签用于文件或文件集的拷贝,其属性如下。
(1).file 表示源文件
(2).tofile 表示目标文件。
(3).todir 表示目标目录。
(4).overwrite 表示指定是否覆盖目标文件,默认值是不覆盖。
(5).includeEmptyDirs 表示制定是否拷贝空目录,默认值为拷贝。
(6).failonerror 表示指定如目标没有发现是否自动停止,默认值是停止。
(7).verbose 表示制定是否显示详细信息,默认值不显示。

 

 

问题:

怎么让系统知道按照build.xml进行编译?