Objc中的Category

objective-c中对一个类的扩展除了使用常见的继承外还提供了一种独特的方法:分类(Category)。

先看一个使用分类扩展类的小例子:

将被扩展的类:

#import <Foundation/Foundation.h>

@interface Fraction : NSObject
{
    int numerator;
    int denominator;
}
@property int numerator, denominator;
-(void) setTo: (int)n over: (int)d;
-(void) print;
@end

@implementation Fraction
-(void) setTo:(int)n over:(int)d
{
    numerator = n;
    denominator = d;
}
-(void) print
{
    NSLog(@"%d / %d", numerator, denominator);
}
@end

使用obj-c的分类语法扩展Fraction类:

#import <Foundation/Foundation.h>
#import "Fraction.h"

@interface Fraction (MathOps)
-(Fraction*) add: (Fraction*)f;
-(Fraction*) mul: (Fraction*)f;
-(Fraction*) sub: (Fraction*)f;
-(Fraction*) div: (Fraction*)f;
@end

@implementation Fraction (MathOps)
-(Fraction*) add:(Fraction *)f
{
    Fraction *result = [[Fraction alloc]init];
    int resultNum, resultDenom;
    resultNum = (numerator * f->denominator) + (denominator * f->numerator);
    resultDenom = denominator * f->denominator;
    [result setTo:resultNum over:resultDenom];
    return result;
}
@end

分类的特点:

  • 只能扩展类的方法,不能扩展变量,否则还得用继承
  • 一个类一旦被分类扩展了,也会影响他的所有子类
  • 不要使用分类去重写被扩展类的方法,一旦这样做了,你将再也不能访问到原方法了,并不像继承那样可以使用super来访问原方法
  • 可以不必实现全部的分类方法,上例中的mul,sub,div没有实现,仅仅是声明一下,可以留待以后去实现

Android 电源管理-Healthd(2)

接上文 「Android电源管理-Healthd (1)」

adb shell进入到/sys/class/power_supply目录,我们可以看到power_supply驱动创建的一些运行时文件(我的设备是Nuxus 7, Android 4.4.2, kernel 3.4.0):

adb root
adb shell
cd /sys/class/power_supply
ll

输出如下:

lrwxrwxrwx root  root  2014-09-19 14:30 ac -> ../../devices/i2c-0/0-0055/power_supply/ac
lrwxrwxrwx root  root  2014-09-19 14:30 battery -> ../../devices/i2c-0/0-0055/power_supply/battery
lrwxrwxrwx root  root  2014-09-19 14:30 usb -> ../../devices/i2c-0/0-0055/power_supply/usb
lrwxrwxrwx root  root  2014-09-19 14:30 wireless -> ../../devices/i2c-0/0-0055/power_supply/wireless

看文件名称就能知道其含义,但是问题是这里一下子列出了4种电源类型,Android系统究竟是怎么判断当前使用的是那一种呢?要回答这个问题,我们不妨进入其中任一个文件夹,看看里面记录的是些什么。

cd usb
ls

输出如下:

lrwxrwxrwx root root        2014-09-20 22:03 device -> ../../../0-0055
-r--r--r-- root root   4096 2014-09-19 14:30 online
drwxr-xr-x root root        2014-09-20 22:03 power
lrwxrwxrwx root root        2014-09-20 22:03 subsystem -> ../../../../../class/power_supply
-r--r--r-- root root   4096 2014-09-19 18:24 type
-rw-r--r-- root root    4096 2014-09-20 22:03 uevent

查看online文件,发现里面的值为1,这是因为我的设备正在使用USB电源,此时如果查看ac或者wireless文件夹中的online文件,你会发现其值为0; 再查看type文件,发现里面的值为USB,这个文件记录了对于的名称。 所以Android系统对当前电源类型的判别逻辑是这样的:

  • 遍历所有系统支持的电源方式

  • 查看online的值,值为1即是当前电源方式

