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 机制封装层面

  1. 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 中。

  1. 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开发的更多相关文章

  1. uni-app&H5&Android混合开发二 || 使用Android Studio打包应用APK

    前言: 在上一章节我们已经讲了如何uni-app离线打包Android平台教程,这一章就该来讲讲如何使用Android Studio打包应用APK提供给Android手机安装使用了. 第一步.首先打开 ...

  2. 深入浅出 - Android系统移植与平台开发(六)- 为Android启动加速

    作者:唐老师,华清远见嵌入式学院讲师. Android的启动速度一直以来是他的诟病,虽然现在Android设备的硬件速度越来越快,但是随着新 版本的出现,其启动速度一直都比较慢,当然,作为程序员,我们 ...

  3. Android兼容包multidex的开发和构建方法

    在Android开发中,函数方法超过65k限制后,我们就常常会用到multidex分包解决,但是multidex的配置,对系统apk的构建.签名.打包复杂性大大的增加,严重的降低了构建效率.那这个问题 ...

  4. 如何给你的Android 安装文件(APK)瘦身

    如何给你的Android 安装文件(APK)瘦身 本文翻译自:Putting Your APKs on Diet           原作者:Cyril Mottier Android的apk文件越来 ...

  5. 第07讲- Android项目的打包apk

    第07讲Android项目的打包apk 方法一:在工作目录bin文件夹下有一个与项目同名的apk文件 (最懒惰的方式,不推荐,不安全,不利于版本更新,只有在开发模式时使用) 方法二:使用key方式 签 ...

  6. 怎样给你的Android 安装文件(APK)减肥

    转自: http://greenrobot.me/devpost/putting-your-apks-on-diet/ Android的apk文件越来越大了这已经是一个不争的事实. 在Android ...

  7. 在Android studio中进行NDK开发

     在Android studio中进行NDK开发  分类: Android平台 软硬件环境 ubuntu kylin 14.04 红米note增强版 Android studio 0.8.6 ndk ...

  8. Xamarin android如何反编译apk文件

    Xamarin android 如何反编译 apk文件 这里推荐一款XamarinAndroid开发的小游戏,撸棍英雄,游戏很简单,的确的是有点大.等一下我们来翻翻译这个Xamarin Android ...

  9. Android逆向分析(2) APK的打包与安装背后的故事

    前言 上一次我们反编译了手Q,并遇到了Apktool反编译直接crash的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次先插入一下,正所谓知其然知其所以然,授之 ...

随机推荐

  1. vue下载后台传过来的乱码流的解决办法

    后台返回的乱码流 解决办法: 请求方式用的是axios,主要加关键的 {responseType: 'blob'} axios封装 export function postDownload(url, ...

  2. Delphi 建立ODBC数据源

    樊伟胜

  3. pytest的使用

    一.python安装 1.windows(server): 双击python-3.6.7-amd64.exe执行安装流程,使用默认安装方式即可. 安装完成后查看是否安装成功: C:\Users\Adm ...

  4. Linux下计划任务:crontab 命令的权限说明

    Linux下的计划任务: 使用crontab命令来执行调度,在 Linux 下可以通过创建文件 /etc/cron.allow 或者 /etc/cron.deny 来控制权限,如果 /etc/cron ...

  5. 初次安装虚拟机WindowsServer2016

    因学习需要,安装windosServer2016,遇到了几个小坑,特地分享一下. 一.windows server 2016 登录 在登录是会出现 输入CTRL+ALT+DEL,才能响应进入用户界面, ...

  6. ubuntu下python3.6.5import tensorflow显示非法指令(核心已转储)

    1.版本 ubuntu版本为14.04 python为3.6.5 tensorflow为pip3安装的1.8.0版本 2.解决 删除原先的tensorflow:sudo pip3 uninstall ...

  7. linux 默认为ipv6的话 ,如果设置ipv4?

    第一步:查出是ens33 第二步:修改文件 对比下方修改就ok TYPE=Ethernet PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=dhcp DEFRO ...

  8. 爱搞事情的webpack

    webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler). 当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency g ...

  9. Js基础知识(五) - 前端性能优化总结

    前端性能优化总结 资源优化 缓存 最好的资源优化就是不加载资源.缓存也是最见效的优化手段.说实话,虽然说客户端缓存发生在浏览器端,但缓存主要还是服务端来控制,与我们前端关系并不是很大.但还是有必要了解 ...

  10. .NET界面开发神器:DevExpress全新发布v19.1.7!快来试用

    DevExpress Universal Subscription(又名DevExpress宇宙版或DXperience Universal Suite)是全球使用广泛的.NET用户界面控件套包,De ...