精通安卓性能优化-第二章(五)

安卓ROM开发入门到精通 包含内容: 第一期主要为ROM开发,ROM定制技术资料,提供一些工具为主 ,让你学会简单做包,和基本的ROM DIY技巧,偶尔附加一些必要的教程。第二期针对ROM技术教程,细分知识点,重点剖析、解释,形成一系列的知识文档供查阅,目前大纲已列出,可以教你从零开始做包。 1.第一期---技术资料(维护更新): 更新日志: 1.书籍-Android系统级深入开发——移植与调试 2.开发工具-And... 阅读详情

Strings

在Java和C/C++使用字符串可能导致性能问题,Java的String使用16位的Unicode字符(UTF-16),而许多C/C++函数简单的使用char*如代表字符串(即C/C++中的string大多数情况下是ASCII或者UTF-8)。怀旧的开发者甚至使用EBCDIC编码。即,Java字符串在JNI可以使用之前需要转换成C/C++字符串。在Listing 2-20给出一个简单的实例。

Listing 2-20 JNI Glue Layer和使用String的Java Native方法

// Java (in Myclass.java)
public class MyClass {
    public static native void doSomethingWithString (String s);
}

// JNI glue layer (in C file)
void JNICALL
Java_com_apress_proandroid_MyClass_doSomethingWithString(JNIEnv *env, jclass clazz, jstring s) {
    const char* str = (*env)->GetStringUTFChars(env, s, NULL);
    if (str != NULL) {
        // 在这里对str字符串做处理
        
        // 记得释放string避免内存泄露
        (*env)->ReleaseStringUTFChars(env, s, str);
    }
}

JNI为字符串提供了多个方法,他们都以同样的方式工作:
(1) Java String必须转换成C/C++字符串
(2) C/C++字符串必须被释放。

表格2-6给出了JNI提供的不同的字符串get/release方法,带有简单的介绍。

Table 2-6 JNI Get/Release的字符串方法


因为内存分配从来不会被释放,你应该在代码中任何可能的地方使用GetStringRegion和GetStringUTFRegion。这样做,你可以:
(1) 避免可能的内存分配
(2) 在预分配的buffer里面复制部分String(很可能在stack)
(3) 避免需要释放字符串,避免忘记释放字符串

NOTE:另外的String方法参考JNI在线文档和NDK的jni.h头文件。

访问字段或者方法

你可以在JNI glue layer访问Java对象或者类的成员变量或者方法,然而,这不像访问C++中的对象或者类那么简单。Java对象或者类的成员变量或者方法通过id访问。访问一个变量或者调用一个方法,需要:
(1) 获取成员变量或者方法的id
(2) 通过一个JNI函数去set/get这个field,或者调用方法

Listing 2-21给出一个实例。

Listing 2-21 从JNI Glue Layer修改字段或者调用方法

// Java (in MyClass.java)

public class MyClass {
    static {
        System.loadLibrary("mylib");
    }
    
    public static int someInteger = 0;
    
    public static native void sayHelloToJNI();
    
    public static void helloFromJNI() {
        Log.i("MyClass", "Greetings! someInteger=" + someInteger);
    }
}


// JNI glue layer (in C file)

void JNICALL
Java_com_apress_proandroid_MyClass_sayHelloToJNI(JNIEnv *env, jclass clazz) {
    // 得到someInteger字段和helloFromJNI方法的id
    jfieldID someIntegerId = (*env)->GetStaticFieldID(env, clazz, "someInteger", "I");
    jfieldID helloFromJNIId = (*env)->GetStaticMethodID(env, clazz, "helloFromJNI", "()V");
    
    // 增加someInteger
    jint value = (*env)->GetStaticIntField(env, clazz, someIntegerId);
    (*env)->SetStaticIntField(env, clazz, value + 1);
    
    // 调用helloFromJNI
    (*env)->CallStaticVoidMethod(env, clazz, helloFromJNIId);
}

为了性能的原因,你不希望每次需要访问字段或者调用方法的时候去获取成员变量或者方法的id。字段和方法的id当类被VM加载的时候被设置,在类被存在的整个过程中有效。如果类被VM卸载并且重新加载,新的id可能和老的不同。也就是说,一个有效的方法是当类被加载的时候获取id,即在静态初始化模块。如Listing 2-22所示。

Listing 2-22 仅获取Field/Method id一次

// Java (in MyClass.java)

public class MyClass {
    static {
        System.loadLibrary("mylib");
        getIds(); // 仅当类加载的时候获取一次id
    }
    
    public static int someInteger = 0;
    
    public static native void sayHelloToJNI();
    
    public static void helloFromJNI() {
        Log.i("MyClass", "Greetings! someInteger=" + someInteger);
    }
    
    private static native void getIds();
}

