Quantcast
Channel: 英特尔开发人员专区文章
Viewing all articles
Browse latest Browse all 172

移植Android*应用到x86架构

$
0
0

移植Android*应用到x86架构

1.移植带NDK的应用

-    获得NDK工具(android-ndk-r10e,https://developer.android.com/ndk/index.html)
-    如果当前程序有makefile文件,修改当前程序的makefile文件(.mk),编辑APP_ABI项包含x86 (如果支持64bit, 则还应包含x86_64)
-    APP_ABI := armeabi armeabi-v7a x86 x86_64(或者APP_ABI := all)
-    如果当前程序没有makefile文件,在命令行中添加x86
-    $ ndk-build APP_ABI="armeabi armeabi-v7a x86 x86_64"
-    将包含新库的包重新打包成APK(以hello-jni为例)
-    $ android.bat update project --path C:/Tools/android-ndk-r10e/samples/hello-jni
      Updated local.properties
      Added f le C:\Tools\android-ndk-r10e\samples\hello-jni\build.xml
      Added file C:\Tools\android-ndk-r10e\samples\hello-jni\proguard.cfg
      $ ant -f hello-jni/build.xml debug
      Buildfile: C:\Tools\android-ndk-r10e\samples\hello-jni\build.xml
      …
      debug:
      [echo] Running zip align on final apk...
      [echo] Debug Package: android-ndk-r10e\samples\hello-jni\bin\HelloJni-debug.apk
      BUILD SUCCESSFUL
-    最后一步在x86架构的设备或者x86的模拟器上来执行和测试其正确性。(注意:需对所有的动态库都加上x86的编译选项,并且编译。如果App用到了第三方SDK, 第三方SDK相关的.so文件也需要将对应的x86版本放置在lib/x86/ 和lib/x86_64/目录中)
-    注: 为保证兼容性,请不要将编译出来的.so文件随便挪移到其他目录进(比如assets),以免运行时发生错误

2.如何判断当前CPU的类型

-    为保证任何情况下都能返回正确的结果,请使用类似如下的java代码来检查当前运行的设备是否是x86设备(不要使用其他的方式,包括NDK中的android_getCpuFamily接口, 以及 android.os.Build.ABI这样的java接口):

InputStreamReader ir = null;

	BufferedReader br = null;

	try {

	Process  process = java.lang.Runtime.getRuntime().exec("getprop ro.product.cpu.abi");

	     ir = new InputStreamReader(process.getInputStream());

	     br = new BufferedReader(ir);

	     final String abi = br.readLine();

	         if(null != abi && abi.contains(“x86”)){

	           // x86 device

	}else if(null != abi && abi.contains(“arm”)){

	    // arm device

	}

	 } catch(Exception ex){

	//@TODO: exception handling

	         }finally{

	 //@TODO: resource clean up

	}

3.了解NDK中的GCC

-    针对x86的默认的GCC编译器标志
      目标:Pentium Pro
      -march=i686 –msse3 -mstackrealign -mfpmath=sse
-    默认情况下,ABI不包含MOVBE, SSSE3, SSE4等
-    IA-32 Only
-    对于大多数x86平台而言,使用默认的GCC标志很可能不能产生最有效率的代码

4.让GCC生成的代码更有效率

-    探测CPU的类型并提供相应的应急方案
-    针对CPU调整编译标志
      -march=core2
      -march=atom
      …
-    使用特别指令集
       -msse4
       -mavx
       -maes
       …
-    更多信息可以参阅:
https://gcc.gnu.org/onlinedocs/gcc-4.5.3/gcc/i386-and-x86_002d64-Options.html

5.让GCC生成的代码更有效率-示例

Example: /jni/Android.mk:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE  := hello-jni
LOCAL_SRC_FILES := hello-jni.c

ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_CFLAGS += -O3 -ffast-math -mtune=atom -msse3 -mfpmath=sse  //针对特定x86平台使用对应的编译参数
else
LOCAL_CFLAGS += …  //针对其它平台使用对应的编译参数
endif

include $(BUILD_SHARED_LIBRARY)

6.ARM vs x86 内存地址对齐问题 1

#define  LOG_TAG       "libtestjni"
#define  LOGI(...)     _android_log_print(ANDROID_LOG_INFO,LOG_TAG,VA_ARGS_)
#define  LOGE(...)     _android_log_print(ANDROID_LOG_ERROR,LOG_TAG,VA_ARGS_)

#define  OFFSET(x, y)  &((x*)0)->y

struct TestStruct
{
    int mVar1; 		//32bit
    long long mVar2; 	//64bit
    int mVar3; 		//32bit
};

JNIEXPORT void JNICALL Java_com_intel_TESTJNILib_run( JNIEnv* env, jobject thiz )
{
    LOGI("TestStruct (size: %d)", sizeof(TestStruct));
    LOGI("-- Var1 offset: %d", OFFSET( TestStruct, mVar1 ) );
    LOGI("-- Var2 offset: %d", OFFSET( TestStruct, mVar2 ) );
    LOGI("-- Var3 offset: %d", OFFSET( TestStruct, mVar3 ) );
}

7.ARM vs x86 内存地址对齐问题 2

-    ARM

	I/libtestjni( 4675): TestStruct (size: 24)

	I/libtestjni( 4675): -- Var1 offset: 0

	I/libtestjni( 4675): -- Var2 offset: 8

	I/libtestjni( 4675): -- Var3 offset: 16

	-    x86

	I/libtestjni( 4079): TestStruct (size: 16)

	I/libtestjni( 4079): -- Var1 offset: 0

	I/libtestjni( 4079): -- Var2 offset: 4

	I/libtestjni( 4079): -- Var3 offset: 12

对于TestStruct而言,64-bit的mVar2会有不同的布局,因为对于像mVar2这样64-bit变量,ARM需要用8-byte来对齐。不过在大多数情况下,这并不会造成什么麻烦,因为x86和ARM应用都需要一个完整的重编译过程。
然而,如果一个应用要序列化类或者结构体,这将造成一种尺寸的不匹配。例如:一个基于ARM的应用要保存TestStruct结构体到一个文件,如果之后这个文件被一个x86设备载入,那么这里将会出现文件内容尺寸不匹配的问题,正如我们能想象到的,相同的问题通常会出现在网络传输,使用第三方游戏引擎的游戏开发中。

8.ARM vs x86 内存地址对齐问题 3

-    GCC的编译选项“-malign-double”能够针对x86和ARM产生相同的内存对齐方式,不过OS并未使用该选项来编译,所以一些OS的调用还是会出现问题
-    变量的对齐方式还可以通过编译器属性来控制,如果我们通知GCC使用align(8)来对齐mVar2,x86和ARM应用将会拥有相同的对齐方式

struct TestStruct
{
    int mVar1;
    long long mVar2 _attribute_ ((aligned(8)));
    int mVar3;
};
输出如下:
ARM
I/libtestjni( 4675): TestStruct (size: 24)		I/libtestjni( 4675): -- Var1 offset: 0
I/libtestjni( 4675): -- Var2 offset: 8		I/libtestjni( 4675): -- Var3 offset: 16
x86
I/libtestjni( 4678): TestStruct (size: 24)		I/libtestjni( 4678): -- Var1 offset: 0
I/libtestjni( 4678): -- Var2 offset: 8		I/libtestjni( 4678): -- Var3 offset: 16

9.ARM vs x86 内存地址对齐问题 4

-    在64bit Android中,则不存在内存地址对齐的问题。就是说,arm64-v8a和x86_64这两种架构下,数据对齐方式是一致的

APK打包及发布

1.APK打包

打包方式11个APK FILE  (推荐方式)同时支持arm /x86架构
注意:a) lib/x86目录和lib/arm*目录下的.so数量务必保持一致;b) 如果应用支持64bit架构,注意lib/x86_64/下的.so数量必须是完全的,以避免在64bit机器上产生库文件找不到的问题;c) 请不要将arm版本的.so文件放置于x86目录;
示范应用:墨迹天气
打包方式22个APK FILE1 个APK FILE 支持x86架构
(包名注明: for x86)
1个APK FILE 支持arm架构
当APK FILE包安装到手机里,运行APK时可以在JAVA代码部分判断当前架构,如果用户下错包,建议增加提示语:如您下载的包有误:请直接从…….下载 或者请下载支持x86芯片的安装程序。

2.发布APP

-    建议上传应用至:Google Play,开发者官方网站,以及国内主流下载市场: Baidu Store, Wandoujia Store, 360 Store, 腾讯应用宝,MM商店,联想商店,Appchina等。

附录

如何快速从apk包来判断一个app是NDK还是Dalvik应用
-    使用zip解压缩工具展开apk包
-    查看展开目录下是否有lib目录,以及lib目录里的内容

                        NDK App                                                                               Dalvik App


Viewing all articles
Browse latest Browse all 172

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>