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

 

 

 

 

 

 

 

 

 

posted on 2019-04-30 00:43  Hello-World3  阅读(957)  评论(0编辑  收藏  举报

导航