回到代码,我们看看是不是真的这样做的呢 查看system/core/healthd/BatteryMonitor.cpp关注下面的片段:

        for (i = 0; i < mChargerNames.size(); i++) {
            String8 path;
            path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
                              mChargerNames[i].string());

            if (readFromFile(path, buf, SIZE) > 0) {
                if (buf[0] != '0') {
                    path.clear();
                    path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
                                      mChargerNames[i].string());
                    switch(readPowerSupplyType(path)) {
                    case ANDROID_POWER_SUPPLY_TYPE_AC:
                        props.chargerAcOnline = true;
                        break;
                    case ANDROID_POWER_SUPPLY_TYPE_USB:
                        props.chargerUsbOnline = true;
                        break;
                    case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
                        props.chargerWirelessOnline = true;
                        break;
                    default:
                        KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
                                     mChargerNames[i].string());
                    }
                }
            }
        }

其中POWER_SUPPLY_SYSFS_PATH的定义为:

#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM

上面的代码很容易理解,当遍历所有支持的电源类型(存储在mChargerNames),当发现其中online文件记录的值不为0的时候,再通过其type文件读取其类型名,并把props结构体中对应的字段设置成true。 注意这个props变量很关键,它的类型为BatteryProperties,它记录并传递关于电源的许多信息。 BatteryProperties的定义在frameworks/native/include/batteryservice/BatteryService.h,下回就看看这个文件。

Android 电源管理-Healthd(1)

OS:Android 4.4.2

Android电源管理底层用的是Linux power supply框架。驱动部分不叙述。只看JAVA、JNI和CPP应用层。

从Android 4.4开始,Google专门提供了一个healthd来监控电源状态。它的路径在:system/core/healthd,编译出来的文件为/sbin/healthd

看一下healthd.cpp中的main函数:

int main(int argc, char **argv) {
    int ch;

    klog_set_level(KLOG_LEVEL);

    while ((ch = getopt(argc, argv, "n")) != -1) {
        switch (ch) {
        case 'n':
            nosvcmgr = true;
            break;
        case '?':
        default:
            KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch);
        }
    }

    healthd_board_init(&healthd_config);
    wakealarm_init();
    uevent_init();
    binder_init();
    gBatteryMonitor = new BatteryMonitor();
    gBatteryMonitor->init(&healthd_config, nosvcmgr);

    healthd_mainloop();
    return 0;
}

这里引人关注的是最后调用的healthd_mainloop(),仅凭函数名就能知道会进入一个无限循环,这样也就能达到监控电源状态的目的了。跟踪代码看一下这个函数的定义:

static void healthd_mainloop(void) {
    struct epoll_event ev;
    int epollfd;
    int maxevents = 0;

    epollfd = epoll_create(MAX_EPOLL_EVENTS);
    if (epollfd == -1) {
        KLOG_ERROR(LOG_TAG,
                   "healthd_mainloop: epoll_create failed; errno=%d\n",
                   errno);
        return;
    }

    if (uevent_fd >= 0) {
        ev.events = EPOLLIN | EPOLLWAKEUP;
        ev.data.ptr = (void *)uevent_event;
        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1)
            KLOG_ERROR(LOG_TAG,
                       "healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n",
                       errno);
        else
            maxevents++;
    }

    if (wakealarm_fd >= 0) {
        ev.events = EPOLLIN | EPOLLWAKEUP;
        ev.data.ptr = (void *)wakealarm_event;
        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1)
            KLOG_ERROR(LOG_TAG,
                       "healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n",
                       errno);
        else
            maxevents++;
   }

    if (binder_fd >= 0) {
        ev.events = EPOLLIN | EPOLLWAKEUP;
        ev.data.ptr= (void *)binder_event;
        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1)
            KLOG_ERROR(LOG_TAG,
                       "healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n",
                       errno);
        else
            maxevents++;
   }

    while (1) {
        struct epoll_event events[maxevents];
        int nevents;

        IPCThreadState::self()->flushCommands();
        nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval);

        if (nevents == -1) {
            if (errno == EINTR)
                continue;
            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
            break;
        }

        for (int n = 0; n < nevents; ++n) {
            if (events[n].data.ptr)
                (*(void (*)())events[n].data.ptr)();
        }

        if (!nevents)
            periodic_chores();
    }

    return;
}

