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的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次先插入一下,正所谓知其然知其所以然,授之 ...
随机推荐
- 自学python:python学习笔记之Ubuntu 16.04网络的配置
Ubuntu 作为一个Linux的发行版,在桌面环境的易用性上做了很多改善,对推动Linux的推广做了很大的贡献.同时,它作为服务器的操作系统也越来越多的被使用.当然,服务器端可能更多的人在使用Red ...
- 一、CentOS 7安装部署GitLab服务器
一.CentOS 7安装部署GitLab服务器 1.安装依赖软件 yum -y install policycoreutils policycoreutils-python openssh-serve ...
- 微信小程序开发(三)点击事件
接着上篇博客继续. 如下修改: // index.wxml <view>Hello World!</view> <button bindtap="but&quo ...
- 8.3.ZooKeeper集群安装配置
1.Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式. 单机模式:Zookeeper只运行在一台服务器上,适合测试环境: 伪集群模式:就是在一台物理机上 ...
- linux下安装google-chrome浏览器和chromedriver
1.root用户下进入到etc/yum.repos.d目录下 [root@f7d6b9f2-1291-4d2f-8805-aef94deac9f7 yum.repos.d]# pwd cd /etc/ ...
- 22_6mybatis中的缓存
1.mybatis中的延时加载 问题:在一对多中,当我们有一个用户,它有100个账户. 在查询用户的时候,要不要把关联的账户查出来? 在查询账户的时候,要不要把关联的用户查出来? 在查询用户时,用户下 ...
- datax部署
1.下载: https://github.com/alibaba/DataX Clone or download下载源码,拉到下面 Quick Start Download DataX下载地址 下载安 ...
- Java动态绑定与多态
在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特性.多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来.在一开始接触多态这个词的时候,我们或许会因为这个词本身而感到困惑, ...
- BBS-登录注册
目录 1注册上传头像 2.登录图片验证码校验 1注册上传头像 创建admin管理员代码:python3 manage.py createsuperuser 1.在setting文件中配置,用户注册成功 ...
- maven项目bulid失败_No compiler is provided in this environment.
错误信息如下: [ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather ...