zoukankan      html  css  js  c++  java
  • LED硬件访问服务(2)——JNI/HAL

    一、系统编程

    1.SystemServer.java类中提供了main()方法,说明它是以一个进程的方式存在的,启动后直接执行其run()

    2.注册服务
    ServiceManager.addService("vibrator", vibrator);
    通过addService来告诉系统,告诉系统指的是service_manager.c表示的进程。service_manager管理着系统中的所有service。要想这些服务
    能被应用程序使用,就必须要注册进service_manager中。
    应用程序向service_manager查询获得某一个service。然后应用程序通过接口ILedService把对硬件操作请求发送给LedService.java,然后由
    这个LedService来实现对硬件的操作。

    3.AIDL文件的实现
    对于一个应用程序来说,它只需要调用ioctl()去控制LED,并不需要open()和close(),因此LED的ILedService.aidl文件中的interface ILedService中
    只需要实现ledIoctl()这一个函数即可。模仿IVibratorService.aidl放到对应的目录下,然后修改Android.mk文件添加:core/java/android/os/ILedService.aidl
    然后执行mmm .命令系统会帮我们在根目录下的out目录下生成ILedService.java文件.

    /* ILedService.aidl文件 */
    package android.os;
    
    /** {@hide} */
    interface ILedService
    {
        int ledControl(int which, int status);
    }
    View Code

    4.应用程序怎么使用ILedService.java
    参考IVibrator的,在frameworks代码中搜索IVibratorService,然后参考SystemVibrator.java。然后对比其aidl看其怎么使用aidl中指定的
    函数的。

    5.Tiny412 Android 内核编译方法
    $ . setenv
    $ lunch full_tiny4412-eng
    $ make snod //修改frameworks时使用重新生成system.img时,out/target/product/tiny4412/system.img
    $ ./gen-image.sh //会在根目录下生成system.img,然后烧写它即可。

    6.自动生成的ILedService.java中的ILedService接口中有一个静态的stub类,它继承于binder类,说明它里面一定实现了binder进程间通信。

    7.无论是addService还是getService还是应用程序执行某个service都会涉及到进程间通信。

    8.参考的vibrator的代码执行路径
    App --> SystemVibrator.java --> VibratorService.java --> com_android_server_VibratorService.cpp --> hal --> driver

    9.使用硬件访问服务后,所有的对led硬件的操作都需要通过LedSevice.java来进行。

    10.LedService的注册流程
    SystemServer.java中的main()直接调用了run(),在run()中:
    (1)System.loadLibrary("android_servers"); //加载libandroid_servers.so,它是由onLoad.cpp和一大堆JNI文件编译而成的。
    onLoad.cpp里面有一个JNI_OnLoad(),它里面为各个Service类注册了本地方法。
    (2)加载完上面的C库后,会执行startOtherServices(),它会注册各个Service, 例如调用startServiceVibrator()注册Vibrator的Service。
    在这个函数中先构造这个VirbateService然后调用ServiceManager.addService("vibrator", vibrator);把它注册到系统中去。注册到系统的
    意思就是把这个Service告诉service_manager进程(service_manager.c)实现的。在VibratorService中实现了对native方法的调用。

    11.LedService.java被系统编译
    添加LedService.java后不需要修改frameworks/base/services/core/Android.mk的原因是它里面通过
    LOCAL_SRC_FILES += $(call all-java-files-under,java) 把下的所有文件都包含进去了。

    12.使JNI文件被系统编译
    修改frameworks/base/services/core/jni/Android.mk 参考com_android_server_VibratorService.cpp添加
    com_android_server_LedService.cpp

    13.修改完后重新编译system.image
    我们修改的aidl文件、jni文件,service文件涉及以下Android.mk,因此应该在“frameworks/base/services/”下执行mm
    frameworks/base/services/core/Android.mk //一般上层的Android.mk会把下层的Android.mk包含进来,但是它没有包含
    frameworks/base/services/core/jni/Android.mk
    frameworks/base/services/Android.mk //它里面指定生成SystemServer.java中指定的libandroid_servers.so库
    在开发板的位置:/system/lib/libandroid_servers.so

    frameworks/base/services/Android.mk中:
    include $(wildcard $(LOCAL_PATH)/*/jni/Android.mk) //会编译到jni目录下的修改
    include $(patsubst %,$(LOCAL_PATH)/%/Android.mk,$(services)) //会编译到core目录下的修改

    14.修改frameworks只需要重新编译system.img即可。
    执行$ mmm frameworks/base/services/ 后,make snod, 然后./gen-img.sh生成system.img

    15.涉及的各个文件
    (1)LedService.java

    package com.android.server;
    
    import android.os.ILedService;
    
    
    /*
    此文件的作用: 调用本地方法来操作硬件
    */
    
    public class LedService extends ILedService.Stub
    {
        private static final String TAG = "LedService"; /*只有在打印信息的时候会使用到*/
    
        /*不要忘记加native*/
        public native static int native_ledOpen();
        public native static int native_ledCtrl(int which, int status);
        public native static int native_ledClose();
    
        @Override
        public int ledControl(int which, int status) throws android.os.RemoteException {
            return native_ledCtrl(which, status);
        }
    
        public LedService() {
            native_ledOpen();
        }
    }
    View Code

    15.向系统中注册LedService涉及的更改

    (1)SystemServer.java中仿照Vibrator添加如下代码来注册LedService

    仿照Vibrator添加:
    Slog.i(TAG, "Led Service");
    led = new LedService();
    ServiceManager.addService("led", led);
    View Code

    二、应用App编程

    1.MainActivity.java中import android.os.ILedService //导入ILedService所在的包,包名由ILedService.aidl生成的ILedService.java中
    的package android.os而来的。


    2.编译报错找不到ILedService这个符号,AS环境中没有ILedService,因此需要包含某些类,需要将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
    包含到AS工程中。
    包含方法:
    AS中 File --> Project Structure --> "+" --> 选中“Import JAR/AAR Package” --> next --> 输入classes.jar的路径 --> ok
    然后稍等一会等系统处理完。然后可以在Project Structure中看到classes和app是属于并列的模块。

    Modules
    app
    classes

    然后还需要让app去引用classes:
    Project Structure窗口中点击app --> Dependencies --> "+" --> "module dependency" --> 然后看到clases,点击ok --> 点击ok退出Project Structure窗口

    为什么包含的不是framework.jar而是classes.jar的原因是Android里面运行的不是原原本本的Java程序,framework.jar是dex格式,dex格式
    是Android对java文件做的一些优化。而编译代码的时候需要原生态的java文件,所以不能使用framework.jar而是使用classes.jar

    3.然后还报找不到ServiceManager符号
    解决:在MainActivity.java中:import android.os.ServiceManager; //ServiceManager.java中package android.os;

    4.然后编译报错:
    iLedService.ledControl(i, 1); 错误: 未报告的异常错误RemoteException; 必须对其进行捕获或声明以便抛出,解决:
    选中这部分代码,然后Ctrl+Alt+t填充捕获异常的代码。

    5.然后编译报错class.jar中方法超过64K
    官网方法:https://developer.android.com/studio/build/multidex#mdex-gradle
    AndroidMenifest.xml中添加:

    <application
        android:name="android.support.multidex.MultiDexApplication"
        ...
    application>
    
    build.gradle(Module:app)中添加:
    defaultConfig {
        ...
        multiDexEnabled true
    }
    dependencies {
        ...
        implementation 'com.android.support:multidex:1.0.3'
    }
    View Code

    6.AS工程可以直接删除工程下的源文件而不需要做添加移出工程的操作。

    7.App相关文件

    (1)MainActivity.java

    package com.example.mm.app_0001_led_demo;
    
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.Button;
    import android.view.View;
    import android.widget.CheckBox;
    import android.widget.Toast;
    import android.os.ILedService; //1.导入这个包
    import android.os.ServiceManager;
    
    public class MainActivity extends AppCompatActivity {
    
        private boolean ledon = false;
        private Button button = null;
    
        private CheckBox checkBoxLed1 = null;
        private CheckBox checkBoxLed2 = null;
        private CheckBox checkBoxLed3 = null;
        private CheckBox checkBoxLed4 = null;
    
        private ILedService iLedService = null;
    
        class MyButtonListener implements View.OnClickListener { //OnClickListener is a inner interface of View
    
            //iLedService hradControl = new iLedService();
    
            /* Ctrl + i  auto override*/
            @Override
            public void onClick(View v) {
                ledon = !ledon;
                if (ledon) {
                    button.setText("ALL OFF");
                    checkBoxLed1.setChecked(true);
                    checkBoxLed2.setChecked(true);
                    checkBoxLed3.setChecked(true);
                    checkBoxLed4.setChecked(true);
                    try {
                        for (int i = 0; i < 4; i++) {
                            /*3.通过该实例直接调用aidl中的函数*/
                            iLedService.ledControl(i, 1);
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    button.setText("ALL ON");
                    checkBoxLed1.setChecked(false);
                    checkBoxLed2.setChecked(false);
                    checkBoxLed3.setChecked(false);
                    checkBoxLed4.setChecked(false);
                    try {
                        for (int i = 0; i < 4; i++) {
                            iLedService.ledControl(i, 0);
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public void onCheckboxClicked(View view) {
            // Is the view now checked?
            boolean checked = ((CheckBox) view).isChecked();
    
            try {
                // Check which checkbox was clicked
                switch(view.getId()) {
                    case R.id.LED1:
                        if (checked) {
                            iLedService.ledControl(0, 1);
                            Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
                        } else {
                            iLedService.ledControl(0, 0);
                            Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
                        }
                        break;
                    case R.id.LED2:
                        if (checked) {
                            iLedService.ledControl(1, 1);
                            Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
                        } else {
                            iLedService.ledControl(1, 0);
                            Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
                        }
                        break;
                    case R.id.LED3:
                        if (checked) {
                            iLedService.ledControl(2, 1);
                            Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();
                        } else {
                            iLedService.ledControl(2, 0);
                            Toast.makeText(getApplicationContext(), "LED3 off",Toast.LENGTH_SHORT).show();
                        }
                        break;
                    case R.id.LED4:
                        if (checked) {
                            iLedService.ledControl(3, 1);
                            Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();
                        } else {
                            iLedService.ledControl(3, 0);
                            Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();
                        }
                        break;
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            /*2.获取一个ILedService的实例*/
            /* name "led" should the same as the name of addService() */
            iLedService = ILedService.Stub.asInterface(ServiceManager.getService("led"));
    
            /* Ctrl+r and then replace all make iLedService-->iLedService */
    
            button = (Button) findViewById(R.id.BUTTON);
            button.setOnClickListener(new MyButtonListener());
    
            /* Ctrl + shift + Space to insert CheckBox to ()*/
            checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);
            checkBoxLed2 = (CheckBox) findViewById(R.id.LED2);
            checkBoxLed3 = (CheckBox) findViewById(R.id.LED3);
            checkBoxLed4 = (CheckBox) findViewById(R.id.LED4);
        }
    }
    View Code

    8.编译生成的apk竟然有7M那么大,它把class.jar直接打包进apk里面去了,若是不想包含class.jar到apk包中:

    File --> Project Structure --> 选中Module下的app --> 看其dependencies --> 将class.jar的scope选项从compile修改为provided(新版本的AS选择为Compile Only).
    作用是使class.jar只是用来编译,并不会把它打包到apk中。然后apk文件就只有1M多了。

    三、JNI文件

    1.若是没有使用HAL,那么JNI文件(C文件)直接操作硬件,若是使用了HAL文件,则JNI文件需要向上提供本地函数,向下加载HAL文件并调用HAL函数,
    HAL文件来访问驱动程序执行硬件操作。

    2.JNI文件和HAL文件都是使用C来写的,JNI文件加载HAL文件的实质就是怎么使用dlopen()来加载HAL文件编译成的动态库文件。
    Android代码中对dlopen()做了一层封装,我们使用的是external/chromium_org/third_party/hwcplus/hardware.c中的hw_get_module()

    可以在frameworks目录下搜索hw_get_module看看别人是怎么使用的,参考com_android_server_lights_LightsService.cpp

    假如要相同hw_get_module("led", &module);加载HAL文件

    (1)"led"如何转换为dlopen(filename)
    由函数hw_module_exists()可知依次会在3个目录下查找name.subname.so:
    a.环境变量HAL_LIBRARY_PATH指示的目录 //tiny4412上使用echo $HAL_LIBRARY_PATH这个环境变量为空
    b./vendor/lib/hw //tiny4412上这个目录不存在
    c./system/lib/hw //tiny4412上这个目录存在,因此把HAL文件库放到这个目录下。
    -------------------------------------
    查找的HAL库文件是led.$(prop).so文件, 其中prop来按优先级源于:
    prop=property_get(ro.hardware.led或ro.hardware或ro.product.board或ro.board.platform或ro.arch)
    使用getprop工具查看:
    ro.hardware.led = 目前等于空
    ro.hardware = tiny4412
    ro.product.board = tiny4412
    ro.board.platform = exynos4
    ro.arch = 目前等于空
    最后还会去尝试加载led.default.so文件(我们这个平台上由Android.mk控制hal生成的文件就是这个)

    因此上面三个路劲下的以下名的so文件都是备选项:
    led.tiny4412.so
    led.exynos4.so
    led.default.so

    注意:如果用户设置了ro.hardware.led这个属性值为“v1”,则会最优先加载led.v1.so, 因此若有多个版本的hal文件可以使用属性控制
    加载哪一个。但是属性是断电后清零的。setprop设置的属性会在系统重新上电后消失。

    (2)调用dlopen是如何进行加载的

    load
        dlopen
            (struct hw_module_t *)dlsym(handle, "HMI") //从so文件中获取名为HMI的hw_module_t类型的结构体
            strcmp(id, hmi->id) //检验hmi->id的id是否等于"led"
            
    函数原型如下:
    int hw_get_module(const char *id, const struct hw_module_t **module)
    void *dlopen(const char *filename, int flag);
    View Code

    3.总结:
    (1)JNI使用HAL的方法
    a.通过hw_get_module()从HAL文件中获取一个名为HMI的hw_module_t类型的结构体
    b.调用这个结构体里面提供的methods->open(),通过它获取设备操作函数集合。
    c.之后就可以通过函数集合里面的函数操作硬件了。

    (2)与之对应的HAL的编写方法
    a.在HAL文件中提供一个hw_module_t的结构体
    b.提供methods->open(),通过它导出设备操作的函数集合。

    4.JNI程序实现

    #define LOG_TAG "LedService"
    
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    
    #include "jni.h"
    #include "JNIHelp.h" /*must contain, else build error*/
    
    #include <utils/misc.h>
    #include <utils/Log.h>
    
    #include <hardware/hardware.h>
    #include <hardware/led_hal.h>
    
    
    namespace android
    {
    
    static led_device_t *g_device;
    
    static int init_native(led_device_t ** device)
    {
        int ret;
        hw_module_t *module;
        led_device_t *led_device;
    
        ret = hw_get_module(LED_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
        if (ret) {
            ALOGE("init_native failed, ret=%d", ret);
            return -1;
        }
    
        ret = module->methods->open(module, NULL, (hw_device_t **)&led_device);
        if (ret < 0) {
            return -1;
        }
    
        *device = led_device;
    
        return 0;
    }
    
    
    static jint led_open(JNIEnv *env, jclass cls) {
        int ret;
        static int first = 0;
    
        if (first == 0) {
            first++;
            ret = init_native(&g_device);
            if (ret) {
                return -1;
            }
        }
    
        if (g_device) {
            return g_device->led_open(g_device);
        } else {
            return -1;
        }
    }
    
    
    static jint led_ctrl(JNIEnv * env, jclass cls, jint which, jint status) {
        if (g_device) {
            return g_device->led_ioctl(g_device, which, status);
        } else {
            return -1;
        }
    }
    
    static void led_close(JNIEnv *env, jclass cls) {
        if (g_device) {
            g_device->led_close(g_device);
        }
    }
    
    
    static const JNINativeMethod method_table[] = {
        { "native_ledOpen", "()I", (void*)led_open },
        { "native_ledCtrl", "(II)I", (void*)led_ctrl },
        { "native_ledClose", "()V", (void*)led_close },
    };
    
    int register_android_server_LedService(JNIEnv *env)
    {
        /*把method_table中的方法注册到类com.android.server.LedService中*/
        return jniRegisterNativeMethods(env, "com/android/server/LedService", method_table, NELEM(method_table));
    }
    
    };
    View Code

    四、HAL文件

    1.编写hal文件参考:
    hardware/qcom/display/msm8974/liblight/lights.c

    hardware/libhardware/modules/vibrator/vibrator.c //vibrator的JNI文件没有使用它

    2.Vibrator的文件组成
    APP: AS中编写的App程序
    AIDL: frameworks/base/core/java/android/os/IVibratorService.aidl
    Service: frameworks/base/services/core/java/com/android/server/VibratorService.java
    JNI: frameworks/base/services/core/jni/com_android_server_VibratorService.cpp
    HAL:hardware/libhardware/modules/vibrator/vibrator.c
    DRIVER: vibrator驱动程序

    5.Hal文件的头文件存放路径
    参考的lights的:hardware/libhardware/include/hardware/lights.h
    hal文件中定义的一些结构需要放在头文件中,因为JNI文件中也会使用到它。
    Hal文件位置:
    hardware/libhardware/modules/led/led_hal.c
    hardware/libhardware/include/hardware/led_hal.h

    6.修改Android.mk编译led_hal.c文件
    还要参考lights.c或者vibrator.c修改Android.mk使hal文件被编译
    led参考的是hardware/libhardware/modules/vibrator/Android.mk 中的LOCAL_MODULE_TAGS := optional需要改为eng(表示开发版本),因为编译
    的时候lunch的是eng版本。

    7.然后编译,然后烧录System.img进行测试
    # mmm frameworks/base/services
    # mmm hardware/libhardware/modules/led
    # make snod
    # ./gen-img.sh

    8.也就是说Android分为四块,Uboot、内核、system(framework/hardware)、data(应用程序)

    9.HAL文件实现

    (1)led_hal.c

    #define LOG_TAG "LedHal"
    
    
    #include <hardware/hardware.h>
    #include <hardware/led_hal.h>
    #include <cutils/log.h>
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <errno.h>
    
    
    #define DEV_FILE     "/dev/leds"
    
    
    int hal_led_open(led_device_t *device) {
        int fd = open(DEV_FILE, O_RDWR);
        if (fd < 0) {
            ALOGE("hal_led_open failed!");
            return -1;
        }
        device->fd = fd;
    
        ALOGI("hal_led_open called");
        return 0;
    }
    
    int hal_led_ioctl(led_device_t *device, int which, int status) {
        int ret;
        ret = ioctl(device->fd, status, which);
        if (ret != 0) {
            ALOGE("hal_led_ioctl failed: which=%d, status=%d", which, status);
            return -1;
        }
        ALOGI("hal_led_ioctl called: which=%d, status=%d", which, status);
        return 0;
    
    }
    
    /*这里传参数是hw_device_t,其它函数传参是led_device_t*/
    int hal_led_close(led_device_t *device) {
        close(device->fd);
        ALOGI("hal_led_close called!");
        return 0;
    }
    
    
    /* here id is device_id, when more than one device, use it to choose */
    /*加上"__unused"可以防止编译器报警告:warning: unused parameter 'id' [-Wunused-parameter]*/
    int led_module_open(const struct hw_module_t* module, const char* id __unused,
        struct hw_device_t** device) {
    
        led_device_t *led_device = calloc(1, sizeof(led_device_t));
        if (!led_device) {
            ALOGE("Can not allocate memory for led_device");
            *device = NULL;
            return -ENOMEM;
        }
    
        led_device->common.tag = HARDWARE_DEVICE_TAG;
        led_device->common.module = (hw_module_t *) module;
        led_device->common.version = HARDWARE_DEVICE_API_VERSION(1,0);
        //led_device->common.close = hal_led_close;
        led_device->led_open = hal_led_open;
        led_device->led_ioctl = hal_led_ioctl;    
        led_device->led_close = hal_led_close;
    
        *device = (hw_device_t *)led_device;
    
        return 0;
    }
    
    
    static struct hw_module_methods_t led_module_methods = {
        .open = led_module_open,
    };
    
    /*这里必须不能加static修饰,否则dlsym()找不到这个HMI结构体*/
    //static hw_module_t HAL_MODULE_INFO_SYM = {
    struct hw_module_t HAL_MODULE_INFO_SYM = {
    
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = LED_API_VERSION,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = LED_HARDWARE_MODULE_ID,
        .name = "Led HAL of Mr.Sun",
        .author = "Mr.Sun",
        .methods = &led_module_methods,
    };
    View Code

    (2)led_hal.h

    #ifndef HARDWARE_LED_HAL_H
    #define HARDWARE_LED_HAL_H
    
    
    #define LED_HARDWARE_MODULE_ID "led"
    
    #define LED_API_VERSION HARDWARE_MODULE_API_VERSION(1,0)
    
    
    
    typedef struct _led_device {
        struct hw_device_t common;
    
        int fd;
        int (*led_open)(led_device_t *device);
        int (*led_ioctl)(led_device_t *device, int which, int status);
        void (*led_close)(led_device_t *device);
    } led_device_t;
    
    
    
    
    
    #endif /*HARDWARE_LED_HAL_H*/
    View Code

    四、驱动

    驱动不变,还是使用上一节的驱动。

    五、时App使用反射机制操作Led

    1.修改App利用反射

    a.注释掉MainActivity.java中的
    //import android.os.ILedService;
    //import android.os.ServiceManager;

    //iLedService = ILedService.Stub.asInterface(ServiceManager.getService("led"));
    b.要使用反射来实现这个句代码:

    //注意到ServiceManager是个标注为hide的方法,其getService实public static的,
    Method getService = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
    //第一个参数应该传入一个实例化对象,但是由于getSevice是static的并不需要,所以传入null.
    IBinder ledService = getService.invoke(null, "led");
    //IBinder是一个可见的接口,所以直接使用就可以了。$表示访问内部类,asInterface需要的参数是IBinder的对象
    Method asInterface = Class.forName("android.os.ILedService$Stub").getMethod("asInterface", IBinder.class);

    //从ILedService.java中看出,执行asInterface方法会返回一个ILedService.Stub.Proxy(obj)的对象,但是我们并没有把ILedService import到
    //MainActivity.java中,因此不能直接使用这个类型。但是可以使用Object(其父类)。
    //第一个参数应该是一个实例化对象,由于是asInterface是一个static方法,因此传null。
    Object proxy = asInterface.invoke(null, ledService);

    //之后就可以使用proxy里面的ledControl()来操作led了。
    //从ILedService.java中也可以看出proxy类中实现了ledControl(), 它里面实现了通过binder驱动与LedService通信的操作。
    Method ledCtl = Class.forName("android.os.ILedService$Stub$Proxy").getMethod("ledControl", int.class, int.class);

    Class.forName是首先获得这个类,之后就可以使用ledCtl来设置led了。

    使用ledCtl来设置led的方法:
    //由于从ILedService中Proxy类中的ledControl方法并不是static的,因此参数1需要指定一个实例化对象。这个实例化对象是
    ledCtl.invoke(proxy, 0, 1);

    需要把proxy还有ledCtl设置为调用类的属性,也就是类MainActivity的属性,添加如下代码:
    Object proxy = null;
    Method ledCtl = null;
    然后上面的修改再使用这两个成员的时候就可以直接使用了。

    然后修改代码把所有的对led的操作都改为ledCtl.invoke(proxy, which, status);

    2.编译报错找不到符号IBinder和Method
    import android.os.IBinder; //IBinder.java中这样package android.os;

    3.编译报错不兼容的类型: Object无法转换为IBinder
    将上面:
    IBinder ledService = getService.invoke(null, "led"); //相当于调用getService方法
    改为:
    Object ledService = getService.invoke(null, "led");

    报错“Object无法转换为IBinder”也就是说getService()返回的是一个Object类型,查看ServiceManager.java源码找到getService()方法发现
    其的确返回一个IBinder对象呀,为什么使用IBinder对象接收就不行呢。
    注意,这里的getService是通过Method getService = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
    得到的,双击这里的"Class" Shift+f1,在打开的网页中搜索getMethod,点击进入其返回值网页,在里面检索invoke,找到invoke的原型为:
    "Object invoke(Object obj, Object... args)",返回的的确是一个Object类型。
    总结:ServiceManager.java中的getService()的确返回一个IBinder对象,但是在调用invoke的时候就向上转化为了Object对象。而这里想让Object向下
    转化为Ibinder的话就需要加IBinder进行强制类型转换。eg: IBinder ledService = (IBinder)getService.invoke(null, "led");

    4.使用反射机制的MainActivity.java

    package com.example.mm.app_0001_led_demo;
    
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.Button;
    import android.view.View;
    import android.widget.CheckBox;
    import android.widget.Toast;
    import android.os.IBinder;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class MainActivity extends AppCompatActivity {
    
        private boolean ledon = false;
        private Button button = null;
    
        private CheckBox checkBoxLed1 = null;
        private CheckBox checkBoxLed2 = null;
        private CheckBox checkBoxLed3 = null;
        private CheckBox checkBoxLed4 = null;
    
        Object proxy = null;
        Method ledCtl = null;
    
        class MyButtonListener implements View.OnClickListener { //OnClickListener is a inner interface of View
    
            /* Ctrl + i  auto override*/
            @Override
            public void onClick(View v) {
                ledon = !ledon;
                if (ledon) {
                    button.setText("ALL OFF");
                    checkBoxLed1.setChecked(true);
                    checkBoxLed2.setChecked(true);
                    checkBoxLed3.setChecked(true);
                    checkBoxLed4.setChecked(true);
                    try {
                        for (int i = 0; i < 4; i++) {
                            ledCtl.invoke(proxy, i, 1);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                } else {
                    button.setText("ALL ON");
                    checkBoxLed1.setChecked(false);
                    checkBoxLed2.setChecked(false);
                    checkBoxLed3.setChecked(false);
                    checkBoxLed4.setChecked(false);
                    try {
                        for (int i = 0; i < 4; i++) {
                            ledCtl.invoke(proxy, i, 0);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public void onCheckboxClicked(View view) {
            // Is the view now checked?
            boolean checked = ((CheckBox) view).isChecked();
    
            try {
                // Check which checkbox was clicked
                switch(view.getId()) {
                    case R.id.LED1:
                        if (checked) {
                            ledCtl.invoke(proxy, 0, 1);
                            Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
                        } else {
                            ledCtl.invoke(proxy, 0, 0);
                            Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
                        }
                        break;
                    case R.id.LED2:
                        if (checked) {
                            ledCtl.invoke(proxy, 1, 1);
                            Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
                        } else {
                            ledCtl.invoke(proxy, 1, 0);
                            Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
                        }
                        break;
                    case R.id.LED3:
                        if (checked) {
                            ledCtl.invoke(proxy, 2, 1);
                            Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();
                        } else {
                            ledCtl.invoke(proxy, 2, 0);
                            Toast.makeText(getApplicationContext(), "LED3 off",Toast.LENGTH_SHORT).show();
                        }
                        break;
                    case R.id.LED4:
                        if (checked) {
                            ledCtl.invoke(proxy, 3, 1);
                            Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();
                        } else {
                            ledCtl.invoke(proxy, 3, 0);
                            Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();
                        }
                        break;
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            /* name "led" should the same as the name of addService() */
    //      iLedService = ILedService.Stub.asInterface(ServiceManager.getService("led"));
            try {
                Method getService = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
                IBinder ledService = (IBinder)getService.invoke(null, "led");
                Method asInterface = Class.forName("android.os.ILedService$Stub").getMethod("asInterface", IBinder.class);
                proxy = asInterface.invoke(null, ledService);
                ledCtl = Class.forName("android.os.ILedService$Stub$Proxy").getMethod("ledControl", int.class, int.class);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
            /* Ctrl+r and then replace all make iLedService-->iLedService */
            button = (Button) findViewById(R.id.BUTTON);
            button.setOnClickListener(new MyButtonListener());
    
            /* Ctrl + shift + Space to insert CheckBox to ()*/
            checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);
            checkBoxLed2 = (CheckBox) findViewById(R.id.LED2);
            checkBoxLed3 = (CheckBox) findViewById(R.id.LED3);
            checkBoxLed4 = (CheckBox) findViewById(R.id.LED4);
        }
    }
    View Code

     

  • 相关阅读:
    线程中断总结
    线程的基本协作和生产者消费者
    synchronized总结
    线程基础总结
    Java集合总结(三):堆与优先级队列
    Java集合总结(二):Map和Set
    Java集合总结(一):列表和队列
    java枚举类型总结
    java 内部类简单总结
    java Integer包装类装箱的一个细节
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/10793731.html
Copyright © 2011-2022 走看看