Android——NativeActivity - C/C++ Apk开发
android基本的四大组件之一Activity,android开发的第一个hello world 创建的就是这个继承了Activity类的类,拥有对应的生命周期,由AMS维护,只需要重写父类对应的方法即可,但这都是在Java层面,如果想往C/C++层跑,就需要JNI去访问,这样基本可以满足许多性能有要求的apk开发需求。
但是那些原生是基于C/C++编写的大型软件程序,就并不是这么好抽取JNI接口让android 的java层调度了。如果想移植实现那就麻烦了, 我的理解 这个NativeActivity 机制相当于一个适配层,将android的那一套组件运行时机制 转义为C/C++开发程序所能接收处理的一层进行调度管理,不禁想起之前在 《程序员的自我修改-链接.装载.库》 中看到那句名言: 计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决
java-apk层面
NativeActivity 为Activity的子类,实现在:\frameworks\base\core\java\android\app\NativeActivity.java
主要是对Activity的一些生命周期函数的封装,贴出值得关注的:
* {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all}
*/
public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
InputQueue.Callback, OnGlobalLayoutListener {
...
public static final String META_DATA_LIB_NAME = "android.app.lib_name";
...
private native long loadNativeCode(String path, String funcname, MessageQueue queue,
String internalDataPath, String obbPath, String externalDataPath, int sdkVersion,
AssetManager assetMgr, byte[] savedState);
...
private native void onStartNative(long handle);
private native void onResumeNative(long handle);
...
@Override
protected void onCreate(Bundle savedInstanceState) {
String libname = "main";
String funcname = "ANativeActivity_onCreate";
...
String ln = ai.metaData.getString(META_DATA_LIB_NAME);
if (ln != null) libname = ln;
ln = ai.metaData.getString(META_DATA_FUNC_NAME);
if (ln != null) funcname = ln;
...
mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(),
getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()),
getAbsolutePath(getExternalFilesDir(null)),
Build.VERSION.SDK_INT, getAssets(), nativeSavedState);
...
@Override
protected void onStart() {
super.onStart();
onStartNative(mNativeHandle);
}
@Override
protected void onStop() {
super.onStop();
onStopNative(mNativeHandle);
}
封装的一堆native方法自然一目了然,关键在 onCreate 中对本地方法 loadNativeCode 的调用,传递的参数 libname funcname才是重点,是去加载对应的code lib 和本地的函数入口。
可以看到会读取apk 的 metaData 中的 android.app.lib_name 来获取libname
先不去管native 的具体实现,先看下这个NativeActivity中提到的samples/native-activity ,下载个NDK,目录在:
android-ndk-r8b\samples\native-activity
看下这个apk的AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.native_activity"
android:versionCode="1"
android:versionName="1.0">
<!-- This is the platform API where NativeActivity was introduced. -->
<uses-sdk android:minSdkVersion="9" />
<!-- This .apk has no Java code itself, so set hasCode to false. -->
<application android:label="@string/app_name" android:hasCode="false">
<!-- Our activity is the built-in NativeActivity framework class.
This will take care of integrating with our NDK code. -->
<activity android:name="android.app.NativeActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<!-- Tell NativeActivity the name of or .so -->
<meta-data android:name="android.app.lib_name"
android:value="native-activity" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->
注意两点即可:
android:hasCode=”false” 所以 android:name=”android.app.NativeActivity” 这个apk的java源程序就是上面的NativeActivity,自己没有Javacode
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := native-activity
LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
LOCAL_STATIC_LIBRARIES := android_native_app_glue
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/native_app_glue)
android_native_app_glue 这个才是重点,次静态库源码在:
android-ndk-r8b\sources\android\native_app_glue
如有兴趣可细看android_native_app_glue.c , 代码也不长,其实也是封了一层,详细代码就不贴了
往下简述一下 从上面的NativeActivity 怎么调用到main.c 里面的流程:
android_native_app_glue 机制封装层面
- loadNativeCode 调用到JNI本地方法:\frameworks\base\core\jni\android_app_NativeActivity.cpp中的:
static jlong
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
jobject messageQueue, jstring internalDataDir, jstring obbDir,
jstring externalDataDir, jint sdkVersion,
jobject jAssetMgr, jbyteArray savedState)
{
LOG_TRACE("loadNativeCode_native");
...
void* handle = dlopen(pathStr, RTLD_LAZY);
...
if (handle != NULL) {
void* funcPtr = NULL;
const char* funcStr = env->GetStringUTFChars(funcName, NULL);
if (needNativeBridge) {
funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
} else {
funcPtr = dlsym(handle, funcStr);
}
code = new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr);
在lib中找的自然就是java中定义的 ANativeActivity_onCreate 这个函数入口
这个实现函数在 android_native_app_glue.c 中。
- android_native_app_glue中的调度到我们自身的代码入口android_main过程:
看入口代码:
void ANativeActivity_onCreate(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
LOGV("Creating: %p\n", activity);
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
activity->instance = android_app_create(activity, savedState, savedStateSize);
}
这里开始把C/C++层面上的接口 去 对接上android activity的运行回调,这些个onStart onResume….
// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------
static struct android_app* android_app_create(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
memset(android_app, 0, sizeof(struct android_app));
android_app->activity = activity;
...
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
...
}
细节省略,可以看到调进来这里的是主线程,也就是最开始 NativeActivity.java里面的onCreate回调函数,比较重要的一个概念:C/C++开发的程序都是运行在一个子线程,这也是必然的,不然那么大一堆程序代码的初始化之类的,主UI线程必定卡死,这也带来一个麻烦事~ 子线程中对UI 的操控,不会有像java子线程一样方便的handle message 给你调,去更新主线程UI了~ 这里是C/C++的pthread_create ,而且UI的资源也一般是在C/C++层,怎么绘制,怎么管理~ 继续往下~
static void* android_app_entry(void* param) {
struct android_app* android_app = (struct android_app*)param;
android_app->config = AConfiguration_new();
AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
print_cur_config(android_app);
android_app->cmdPollSource.id = LOOPER_ID_MAIN;
android_app->cmdPollSource.app = android_app;
android_app->cmdPollSource.process = process_cmd;
android_app->inputPollSource.id = LOOPER_ID_INPUT;
android_app->inputPollSource.app = android_app;
android_app->inputPollSource.process = process_input;
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
&android_app->cmdPollSource);
android_app->looper = looper;
pthread_mutex_lock(&android_app->mutex);
android_app->running = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
android_main(android_app);
...
}
这里的 looper 机制比较复杂~ 不去深究了,注意process_cmd process_input ,android_app 结构体
这两个 process_xxx 是作为looper中检测到事件以及命令时触发的处理逻辑
往下进入 android_main(android_app) 也就正式进入 我们自己开发的 C/C++ 代码了~
c/c++ lib库实现层面
还是看例子:\android-ndk-r8b\samples\native-activity\jni\main.c
/**
* This is the main entry point of a native application that is using
* android_native_app_glue. It runs in its own thread, with its own
* event loop for receiving input events and doing other things.
*/
void android_main(struct android_app* state) {
struct engine engine;
// Make sure glue isn't stripped.
app_dummy();
memset(&engine, 0, sizeof(engine));
state->userData = &engine;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
engine.app = state;
...
// Prepare to monitor accelerometer
engine.sensorManager = ASensorManager_getInstance();
engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager,
ASENSOR_TYPE_ACCELEROMETER);
engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager,
state->looper, LOOPER_ID_USER, NULL, NULL);
...
// loop waiting for stuff to do.
while (1) {
...
struct android_poll_source* source;
// If not animating, we will block forever waiting for events.
// If animating, we loop until all events are read, then continue
// to draw the next frame of animation.
while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events,
(void**)&source)) >= 0) {
// Process this event.
if (source != NULL) {
source->process(state, source);
}
...
}
}
这个例子 sensor部分的不做关注~ 只关注机制,可以看到 :
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
这两行是重点,实现对上面说到过的 process_xxx 的处理了,怎么对应上的可查看 android_native_app_glue.c 的代码~
往下进入while ,ALooper_pollAll 去等待事件,调用source->process(state, source) 去处理
先转到android_native_app_glue ,再转到这里的engine_handle_cmd 或者 engine_handle_input
详细的处理就不说了~
还有上面说到过的在这个子线程里面~UI 怎么办呢~
这个例子里面是这么做的,调EGL的api绘图~所以前面的Android.mk 需要加入lib库 -lEGL -lGLESv1_CM,绘制显示的初始化函数engine_init_display
总结
主要是从上往下,理清层次关系,调用逻辑,弄清楚运行的机制,还是很有意思的~
这个例子比较简单,后面有机会分析C/C++大型软件用这种方式适配成apk的例子,除了Activity的一些生命周期,事件处理,还可以传递广播以及监听
Android——NativeActivity - C/C++ Apk开发的更多相关文章
- uni-app&H5&Android混合开发二 || 使用Android Studio打包应用APK
前言: 在上一章节我们已经讲了如何uni-app离线打包Android平台教程,这一章就该来讲讲如何使用Android Studio打包应用APK提供给Android手机安装使用了. 第一步.首先打开 ...
- 深入浅出 - Android系统移植与平台开发(六)- 为Android启动加速
作者:唐老师,华清远见嵌入式学院讲师. Android的启动速度一直以来是他的诟病,虽然现在Android设备的硬件速度越来越快,但是随着新 版本的出现,其启动速度一直都比较慢,当然,作为程序员,我们 ...
- Android兼容包multidex的开发和构建方法
在Android开发中,函数方法超过65k限制后,我们就常常会用到multidex分包解决,但是multidex的配置,对系统apk的构建.签名.打包复杂性大大的增加,严重的降低了构建效率.那这个问题 ...
- 如何给你的Android 安装文件(APK)瘦身
如何给你的Android 安装文件(APK)瘦身 本文翻译自:Putting Your APKs on Diet 原作者:Cyril Mottier Android的apk文件越来 ...
- 第07讲- Android项目的打包apk
第07讲Android项目的打包apk 方法一:在工作目录bin文件夹下有一个与项目同名的apk文件 (最懒惰的方式,不推荐,不安全,不利于版本更新,只有在开发模式时使用) 方法二:使用key方式 签 ...
- 怎样给你的Android 安装文件(APK)减肥
转自: http://greenrobot.me/devpost/putting-your-apks-on-diet/ Android的apk文件越来越大了这已经是一个不争的事实. 在Android ...
- 在Android studio中进行NDK开发
在Android studio中进行NDK开发 分类: Android平台 软硬件环境 ubuntu kylin 14.04 红米note增强版 Android studio 0.8.6 ndk ...
- Xamarin android如何反编译apk文件
Xamarin android 如何反编译 apk文件 这里推荐一款XamarinAndroid开发的小游戏,撸棍英雄,游戏很简单,的确的是有点大.等一下我们来翻翻译这个Xamarin Android ...
- Android逆向分析(2) APK的打包与安装背后的故事
前言 上一次我们反编译了手Q,并遇到了Apktool反编译直接crash的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次先插入一下,正所谓知其然知其所以然,授之 ...
随机推荐
- shell脚本作业
.判断/etc/inittab文件是否大于100行,如果大于,则显示”/etc/inittab is a big file.”否者显示”/etc/inittab is a small file.” # ...
- DHCP显示
两种PXE启动芯片 开机显示:Inter® Boot Agent GE V1.2.45或者Intel UNDI PXE2.0 (Build 082):其中UNDI是Universal Network ...
- bootstap 表格自动换行 截取超长数据
<table class="table" style="TABLE-LAYOUT:fixed;WORD-WRAP:break_word">
- 离线(不联网)安装gcc4.8.5
1 楔子 原来机器gcc版本是4.4.6,不支持cpp11,很麻烦需要升级,但是机器不能连外网,只能离线安装,十分麻烦: 2 gcc4.8.5离线安装,通过rpm包: 资源链接: https://pa ...
- 异步消息处理机制相关面试问题-handler面试问题详解
什么是handler? 这个异常应该也就是引出handler的原因,也就是默认在非UI线程中是无法去更新UI的东东滴,那到底什么上handler呢? handler通过发送和处理Message和Run ...
- 使用google身份验证器实现动态口令验证
最近有用户反应我们现有的短信+邮件验证,不安全及短信条数限制和邮件收验证码比较慢的问题,希望我们 也能做一个类似银行动态口令的验证方式.经过对可行性的分析及慎重考虑,可以实现一个这样的功能. 怎么实现 ...
- IOS下图片不能显示问题的解决办法
最近遇到这样一个问题,在HTML5手机页面中,直接给<img>标签设置宽高,即便图片路径正常,在IOS真机下也是无法显示的,而在安卓以及浏览器的模拟真机上都是正常显示的,这是为什么呢? h ...
- 处理springboot OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
springboot项目中添加了字体等文件后,页面无法识别,浏览器调试窗口报错如下: Failed to decode downloaded font: http://localhost:8080/f ...
- 基于LVM 测试磁盘写性能.md
准备工作 /dev/sdb 创建一个卷组,基于卷组创建5个逻辑卷,各100G 在10.10.88.214 新建5台虚拟机,每台虚拟机用到lvm建的逻辑卷 dd 压测 在每台虚拟机上执行dd 命令: d ...
- for 循环用了那么多次,但你真的了解它么?
其实我们写代码的时候一直都在使用for循环,但是偶尔还是会纠结用哪一个循环. 一.基础的for循环 0.使用while也是一种循环方式,此处探究for相关的循环,就不做拓展了. 1.遍历数组的时候,初 ...