// JNI glue layer (in C file)

static jfieldID someIntegerId;
static jfieldID helloFromJNIId;

void JNICALL
Java_com_apress_proandroid_MyClass_sayHelloToJNI(JNIEnv *env, jclass clazz) {
    // 不需要在这里获取id
    
    // 增加someInteger
    jint value = (*env)->GetStaticIntField(env, clazz, someIntegerId);
    (*env)->SetStaticIntField(env, clazz, value + 1);
    
    // 调用helloFromJNI
    (*env)->CallStaticVoidMethod(env, clazz, helloFromJNIId);
}

void JNICALL
Java_com_apress_proandroid_MyClass_getIds(JNIEnv *env, jclass clazz) {
    // 获取someInteger字段和helloFromJNI方法的id
    someIntegerId = (*env)->GetStaticFieldID(env, clazz, "someInteger", "I");
    helloFromJNIId = (*env)->GetStaticMethodID(env, clazz, "helloFromJNI", "()V");
}

JNI定义了许多的函数可以用来访问成员变量和调用方法。比如,访问一个整型变量和一个布尔型变量是两个操作,由两个不同的函数完成。类似的,定义了不同的函数去调用静态方法和非静态方法。

NOTE:参考JNI在线文档和NDK的jni.h头文件查看你可以使用的完整的函数列表。

Android定义了自己的函数和数据结构去访问大多数native代码使用到的类。比如,在android/bitmap.h(在NDK release 4b中引入)中定义的API允许访问bitmap对象的pixel buffer:
(1) AndroidBitmap_getInfo
(2) AndroidBitmap_lockPixels
(3) AndroidBitmap_unlockPixels


NDK 5引入了许多新的API,应用开发者可以在native代码中使用去访问部分java framework,而不需要依赖于JNI特性(比如, JNIEnv, jclass, jobject)。

Native Activity

现在我们已经看到了怎样在一个应用中混合使用Java和C/C++代码。Android 2.3更进一步定义了NativeActivity类,允许你用C/C++实现整个应用但是并不强制你去做,因为你仍然需要通过JNI访问整个的Android Java Framework。

NOTE:应用不需要所有的activity都使用NativeActivity。比如,你可以写一个两个activity的应用:一个NativeActivity,一个ListActivity。

如果你选择读原文件或者头文件代替更多的正式文档,你是在享受。事实上,大多数在头文件中提到的文档,你可以在NDK的platforms/android-9/arch-arm/usr/include/android路径下找到。Table 2-7列出了这些头文件。

Table 2-7 Native Activities使用的头文件



创建一个native activity非常简单。第一步是定义应用的manifest文件,让Android知道你要使用一个native activity。对每一个native activiy,你需要在应用的AndroidManifest.xml中指定两个事情:
(1) 需要实例化的类
(2) 需要加载的库和它的入口

第一项和其他非native的activity没什么不同。当activity创建后,Android需要去实例化正确的类,这是为什么android:name在<activity>标签里的原因。在大多数情况下,不需要你的应用去继承NativeActivity类,所以你需要使用android.app.NativeActivity类作为实例化的类。没有什么阻止你实例化一个你创建的类继承自NativeActivity。

第二项用来让Android知道哪个库包含你的native代码,这样可以在activity创建的时候自动
的加载。这条给安卓的metadata信息以键值对的方式:name需要设置为android.app.lib_name,value指定library的名字不要带有lib前缀或者.so扩展名。可选的,你同样可以以键值对的方式指定库的entry point,name设置为android.app.func_name,value设置为函数名。默认情况下,函数名字被设置为ANativeActivity_onCreate。

Listing 2-23给出了manifest文件的一个示例。最小的SDK version被设置为9,因为NativeActivity在Android 2.3被引入,activity的native代码在libmyapp.so中。

Listing 2-23 Native应用的AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.apress.proandroid"
    android:versionCode="1"
    android:versionName="1.0">
    
    <uses-sdk android:minSdkVersion="9" />
    
    <application android:icon="@drawable/icon"
                 android:label="@string/app_name"
                 android:hasCode="false">
        <activity android:name="android.app.NativeActivity"
                  android:lable="@string/app_name">
            <meta-data android:name="android.app.lib_name"
                       android:value="myapp" />
            <meta-data android:name="android.app.func_name"
                       android:value="ANativeActivity_onCreate" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

NOTE: 可选的,如果你的应用不含有任何的Java代码,你可以在<application>标签中设置android:hasCode为false。

加载这个应用将会导致一个Runtime错误,因为libmyapp.so还不存在。结果,下一步是去编译这个丢失的库。通过NDK的ndk-build工具完成。

编译丢失的库