果不其然,我们看到了while(1)。Android使用了linux epoll来侦听三个文件描述符的变化,分别是uevent_fd、wakealarm_fd和binder_fd。以uevent_fd为例,在这个描述符上关心的事件有EPOLLIN和EPOLLWAKEUP,当底层出现这两个事件的时候,会回调函数uevent_event(),看看这个函数会做些什么:

#define UEVENT_MSG_LEN 1024
static void uevent_event(void) {
    char msg[UEVENT_MSG_LEN+2];
    char *cp;
    int n;

    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
    if (n <= 0)
        return;
    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
        return;

    msg[n] = '\0';
    msg[n+1] = '\0';
    cp = msg;

    while (*cp) {
        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
            battery_update();
            break;
        }

        /* advance to after the next \0 */
        while (*cp++)
            ;
    }
}

它会读取socket中的字符串,然后判断事件来源是否是由kernel的power_supply发出的,如果是,那就调用battery_update()更新电源状态。下面来看看battery_update()是如何更新电源状态的:

static void battery_update(void) {
    // Fast wake interval when on charger (watch for overheat);
    // slow wake interval when on battery (watch for drained battery).

   int new_wake_interval = gBatteryMonitor->update() ?
       healthd_config.periodic_chores_interval_fast :
           healthd_config.periodic_chores_interval_slow;

    if (new_wake_interval != wakealarm_wake_interval)
            wakealarm_set_interval(new_wake_interval);

    // During awake periods poll at fast rate.  If wake alarm is set at fast
    // rate then just use the alarm; if wake alarm is set at slow rate then
    // poll at fast rate while awake and let alarm wake up at slow rate when
    // asleep.

    if (healthd_config.periodic_chores_interval_fast == -1)
        awake_poll_interval = -1;
    else
        awake_poll_interval =
            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
                -1 : healthd_config.periodic_chores_interval_fast * 1000;
}

这里的重点是gBatteryMonitor->update(),gBatteryMonitor的类型为BatteryMonitor。那接下来就把目光专注到system/core/healthd/BatteryMonitor.hsystem/core/healthd/BatteryMonitor.cpp

在BatteryMonitor.h中,我们看到了一个enum,它列出了Android所支持的电源类型:

    enum PowerSupplyType {
        ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,
        ANDROID_POWER_SUPPLY_TYPE_AC,
        ANDROID_POWER_SUPPLY_TYPE_USB,
        ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
        ANDROID_POWER_SUPPLY_TYPE_BATTERY
    };

可以看出Android当前支持的电源类型有4种:AC,USB,WIRELESS,BATTERY。在这里你可以增加你自己的电源类型,比如CAR(车载)… … 把目光聚焦到BatteryMonitor.cpp,这里有获取电源状态的核心代码。但是在继续看代码之前,我们先来看一下power_supply电源驱动是如何管理系统中的不同电源类型的。

呃,太晚咯,明天还加班,待续好了 。。。

Android 控制生成 ODEX

如果没有指定DISABLE_DEXPREOPT,那么user版将生成odex,其他版本不会生成odex文件

相关文件在build/core/main.mk

 ifneq (true,$(DISABLE_DEXPREOPT))
    ifeq ($(user_variant),user)
      ifeq ($(HOST_OS),linux)
        WITH_DEXPREOPT := true
      endif
    endif
  endif
  • 如果user版不想生成odex,那么需要定义

    DISABLE_DEXPREOPT := true
    
  • 如果eng版要生成odex,那么需要定义

    WITH_DEXPREOPT := true
    
  • 如果想单独控制某一应用是否生成odex,可以在应用的Android.mk中定义

    WITH_DEXPREOPT := true
    

Install Skype on Ubuntu 12.04 64bit

sudo dpkg -P skype

rm -rf ~/.Skype

sudo apt-get update

sudo apt-get install gdebi

sudo apt-get install sni-qt:i386

wget download.skype.com/linux/skype-ubuntu-precise4.3.0.37-1i386.deb

sudo gdebi skype-ubuntu-precise4.3.0.37-1i386.deb