zoukankan      html  css  js  c++  java
  • Android按键添加和处理的方案【转】

    本文转载自:http://www.cnblogs.com/skywang12345/p/3142851.html

    Android按键添加和处理的方案

     版本号 说明 作者 日期 
    1.0 

    Android按键添加和处理的方案

    Sky Wang  2013/06/18 
           

    需求:Android机器上有个Wifi物理按键,现在需求通过点击“wifi物理按键”能够快速的开启/关闭wifi。

    实现方案
    经过思考之后,拟出下面几种方案:
    方案一,在linux kernel的驱动中捕获“wifi物理按键”。在kernel的按键驱动中截获“wifi”按键,并对其进行处理:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。
    方案二,在Android中添加一个服务,监听wifi按键消息。若监听到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。
    方案三,在Android的input输入子系统的框架层中捕获wifi按键,并进行相应处理。若捕获到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。


    方案一

    方案思路: 在linux kernel的驱动中捕获“wifi物理按键”。在kernel的按键驱动中截获“wifi”按键,并对其进行处理:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。

    方案分析: 若采用此方案需要解决以下问题
    01,在kerne的按键驱动中捕获“wifi”按键。
    -- 这个问题很好实现。在kernel的按键驱动中,对按键值进行判断,若是wifi按键,则进行相应处理。
    02,在kernel中读取并设置wifi的开/关状态。
    -- 这个较难实现。因为wifi驱动的开/关相关的API很难获取到。一般来来说,wifi模组的驱动都是wifi厂家写好并以.ko文件加载的。若需要获取wifi的操作API,需要更厂家一起合作;让它们将接口开放,并让其它设备在kernel中可以读取到。
    03,在kernel中将wifi的状态上报到Android系统中。若单单只是实现02步,只是简单的能开/关wifi了;但还需要向办法让Android系统直到wifi的开/关行为。
    -- 可以实现,但是太麻烦了。

    方案结论: 实现难度太大!


    方案二

    方案思路: 在Android中添加一个服务,监听wifi按键消息。若监听到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。

    方案分析: 若采用此方案需要解决以下问题
    01,将kernel的wifi按键上传到Android系统中。
    -- 这个可以实现。首先,我们将wifi按键映射到一个sys文件节点上:按下wifi按键时,sys文件节点的值为1;未按下wifi按键时,sys文件节点的值为0。其次,通过NDK编程,读取该sys文件节点,并将读取的接口映射注册到JNI中。最后,通过JNI,将该接口对应注册到Android系统中,使应用程序能够读取该接口。
    02,在Android系统中添加一个服务,不断读取wifi按键状态。
    -- 这个也可以实现。由于“01”中,我们已经将wifi的按键状态通过JNI注册到Android系统中;我们这里就可以读取到。
    03,读取并设置wifi的开/关状态。
    -- 这个也可以实现。在Android系统中,我们可以通过WifiManager去读取/设置wifi的开/关状态。通过WifiManager设置的wifi状态,是全局的。

    架构图:

     

    具体实现:
    通过驱动,将wifi按键状态映射到文件节点。由于不同平台差异,具体的代码接口可能有所差异;我所工作的平台是RK3066,所以还是以此来进行介绍。

    01 将kernel的wifi按键上传到Android系统中

    在按键驱动中编辑wifi按键的驱动:主要的目的是将wifi按键映射到某个键值上,方便后面Android系统调用。因为Android系统使用的按键值和Linux内核使用的按键值不一样,Android会通过一个键值映射表,将Linux的按键值和Android的按键值映射起来。

    我们的项目中,wifi按键是通过ADC值来捕获的,而不是中断。下面是“wifi按键相关信息”,代码如下:

    复制代码
    static struct rk29_keys_button key_button[] = { 
    
        ...
        // 将 wifi 开关按键定义为KEY_F16,
        // 处理时,捕获KEY_F16进行处理即可。
        {   
            .desc   = "wifi",
            .code   = KEY_F16,
            .adc_value  = 4,
            .gpio = INVALID_GPIO,
            .active_low = PRESS_LEV_LOW,
        },  
        ...
    };
    复制代码

    从中,我们可以看出wifi的adc值大概是4,它所对应的按键值(即code值)是KEY_F16。
    这里,KEY_F16是我们自己定义的(因为linux中没有wifi开关按键),你也可以定义为别的值。记得两点:一,这里的所定义的wifi的code,必须和Android中要处理的按键值(后面会讲到)保持一致;二,不要使用系统中已用到的值。另外,KEY_F16的值为186,可以参考“include/linux/input.h”文件去查看。


    在按键驱动中,会将key_button注册到系统中。在按键驱动中,我们将下面的callback函数注册到adc总线上;adc驱动会通过工作队列,判断的读取adc值,并调用callback,从而判断是否有响应的按键按下。下面是callback函数:

    复制代码
    static void callback(struct adc_client *client, void *client_param, int result)
    {
        struct rk29_keys_drvdata *ddata = (struct rk29_keys_drvdata *)client_param;
        int i;
    
        if(result < EMPTY_ADVALUE)
            ddata->result = result;
    
        // 依次查找key_button中的按键,判断是否需要响应
        for (i = 0; i < ddata->nbuttons; i++) {
            struct rk29_button_data *bdata = &ddata->data[i];
            struct rk29_keys_button *button = bdata->button;
            if(!button->adc_value)
                continue;
            int pre_state = button->adc_state;
            if(result < button->adc_value + DRIFT_ADVALUE &&
                result > button->adc_value - DRIFT_ADVALUE) {
    
                button->adc_state = 1;
            } else {
                button->adc_state = 0;
            }   
            // 同步按键状态
            synKeyDone(button->code, pre_state, button->adc_state); 
    
            if(bdata->state != button->adc_state)
                mod_timer(&bdata->timer,
                    jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
        }   
        return;
    }
    复制代码

    前面已经说过,这个callback会不断的被adc检测的工作队列调用。若检测到adc值在“某按键定义的adc值范围”内,则该按键被按下;否则,没有按下。
    下面是synKeyDone()的代码:

    复制代码
    static void synKeyDone(int keycode, int pre_status, int cur_status) 
    {
        if (cur_status == pre_status)
            return ;
              
        if (keycode==KEY_F16)
            set_wifikey(cur_status);     
    }
    复制代码

    它的作用是同步wifi按键按下状态,根据wifi按键状态,通过set_wifikey()改变对应wifi节点状态。
    例如:wifi键按下时,sys/devices/platform/misc_ctl/wifikey_onoff为1; wifi未按下时,sys/devices/platform/misc_ctl/wifikey_onoff为0。

    set_wifikey()本身以及它相关的函数如下:

    复制代码
    // 保存按键状态的结构体
    typedef struct  combo_module__t {
        unsigned char           status_wifikey;
    }   combo_module_t  ;
    
    static  combo_module_t  combo_module;
    
    
    // 设置wifi状态。
    // 这是对外提供的接口
    void set_wifikey(int on)             
    {
        printk("%s on=%d
    ", __func__, on);
        combo_module.status_wifikey = on;
    }         
    EXPORT_SYMBOL(set_wifikey);          
              
    // 应用层读取wifi节点的回调函数
    static  ssize_t show_wifikey_onoff      (struct device *dev, struct device_attribute *attr, char *buf)             
    {
        return  sprintf(buf, "%d
    ", combo_module.status_wifikey);
    }         
              
    // 应用层设置wifi节点的回调函数
    static  ssize_t set_wifikey_onoff       (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
    {
        unsigned int    val;             
        if(!(sscanf(buf, "%d
    ", &val))) {
            printk("%s error
    ", __func__); 
            return  -EINVAL; 
        }     
    
        if(!val) {
            combo_module.status_wifikey = 0;
        } else {
            combo_module.status_wifikey = 1;
        }
        printk("%s status_wifikey=%d
    ", __func__, combo_module.status_wifikey);
    
        return 0;
    }
    
    // 将wifi的读取/设置函数和节点对应
    static  ssize_t show_wifikey_onoff  (struct device *dev, struct device_attribute *attr, char *buf);
    static  ssize_t set_wifikey_onoff   (struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
    static  DEVICE_ATTR(wifikey_onoff, S_IRWXUGO, show_wifikey_onoff, set_wifikey_onoff);
    复制代码

    代码说明:
    (01) set_wifikey()提供的对外接口。用于在按键驱动中,当wifi按键按下/松开时调用;这样,就对应的改变wifi节点的值。
    (02) DEVICE_ATTR(wifikey_onoff, S_IRWXUGO, show_wifikey_onoff, set_wifikey_onoff); 声明wifi的节点为wifikey_onoff节点,并且设置节点的权限为S_IRWXUGO,设置“应用程序读取节点时的回调函数”为show_wifikey_onoff(),设置“应用程序设置节点时的回调函数”为set_wifikey_onoff(),


    DEVICE_ATTR只是声明了wifi节点,具体的注册要先将wifikey_onoff注册到attribute_group中;并且将attribute_group注册到sysfs中才能在系统中看到文件节点。下面是实现代码:

    复制代码
    // 将wifikey_onoff注册到attribute中
    static struct attribute *control_sysfs_entries[] = {
        &dev_attr_wifikey_onoff.attr,
        NULL
    };
    
    static struct attribute_group control_sysfs_attr_group = {
        .name   = NULL,
        .attrs  = control_sysfs_entries,
    };
    
    
    // 对应的probe函数。主要作用是将attribute_group注册到sysfs中
    static int control_sysfs_probe(struct platform_device *pdev)
    {
        return  sysfs_create_group(&pdev->dev.kobj, &control_sysfs_attr_group);
    }
    
    // 对应的remove函数。主要作用是将attribute_group从sysfs中注销
    static  int     control_sysfs_remove        (struct platform_device *pdev)
    {
        sysfs_remove_group(&pdev->dev.kobj, &control_sysfs_attr_group);
    
        return  0;
    }
    复制代码

    02 将Wifi读取接口注册到Android系统中
    通过JNI,将wifi读取接口注册到Android系统中,下面是对应的JNI函数control_service.c的代码:

    复制代码
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <jni.h>
    #include <fcntl.h>
    #include <assert.h>
    
    
    // 获取数组的大小
    # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
    // 指定要注册的类,对应完整的java类名
    #define JNIREG_CLASS "com/skywang/control/ControlService"
    
    // 引入log头文件
    #include <android/log.h>  
    
    // log标签
    #define  TAG    "WifiControl"
    // 定义debug信息
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
    // 定义error信息
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
    
    #define WIFI_ONOFF_CONTROL      "/sys/devices/platform/misc_ctl/wifikey_onoff"
    
    
    // 设置wifi电源开关
    JNIEXPORT jint JNICALL is_wifi_key_down(JNIEnv *env, jclass clazz)
    {
        int fd;
        int ret;
        char buf[2];
    
    //    LOGD("%s 
    ", __func__);
        if((fd = open(WIFI_ONOFF_CONTROL, O_RDONLY)) < 0) {
            LOGE("%s : Cannot access "%s"", __func__, WIFI_ONOFF_CONTROL);
            return; // fd open fail
        }
    
        memset((void *)buf, 0x00, sizeof(buf));
        ssize_t count = read(fd, buf, 1);
        if (count == 1) {
            buf[count] = '';
            ret = atoi(buf);
        } else {
            buf[0] = '';
        }
    
    //    LOGD("%s buf=%s, ret=%d
    ", __func__, buf, ret);
        close(fd);
        
        return ret;
    }
    
    // 清除wifi的按下状态
    JNIEXPORT void JNICALL clr_wifi_key_status(JNIEnv *env, jclass clazz)
    {
        int fd;
        int nwr;
        char buf[2];
    
        if((fd = open(WIFI_ONOFF_CONTROL, O_RDWR)) < 0) {
            LOGE("%s : Cannot access "%s"", __func__, WIFI_ONOFF_CONTROL);
            return; // fd open fail
        }
    
        nwr = sprintf(buf, "%d
    ", 0);
        write(fd, buf, nwr);
    
        LOGE("%s 
    ", __func__);
    
        close(fd);
    }
    
    // Java和JNI函数的绑定表
    static JNINativeMethod method_table[] = {
        // wifi按键相关函数
        { "is_wifi_key_down", "()I", (void*)is_wifi_key_down },
        { "clr_wifi_key_status", "()V", (void*)clr_wifi_key_status },
    };
    
    // 注册native方法到java中
    static int registerNativeMethods(JNIEnv* env, const char* className,
            JNINativeMethod* gMethods, int numMethods)
    {
        jclass clazz;
        clazz = (*env)->FindClass(env, className);
        if (clazz == NULL) {
            return JNI_FALSE;
        }
        if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
            return JNI_FALSE;
        }
    
        return JNI_TRUE;
    }
    
    int register_wifi_control(JNIEnv *env)
    {
        // 调用注册方法
        return registerNativeMethods(env, JNIREG_CLASS,
                method_table, NELEM(method_table));
    }
    
    JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
    {
        JNIEnv* env = NULL;
        jint result = -1; 
    
        if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
            return result;
        }   
    
        register_wifi_control(env);
    
        // 返回jni的版本
        return JNI_VERSION_1_4;
    }
    复制代码

    代码说明:
    (01) Android 的JVM会回调JNI_OnLoad()函数。在JNI_OnLoad()中,调用register_wifi_control(env)。
    (02) register_wifi_control(env)调用 registerNativeMethods(env, JNIREG_CLASS, method_table, NELEM(method_table)) 将method_table表格中的函数注册到Android的JNIREG_CLASS类中。JNIREG_CLASS为com/skywang/control/ControlService,所以它对应的类是com.skywang.control.ControlService.java。
    (03) method_table的内容如下:

    JNINativeMethod method_table[] = {
        // wifi按键相关函数
        { "is_wifi_key_down", "()I", (void*)is_wifi_key_down },
        { "clr_wifi_key_status", "()V", (void*)clr_wifi_key_status },
    }

    这意味着,将该文件中的is_wifi_key_down()函数和JNIREG_CLASS类的is_wifi_key_down()绑定。
    将该文件中的clr_wifi_key_status()函数和JNIREG_CLASS类的clr_wifi_key_status()绑定。

    该文件对应的Android.mk的代码如下:

    复制代码
    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE    := control_service
    LOCAL_SRC_FILES := control_service.c
    # 添加对log库的支持
    LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog
    #  注:若生成static的.a,只需添加 LOCAL_LDLIBS:=-llog 
    
    include $(BUILD_SHARED_LIBRARY)
    
    LOCAL_PATH := $(call my-dir)
    复制代码

    用ndk-build编译上面两个文件,得到so库文件libcontrol_service.so。


    关于Android NDK编程更详细的内容,请参考“Android JNI和NDK学习

    03 Android读取wifi的开关/状态
    在Android创建一个com.skywang.control.ControlService.java。例如,在Launcher的目录下创建packages/apps/Launcher2/src/com/skywang/control/ControlService.java
    ControlService.java代码如下:

    复制代码
    package com.skywang.control;
    
    import android.os.IBinder;
    import android.app.Service;
    import android.content.Intent;
    import android.content.Context;
    import android.net.wifi.WifiManager;
    import android.util.Log;
    
    public class ControlService extends Service {
        private static final String TAG = "ControlService";
    
        private WifiManager mWM;
        private ReadThread mReadThread;
        private boolean bWifi;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            Log.e(TAG, "start ControlService");
            mWM = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
            mReadThread = new ReadThread();
            mReadThread.start();
    
            bWifi = mWM.isWifiEnabled();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
    
            if (mReadThread != null)
                mReadThread.interrupt();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        // 处理wifi按键
        private synchronized void handleWifiKey() {
            if (is_wifi_key_down()==1) {
    
                // 清空wifi的按下状态。目的是“防止不断的产生wifi按下事件”
                clr_wifi_key_status();
                Log.d(TAG, "wifi key down");
                if (!mWM.isWifiEnabled()) {
                    Log.e(TAG, "open wifi");
                    mWM.setWifiEnabled(true);
                } else {
                    Log.e(TAG, "close wifi");
                    mWM.setWifiEnabled(false);
                }
            }
        }
    
        // 和Activity界面通信的接口
        private class ReadThread extends Thread {
    
            @Override
            public void  run() {
                super.run();
    
                while (!isInterrupted()) {
                    handleWifiKey();
                }
            }
        }
    
        // wifi按键相关函数
        private native int is_wifi_key_down();
        private native void clr_wifi_key_status();
    
        static {
            // 加载本地.so库文件
            System.loadLibrary("control_service");
        }
    }
    复制代码

    代码说明:
    (01) System.loadLibrary("control_service"); 这是在ControlService启动的时候自动执行的,目的是加载libcontrol_service.so库。即上一步所生成的so库文件。
    (02) ControlService.java是服务程序,它继承于Service。ReadThread是启动时会自动开启的线程。ReadThread的作用就是不断的调用handleWifiKey()处理wifi按键值。

    接下来,我们在AndroidManifest.xml中声明该服务,就可以在其它地方调用执行了。下面是manifest中声明ControlService的代码:

    <service android:name="com.skywang.control.ControlService">
        <intent-filter >
            <action android:name="com.skywang.control.CONTROLSERVICE" />
        </intent-filter>
    </service>

    我们在Launcher.java的onCreate()函数中启动该服务。这样,随着系统系统服务就会一直运行了。启动服务的代码如下:

    startService(new Intent("com.skywang.control.CONTROLSERVICE"));

    方案结论: 工作正常,但消耗系统资源较多,会增加系统功耗!
    经过测试发现,此方案运行很正常。但存在一个问题:由于添加了一个不停运行的服务,消耗很多系统资源,导致机器的功能也增加了很多。
    因此,再实现方案三,对比看看效果如何。


    方案三

    方案思路: 在Android的input输入子系统的框架层中捕获wifi按键,并进行相应处理。若捕获到“wifi”按键,则读取wifi的状态:若是“wifi”是开启的,则关闭wifi;否则,打开wifi。

    方案分析: 若采用此方案需要解决以下问题
    01, 将kernel的wifi按键值映射到Android系统的某键值上。
    -- 这个可以实现。和“方案二”一样,我们通过ADC驱动将wifi按键映射到键值KEY_F16上;然后,将kernel的KEY_F16和Android的某一键值对应。
    02, 在Android的framework层的键值处理函数中,捕获按键,并进行相应处理。
    -- 这个可以实现。在input子系统的framework层,捕获到wifi按键对应的Android系统中的按键


    架构图:


    具体实现:
    01, 将kernel的wifi按键值映射到Android系统的某键值上。
    01.01, wifi按键驱动

    在按键驱动中编辑wifi按键的驱动:主要的目的是将wifi按键映射到某个键值上,方便后面Android系统调用。因为Android系统使用的按键值和Linux内核使用的按键值不一样,Android会通过一个键值映射表,将Linux的按键值和Android的按键值映射起来。

    我们的项目中,wifi按键是通过ADC值来捕获的,而不是中断。下面是“wifi按键相关信息”,代码如下:

    复制代码
    static struct rk29_keys_button key_button[] = { 
    
        ...
        // 将 wifi 开关按键定义为KEY_F16,
        // 处理时,捕获KEY_F16进行处理即可。
        {   
            .desc   = "wifi",
            .code   = KEY_F16,
            .adc_value  = 4,
            .gpio = INVALID_GPIO,
            .active_low = PRESS_LEV_LOW,
        },  
        ...
    };
    复制代码

    从中,我们可以看出wifi的adc值大概是4,它所对应的按键值(即code值)是KEY_F16。
    这里,KEY_F16是我们自己定义的(因为linux中没有wifi开关按键),你也可以定义为别的值。记得两点:一,这里的所定义的wifi的code,必须和Android中要处理的按键值(后面会讲到)保持一致;二,不要使用系统中已用到的值。另外,KEY_F16的值为186,可以参考“include/linux/input.h”文件去查看。


    在按键驱动中,会将key_button注册到系统中。在按键驱动中,我们将下面的callback函数注册到adc总线上;adc驱动会通过工作队列,判断的读取adc值,并调用callback,从而判断是否有响应的按键按下。下面是callback函数:

    复制代码
    static void callback(struct adc_client *client, void *client_param, int result)
    {
        struct rk29_keys_drvdata *ddata = (struct rk29_keys_drvdata *)client_param;
        int i;
    
        if(result < EMPTY_ADVALUE)
            ddata->result = result;
    
        // 依次查找key_button中的按键,判断是否需要响应
        for (i = 0; i < ddata->nbuttons; i++) {
            struct rk29_button_data *bdata = &ddata->data[i];
            struct rk29_keys_button *button = bdata->button;
            if(!button->adc_value)
                continue;
            int pre_state = button->adc_state;
            if(result < button->adc_value + DRIFT_ADVALUE &&
                result > button->adc_value - DRIFT_ADVALUE) {
    
                button->adc_state = 1;
            } else {
                button->adc_state = 0;
            }   
    
            if(bdata->state != button->adc_state)
                mod_timer(&bdata->timer,
                    jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
        }   
        return;
    }
    复制代码

    这里的callback和“方案二”中的callback有区别。这里没有调用synKeyDone()函数。


    01.02, 键值映射
    映射文件
    Linux中的按键值和Android中的按键值不一样。它们是通过.kl映射文件,建立对应关系的。
    默认的映射文件是 qwerty.kl;但不同的平台可能有效的映射文件不同。用户可以通过查看"/system/usr/keylayout/"目录下的.kl映射文件,来进行验证哪个是有效的。映射方法:一,可以通过查看调用.kl的代码。二,修改.kl文件来验证。
    在rk3066中,有效的映射文件是“rk29-keypad.kl”。在“rk29-keypad.kl”中添加以下代码将wifi按键和Android中的“AVR_POWER按键”对应。
    key 186 AVR_POWER

    说明:
    key -- 是关键字。固定值,不需要改变。
    186 -- wifi按键在linux驱动中对应的键值,这里对应KEY_F16的键值,即wifi对应的按键值。关于linux驱动中的各个键值,可以查看“include/linux/input.h”
    AVR_POWER -- wifi按键映射到Android中的按键,它对应是“KeycodeLabels.h”文件中的KEYCODES表格元素的“literal”值。

    KeycodeLabels.h中KEYCODES定义如下:

     View Code

    KeycodeLabels.h中的按键与Android框架层的KeyEvent.java中的按键值对应。
    例如:“AVR_POWER”对应“KeyEvent.java中的键值”如下:
    public static final int KEYCODE_AVR_POWER = 181;

    这样,我们发现:将驱动的wifi按键就和Android系统中的KEYCODE_AVR_POWER就对应起来了!


    02, 在Android的framework层的键值处理函数中,捕获按键,并进行相应处理。
    在framework层的input系统中,加入对wifi按键的捕获。
    添加的文件是:frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
    添加的具体方法:在PhoneWindowManager.java的interceptKeyBeforeQueueing()函数中,捕获wifi按键。
    代码如下:

     View Code

    在上面的代码中,我们捕获了KeyEvent.KEYCODE_AVR_POWER,并对其进行处理。


    方案结论: 方案可行。而且运行效率比“方案二”高,不会造成功耗很大的问题。


    最终总结:方案三最好!

  • 相关阅读:
    使用jvisualvm和飞行记录器分析Java程序cpu占用率过高
    Callable、Future和FutureTask
    CountDownLatch(闭锁)
    ArrayBlockingQueue和LinkedBlockingQueue分析
    并发容器之CopyOnWriteArrayList(转载)
    svn 文件夹 无法提交
    rsync 不能同不子级目录的问题
    nginx 匹配.zip .apk 结尾的文件 直接下载
    Android文件Apk下载变ZIP压缩包解决方案
    nginx: [warn] conflicting server name "locahost" on 0.0.0.0:80, ignored
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7615673.html
Copyright © 2011-2022 走看看