你需要定义Application.mk文件和Android.mk文件。当使用native activity,Android.mk有不同,如Listing 2-24所示。你同样需要包含应用的实际实现一个文件,myapp.c(在Listing 2-25)。

Listing 2-24 Native应用的Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := myapp
LOCAL_SRC_FILES := myapp.c
LOCAL_LDLIBS := -llog -landroid
LOCAL_STATIC_LIBRARIES := android_native_app_glue

include $(BUILD_SHARED_LIBRARY)

$(call import-module, android/native_app_glue)

这个Android.mk和前面我们使用的不同在:
(1) 连接的shared libraries不同
(2) 连接的static library不同

因为你的native activity使用Android native应用API,你需要添加-landroid到LOCAL_LDLIBS。依赖于你要使用什么,你需要去连接更多的库。比如,-llog用来连接logging库,允许你使用logcat调试机制。


Android NDK提供了更简单的方式去创建一个native应用,在NDK的native_app_glue模块实现。使用这个模块,你不仅需要把它添加到LOCAL_STATIC_LIBRARIES,同样需要在你的工程中import,通过import-module功能宏,如Listing 2-24最后一行那样。

NOTE:native_app_glue模块在NDK实现,源代码的位置在android-ndk-r7/sources/android/native_app_glue目录下。你可以自由的修改实现并且使用你自己修改的版本编译你的应用,因为库是静态链接的。

Listing 2-25给出了应用的一个示例,在myapp.c中实现,监听如下的事件:
(1) Application events (类似于Java的onStart方法)
(2) Input events(key, motion)
(3) Accelerometer
(4) Gyroscope(callback-based)

这个应用除了建立sensors和告诉你如何处理sersor事件没有做任何其他的事情。在这个特殊的例子中,sersor的值通过调用_android_log_print显示。使用这个应用作为你自己需要的骨架。


Listing 2-25 myapp.c的实现

#include <android_native_app_glue.h>
#include <android/sensor.h>
#include <android/log.h>

#define TAG "myapp"

typedef struct {
    // 加速计
    const Asensor* accelerometer_sensor;
    AsensorEventQueue* accelerometer_event_queue;
    // 陀螺仪
    const Asensor* gyroscope_sensor;
    ASensorEventQueue* gyroscope_event_queue;
} my_user_data_t;

static int32_t on_key_event (struct android_app* app, AInputEvent* event)
{
    // 使用AKeyEvent_xxx API
    return 0; // 或者1,如果已经处理了这个事件
}

static int32_t on_motion_event(struct android_app* app, AInputEvent* event) 
{
    // 使用AMotionEvent_xxx API
    return 0; // 或者1,如果已经处理了这个事件
}

// 简单的检测事件类型,调用合适的函数
static int32_t on_input_event(struct android_app* app, AInputEvent* event)
{
    int32_t type = AInputEvent_getType(event);
    int32_t handled = 0;
    
    switch(type) {
        case AINPUT_EVENT_TYPE_KEY:
            handled = on_key_event(app, event);
            break;
        
        case AINPUT_EVENT_TYPE_MOTION:
            handled = on_motion_event(app, event);
            break;
    }
    
    return handled;
}

// 一些函数还没有实现
static void on_input_changed (struct android_app* app) {}
static void on_init_window (struct android_app* app) {}
static void on_term_window (struct android_app* app) {}
static void on_window_resized (struct android_app* app) {}
static void on_window_redraw_needed (struct android_app* app) {}
static void on_content_rect_changed (struct android_app* app) {}

// 在这里开启传感器
static void on_gained_focus (struct android_app* app)
{
    my_user_data_t* user_data = app->userData;
    
    if (user_data->accelerometer_sensor != NULL) {
        ASensorEventQueue_enableSensor(user_data->accelerometer_event_queue, user_data->accelerometer_sensor);
        ASensorEventQueue_setEventRate(user_data->accelerometer_event_queue, user_data->accelerometer_sersor, 1000000L/60);
    }
    
    if (user_data->gyroscope_sensor != NULL) {
        ASensorEventQueue_enableSensor(user_data->gyroscope_event_queue, user_data->gyroscope_sensor);
        ASensorEventQueue_setEventRate(user_data->gyroscope_event_queue, user_data->gyroscope_sensor, 1000000L/60);
    }
}

// 当失去焦点的时候关闭传感器
static void on_lost_focus (struct android_app* app)
{
    my_user_data_t* user_data = app->userData;
    
    if (user_data->accelerometer_sensor != NULL) {
        ASensorEventQueue_disableSensor(user_data->accelerometer_event_queue, user_data->accelerometer_sensor);
    }
    
    if (user_data->gyroscope_sensor != NULL) {
        ASensorEventQueue_disableSensor(user_data->gyroscope_event_queue, user_data->gyroscope_sensor);
    }
}

