Skip to main content
 首页 » 编程设计

Android LED硬件访问服务(2)——JNI/HAL

2022年07月19日151haluo1

一、系统编程

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); 
}

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(); 
    } 
}

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

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

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

二、应用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' 
}

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); 
    } 
}

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);

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)); 
} 
 
};

四、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, 
};

(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*/

四、驱动

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

五、时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); 
    } 
}

 


本文参考链接:https://www.cnblogs.com/hellokitty2/p/10793731.html