十一、Android 本地开发套件(NDK)
Abstract
本章涵盖了 Android 本地开发工具包,或 NDK。我从 NDK 的概述开始,讨论 NDK 实际上是什么,然后检查为了使用这个工具包开发 Android 程序必须满足的系统和软件需求。然后,讨论 Java 本机接口,或 JNI,包括如何使用它来创建在从 C/C++ 代码编译的本机代码中运行的函数,并可以从我们在本书中使用的默认 Java 框架内的 Java 函数中调用。接下来,给出了一个使用 JNI 的“Hello World”示例,该示例提供了创建一个简单 Android 程序的分步指南,该程序使用 JNI 和用 C 语言编写的本机代码来输出字符串。最后,我们将展示另一个动手操作的示例,它从我们的无人机网格游戏中提取一些现有代码,并将现有的 Java 代码转换为本地代码。
本章涵盖了 Android 本地开发工具包,或 NDK。我从 NDK 的概述开始,讨论 NDK 实际上是什么,然后检查为了使用这个工具包开发 Android 程序必须满足的系统和软件需求。然后,讨论 Java 本机接口,或 JNI,包括如何使用它来创建在从 C/C++ 代码编译的本机代码中运行的函数,并可以从我们在本书中使用的默认 Java 框架内的 Java 函数中调用。接下来,给出了一个使用 JNI 的“Hello World”示例,该示例提供了创建一个简单 Android 程序的分步指南,该程序使用 JNI 和用 C 语言编写的本机代码来输出字符串。最后,我们将展示另一个动手操作的示例,它从我们的无人机网格游戏中提取一些现有代码,并将现有的 Java 代码转换为本地代码。
NDK 概述
NDK 是一组工具,旨在与现有的 Android 开发工具(如 Eclipse)一起工作,将从 C 和 C++ 代码编译的本机代码嵌入到 Android 程序中。NDK 可以用来将 C/C++ 代码编译成一个库,然后由 Eclipse 用来编译最终的 Android 应用。一个重要的问题是,只有 Android 操作系统版本 1.5 (Cupcake)或更高版本可以使用 NDK 在 Android 应用中嵌入本机代码。
NDK 系统要求
需要完整的 Android SDK 安装(包括所有依赖项)。需要 Android 1.5 SDK 或更高版本。
支持的操作系统包括:
- Windows XP (32 位)或 Vista (32 位或 64 位)
- Mac OS X 10.4.8 或更高版本(仅限 x86)
- Linux (32 位或 64 位;Ubuntu 8.04 或其他使用 GLibc 2.7 或更高版本的 Linux 发行版)
所需的开发工具:
- 对于所有开发平台,都需要 GNU Make 3.81 或更高版本。GNU Make 的早期版本可能可以工作,但是还没有经过测试。
- 还需要 awk 的最新版本(GNU Awk 或 Nawk)。
- 对于 Windows,需要 Cygwin 1.7 或更高版本。NDK 不适用于 Cygwin 1.5 安装。
Note
Cygwin 程序可以从 www.cygwin.com
下载。
Android 平台兼容性
由 NDK 生成的针对特定 CPU 架构的本机代码需要最低 Android 操作系统版本,具体取决于所针对的 CPU。
- ARM,ARM-NEON 目标代码需要 Android 1.5(API 3 级)及更高版本。实际上,目前几乎 100%的可用 Android 设备至少是 1.5 或更高。
- x86 目标代码需要 Android 2.3 (API Level 9)及更高版本。
- MIPS 目标代码需要 Android 2.3 (API Level 9)及更高版本。
安装 Android NDK
安卓 NDK 主安装文件位于安卓官方网站 www.android.com
。NDK 是一个 zip 文件,你必须下载并解压到你的硬盘上。(见图 11-1 。)
图 11-1。
Android NDK downloads
如果您在 Windows 平台上进行开发,您必须下载并安装 Cygwin,这是一个 Unix 风格的命令行界面,允许您在 PC 上执行 Unix 命令。(见图 11-2 。)
图 11-2。
Cygwin Unix command shell for Windows
安卓 NDK 的使用方法
有两种方法可以使用安卓 NDK。
- 使用 Android Java 框架,并使用 Java 本地接口或 JNI 从基于 Java 的 Android 程序中调用本地代码。
- 使用 Android SDK 提供的 NativeActivity 类正常替换 Java 语言生命周期回调,如
onCreate()
、onPause()
、onResume()
等。,用 C/C++ 写的原生代码。但是原生活动必须运行在 Android 操作系统 2.3 版(API Level 9)及更高版本上。此外,一些 Android 框架服务不能本地访问。
在这一章中,我将介绍如何使用 JNI 从 Java 框架中访问本地代码。
Java 本地接口概述
JNI 允许在 Android 虚拟机中运行的 Java 代码与用其他编程语言(如 C、C++ 和汇编语言)编写的应用和库一起运行。本节将讨论 Java 接口指针。处理本机 C/C++ 编码方法,包括与 JNI 一起使用的变量类型、本机 C/C++ 函数所需的命名约定以及这些函数所需的输入参数。给出了将本机函数集成到 Java 代码中的过程。本节还提供了如何从 Java 中使用本机代码以及如何从本机代码中使用 Java 函数的示例。
Java 接口指针
C/C++ 中的本机代码通过 JNI 函数访问 Java 虚拟机,这些函数是通过 Java 接口指针访问的。JNI 接口指针是指向 JNI 函数的指针数组的指针。(见图 11-3 。)
图 11-3。
The Java Interface Pointer
加载和链接本机 C/C++ 方法
为了在 Android Java 代码中使用本地类(参见清单 11-1),您必须
Load the compiled library by using the System.loadLibrary()
function. Declare the native class that is defined in the C/C++ source code as native in the Android Java code by using the native keyword in a function declaration.
清单 11-1。从 Android Java 代码加载和链接本地 C/C++ 方法
package robs.gldemo.robsgl20tutorial;
class GLES20TriangleRenderer implements GLSurfaceView.Renderer
{
/* this is used to load the 'hello-jni' library on application
* startup. The library has already been unpacked into
* /data/data/com.example.hellojni/lib/libhello-jni.so at
* installation time by the package manager.
*/
static {
System.loadLibrary("hello-jni");
}
public native String RobsstringFromJNI();
}
命名本机函数
Java 代码中声明的本机函数必须根据特定格式与本机 C/C++ 代码中声明的函数名匹配,如下所示:
The function starts with “Java.” It is followed by the package name “robs_gldemo_robsgl20tutorial
” from the example in Listing 11-1. This is followed by the class name “GLES20TriangleRenderer
” from the example in Listing 11-1. Next comes the function name “RobsstringFromJNI
” from the example in Listing 11-1.
在清单 11-2 中可以看到完整的函数名。
清单 11-2。原生RobsstringFromJNI()
函数
jstring
Java_robs_gldemo_robsgl20tutorial_GLES20TriangleRenderer_RobsstringFromJNI
(JNIEnv* env, jobject thiz )
{
return (*env)->NewStringUTF(env, "Rob’s String Text Message!");
}
本机函数参数
本地函数的参数列表总是以一个指向 JNIEnv 的指针开始,它是 Java 接口指针,例如,在清单 11-2 的本地函数中的env
。
JNIEnv* env
第二个参数是对对象的引用,如果本地函数是非静态的,例如清单 11-2 中的本地函数示例中的thiz
。
jobject thiz
但是,如果本机函数是静态的,第二个参数是对其 Java 类的引用。
C 与 C++ 本机函数格式
清单 11-2 中的函数是利用 Java 本地接口的 C 本地函数。C++ 版本略有不同,但是底层机制是相同的(见清单 11-3)。主要区别是
The extern "C"
specification The change from (*env)->
to env->
for accessing the JNI functions The removal of env
as the first parameter of the JNI function call
清单 11-3。相当于 C++ 的本机函数
extern "C" /* specify the C calling convention */
jstring
Java_robs_gldemo_robsgl20tutorial_GLES20TriangleRenderer_RobsstringFromJNI
(JNIEnv* env, jobject thiz )
{
return env->NewStringUTF("Rob’s String Text Message!");
}
本地类型
JNI 本地数据类型及其 Java 等价物包括
- jboolean:这种本机类型相当于布尔型 Java 类型,大小为无符号 8 位。
- jbyte:这种本机类型相当于 byte Java 类型,大小为 8 位。
- jchar:这种本机类型相当于 char Java 类型,大小是无符号的 16 位。
- jshort:这种本机类型相当于短 Java 类型,大小为 16 位。
- jint:这种本机类型相当于 int Java 类型,大小为 32 位。
- jlong:这种本地类型相当于 long Java 类型,大小为 64 位。
- jfloat:这个本机类型相当于 float Java 类型,大小为 32 位。
- jdouble:这种本机类型相当于双 Java 类型,大小为 64 位。
参考类型
JNI 包括一些对应于各种 Java 对象的引用类型。参见图 11-4 ,了解分层视图中这些参考类型的列表。
图 11-4。
Reference type hierarchy
JNI 签名类型
JNI 使用 Java 虚拟机的签名类型表示,这些签名类型用于定义特定的函数,包括其返回值类型和输入参数的类型。签名类型如下:
- z 布尔类型
- b 字节类型
- c 字符类型
- s 短型
- I int 类型
- j 长型
- f 浮动型
- d 双型
- l 完全合格级;完全合格的类别
- [ type type[]数组
- (arg-types) ret-type(方法)返回类型
- 五.无效
例如,Java 方法
long JavaMethod1(int number, String str, int[] intarray1);
具有以下类型签名:
(ILjava/lang/String;[I)J
另一个例子是 Orientation 类中的AddRotation()
函数。
void AddRotation(float AngleIncrementDegrees)
这个函数的签名类型是
(F)V
F
表示浮点输入参数,而V
表示 void 返回类型。
从 Java 调用本机代码和从本机代码访问 Java 方法
要从 Java 代码中调用本机函数,您可以使用不带额外标识信息的损坏前缀的本机函数名。例如,要调用清单 11-5 所示的本机函数AddRotationNative()
,可以使用清单 11-4 所示的代码。
清单 11-4。从 Java 调用本机代码
void AddRotationToObject(Orientation O, float AngleAmount)
{
AddRotationNative(O, AngleAmount);
}
清单 11-5 中显示的本地函数AddRotationNative()
为定向对象调用AddRotation()
函数,该定向对象被传递到函数中并保存在Orient
变量中。
AddRotationNative()
功能执行以下操作:
Gets the class of the Java object Orient
by calling the GetObjectClass()
JNI function and assigns it to the OrientationClass
variable Gets the method id of a specific function by calling the GetMethodID()
JNI function with parameters including the function name, which is "AddRotation"
; the function signature type, which is "(F)V"
; and the Java class object that contains the function, which is OrientationClass
. This method id is assigned to the MethodID
variable. Calls the Orient
Java object’s AddRotation()
function with parameter RotationAngle
by calling the CallVoidMethod()
JNI function
清单 11-5。从本机代码访问 Java 方法
Java_robs_gldemo_robsgl20tutorial_Physics_AddRotationNative(JNIEnv* env,
jobject thiz,
jobject Orient,
jfloat RotationAngle)
{
/*
GetObjectClass
jclass GetObjectClass(JNIEnv *env, jobject obj);
*/
jclass OrientationClass = (*env)->GetObjectClass(env, Orient);
/*
GetMethodID
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
*/
jmethodID MethodID = (*env)->GetMethodID(env,
OrientationClass,
"AddRotation",
"(F)V");
/*
NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
*/
(*env)->CallVoidMethod(env, Orient, MethodID, RotationAngle);
}
JNI 函数
除了清单 11-5 中讨论的,还有很多 JNI 函数。例如,如果我们要调用的 Java 函数返回一个双数值,那么对于返回 void 的函数,我们必须调用CallDoubleMethod()
函数而不是CallVoidMethod
。我们不会在这里尝试和讨论每一个 JNI 函数,因为这不是 JNI 的参考手册。如果您想了解更多关于支持的 JNI 功能的完整列表,请访问 http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp9502
。
Note
JNI 规格的主要网站是 http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html
。
Android JNI Makefile
Android makefile ( Android.mk
)是一个描述您想要编译到 NDK 编译系统的本机代码的文件。
LOCAL_PATH
变量保存源文件的位置。NDK 编译系统已经定义了my-dir
的值,指向包含Android.mk
makefile 的当前目录。你要做的是把这个 makefile 文件放在 JNI 目录下,连同你要编译的所有 C 源代码文件。
LOCAL_PATH := $(call my-dir)
CLEAR_VARS
变量已经由 NDK 构建系统定义,并指向一个 makefile,该 makefile 将清除构建系统中使用的许多局部变量。
include $(CLEAR_VARS)
LOCAL_MODULE
变量设置将从本地源代码文件生成的库名。库名格式将是前缀“lib
”+“hello-jni
”+后缀“.so
”。但是,如果库名已经以“lib
”开头,则前缀“lib
”不会添加到最终文件名中。
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES
变量保存 C/C++ 源文件的名称,NDK 编译系统将编译这些文件并从中创建最终的库。
LOCAL_SRC_FILES := hello-jni.c
BUILD_SHARED_LIBRARY
变量由 NDK 构建系统定义,指向一个 makefile 文件,该文件收集并处理构建最终库所需的所有信息。
include $(BUILD_SHARED_LIBRARY)
完整的 makefile 如清单 11-6 所示。
清单 11-6。Android JNI Makefile
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
实际操作示例:“来自 JNI 和本地代码的 Hello World”
在这个动手示例中,讨论了一个简单的“Hello World”示例,其中实际的字符串“Hello World from JNI 和本机代码”是从本机 C 代码生成的,并返回给 Java 调用者,然后在那里打印到日志窗口。
首先,我们必须为 Android 项目创建jni
目录。选择要在其中创建jni
目录的主项目目录。转到文件➤新➤文件夹,调出新文件夹对话框窗口。在文件夹名称编辑框中输入文件名“jni ”,点击完成按钮,创建一个名为jni
的新目录。(见图 11-5 。)
图 11-5。
New Folder window dialog
jni
目录应该出现在主项目目录下。(见图 11-6 。)
图 11-6。
Creating the jni
directory
接下来,通过选择文件➤新➤文件在jni
目录下创建一个新文件,以打开一个新文件窗口对话框。(见图 11-7 。)
图 11-7。
New File window dialog
键入“Android.mk”作为文件名,然后单击 Finish 按钮创建新文件。双击 Package Explorer 窗口中的文件,在 Eclipse 源代码区域显示文本。将清单 11-6 中的 makefile 代码复制到新文件中,并通过选择文件➤全部保存来保存它。
接下来,重复前面的步骤,为 C 源代码文件创建一个新文件,命名为“hello-jni.c”,将清单 11-7 所示的源代码复制到hello-jni.c
。
清单 11-7。Hello-Jni.c 源代码
#include <string.h>
#include <jni.h>
// package = com.robsexample.glhelloworld;
// class = MyGLRenderer
jstring
Java_com_robsexample_glhelloworld_MyGLRenderer_RobsstringFromJNI(JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello World from JNI and Native Code.");
}
清单 11-7 显示了本机 C 函数RobsstringFromJNI()
。该函数通过调用NewStringUTF()
函数创建一个新的 Java 字符串,并将该字符串返回给 Java 调用者。
接下来,必须使用 NDK 编译系统编译本机代码。为了做到这一点,我们必须启动 Cygwin Unix 模拟器,如果您使用 PC 进行 Android 开发,它允许您在 Windows PC 上运行 Unix 命令。
您必须使用 Unix 命令导航到您之前创建的jni
目录。使用“cd …”命令将目录更改为上一级目录,命令“cd foldername”将当前目录更改为文件夹名。使用“ls”命令列出当前目录中的文件和文件夹。使用“pwd”命令获取当前所在的目录路径。
您必须转到根目录,并将目录更改为cygdrive/
文件夹。然后将目录更改为存储 Android 项目的驱动器,并转到本地源文件所在的特定文件夹。一旦你进入包含 makefile 和源代码的jni
目录,你必须从你下载并解压到硬盘的 Android NDK 中运行ndk-build
脚本。例如,假设您的文件在/cygdrive/c/AndroidWorkSpaces/WorkSpace1/MainActivity/jni
目录中。您可以在当前目录下通过输入脚本的完整路径来执行ndk-build
脚本,比如/cygdrive/c/AndroidNDK/andoird-ndk-r9/ndk-build
。然后,构建脚本将执行并产生如图 11-8 所示的输出。
图 11-8。
Running the ndk-build
script in the jni
directory
ndk-build
脚本将处理您的本地代码文件,并将它们打包到一个名为libhello-jni.so
的共享库,该库位于libs/armeabi
目录中。(见图 11-9 。)
图 11-9。
The shared library generated by the ndk-build
script
修改 MyGLRenderer 类
为了使用编译后的本地代码,我们必须对上一章的动手示例中的 MyGLRenderer 类进行一些修改。
必须使用loadLibrary()
函数加载带有本机代码的共享库。
static {
System.loadLibrary("hello-jni");
}
库中的本机 C 函数必须用 native 关键字声明,才能被识别和使用。
public native String RobsstringFromJNI();
在onDrawFrame()
函数中,String
变量TestJNIString
被赋予调用RobsstringFromJNI()
函数的返回值。该返回值包含在调试日志语句中。(参见清单 11-8。)
清单 11-8。修改onDrawFrame()
功能
String TestJNIString = RobsstringFromJNI();
Log.e("RENDERER", "JNI STRING = " + TestJNIString);
运行程序。您应该在日志窗口中看到调试语句。log 语句的最终输出如图 11-10 所示。
图 11-10。
JNI test results in Log window
实践示例:将本机功能添加到无人机网格游戏案例研究
这个动手的例子将演示如何将我们的无人机网格游戏的一部分从 Java 代码转换为本机 C 代码,以及如何从本机 C 代码调用 Java 函数。
在本机代码中计算重力
重力计算可以在我们的无人机网格游戏中修改,这样实际的重力计算就可以用 C 编译,并在 Android 上用本机代码执行。为此,我们必须修改hello-jni.c
文件和物理类。
修改hello-jni.c
文件
必须修改hello-jni.c
文件以包含 C 源代码。
Gravity
变量保存我们的 3D 游戏世界的重力加速度值。
float Gravity = 0.010f;
ApplyGravityToObjectNative()
函数计算我们游戏中重力加速度应用后物体的新加速度。然后返回这个新的加速度。(参见清单 11-9。)
清单 11-9。计算物体的新的 Y 加速度
jfloat
Java_com_robsexample_glhelloworld_Physics_ApplyGravityToObjectNative(JNIEnv* env,
jobject thiz,
jfloat YAccel)
{
YAccel = YAccel - Gravity;
return YAccel;
}
修改物理课
接下来,必须修改物理类来调用 native 类。
必须在物理类中将ApplyGravityToObjectNative()
函数声明为原生函数。
native float ApplyGravityToObjectNative(float YAccel);
ApplyGravityToObject()
函数通过用对象的当前 y 加速度调用本机函数ApplyGravityToObjectNative()
来计算加速度的新 y 分量。(参见清单 11-10。)
清单 11-10。调用本机重力计算函数
void ApplyGravityToObject()
{
// Do Native Apply Gravity
float YAccel = m_Acceleration.y;
m_Acceleration.y = ApplyGravityToObjectNative(YAccel);
}
从本机代码旋转对象
使用 Java 函数演示对象从本机 C 代码的旋转需要修改hello-jni.c
文件、Physics 类和 MyGLRenderer 类。
修改hello-jni.c
文件
必须修改hello-jni.c
文件来添加本地类AddRotationNative()
。
AddRotationNative()
函数几乎与我们在清单 11-5 中讨论的函数相同。不同之处在于完整的函数名涉及不同的包和类。AddRotationNative()
所做的是将RotationAngle
度加到对象的旋转上,该对象的方向由输入的Orient
参数表示。这是通过调用实际的 Java 语言方法"AddRotation()"
来完成的。(参见清单 11-11。)
清单 11-11。添加旋转
Java_``com_robsexample_glhelloworld_Physics
jobject thiz,
jobject Orient,
jfloat RotationAngle)
{
/*
GetObjectClass
jclass GetObjectClass(JNIEnv *env, jobject obj);
*/
jclass OrientationClass = (*env)->GetObjectClass(env, Orient);
/*
GetMethodID
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
*/
jmethodID MethodID = (*env)->GetMethodID(env, OrientationClass,"AddRotation", "(F)V");
/*
NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
*/
(*env)->CallVoidMethod(env, Orient, MethodID, RotationAngle);
}
修改物理课
物理类也必须被修改以使用自然旋转函数。
必须在 Physics 类中用 native 关键字声明AddRotationNative()
函数。
native void AddRotationNative(Orientation O, float RotationAngle);
AddRotationToObject()
函数调用AddRotationNative()
函数在对象上执行旋转,并充当本机函数的 Java 包装器接口。(参见清单 11-12。)
清单 11-12。AddRotationNative()
的包装功能
void AddRotationToObject(Orientation O, float AngleAmount)
{
AddRotationNative(O, AngleAmount);
}
通过添加 Java 函数AddRotationToObject()
来修改UpdatePhysicsObject()
函数,该函数调用本地 C 函数来旋转对象,作为物理更新的一部分。旧的AddRotation()
功能也被注释掉了。(参见清单 11-13。)
清单 11-13。修改UpdatePhysicsObject()
功能
void UpdatePhysicsObject(Orientation orientation)
{
// 0\. Apply Gravity if needed
if (m_ApplyGravity)
{
ApplyGravityToObject();
}
// 1\. Update Linear Velocity
/
m_Acceleration.x = TestSetLimitValue(m_Acceleration.x, m_MaxAcceleration.x);
m_Acceleration.y = TestSetLimitValue(m_Acceleration.y, m_MaxAcceleration.y);
m_Acceleration.z = TestSetLimitValue(m_Acceleration.z, m_MaxAcceleration.z);
m_Velocity.Add(m_Acceleration);
m_Velocity.x = TestSetLimitValue(m_Velocity.x, m_MaxVelocity.x);
m_Velocity.y = TestSetLimitValue(m_Velocity.y, m_MaxVelocity.y);
m_Velocity.z = TestSetLimitValue(m_Velocity.z, m_MaxVelocity.z);
// 2\. Update Angular Velocity
/
m_AngularAcceleration = TestSetLimitValue(m_AngularAcceleration, m_MaxAngularAcceleration);
m_AngularVelocity += m_AngularAcceleration;
m_AngularVelocity = TestSetLimitValue(m_AngularVelocity,m_MaxAngularVelocity);
// 3\. Reset Forces acting on Object
// Rebuild forces acting on object for each update
m_Acceleration.Clear();
m_AngularAcceleration = 0;
//4\. Update Object Linear Position
Vector3 pos = orientation.GetPosition();
pos.Add(m_Velocity);
// Check for object hitting ground if gravity is on.
if (m_ApplyGravity)
{
if ((pos.y < m_GroundLevel)&& (m_Velocity.y < 0))
{
if (Math.abs(m_Velocity.y) > Math.abs(m_Gravity))
{
m_JustHitGround = true;
}
pos.y = m_GroundLevel;
m_Velocity.y = 0;
}
}
//5\. Update Object Angular Position
// Add Rotation to Rotation Matrix
//orientation.AddRotation(m_AngularVelocity);
// Call Native Method
AddRotationToObject(orientation, m_AngularVelocity);
}
修改 MyGLRenderer 类
最后,必须修改 MyGLRenderer 类。
必须修改CreateArenaObjectsSet()
函数,以将旋转力应用到竞技场对象,从而演示在旋转对象中使用原生函数。应用于竞技场对象的旋转力值保存在RotationalForce
变量中,并设置为 5000。ApplyRotationalForce()
功能用于对竞技场物体施加实际的力。(参见清单 11-14。)
清单 11-14。修改竞技场对象集创建功能
void CreateArenaObjectsSet(Context iContext)
{
m_ArenaObjectsSet = new ArenaObjectSet(iContext);
// Cube
float RotationalForce = 5000;
float MaxVelocity = 0.1f;
ArenaObject3d Obj = CreateArenaObjectCube1(iContext);
Obj.SetArenaObjectID("cube1");
Obj.GetObjectStats().SetDamageValue(10);
Obj.GetObjectPhysics().GetMaxVelocity().Set(MaxVelocity, 1, MaxVelocity);
Obj.GetObjectPhysics().ApplyRotationalForce(RotationalForce, 1);
// Add new Object
boolean result = m_ArenaObjectsSet.AddNewArenaObject(Obj);
///
Obj = CreateArenaObjectCube2(iContext);
Obj.SetArenaObjectID("cube2");
Obj.GetObjectStats().SetDamageValue(10);
Obj.GetObjectPhysics().GetMaxVelocity().Set(MaxVelocity, 1, MaxVelocity);
Obj.GetObjectPhysics().ApplyRotationalForce(RotationalForce, 1);
// Add new Object
result = m_ArenaObjectsSet.AddNewArenaObject(Obj);
}
从本机代码计算碰撞的反作用力
为了计算碰撞的反作用力,必须对hello-jni.c
文件和物理类进行修改。
修改hello-jni.c
文件
必须通过添加两个函数来修改hello-jni.c
文件。
DotProduct()
函数计算两个向量(x1,y1,z1)和(x2,y2,z2)之间的点积并返回。(参见清单 11-15。)
清单 11-15。计算两个向量的点积
float DotProduct(float x1, float y1, float z1,
float x2, float y2, float z2)
{
return ((x1 * x2) + (y1 * y2) + (z1 * z2));
}
CalculateCollisionImpulseNative()
函数计算两个物体相互碰撞产生的碰撞反作用力,并返回值。(参见清单 11-16。)
清单 11-16。计算碰撞的反作用力
jfloat
Java_com_robsexample_glhelloworld_Physics_CalculateCollisionImpulseNative(JNIEnv* env,
jobject thiz,
jfloat CoefficientOfRestitution,
jfloat Mass1,
jfloat Mass2,
jfloat RelativeVelocityX,
jfloat RelativeVelocityY,
jfloat RelativeVelocityZ,
jfloat CollisionNormalX,
jfloat CollisionNormalY,
jfloat CollisionNormalZ)
{
// 1\. Calculate the impulse along the line of action of the Collision Normal
//float Impulse = (-(1+CoefficientOfRestitution) * (RelativeVelocity.DotProduct(CollisionNormal))) /
// (1/Mass1 + 1/Mass2);
float RelativeVelocityDotCollisionNormal = DotProduct(RelativeVelocityX, RelativeVelocityY, RelativeVelocityZ, CollisionNormalX, CollisionNormalY, CollisionNormalZ);
float Impulse = (-(1+CoefficientOfRestitution) * RelativeVelocityDotCollisionNormal)/(1/Mass1 + 1/Mass2);
return Impulse;
}
修改物理课
物理类也必须修改以实现反作用力计算。
CalculateCollisionImpulseNative()
函数必须声明为本机函数才能使用。
native float CalculateCollisionImpulseNative(float CoefficientOfRestitution,
float Mass1,float Mass2,
float RelativeVelocityX, float RelativeVelocityY, float RelativeVelocityZ,
float CollisionNormalX, float CollisionNormalY, float CollisionNormalZ);
必须修改ApplyLinearImpulse()
函数,以便它调用CalculateCollisionImpulseNative()
函数来计算碰撞的反作用力。对现有的反作用力计算进行了评论。(参见清单 11-17。)
清单 11-17。修改ApplyLinearImpulse()
功能
void ApplyLinearImpulse(Object3d body1, Object3d body2)
{
float m_Impulse = 0;
/*
// 1\. Calculate the impulse along the line of action of the Collision Normal
m_Impulse = (-(1+m_CoefficientOfRestitution) * (m_RelativeVelocity.DotProduct(m_CollisionNormal))) / ((1/body1.GetObjectPhysics().GetMass() + 1/body2.GetObjectPhysics().GetMass()));
*/
m_Impulse = CalculateCollisionImpulseNative(m_CoefficientOfRestitution,
body1.GetObjectPhysics().GetMass(),
body2.GetObjectPhysics().GetMass(),
m_RelativeVelocity.x,
m_RelativeVelocity.y,
m_RelativeVelocity.z,
m_CollisionNormal.x,
m_CollisionNormal.y,
m_CollisionNormal.z);
// 2\. Apply Translational Force to bodies
// f = ma;
// f/m = a;
Vector3 Force1 = Vector3.Multiply( m_Impulse, m_CollisionNormal);
Vector3 Force2 = Vector3.Multiply(-m_Impulse, m_CollisionNormal);
body1.GetObjectPhysics().ApplyTranslationalForce(Force1);
body2.GetObjectPhysics().ApplyTranslationalForce(Force2);
}
运行程序。在图 11-11 中,自然计算出的重力应该拉向地面物体,例如坦克。竞技场物体应该如图 11-12 旋转。碰撞后作用在物体上的碰撞力应该使物体相互偏离,如图 11-13 所示。
图 11-13。
Collision between player’s ammunition and an arena object with reaction force calculated natively
图 11-12。
Arena object turning from natively called rotation
图 11-11。
Tank falling from natively calculated gravity
摘要
在这一章中,我介绍了 Android 本地开发工具包(NDK)。我首先概述了 NDK 到底是什么,以及使用 NDK 需要哪些系统要求、软件要求和实际的 Android 硬件要求。然后,讲述了 Java 本机接口,或 JNI,并用来演示 Java 函数如何调用用 C 编写的本机函数,以及用 C 编写的本机函数如何用于调用 Java 函数。然后介绍了一个简单的“Hello World from JNI 和本机代码”实践示例,并带您一步步地将本机代码实现到现有的 Java 程序中。最后,我们展示了另一个实践示例,展示了如何将本机代码集成到我们现有的无人机网格游戏案例研究中。
十二、发布和营销您的最终游戏
Abstract
在这一章中,我将介绍你最终游戏的出版和营销。我首先讨论如何创建用户将要安装的最终游戏发行版文件。然后,我将介绍如何通过复制发行版文件并将其安装在实际的 Android 设备上来测试它。接下来,我将介绍一个 Android 市场列表,您可以在其中上传游戏发行文件进行销售和/或下载。然后,众多支持 Android 的广告网络呈现给那些想通过广告从他们的游戏中赚钱的人。提供了评论 Android 游戏的游戏网站列表。最后,列出了其他对 Android 游戏开发者有帮助的网站。
在这一章中,我将介绍你最终游戏的出版和营销。我首先讨论如何创建用户将要安装的最终游戏发行版文件。然后,我将介绍如何通过复制发行版文件并将其安装在实际的 Android 设备上来测试它。接下来,我将介绍一个 Android 市场列表,您可以在其中上传游戏发行文件进行销售和/或下载。然后,众多支持 Android 的广告网络呈现给那些想通过广告从他们的游戏中赚钱的人。提供了评论 Android 游戏的游戏网站列表。最后,列出了其他对 Android 游戏开发者有帮助的网站。
创建最终分发文件
您将提交给用户下载和安装的最终发行版文件是一个从安装了 Android 开发工具的 Eclipse 程序生成的.apk
文件。
要开始创建一个.apk
分发文件,选择 File ➤ Export from Eclipse,如图 12-1 所示。
图 12-1。
Selecting Export from the File menu
应该会弹出“导出窗口”对话框。在 Android 文件夹下,选择导出 Android 应用,然后单击下一步按钮。(见图 12-2 。)
图 12-2。
Exporting an Android application
接下来,应该会弹出导出 Android 应用窗口。点击 Browse 按钮,选择一个要导出的 Android 项目,并将其转换成一个.apk
分发文件。一旦选择了一个项目,就会检查是否有任何可能妨碍项目打包的错误。然后,单击“下一步”按钮进入下一个屏幕。(见图 12-3 。)
图 12-3。
Select application to export
应该会出现密钥库选择窗口。选择“创建新的密钥库”单选按钮。单击浏览按钮并选择要存储新密钥库文件的目录。在密码框中键入密码,并在确认框中确认密码。单击“下一步”按钮继续。(见图 12-4 。)
图 12-4。
Create a new keystore selection
现在应该会显示密钥创建窗口。填写此表格将创建一个密钥,用于签署您的申请。填写表格,然后单击“下一步”按钮。(见图 12-5 。)
图 12-5。
Key Creation window Note
建议您将密钥库文件备份到安全的位置。如果您想要更新当前使用这个密钥库文件发布的游戏,您将必须使用这个密钥库文件。
应该会出现目的地和密钥/证书检查窗口。单击 Browse 按钮为您的分发文件.apk
输入一个目录和文件名。单击 Finish 按钮开始创建您的最终发行版.apk
文件。(见图 12-6 。)
图 12-6。
Creating the final .apk
file
测试发行版.apk
文件
现在让我们测试您刚刚创建的发行版.apk
文件,将它安装在 Android 设备上。首先,必须将.apk
文件复制到实际的 Android 设备上。有很多方法可以做到这一点,取决于你使用的是什么版本的 Android 操作系统,你的 Android 上安装了什么软件(比如 FTP),你设置了什么网络连接。我将演示适用于所有 Android 操作系统的复制方法,无论您安装了什么文件传输软件或设置了什么网络连接。为此,我们可以使用 Android Debug Bridge (adb) push 命令将文件放在通过 USB 电缆连接到计算机的设备上。该命令的一般形式如下:
adb push Filename LocationOnAndroidDevice
使用位于C:\Android\adt-bundle-windows-x86\sdk\platform-tools
的 adb 将MainActivity.apk
文件放到安卓设备上的位置/sdcard/Download
的具体命令是
C:\Android\adt-bundle-windows-x86\sdk\platform-tools\adb push MainActivity.apk /sdcard/Download
C:
指的是安装 Android SDK 的驱动器盘符,可能会根据您存储 SDK 的位置和您使用的具体操作系统而有所不同。执行完命令后,MainActivity.apk
文件现在应该在您的 Android 设备上的/sdcard/Download
了,假设这个目录已经存在。(见图 12-7 。)
图 12-7。
Copying MainActivity.apk
on an Android device
在尝试安装.apk
文件之前,如果您使用的是 2.2 等较旧版本的 Android 操作系统,您必须转到“设置➤应用”部分,并选中“未知来源”下的复选框,该复选框允许安装来自未知来源的应用。在更高版本的 Android 操作系统上,你必须在设置➤安全下查找。(见图 12-8 。)
图 12-8。
Allowing installation of .apk
files from unknown sources
回到 Android 的文件管理程序,导航到你复制了.apk
文件的目录。点击.apk
文件开始安装过程。应该会出现一个屏幕,询问您是否要安装该应用。单击安装按钮。(见图 12-9 。)
图 12-9。
Installing the .apk
安装完成后,应出现另一个屏幕,确认.apk
已成功安装。(参见图 12-10 。)
图 12-10。
App installed verification screen
点击打开按钮开始无人机网格游戏。(参见图 12-11 。)
图 12-11。
The game running from the newly installed .apk
file
既然你的游戏已经安装并运行在 Android 设备上,是时候开始营销你的游戏了。
Android 市场和政策列表
本节列出了一些 Android 市场,您可以在其中上传您的应用供其他用户下载。每个市场都有自己的政策,这些政策会根据市场情况经常变化。例如,谷歌最近收紧了对其 Google Play 市场销售的应用中可以使用的广告类型的限制。亚马逊最近取消了其在 Amazon.com
商店销售游戏和应用的年费。
Google Play
Google Play 是主要的 Android 市场。注册 Google publisher 帐户的链接是
https://play.google.com/apps/publish
使用谷歌钱包需要支付 25 美元的一次性注册费。如果你想出售物品,你还需要一个谷歌钱包商家账户。您可以在 Google 开发者控制台中导航至财务报告➤立即设置商户帐户选项卡,申请 Google 钱包商户帐户。这应该会带你到谷歌钱包网站注册成为一个商家。
您可以通过开发者控制台快速发布和取消发布您的应用或游戏。谷歌在公开你的安卓程序之前不会对其进行筛选。
Google 政策的完整描述位于
http://play.google.com/about/developer-content-policy.html
Note
如果您决定在游戏中加入广告,请确保它们符合 Google Play 的广告政策,否则您的游戏可能会被禁止,您的帐户可能会永久停用。
应用商店
亚马逊运营着一个安卓应用和游戏商店,你可以在里面出售你的游戏或者提供免费下载。注册开发者账户的网址是
https://developer.amazon.com/welcome.html
注册是免费的。如果你通过亚马逊的 Appstore 销售程序,你将获得该商品标价的 70%。
您需要将游戏提交给 Amazon 进行测试和验证,然后才能下载或购买。复习一般需要几天。
三星应用商店
三星运营着自己的安卓商店,你可以上传你的应用和游戏出售或免费下载。作为开发人员登录的 web 链接是
http://seller.samsungapps.com/login/signIn.as
注册是免费的。如果您通过商店销售游戏,您将获得物品标价的 70%。
在销售或下载之前,您必须将游戏提交给三星审查。复习通常需要一周时间,但这取决于你选择测试游戏的设备数量。三星专门用不同型号的三星手机和平板电脑测试您的游戏,具体取决于您选择的型号。
阿普唑仑(抗抑郁药物)
Aptoide 不同于之前讨论的商店,因为每个开发者或发行者管理他/她自己的商店,并且用户必须下载 Aptoide 客户端并安装它,以便从这些商店下载和安装 Android 软件。官方网站是
以下是官方网站的描述:“Aptoide 是一个网站,你可以通过一个软件客户端 Aptoide 将免费的应用下载到移动 Android 设备上。在 Aptoide 中,你还可以上传安卓应用,与他人分享。”
资本主义
Appitalism 是一个类似于 Google Play 的应用商店,开发者可以在其中销售或上传免费应用进行分发。官方网站是
不收报名费。
利润方面,一件物品价格的 70%返还给开发商。
GetJar
GetJar 允许你在它的网站上免费发布你的游戏或应用。主要网站是
开发者登录链接是
GetJar 声称其网站每天有超过 300 万次下载。但是,GetJar 不接受付费应用。
SlideMe
SlideMe 是一个 Android 应用和游戏商店,你可以上传你的免费和付费 Android 游戏进行分发和/或销售。官方网站是
没有开发者费用。
社会主义者 Io 商城
社会主义者 Io Mall 是一个 Android 应用和游戏商店,接受免费和付费应用。官方开发者网站是
提交应用或游戏是免费的。
自己的 WebSite.Com
请记住,使用 Android,您可以在自己的网站上发布您的最终发行版文件。然而,如果你希望为此获得收入,你可能不得不依赖其他实体,如处理信用卡和借记卡交易的支付处理器,或为你的程序用户点击他们的广告付费的广告网络。
Android 广告网络列表
从你的游戏或应用中赚钱的一种方式是使用 Android 广告网络,根据用户点击网络在你的游戏中放置的广告的数量向你支付费用。每个广告网络通常都有自己的软件开发工具包(SDK ),你必须将它集成到你的游戏中。SDK 通常由一个以.jar
文件形式的 Android 库和使用该库中的函数来显示广告的代码组成。不同的广告网络有不同风格的广告可供选择。这一部分首先列出了 Android 开发者社区中一些比较突出的公司,然后列出了其他广告网络和营销公司,它们可能对 Android 开发者赚钱和推广他们的游戏有用。
AppFlood
AppFlood 是 PapayaMobile 的一个广告系统,总部位于中国北京,在美国旧金山和英国伦敦设有办事处。它有一个网站
它提供以下类型的广告:
- 通知广告:这些广告类型是推送到用户 Android 手机的通知。
- 图标广告:这些广告类型在用户手机屏幕上放置一个图标。请注意,这种类型的广告对许多用户来说很烦人,可能不符合 Google Play 的最新广告政策。
图 12-14。
AppFlood more games ad
- 更多游戏广告:这些广告显示一个大游戏广告,以及四个小游戏广告。(参见图 12-14 。)
图 12-13。
AppFlood app list
- 应用列表:这些广告模仿了典型的 Android 应用/游戏商店的外观和感觉。(参见图 12-13 。)
图 12-12。
AppFlood interstitial ad
- 间隙广告:这是全屏广告,通常在游戏中的自然断点处显示,如关卡结束或游戏结束。(参见图 12-12 。)
阿帕维兹
Appwiz 是一家成立于 2012 年的广告网络,其网站位于
它提供给开发者的广告类型有
- 搜索图标:用户的主屏幕上有一个搜索图标。但是,请注意,这种类型的广告对许多用户来说是非常讨厌的。
- 书签:书签放置在用户的 web 浏览器中。
- Offer wall:在 Appwiz 提供的其他子格式之间动态优化的全屏广告,如 AppWall、SmartWall、对话广告、视频广告和富媒体。
- 高级广告:放置在主屏幕上的快捷方式,链接到免费应用和热门交易。
铅螺栓
LeadBolt 是一家成立于 2010 年的广告网络,位于澳大利亚悉尼。它的网站是
可用的广告类型有
- 旗帜
- 推送通知
- 主屏幕图标
- 浏览器书签
- 空隙的
AppBucks
AppBucks 是一家位于美国佛罗里达州迈尔斯堡的广告网络。该公司的网站是
AppBucks 提供的广告类型有
- 横幅广告:横幅广告显示横幅,通常横跨屏幕的顶部或底部。
图 12-16。
AppBucks slider ad
- 滑动广告:这种类型的广告在壁纸和面向服务的应用中效果很好,从屏幕的一侧滑出。(参见图 12-16 。)
图 12-15。
AppBucks interstitial ad
- 插播广告:这种类型的广告填满整个屏幕,通常用于游戏中的关键点,以引起用户的注意,例如一个关卡的结束。参见图 12-15 中 AppBucks 的插播广告示例。
移动核心
MobileCore 是一家位于以色列特拉维夫的广告网络,成立于 2009 年。该公司的网站是
MobileCore 提供的广告类型有
- AppWall 广告:提供其他应用或交易的半屏或全屏广告。开发者将从 AppWall 产生的每次点击或安装中获得报酬。
- 滑动广告:从屏幕一侧滑出的广告。
AdMob
AdMob 由谷歌运营,如果你希望你的应用符合谷歌的市场政策,它可能是最安全的。违反这些政策会导致你的游戏或应用被禁和/或你的账户被冻结。AdMob 的网站是
AdMob 有以下类型的广告:
图 12-18。
AdMob interstitial ad
- 插播广告:插播广告是大型全屏广告,旨在吸引眼球。(参见图 12-18 。)
图 12-17。
AdMob banner ad
- 横幅广告:这些广告占据屏幕的一小部分,允许用户点击进入更详细的信息页面或网站。(参见图 12-17 。)
启动应用
StartApp 是 2010 年开始的移动广告平台。总部设在美国纽约,公司的网站是
提供的广告类型有
图 12-23。
StartApp splash screen
- 闪屏:在程序加载时显示广告。(参见图 12-23 。)
图 12-22。
StartApp search box
- 搜索框:显示一个有用的滑动搜索框。(参见图 12-22 。)
图 12-21。
StartApp exit ad
- 退出广告:显示当用户点击后退按钮或主页按钮退出应用时出现的广告。(参见图 12-21 。)
图 12-20。
StartApp 3D banner ad
- 横幅广告:3D 横幅广告。(参见图 12-20 。)
图 12-19。
StartApp interstitial ad
- 间隙广告:全屏广告出现在开发者选择的任何位置。(参见图 12-19 。)
其他广告网络和营销相关公司
下面的列表涵盖了广告网络和营销相关的公司,它们可能对你的游戏营销和从游戏中投放的广告中赚钱都有帮助。
- Aarki (
http://aarki.com
): Aarki 是一家位于加州硅谷的移动广告供应器。 - AdColony(
http://adcolony.com
):AdColony 于 2011 年推出,是一家领先的移动视频广告和货币化平台,以闪电般的速度播放清晰的高清视频,并推动内容的深度参与。 - Adfonic (
http://adfonic.com
): Adfonic 是一个移动广告购买平台,总部位于伦敦。 - AdIQuity(
http://adiquity.com
):AdIQuity 是一个全球移动广告平台,帮助移动发行商和应用开发者从他们的移动库存中获得高额收入。它还帮助广告公司、广告网络和其他媒体购买者获得高质量的全球移动流量。 - AdMarvel(
www.admarvel.com
):AdMarvel 是全球最大的出版商、代理商和运营商使用的移动广告优化工具。 - Admoda (
www.admoda.com
): Admoda 是一个移动广告网络。它的主要重点是为基于绩效的营销部门和联盟营销部门提供流量。 - Applifier(
www.applifier.com
):Applifier 通过交叉推广,帮助各种规模的游戏和应用发行商增长应用。 - Apprupt (
www.apprupt.com
): Apprupt 由移动营销专家组成。 - Avocarrot(
www.avocarrot.com
):Avocarrot 是一家专门从事高参与度原生广告的独特移动广告网络。从一系列可定制的广告单元中进行选择,以创建无缝的用户体验,从而带来更高的收入。 - buzz city(
www.buzzcity.com
):buzz city 是一个全球性的广告网络。 - chart boost(
www.chartboost.com
):chart boost 是一款专注于游戏的手机游戏服务。 - Epom(
http://epom.com
):Epom 成立于 2010 年,专注于广告服务和广告管理。 - 第四屏广告(
www.4th-screen.com
):成立于 2006 年,现在是 Opera 软件集团的一部分,第四屏广告是欧洲领先的优质移动广告销售机构。 - Hunt Mobile Ads(
www.huntmads.com
):Hunt Mobile 是领先的独立移动广告公司,面向西班牙语市场,包括所有拉丁美洲和美国西班牙语市场,并提供发现、建立品牌和利用移动互联网领域的解决方案。 - InMobi (
www.inmobi.com
): InMobi 是一个基于表现的移动广告网络,由 Soft Bank 和 Kleiner Perkins cau field&Byers 支持。该公司于 2007 年在印度成立,并在多个国家设有办事处。 - inner active(
http://inner-active.com
):inner active 是一个面向移动发行商的全球程序化广告栈,专注于视频、超本地和应用内搜索广告。 - Jampp (
www.jampp.com
): Jampp 是一个领先的数据驱动的移动应用营销平台,连接了大量的移动广告网络和实时竞价交易所。 - Kiip (
www.kiip.com
): Kiip 提供虚拟成就的真实奖励。 - Komli Mobile(
www.komlimobile.com
):Komli Mobile 是全球领先的移动广告和出版网络。 - lean market(
www.lean.com
):lean market 专门研究营销效率问题。 - loop me Media(
http://loopme.biz
):loop me 是智能手机和平板电脑上社交广告发现领域的全球领先先驱。LoopMe 使消费者能够对广告进行反馈(“喜欢”、“停止”和“分享”),这增加了点击互动、品牌参与度和通过社交认可的价值。 - MdotM (
www.mdotm.com
): MdotM 是一家移动营销服务公司。 - media lets(
http://medialets.com
):media lets 是一家移动广告公司。Medialets 的移动和平板广告服务平台 Servo 提供先进的测量技术和分析以及简化的活动管理。 - 千禧传媒(
www.millennialmedia.com
):千禧传媒是一家移动营销和广告公司。 - MKmob (
www.mkmob.com/
): MKmob 是一个全球性的移动广告网络。 - MMedia (
http://mmedia.com
): MMedia 是一个移动广告和货币化网络。 - Mobbnet (
www.mobbnet.com
): Mobbnet 是一个全球性的广告网络。 - Mobfox (
www.mobfox.com
): MobFox 是一家移动广告网络,在 iPhone、Android、黑莓、Windows Mobile 和移动网站上运营。 - Mobgold (
www.mobgold.com
): MobGold 帮助广告商在各种移动设备上接触目标用户,并帮助出版商将他们的移动流量货币化。 - mobile fuse(
www.mobilefuse.com
):mobile fuse 是一个移动广告网络,由战略上精选的优质网站和应用组成,覆盖 8500 万唯一用户。 - 移动理论(
http://mobiletheory.com
):移动理论提供移动广告和服务。 - Mocean Mobile(
www.moceanmobile.com
):Mocean Mobile market place(MMM)是全球最大的移动广告市场。 - Mojiva (
www.mojiva.com
): Mojiva 是一家专注于智能手机和平板电脑的移动广告网络。它最出名的是 Mojiva tab,这是一个专门为平板电脑设计的广告网络。 - MoPub (
www.mopub.com
): MoPub 是专为移动出版商打造的托管广告服务解决方案。 - Nexage (
www.nexage.com
): Nexage 通过增加移动广告收入和降低运营成本的解决方案来加强出版商和开发商的移动广告业务。 - OnMOBi (
http://on-mobi.com
): OnMOBi 是一家专注于游戏和金融的广告网络。 - place play(
www.placeplay.com
):place play 是一个针对 iOS 和 Android 的移动广告网络。 - play haven(
www.playhaven.com
):play haven 是一家专注于游戏的移动广告公司。 - ponti flex(
www.pontiflex.com
):ponti flex 是一家专注于注册式广告的移动广告公司。 - Revmob (
www.revmobmobileadnetwork.com
): Revmob 为 Android 和 iOS 提供移动广告。 - sell aring(
www.sellaring.com
):sell aring 提供移动广告,专注于替代现有铃声的音频广告。 - send droid(
http://senddroid.com
):send droid 是一家专注于 Android 推送通知广告的移动广告公司。 - session m(
www.sessionm.com
):session m 是一家专注于游戏的移动广告公司。 - Smaato (
www.smaato.com
): Smaato 是全球领先的移动广告交易所。Smaato 的 SMX 平台是全球领先的移动实时竞价广告交易所,帮助移动应用开发商和出版商增加全球广告收入。 - Sofialys(
www.sofialys.com
):Sofialys 提供移动广告和营销解决方案,包括广告服务器和移动广告网络。 - sponsor pay(
www.sponsorpay.com
):sponsor pay 是一家广告货币化公司。 - strike ad(
www.strikead.com
):strike ad 是一家总部位于美国和英国的移动广告公司。 - Tapgage (
www.tapgage.com
): Tapgage 是一个移动的间隙广告网络,帮助应用开发者和出版商从他们的应用和网站中赚钱。 - 塔皮特。(
www.tapit.com
): TapIt!提供移动广告。 - Tapjoy (
www.tapjoy.com
): Tapjoy 是一家移动广告公司,允许用户安装一个应用来代替游戏内支付。 - think near(
www.thinknear.com
):think near 是一家专注于基于位置的广告的移动广告公司。 - toda cell(
www.todacell.com
):toda cell 是一家优质的移动广告公司。 - trade mob(
www.trademob.com
):trade mob 总部位于欧洲,提供移动应用营销。 - Vserv (
www.vserv.mobi
): Vserv 是一家专注于新兴市场的移动广告交易所。 - Wapstart (
wapstart.ru/en
): Wapstart 是一家俄罗斯移动广告公司。 - Webmoblink(
www.webmoblink.com
):Webmoblink 是一家领先的移动广告网络,面向拉丁美洲(西班牙语和葡萄牙语)和美国西班牙语市场。 - Widespace(
www.widespace.com
):Widespace 是一家总部位于欧洲的优质移动广告网络。 - XAd (
www.xad.com
): XAd 提供基于位置的移动广告。 - y brant Mobile(
www.ybrantmobile.com
):y brant Mobile 通过定向广告活动提供移动广告。 - YOC 移动广告(
http://group.yoc.com
): YOC 移动广告是欧洲最大的优质移动广告网络,在英国、德国、法国、西班牙和澳大利亚这五个主要市场拥有强大的影响力。 - YOOSE (
www.yoose.com
): YOOSE 是一个专注于定位广告的移动广告网络。 - Zumobi (
www.zumobi.com
): Zumobi 是一家移动媒体和广告公司。
Android 游戏评论网站列表
本部分列出了评论 Android 游戏的网站。Android 游戏评论网站是为你的游戏获得免费宣传的绝佳场所。其中一些网站专门致力于 Android,而另一些则是多平台的 Android 板块。
- 安德夫:??]
- 安卓和我:
http://androidandme.com
- Android 应用日志:
www.androidapplog.com
- Android 语句:
www.androidappdictions.com
- Android 应用:
http://android-apps.com
- Android 应用:
www.androidapps.com
- Android 应用:
www.androidapps.org
- 安卓应用图库:
www.androidappsgallery.com
- 安卓应用点评:
www.androidapps-reviews.com
- 安卓权威:
www.androidauthority.com
- 安卓强哥:
www.androidbloke.co.uk
- 安卓中央:
www.androidcentral.com
- 安卓社区:
http://androidcommunity.com
- Android 百科全书:
www.androidencyclopedia.com
- Android et te:
www.androidetvous.com
- Android 论坛:
http://androidforums.com
- Android 法国:??〔??〕??〕
- 安卓游戏:
www.android-games.com
- 安卓游戏:
www.android-games.fr
- 安卓游戏回顾:
www.androidgamesreview.com
- Android gen:“??””
- 安卓家伙:
www.androidguys.com
- 安卓头条:
www.androidheadlines.com
- 安卓基:
http://androidki.com
- Android 实验室:
www.androidlab.it
- 安卓市场应用:
www.androidmarketapps.com
- Android MT:
www.android-mt.com
- 雄激素:
www.androidng.com
- 安卓手机主题:
www.androidphonethemes.com
- Android pips:
http://androidpimps.com
- Android Pit:
www.androidpit.com
- Android Pit(法国):
www.androidpit.fr
- 安卓警察:
www.androidpolice.com
- 安卓预览来源:
www.androidappreviewsource.com
- 安卓破落:
www.androidrundown.com
- 安卓震惊:
www.androidshock.com
- 安卓社交媒体:
www.androidsocialmedia.com
- Android 自旋:
http://androidspin.com
- 安卓平板:
www.androidtablets.net
- Android Tapp:
www.androidtapp.com
- Android Techie:
www.androidtechie.com
- 安卓视频点评:
www.androidvideoreview.net
- 安卓病毒:
www.androidviral.com
- 安卓世界:
www.androidworld.it
- Android 缩放:
www.androidzoom.com
- 自由日:
www.androlib.com
- 雄激素:
www.andronica.com
- Apkfile:
http://androidgamesapps.apkfile.us
- App 大脑:
www.appbrain.com
- App 彩蛋:
www.appeggs.com
- app dang:
www.appgefahren.de
- 应用安卓:
www.applicationandroid.com
- 申请人:
www.applorer.com
- App 模式:
www.appmodo.com
- App 点评中心:
www.appreviewcentral.net
- 应用 400:
www.apps400.com
- Appsplit:
http://appsplit.com
- App 风暴:
http://android.appstorm.net
- 要使用的应用:
www.appstouse.com
- 应用缩放:
www.appszoom.com
- 问你的安卓:
www.askyourandroid.com
- atterods:
www.attdroids.com
- 最佳安卓应用点评:
www.bestandroidappsreview.com
- 最佳安卓游戏奖:
www.bestandroidgameaward.com
- 最佳应用:
http://best-apps.??.com
- 最佳机器人游戏:
www.bestandroidgames.net
- Cnet:
http://reviews.cnet.com
- 疯狂麦克风应用:
www.crazymikesapps.com
- 每日 App 展示:
www.dailyappshow.com
- Droid 安卓游戏:
www.droidandroidgames.com
- 今日机器人应用:
http://droidappoftheday.com
- DroidForums:
www.droidforums.net
- 机器人玩家:??、??、??
- 机器人游戏:
www.droidgaming.net
- 机器人偶像:
www.droididol.com
- 机器人生命:
www.droid-life.com
www.droidologist.com
- Droid 评论中心:
www.droidreviewcentral.com
- Droid Soft:
www.droidsoft.fr
- Android 椭圆:
www.elandroidelibre.com
- 欧元机器人:
www.eurodroid.com
- 欧元玩家:
www.eurogamer.net
- 万物安卓:
www.everythingandroid.org
- 弗兰德:
www.frandroid.com
- 游戏阁楼:
www.gameloft.com/android-games
- 今日游戏玩法:
www.gameplaytoday.com
- gampro:
www.gamepro.de
- 游戏规则:
- 游戏地点:
www.gamespot.com
- gambino:“??””
- 获取安卓玩意儿:
http://getandroidstuff.com
- GiggleApps:
www.giggleapps.com
- 铁杆机器人:
www.hardcoredroid.com
- 迷上安卓:
www.hookedondroid.com
- HTC 欲望游戏:
www.htcdesireforum.com/htc-desire-games
- IGN:
www.ign.com/games/reviews/android
- iorpg:
www.iosrpg.com
- jexadroid:
www.jeuxandroid.org
- 了解你的手机:
www.knowyourmobile.com
- Kotaku:
http://kotaku.com
- 最新安卓应用:
www.latestandroidapps.net
- 安卓的一生:
www.lifeofandroid.com
- mobifip:
www.mobiFlip.de
- 手机应用图库:
www.mobileappsgallery.com
- 手机 24:
http://forum.mobiles24.com
- 动员主义:
www.mobilism.org
- N-Droid:
www.n-droid.de
- 新应用回顾:
www.newappsreview.com
- OmgDroid:
www.omgdroid.com
- 100 款最佳安卓应用:
www.100bestandroidapps.com
- 101 款最佳安卓应用:
www.101bestandroidapps.com
- 148 个应用:
www.148apps.com
- phadroid:
www.phandroid.com
- PhoneDog:
www.phonedog.com
- 玩安卓:
www.playandroid.com
- 玩机器人:
http://playdroid.blogspot.com
- 口袋游戏玩家:
www.pocketgamer.co.uk
- 口袋不掉毛:
www.pocket-lint.com
- 口袋战术:
www.pockettactics.com
- Rpg 手表:
www.rpgwatch.com
- 三星 Galaxy S 论坛:??
- 螺旋攻击:
www.screwattack.com
- 幻灯片播放:
www.slidetoplay.com
- 巧凯泰:
www.smartkeitai.com
- 智能手机日报:
www.smartphonedaily.co.uk
- 平板电脑:
http://tablette.com
- 安卓谈话:
www.talkandroid.com
- Tapscape:
www.tapscape.com
- Tap 区:
www.tapzone.info
- 科技蜂巢:
www.techhive.com
- 安卓星系:
www.theandroidgalaxy.com
- 安卓网站:
www.theandroidsite.com
- 小贴士 4 理工:
www.tips4tech.net
- 最佳免费应用:
http://topbestfreeapps.com
- 触摸街机:
www.toucharcade.com
- 24 Android:
www.24android.com
其他对 Android 开发者有帮助的网站列表
下面的列表包含了其他对 Android 开发者有帮助的网站。在这些网站中,有一些提供免费图形和图形相关工具。
- 打开剪贴画(
www.openclipart.org
):包含公共领域和免版税的图形。 - 矢量公开股票(
www.vectoropenstock.com
):包含自由矢量剪贴画 - Blender 3D 渲染器(
www.blender.org
):适用于 Mac OS X、Linux 和 Windows 的免费 3D 模型生成器和渲染器。 - 用安卓赚钱(
www.makingmoneywithandroid.com
):专注于用安卓赚钱的网站。有一个论坛,里面有很多关于 Android 可用的最佳广告网络的讨论。
摘要
在这一章中,我讨论了你的游戏的出版和营销。我首先介绍了如何为你的游戏创建最终发行版文件,以及如何在实际的 Android 设备上测试这个最终发行版文件。接下来,我介绍了一些可用的 Android 市场,你可以在那里出售你的游戏或者提供免费下载。然后,我提出了一个广告网络列表,通过让这些广告网络在你的游戏中放置广告供用户查看和点击,你可以从中赚钱。接下来,我提供了一个游戏评论网站的列表,您可以从这些网站上获得免费的游戏宣传。最后,给出了其他有用网站的列表。