// 在这里实现更多的函数
static void on_config_changed (struct android_app* app) {}
static void on_low_memory (struct android_app* app) {}
static void on_start (struct android_app* app) {}
static void on_resume (struct android_app* app) {}
static void on_save_state (struct android_app* app) {}
static void on_pause (struct android_app* app) {}
static void on_stop (struct android_app* app) {}
static void on_destroy (struct android_app* app) {}

// 简单的检测指令并调用正确的函数
static void on_app_command (struct android_app* app, int32_t cmd) {
    switch (cmd) {
        case APP_CMD_INPUT_CHANGED:
            on_input_changed(app);
            break;
        case APP_CMD_INIT_WINDOW:
            on_init_window(app);
            break;
        case APP_CMD_TERM_WINDOW:
            on_term_window(app);
            break;
        case APP_CMD_WINDOW_RESIZED:
            on_window_resized(app);
            break;
        case APP_CMD_WINDOW_REDRAW_NEEDED:
            on_window_redraw_needed(app);
            break;
        case APP_CMD_CONTENT_RECT_CHANGED:
            on_content_rect_changed(app);
            break;
        case APP_CMD_GAINED_FOCUS:
            on_gained_focus(app);
            break;
        case APP_CMD_LOST_FOCUS:
            on_lost_focus(app);
            break;
        case APP_CMD_CONFIG_CHANGED:
            on_config_changed(app);
            break;
        case APP_CMD_LOW_MEMORY:
            on_low_memory(app);
            break;
        case APP_CMD_START:
            on_start(app);
            break;
        case APP_CMD_RESUME:
            on_resume(app);
            break;
        case APP_CMD_SAVE_STATE:
            on_save_state(app);
            break;
        case APP_CMD_PAUSE:
            on_pause(app);
            break;
        case APP_CMD_STOP:
            on_stop(app);
            break;
        case APP_CMD_DESTROY:
            on_destroy(app);
            break;
    }
}


// 使用定义的looper id
#define LOOPER_ID_USER_ACCELEROMETER (LOOPER_ID_USER + 0)
#define LOOPER_ID_USER_GYROSCOPE     (LOOPER_ID_USER + 1);

// 将可以一次最多获取8个事件
#define NB_SENSOR_EVENTS 8

static int gyroscope_callback (int fd, int events, void* data) {
    // 在这里log所有事情不是好的想法,因为可能会比你期望的更多
    __android_log_write(ANDROID_LOG_INFO, TAG, "gyroscope_callback");
    return 1;
}

static void list_all_sensors (ASensorManager* sm) {
    ASensorList list;
    int i, n;
    n = ASensorManager_getSensorList(sm, & list);
    
    for (i=0; i<n; i++) {
        const ASensor* sensor = list[i];
        const char* name = ASensor_getName(sensor);
        const char* vendor = ASensor_getVendor(sensor);
        int type = ASensor_getType(sensor);
        int min_delay = ASensor_getMinDelay(sensor);
        float resolution = ASensor_getResolution(sensor);
        
        __android_log_print(ANDROID_LOG_INFO, TAG, "%s (%s) %d %d %f", name, vendor, type, min_delay, resolution);
    }
}

// 入口点
void android_main (struct android_app* app)
{
    my_user_data_t user_data;
    ASensorManager* sm = ASensorManager_getInstance();
    
    app_dummy(); // 不要忘了这个调用
    
    // 简单的列出设备上的传感器
    list_all_sensors(sm);
    
    state->userData = & user_data;
    state->onAppCmd = on_app_command;
    state->onInputEvent = on_input_event;
    
    // 加速计
    user_data.accelerometersensor = ASensorManager_getDefaultSensor(sm, ASENSOR_TYPE_ACCELEROMETER);
    user_data.accelerometer_event_queue = ASensorManager_createEventQueue(sm, state->looper, LOOPER_ID_USER_ACCELEROMETER, NULL, NULL);
    
    // 陀螺仪 (callback-based)
    user_data.gyroscope_sensor = ASensorManager_getDefaultSensor(sm, ASENSOR_TYPE_GYROSCOPE);
    user_data.gyroscope_event_queue = ASensorManager_createEventQueue(sm, state->looper, LOOPER_ID_USER_GYROSCOPE, gyroscope_callback, NULL);
    
    while (1) {
        int ident;
        int events;
        struct android_poll_source* source;
        
        while ((ident = ALooper_pollAll(-1, NULL, &events, (void**)&source)) >= 0) {
            // "standard"事件优先
            if ((ident == LOOPER_ID_MAIN) || (ident == LOOPER_ID_INPUT)) {
                // source should not be NULL but we check anyway
                if (source != NULL) {
                    // 这将调用on_app_command或者on_input_event
                    source->process(source->app, source);
                }
            }
            
            // 加速计事件
            if (ident == LOOPER_ID_USER_ACCELEROMETER) {
                ASensorEvent sensor_events[NB_SENSOR_EVENTS];
                int i, n;
                while (( n = ASensorEventQueue_getEvents(user_data.accelerometer_event_queue, sersor_events, NB_SENSOR_EVENTS)) > 0) {
                    for (i=0; i<n; i++) {
                        ASensorVector* vector = &sensor_events[i].vector;
                        __android_log_print(ANDROID_LOG_INFO, TAG, "%d accelerometer x=%f y=%f z=%f", i, vector->x, vector->y, vector->z);
                    }
                }               
            }
            
            // 在这里处理其他事件
            
            // 不要忘记检测是否到了返回的时间
            if (state->destroyRequested != 0) {
                ASensorManager_destroyEventQueue(sm, user_data.accelerometer_event_queue);
                ASensorManager_destroyEventQueue(sm, user_data.gyroscope_event_queue);
                return;
            }
        }
        
        // 所有的事情处理完之后在这里做渲染
    }
}

