Android 源码编译 AIDL

在Eclipse下编译AIDL基本不用做什么特殊的工作,Eclipse都帮你完成了。但是如果需要在Android Source下编译AIDL,就需要你在Android.mk中加上AIDL文件声明。

LOCAL_SRC_FILES = \
                 $(call all-java-files-under, src) \
                 src/net/lnmcc/service/IMyService.aidl

Low Memory Killer in Android

Low Memory Killer(LMK)在用户空间中指定了一组内存临界值,当其中的某个值与进程描述中的oom_adj值在同一范围时,该进程将被Kill掉。

LMK的策略记录在下面两个文件中:

  1. /sys/module/lowmemorykiller/parameters/adj 这个文件指定了发生LMK时,需要kill掉的进程号区间。
  2. /sys/module/lowmemorykiller/parameters/minfree 这个文件中储存了将导致调用LMK的空闲页面数量值。

看一个例子

adj文件的内容为:

0,58,117,176,529,1000

minfree文件的内容为:

12288,15360,18432,21504,24576,30720

这里些数组的具体含义是指: 当一个进程的空闲空间下降到了30720个页面时,系统将kill掉进程号 >= 1000的进程; 当一个进程的空闲空间下降到了24587个页面时,系统将kill掉进程号 >= 529的进程; … …

怎样让你的App不被KML

你可以在AndroidManifest.xml中的Application中增加属性

android:persistent="true"

这个属性会把你的APP提升为Android核心级别,在这个级别上的APP,即使你用ps -9也无法kill掉它(它会立刻重启)。

但是这个方法有一个限制,就是你的APP需要是系统APP,也就是说需要被push到/system/app/或者/system/priv-app/下,否则android:persistent="true"这个属性不会生效。

当你为你的APP设置了persistent后,查看一下你的APP的oom_adj值:

cat /proc/3730/oom_adj  #3730为你APP的PID

我的APP的oom_adj值为-12,从上面的LMK策略可以看到LMK将永远不会kill一个oom_adj < 0 的进程,实际上它是一个CORE_SERVICE_ADJ. (ps:源码路径frameworks/base/services/java/com/android/server/am/ActivityManagerService.java)

Vim sed 最小匹配

默认情况下vim使用的贪婪匹配,但是有时候我们需要的是最小匹配,通过 :h non-greedy 查看文档发现vim可以通过下面的方式来启用最小匹配:

使用 " \{-} "来替代通配符 "*"

比如有一个文件内容如下:

(a + b) + c)

现在需要把(a + b)替换成 x

贪婪匹配

:1,$s/(.*)/x/g

上面的命令会把文件内容替换成了:

x

因为是贪婪匹配,所有这里的表达式 (.*) 匹配到了第二个右括号。

最小匹配

:1,$s/(.\{-})/x/g

结果文件内容变成了:

x + c)

可以看出这里表达式 (.{-} 只匹配了第一个右括号。

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,下回就看看这个文件。