替代方式

创建native应用的另一种方式是去实现native版本的onCreate(),在这里你不仅初始化你的应用,同样定义所有的其他callbacks(即等同于onStart, onResume, 。。。。)。这是native_app_glue模块实现用来简化你的开发。同样的,native_app_glue模块保证这些事件在一个单独的线程处理,允许你的应用保持响应。定义你自己的onCreate实现,不需要连接native_app_glue库,需要实现ANativeActivity_onCreate而不是android_main。如Listing 2-26所示。

Listing 2-26 ANativeActivity_onCreate的实现

#include <android/native_activity.h>

void ANativeActivity_onCreate (ANativeActivity* activity, void* savedState, size_t savedStateSize)
{
    // 在这里设置所有的回调
    activity->callbacks->onStart = my_on_start;
    activity->callback->onResume = my_on_resume;
    ...
    
    // 设置activity->instance为实例特有的数据
    activity->instance = my_own_instance; // 和userData相似
    
    // 这里没有事件循环,简单的返回,NativeActivity接着调用你定义的回调
}

这看起来是简单的,在你开始监听其他事件和在屏幕上画东西的时候将会变得复杂(比如sensors)。

TIP:如果你使用native_app_glue模块,不要在你的manifest文件中修改库的入口点名字,因为它实现了ANativeActivity_onCreate。

新的NativeActivity类本身不会提升性能。它只是简化了创建native应用开发的机制。实际上,你可以在老的安卓版本自己的应用上实现同样的机制用来写native应用。尽管你的应用可以全部用C/C++写,它仍然运行在Dalvik虚拟机上,它依然依赖于NativeActivity Java类。


总结

我们已经看到如何使用native代码可以提升性能。尽管精心打造的原生代码很少导致性能下降,性能不是你使用NDK的唯一原因。如下是使用NDK的好的原因:
(1) 你希望去复用存在的代码而不需要用Java去重写
(2) 你希望代码在其他不支持Java的平台上使用
(3) 你希望在不支持JIT编译器的老的安卓设备上使用(Android 2.1或者更早),这种情况下原生代码是提供一个好的用户体验的唯一方式
(4) 在应用中使用原生代码使得用户体验更好,即使安卓设备存在JIT编译器


前两个原因很重要,实际上你可能在某些情况下牺牲性能(确保用户体验不受影响,或者在一个可接受的阈值内)。像许多开发者一样,你的资源是有限的,你希望你的应用能够尽量多的人使用。把自己局限于一个单一的平台将无法最大化您的投资。

Android开发初期之后怎么提升?怎么才能叫精通?方向在哪? 1. Android开发初期之后怎么提升? 首先第一个问题:Android开发初期之后怎么提升?说到这个问题,就不得不先搞清楚 “为什么很多人会一直停留在两三年的水平上,而后一直在重复以往的经验,原地踏步?” 我认为最主要的一个原因就是这部分开发者缺乏主观能动性,或者说缺乏能支撑你坚持学习的兴趣。如果你对Android开发没有太大的兴趣,那么还是尽早换方向吧。有了兴趣,然后就是要有一个比较正确的钻研路线,不要这也搞那也抓,最后什么都知道点,但也就只是知道点儿。 一个很好的例子就是我们平时在开发过程中都会用 阅读详情

相关推荐

Android/JAVA性能优化:从入门到精通(万字讲解!!!)

在当今快速发展的移动应用市场中,用户体验是决定应用成功与否的关键因素之一。无论是在Android还是其他平台上,应用的性能表现直接影响用户的满意度和应用的市场竞争力。作为开发者,掌握Java性能优化的技巧不仅能够提升应用的流畅度,还能延长应用的生命周期,从而在激烈的市场竞争中脱颖而出。本文将从内存管理、线程与并发、代码优化、网络优化、数据库优化等多个方面,深入探讨Java性能优化的策略和技巧,帮助开发者在实际开发中提升应用性能,优化用户体验。提示:以下是本篇文章正文内容,下面案例可供参考。

学无止境 1222

Android 开发学习(四)

1. Fragment 动态添加和管理 2. Activity 与 Fragment通信(原生方案 Bundle) 3. 动态添加Fragment 5个步骤 4. Activity 与 Fragment通信(Java语言中类与类自己通信常用方法:接口) 5. Fragment生命周期 6. ViewPager2的使用(场景:页面来回切换) 7. Fragment 与 ViewPager 联合操作(经常使用!) 8. 什么是Activity? 9. Activity的创建 10. Activity之间的跳转

探索技术与实践的旅程,分享编程经验与工具使用心得,助力开发者提升技能。 1329

从零到精通:Android开发学习路径全解析

Android开发作为移动互联网核心技术之一,其技术栈经历了从基础SDK到现代架构的演进。理解Android系统架构需要扎实的Java/Kotlin基础,掌握Activity生命周期、UI组件等核心概念。在进阶阶段,开发者需要关注性能优化、架构设计等关键技术,如内存管理、MVVM模式实现等。通过项目实践和源码阅读,开发者可以深入理解Binder机制、AMS等底层原理。本文以知名博主stormzhang的学习路径为例,系统梳理了从Java基础到Flutter跨平台的完整学习框架,为移动开发者提供了一条清晰的技

weixin_34326558的博客 268

AndroidManifest.xml详解

我们在进行APP开发的时候都会遇到一个文件:AndroidManifest.xml。从刚开始进行Android开发,到现在已经过去了几个月,还是对这个文件一知半解,只知道它是配置用的。但是这文件里的东西具体有什么用,该怎么用一直都没有理解。借着做项目的机会,仔细研究一下这个文件。 研究AndroidManifest.xml最好的方式自然就是对照着官方文档详细理解项目中每个字段的作用,并且做出一些修...

lovemaitian的博客 999

android游戏开发从入门到精通,androidstudio插件开发

如果基础知识有太多不会的就危险了,必须好好准备,这是躲不过的。这个阶段有不会的很正常,因为对方正在确定你的水平,但态度一定要积极主动,即使之前没有研究过,也要结合一些经验说出推测的结果,切忌直接说“我不会”。我只贴上我写过内容的链接,剩下的朋友们去网上一搜,或者去买本书看下就好了,建议看 《Java 编程思想》,不仅解答了 What 还有探讨 Why。我这次面试算法问的比较少,只手写过插入排序,建议把冒泡、插入、归并时间复杂度和实现熟悉熟悉,最好手写下,不然现场手写的时候容易出错。

2401_84408418的博客 1419

Android面试指南与参考答案——基于领星安卓开发职位要求

本文基于领星安卓开发职位要求,整理Android开发岗位常见面试问题与参考答案。核心内容包括:1.基础知识:Android四层架构、SDK组件及API使用;2.编程语言:Kotlin优势、Jetpack组件应用及MVVM/MVP架构对比;3.开发工具:性能优化方法及版本适配策略;4.实战经验:第三方SDK集成和APP发布流程。针对1-3年经验者及应届生,提供代码示例和项目实例说明,覆盖框架搭建、性能优化等关键技术点,帮助候选人系统准备面试,突出技术能力和问题解决思维。

郑伟强dev的专栏 1192

Android13 App 预装详解

* Android13系统App权限配置 * Android13系统签名 * Android13系统开发实用工具

老皮的博客 7298

台积电技术研发实力

台积电技术研发实力 台积电在半导体行业的地位毋庸置疑。他们究竟有多强大,大部分读者了解得可能非常片面。从最新公布的2019年财报里,看看台积电的真正实力。 一、台积电在市场上的地位 根据公司财报,台积公司在先进制程技术、特殊制程技术,以及先进封装技术的发展上持续领先全球集成电路制造服务领域,2019年的市场占有率为52%。台积公司总体营收以地区划分(主要依据客户营运总部所在地),来自北美市场的营收占台积公司总体营收的60%、日本与中国大陆以外的亚太市场占9%、中国大陆市场占20%、欧洲、中东及非洲市场占6

吴建明wujianming_110117 1900

Android7.1 使用ToolBar, XML文件报错 androidx.appcompat.widget.Toolbar, was not found in the project解决方案

完整报错信息: Class referenced in the layout file, androidx.appcompat.widget.Toolbar, was not found in the project or the libraries Inspection info:If a class is referenced in the manifest or in a layout file, it must also exist in the project (or in one of the

算法、原理、设计模式、非线性控制、深度强化学习 3164

手把手教你用Simulink+CANoe+Carsim搭建硬件在环仿真环境(避坑指南)

本文是一份详细的硬件在环仿真环境搭建指南,重点讲解如何将Simulink、CANoe和CarSim三大工具进行实战融合。内容涵盖软件版本兼容性、授权避坑、环境配置,并分步演示了从离线联合仿真到实现软实时同步的完整流程,旨在帮助汽车电子工程师高效构建稳定的HIL测试平台。

weixin_29092579的博客 403

SAP SM30数据维护实战:条件筛选与动态校验的实现技巧

本文详细介绍了SAP SM30数据维护的实战技巧,重点讲解如何通过条件筛选实现精准数据查取,以及利用表维护生成器事件实现动态数据检查与校验。文章提供了从基础视图创建到高级事件编程的完整流程,包括调用函数VIEW_MAINTENANCE_CALL传递筛选条件,以及在NEW_DATA和CHANGE_SAVE事件中编写业务规则校验逻辑,帮助开发者提升SM30的管控能力与数据质量。

q9w8e7r6t5的博客 521

Android从入门到精通

全书共分15章,内容包括Android快速入门、Android模拟器与常用命令、用户界面设计、高级用户界面设计、基本程序单元Activity、Android应用核心Intent、Android事件处理、资源访问、图形图像处理技术、多媒体应用开发、Content Provider实现数据共享线程与消息处理、Service应用、网络编程及Internet应用和基于Android的家庭理财通。[Android.游戏开发入门](美)Mario.Zechner.插图版.pdf。

github_38514857的博客 878

LFM线性调频信号的MATLAB仿真

线性调频(LFM)是一种不需要伪随机编码序列的扩展频谱调制技术。由于线性调频信号占用的频带宽度远大于信息带宽,所以也可以获得很大的系统处理增益。线性调频信号又称鸟声(Chirp)信号,因为其频谱带宽落于可听范围,则听若鸟声,所以又称Chirp扩展频谱(CSS)技术。LFM技术在雷达、声纳技术中有广泛应用,如在雷达定位技术中,它可在增大射频脉冲宽度、提高平均发射功率、加大通信距离同时又保持足够的信号频谱宽度,不降低雷达的距离分辨率。线性调频(LFM)是一种不需要伪随机编码序列的扩展频谱调制技术。

平时工作较为繁忙,私信消息一般晚上回复,谢谢大家~~ 902

精通 Kotlin 安卓开发(五)

在这一简短但重要的章节中,我们强调了关于维护和实现良好应用性能和响应能力的重要要点。这些建议在应用程序优化中至关重要。因此,如果您的应用程序不遵循这些规则,您必须相应地进行优化。通过完成这一章,我们涵盖了您开发 Android 应用程序所需的一切。在下一章中,我们将对其进行测试。准备好编写一些单元测试和仪器测试!在本章中,我们学习了如何为我们的应用程序编写和运行测试。这是迈向生产的又一步。我们建立了一个书写良好且无 bug 的产品。很快,我们将实际发布它。请耐心等待,因为那一刻即将到来!

龙哥盟 652

C# 与华中数控设备通信:数控机床自动化控制与实时监测系统

华中数控系统与C#通信开发指南 核心要点 通信基础 支持串口(RS232/RS485)和以太网两种连接方式 协议选择:通用Modbus协议或华中自定义HNC协议 关键数据映射表明确各参数对应的寄存器地址和功能码 系统架构 四层架构:设备层→通信层→业务层→应用层 包含实时数据采集、指令下发、状态监控、异常处理全流程 支持Modbus和HNC协议动态切换 代码实现 串口通信使用NModbus库简化开发 提供连接管理、数据读取、指令下发完整示例 包含异常处理和超时重连机制 应用价值 实现数控机床远程监控和自动化

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 2015

DISM进行系统修复,进度条卡在63.2%的解决方法

一时用不了软件,安装修复的时候也出现问题 DISM一直卡在63.2%,等了好久时间,直接强制退出寻找最终的解决方案

码农研究僧的博客 1万+

想学Android开发,是否必须精通Java?

想学Android开发,必然需要先精通Java,其次c++,再后面是kotlin,基本代码上系统源码framework层面都是java和c++,基本上找不到任何kotlin,源码中只有几个简单app使用是kotlin。在这里,我为大家整理了一些列Android学习资料,有需要的朋友可以扫描下方二维码,获取更多相关资料。

Eqiqi的博客 1988

【GameFramework框架内置模块】6、实体(Entity)

实体Entity就是一个实体的基本单元,类似于Unity的GameObject对象本体,然后这个本体上面附加其他各种各样组件,才能实现各种各样的功能。一般需要在游戏运行时动态加载的游戏对象,比如怪物、武器、子弹、特效,都需要给这个物体挂载一个实体Entity,这样的话就可以方便的对实例出来的对象进行管理。下面就来看一下如何使用吧。

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 3136

android性能优化从入门到精通

什么是性能优化 一款app除了要有强大的功能和交互外,在性能上也应该追求更高的要求,这样才能更好的提高用户体验 优化目的 性能指标 优化的方向 更快 流畅性 启动速度页面响应速度响应速度 更稳定 稳定性 避免出现应用崩溃(crash)避免出现应用无响应(ANR) 更省 资源节省性 内存大小安装包大小耗电量网络流量 优化详解 速度优化 内存优化 数据结构优化:数据结...

qq_23081779的博客 1765

使用verilog设计实现QR分解

QR(正交三角)分解法是求一般矩阵全部特征值的最有效并广泛应用的方法,一般矩阵先经过正交相似变化成为Hessenberg矩阵,然后再应用QR方法求特征值和特征向量。 使用verilog对QR实现方法进行设计并进行功能仿真: 如下: 顶层模块代码: module TOP( H1R, H1I, H2R, H2I, H3R, H3I, H4R, H4I, clk, rst_n, Y1R, Y1I, Y2R, Y2I, X1R, X1I, X2R, X2I ); parameter width = 16; // I

QQ_778132974的博客 2149

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Android性能优化:从入门到精通

本文从基础知识、优化方法和实战案例分析三个方面,详细介绍了Android性能优化的方法和技巧。性能优化是Android开发中不可或缺的一部分,对提升用户体验具有重要意义。开发者应掌握性能优化的基础知识,合理运用优化方法,以实现应用的高性能表现。未来,随着移动设备的性能不断提升,性能优化将更加重要。开发者需要关注新的优化技术和工具,不断更新优化策略,以满足用户对高性能应用的需求。同时,性能优化也需要与用户需求相结合,避免过度优化,确保应用的平衡发展。持续关注新技术。

2501_92486623的博客 357

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

QtScrcpy安卓投屏完整攻略:从入门到精通

还在为手机屏幕太小而烦恼吗?想要在大屏幕上畅玩手游却不知从何下手?QtScrcpy这款开源安卓投屏工具正是你需要的解决方案!它能将手机屏幕实时投射到电脑,支持键盘鼠标操作,让你彻底告别触摸屏的局限。 ## 🎯 为什么选择QtScrcpy? **传统投屏痛点**: - 连接复杂,步骤繁琐 - 延迟高,画面卡顿 - 不支持键鼠操作 - 无法多设备管理 **QtScrcpy优势对比**: ✅ *

gitblog_00510的博客 3万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Android面试官:你究竟有多大的勇气,在简历上写了“精通”?

自定义 View 可以让我们产生更好的效果,app UI 美观,交互界面友好,用户青睐,用户留存率高,这就是app成功的第一步了。高级UI与自定义View有着不可分割的联系。自定义View其实并不难,为什么呢?因为自定义View无非就是显示+交互:显示由 onMeasure ->onLayout->onDraw的整个流程就将显示整个流程处理完了,而交互则是touch事件的处理;另外其他的一切你比如Canvas,Animation,Paint都是围绕着draw来进行的,所以我们需要搞懂其中的根本原理。

the shy 127

零基础玩转Windows Subsystem for Android:从安装到精通的实用指南

Windows Subsystem for Android(简称WSA)是微软推出的革命性功能,它就像在Windows 11系统中搭建了一座通往Android世界的桥梁????,让你的电脑无需复杂配置就能直接运行手机应用。想象一下,你可以在电脑上刷短视频、使用生产力APP,甚至畅玩安卓游戏,所有操作都在熟悉的Windows环境中完成。对于开发者而言,这更是测试安卓应用的利器;对于普通用户,它则打破了设...

gitblog_01031的博客 1645

3分钟精通安卓虚拟相机:从零配置到多场景实战

想要在安卓设备上实现摄像头内容的灵活替换吗?VCAM虚拟相机为您提供了完美的解决方案。这款基于Xposed框架的安卓虚拟相机支持安卓5.0及以上系统,能够为直播、视频会议、隐私保护等场景创建虚拟摄像头接口,让您的手机摄像体验更加丰富多彩! ## 🎯 基础入门:环境准备与快速启动 ### 必备条件清单 在开始使用VCAM虚拟相机前,请确保您的设备满足以下基本要求: - ✅ 已安装Xposed

gitblog_00738的博客 666
上一篇: 精通安卓性能优化-第二章(四)
下一篇: 精通安卓性能优化-第三章(一)
北冥_有鱼
博客等级 码龄19年 14粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值