android5.0.2,android5.1下载,深入解析android5.1 healthd

android5.0.2,android5.1下载,深入解析android5.1 healthd

这篇文章主要为大家详细介绍了安卓5.1健康d的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

健康d主要是读取电池节点的信息,传给电池服务。或者在关机充电等使用。注意健康d中使用的是核心的日志。

下面先从主要的函数分析

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

内部通道

内部ret

klog _ set _ LEVEL(KLOG _ LEVEL);

healthd _ mode _ ops=android _ ops

如果(!strcmp(basename(argv[0],' charger '){//解析输入参数如果是充电器的使用充电器_ops,这里就不做介绍

healthd _ mode _ ops=charger _ ops

}否则{

while ((ch=getopt(argc,argv,' cr '))!=-1) {//分析输入命令,各个命令对应不同的充电器_操作

开关(通道){

案例“c”:

healthd _ mode _ ops=charger _ ops

打破;

案例“r”:

healthd _ mode _ ops=恢复_ ops

打破;

案子吗?

默认值:

日志_错误(LOG_TAG,'无法识别的健康d选项:%c\n ',

optopt);

出口(1);

}

}

}

ret=healthd _ init();//healthd做初始化

if (ret) {

KLOG _错误('初始化失败,正在退出\ n’);

出口(2);

}

healthd _ main loop();//主函数

KLOG _错误('主循环终止,正在退出\ n’);

返回3;

}

如果是正常开机,不走关机充电等,healthd _ mode _ ops=android _ ops而这里面具体的函数在后面进行详细的介绍。

静态结构healthd_mode_ops android_ops={。init=healthd_mode_android_init。preparetowait=healthd _ mode _ Android _ preparetowait。心跳=healthd_mode_nop_heartbeat。电池_更新=健康d _模式_安卓_电池_更新,

};

下面分析下healthd_init函数,heathd使用了epoll进行超正析象管(图片Orthicon)复用。

静态int healthd_init() {

epollfd=EPOLL _ create(MAX _ EPOLL _ EVENTS);

if (epollfd==-1) {

日志_错误(日志_标签,

epoll_create失败;错误号=%d\n ',

errno);

return-1;

}

healthd _ board _ init(healthd _ config);

healthd _ mode _ ops-init(healthd _ config);

唤醒alarm _ init();

u event _ init()。

gBatteryMonitor=新电池监视器();

gBatteryMonitor-init(healthd _ config);

返回0;

}

这里的healthd_mode_ops-init的函数是android_ops的健康d _模式_安卓_初始化函数,这里主要是将粘合剂通信的软驱也加入epoll,而不像普通粘合剂进程最后使用IPCThreadState:self()-joinThreadPool。这样所有的软驱全在epoll管理,只用了一个线程

int healthd _ mode _ Android _ preparetowait(void){

IPCThreadState:self()-flush commands();

return-1;

}

静态void binder _ event(uint 32 _ t/* epevents */){

IPCThreadState:self()-handlePolledCommands();

}

void healthd _ mode _ Android _ init(struct healthd _ config */* config */){

ProcessState:self()-setThreadPoolMaxThreadCount(0);

IPCThreadState:self()-disableBackgroundScheduling(true);

IPCThreadState:self()-设置轮询(gBinderFd);

if (gBinderFd=0) {

if(healthd _ register _ event(gBinderFd,binder_event))

日志_错误(日志_标签,

注册活页夹事件失败\ n’);

}

gBatteryPropertiesRegistrar=new BatteryPropertiesRegistrar();

gBatteryPropertiesRegistrar-publish();

}

gbatterypropertysregistrar-发布将'电池属性'这个服务加入到服务经理中

void BatteryPropertiesRegistrar:publish(){

defaultServiceManager()-addService(字符串16('电池属性'),this);

}

接下来再来看下唤醒警报_初始化

静态void wakealarm_init(void) {

wake ALARM _ FD=timer FD _ create(CLOCK _ boot time _ ALARM,TFD _ non block);

if (wakealarm_fd==-1) {

日志_错误(LOG_TAG,' wakealarm_init: timerfd_create失败\ n’);

返回;

}

if(healthd _ register _ event(唤醒报警_ FD,唤醒报警_事件))

日志_错误(日志_标签,

注册唤醒警报事件失败\ n’);

唤醒报警设置间隔(healthd _ config。periodic _ feathers _ interval _ fast);

}

唤醒警报_初始化设置警报唤醒的区间,再来看下时间处理函数

静态无效唤醒警报_事件(uint32_t /*epevents*/) {

未签名的长时间唤醒;

if (read(wakealarm_fd,wakeups,sizeof(wakeups))==-1) {//出错结束

日志_错误(LOG_TAG,'唤醒警报_事件:读取唤醒警报软驱失败\ n’);

返回;

}

日志_错误(日志_标记,'唤醒报警_事件\ n’);

periodic _ Java ships();

}

静态空周期_杂务(){

healthd _ battery _ update();

}

void healthd _ battery _ update(void){

//充电时快速唤醒间隔(注意过热);

//使用电池时唤醒时间间隔缓慢(注意电池电量耗尽)。

日志_错误(LOG_TAG,' healthd _ battery _ update enter \ n ');

int new _ wake _ interval=gBatteryMonitor-update()?//调用主要的更新函数,根据返回值,如果当前在充电返回真实的

healthd _ config。periodic _ whaterials _ interval _ fast://时间设置一分钟

healthd _ config。周期_羽毛_间隔_缓慢;

日志_错误(LOG_TAG,' healthd _ battery _ update after \ n ');

if (new_wake_interval!=唤醒警报_唤醒_间隔)

唤醒警报设置间隔(新_唤醒_间隔);

//在清醒期间快速轮询。如果唤醒警报设置为快速

//速率然后只使用报警;如果唤醒警报设置为慢速,则

//唤醒时以快速速率轮询,并在以下情况下让闹钟以慢速唤醒

//睡着了。

if(healthd _ config。periodic _ whaterials _ interval _ fast==-1)

awake _ poll _ interval=-1;

其他

唤醒_轮询_间隔=

新唤醒间隔==健康配置。periodic _ feathers _ interval _ fast?//当前时间是一分钟,epoll为永远阻塞,否则为一分钟

-1:healthd _ config。periodic _ feathers _ interval _ fast * 1000;

}

接下来再来看看uEvent的,

静态void uevent_init(void) {

UE vent _ FD=UE vent _ open _ socket(64 * 1024,true);

if (uevent_fd 0) {

日志_错误(LOG_TAG,' uevent_init: uevent_open_socket失败\ n’);

返回;

}

fcntl(uevent_fd,F_SETFL,O _ non block);

if(healthd _ register _ event(u event _ FD,uevent_event))

日志_错误(日志_标签,

注册uevent事件失败\ n’);

}

看看uevent_event的处理函数,获取uevent后主要判断是否是电源系统的,如果是调用健康d _电池_更新函数

静态void u event _ event(uint 32 _ t/* epevents */){

char MSG[u event _ MSG _ l EN 2];

char * cp

int n;

n=u event _ kernel _多播_ recv(u event _ FD,msg,u event _ MSG _ LEN);

如果(n=0)

返回;

if(n=u event _ MSG _ LEN)/* overflow-discard */

返回;

msg[n]=' \ 0 ';

msg[n 1]=' \ 0 ';

cp=消息

KLOG错误(日志标记,'事件事件\ n’);

while (*cp) {

如果(!strcmp(cp,' SUBSYSTEM=' POWER _ SUPPLY _ SUBSYSTEM)){//是这个子系统的调用健康d _电池_更新函数

healthd _ battery _ update();

打破;

}

/*前进到下一个\0之后*/

while (*cp)

}

}

下面分析下健康d _主循环这个主函数,主函数主要是epoll函数监听3个fd,有事件就处理。

静态void healthd_mainloop(void) {

while (1) {

struct epoll _ event events[event CT];

事件;

int time out=awake _ poll _ interval;

int模式_超时

mode _ time out=healthd _ mode _ ops-preparetowait();

如果(超时0 ||(模式超时0模式超时))

超时=模式_超时;

nevents=epoll_wait(epollfd,events,eventct,time out);//epoll_wait等待各个软驱的事件,超时为超时时间

日志_错误(LOG_TAG,'陈康healthd _ main loop epoll _ wait \ n’);

if (nevents==-1) {

if (errno==EINTR)

继续;

日志_错误(LOG_TAG,' healthd_mainloop: epoll_wait失败\ n’);

打破;

}

for(int n=0;neventsn) {

if (events[n].data.ptr)//遍历各个软驱的事件上来,每个处理函数处理

(*(void(*)(int))事件[n]。数据。ptr)(事件[n]).事件);

}

如果(!nevents)//当什么事件没有的时候,是因为epoll超时设置走下来的,这时候也要更新下

periodic _ Java ships();

healthd _ mode _ ops-心跳();

}

返回;

}

初始化函数主要将healthd_config对象传入,并且将里面的成员的一些地址信息去初始化保存起来。主要是保存一些地址信息,以及充电方式。

void电池监控器:init(struct healthd _ config * HC){

字符串8路径;

char pval[PROPERTY _ VALUE _ MAX];

mHealthdConfig=hc//将外面传进来的heathdconfig的指针赋给成员变量

DIR * DIR=opendir(POWER _ SUPPLY _ SYSFS _ PATH);//打开地址/sys/class/power _ support

if (dir==NULL) {

日志_错误(日志_标记,'无法打开%s\n ',电源_系统_路径);

}否则{

结构目录*条目;

while ((entry=readdir(dir))) {

const char * name=entry-d _ name;

如果(!strcmp(名称,'.') || !strcmp(名称,'.'))

继续;

char buf[20];

//在每个子目录中查找"类型"文件

路径。clear();

path.appendFormat('%s/%s/type ',POWER_SUPPLY_SYSFS_PATH,name);

开关(readPowerSupplyType(path)) {//读取各个目录下类型的值,比如/sys/class/电源/电池下类型的值为电池,在readPowerSupplyType读取并且转化为安卓_电源_类型_电池

案例安卓_电源_类型_交流:

if(mHealthdConfig-acchargeheathpath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/health ',电源_系统_路径,

姓名);

if (access(path,R_OK)==0)

mHealthdConfig-acChargeHeathPath=path;//配置路径

}

路径。clear();

路径。追加格式(' % s/% s/online ',电源_系统_路径,名称);

if (access(path.string(),R_OK)==0)

麦克哈尔格名。add(字符串8(名称));//chargername就是当前目录名字:交流

打破;

案例ANDROID _ POWER _ SUPPLY _ TYPE _ USB://USB类似交流电

if(mHealthdConfig-usbchargeheathpath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/health ',电源_系统_路径,

姓名);

if (access(path,R_OK)==0)

mHealthdConfig-usbChargeHeathPath=path;

}

路径。clear();

路径。追加格式(' % s/% s/online ',电源_系统_路径,名称);

if (access(path.string(),R_OK)==0)

麦克哈尔格名。add(字符串8(名称));

打破;

案例ANDROID _ POWER _ support _ TYPE _ WIRELESS://类似

路径。clear();

路径。追加格式(' % s/% s/online ',电源_系统_路径,名称);

if (access(path.string(),R_OK)==0)

麦克哈尔格名。add(字符串8(名称));

打破;

机箱安卓_电源_类型_电池://电池

mBatteryDevicePresent=true

if(mHealthdConfig-batterystatuspath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/status ',电源_系统_路径,

姓名);

if (access(path,R_OK)==0)

mHealthdConfig-batteryStatusPath=path;

}

if(mHealthdConfig-batteryhealthpath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/health ',电源_系统_路径,

姓名);

if (access(path,R_OK)==0)

mHealthdConfig-batteryHealthPath=path;

}

if(mHealthdConfig-battery present path。isempty()){

路径。clear();

路径。附加格式(' % s/% s/present ',电源_系统_路径,

姓名);

if (access(path,R_OK)==0)

mHealthdConfig-batteryPresentPath=path;

}

if(mHealthdConfig-batterycapactypath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/capacity ',电源_系统_路径,

姓名);

if (access(path,R_OK)==0)

mHealthdConfig-batterycapactypath=path;

}

if(mHealthdConfig-batteryvoltagepath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/voltage _ now ',

电源_电源_系统文件系统_路径,名称);

if (access(path,R_OK)==0) {

mHealthdConfig-batteryVoltagePath=path;

}否则{

路径。clear();

路径。附加格式(' % s/% s/batt _ vol ',

电源_电源_系统文件系统_路径,名称);

if (access(path,R_OK)==0)

mHealthdConfig-batteryVoltagePath=path;

}

}

if(mHealthdConfig-batterycurrentnowpath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/current _ now ',

电源_电源_系统文件系统_路径,名称);

if (access(path,R_OK)==0)

mHealthdConfig-batteryCurrentNowPath=path;

}

if(mHealthdConfig-batterycurrentavpath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/current _ avg ',

电源_电源_系统文件系统_路径,名称);

if (access(path,R_OK)==0)

mHealthdConfig-batterycurrentavpath=path;

}

if(mHealthdConfig-batterychargecounterpath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/charge _ counter ',

电源_电源_系统文件系统_路径,名称);

if (access(path,R_OK)==0)

mHealthdConfig-batteryChargeCounterPath=path;

}

if(mHealthdConfig-电池温度路径。isempty()){

路径。clear();

path.appendFormat('%s/%s/temp ',POWER_SUPPLY_SYSFS_PATH,

姓名);

if (access(path,R_OK)==0) {

mHealthdConfig-电池温度path=path

}否则{

路径。clear();

路径。附加格式(' % s/% s/batt _ temp ',

电源_电源_系统文件系统_路径,名称);

if (access(path,R_OK)==0)

mHealthdConfig-电池温度path=path

}

}

if(mHealthdConfig-batterytechnologypath。isempty()){

路径。clear();

路径。附加格式(' % s/% s/technology ',

电源_电源_系统文件系统_路径,名称);

if (access(path,R_OK)==0)

mHealthdConfig-batteryTechnologyPath=path;

}

打破;

案例ANDROID _ POWER _ support _ TYPE _ UNKNOWN:

打破;

}

}

closedir(dir);

}

如果(!mChargerNames.size())

日志_错误(LOG_TAG,'没有找到充电器电源\ n’);

如果(!mBatteryDevicePresent) {//主要由电池该成员变量就为真实的

日志_警告(LOG_TAG,'未找到电池设备\ n’);

HC-periodic _ feathers _ interval _ fast=-1;

HC-periodic _ feathers _ interval _ slow=-1;

}否则{

if(mHealthdConfig-batterystatuspath。isempty())

日志_警告(LOG_TAG,'未找到电池状态路径\ n’);。//这里都是一些警告

}

if(property _ get(' ro。靴子。' fake _ battery ',pval,NULL) 0

strtol(pval,NULL,10)!=0) {

mbatteryfixedpacity=FAKE _ BATTERY _ CAPACITY;

mBatteryFixedTemperature=FAKE _ BATTERY _ TEMPERATURE;

}

}

下面就是更新函数,将数据封装在电池属性中,并且通过健康模式操作电池更新把电池属性发给上层。

布尔电池监控器:更新(无效){

布尔罗吉斯

props.chargerAcOnline=false

props.chargerUsbOnline=false

道具。chargerwirelessonline=false

道具。电池状态=电池状态_未知;

道具。电池健康=电池健康未知;

//都是从之前配置的mHealthd中取地址,读取节点信息,保存到小道具成员变量中

如果(!mHealthdConfig-battery present path。isempty())

道具。电池存在=getBooleanField(mHealthdConfig-电池存在路径);

其他

道具。电池存在=mBatteryDevicePresent

道具。电池电量=mbatteryfixedpacity?

mBatteryFixedCapacity:

getint字段(mHealthdConfig-batterycapactypath);

道具。电池电压=getint字段(mHealthdConfig-电池电压路径)/1000;

道具。电池温度=mBatteryFixedTemperature?

mBatteryFixedTemperature:

getint字段(mHealthdConfig-电池温度路径);

const int SIZE=128

char buf[SIZE];

String8 btech

if(从文件读取(mHealthdConfig-batteryStatusPath,buf,SIZE) 0)

道具。电池状态=get battery status(buf);

if(从文件读取(mHealthdConfig-电池健康路径,buf,SIZE) 0)

道具。电池健康=get battery health(buf);

if(从文件读取(mHealthdConfig-batteryTechnologyPath,buf,SIZE) 0)

道具。电池技术=串8(buf);

if(从文件读取(mHealthdConfig-acChargeHeathPath,buf,SIZE) 0)

道具。acchargeheath=string 8(buf);

if(从文件读取(mHealthdConfig-usbChargeHeathPath,buf,SIZE) 0)

道具。usbchargeheath=string 8(buf);

无符号int I;

for(I=0;我叫麦克哈尔格奈斯。size();i ) {//遍历之前保存的各个充电方式

字符串8路径;

路径。附加格式(' % s/% s/online ',电源_系统_路径,

mChargerNames[i].string());//路径就是每个目录下的在线的字段,比如/sys/class/电源/usb下的在线的

if (readFromFile(path,buf,SIZE) 0) {

if (buf[0]!='0') {//读取在线的里面的内容,如果当前在通用串行总线线上充电,那么通用串行总线下在线的里面的内容为一

路径。clear();

path.appendFormat('%s/%s/type ',POWER_SUPPLY_SYSFS_PATH,

mChargerNames[i].string());//这里看看是哪个类型的

开关(readPowerSupplyType(path)) {

案例安卓_电源_类型_交流:

props.chargerAcOnline=true

打破;

案例ANDROID _ POWER _ SUPPLY _ TYPE _ USB://将其值赋成真实的

props.chargerUsbOnline=true

打破;

案例安卓_电源_类型_无线:

道具。chargerwirelessonline=true

打破;

默认值:

日志_警告(日志标记,' %s:未知的电源类型\n ',

mChargerNames[i].string());

}

}

}

}

logthis=!healthd_board_battery_update(道具);

if (logthis) {

char dmes gline[256];

if (props.batteryPresent) {

snprintf(dmesgline),sizeof(dmesgline),

电池' l=%d v=%d t=%s%d,%d h=%d st=%d ',

道具。电池电平,道具。电池电压,

props.batteryTemperature 0?'-' : '',

abs(道具电池温度/10),

ABS(道具。电池温度% 10),props.batteryHealth,

道具。电池状态);

如果(!mHealthdConfig-batterycurrentnowpath。isempty()){

int c=getint字段(mHealthdConfig-batteryCurrentNowPath);

char b[20];

snprintf(b,sizeof(b),' c=%d ',c/1000);

strl cat(dmesglineb,sizeof(dmesglinea));

}

}否则{

snprintf(dmesgline),sizeof(dmesgline),

电池无');

}

日志_警告(日志标记,' %s更改=%s%s%s\n ',dmesgline,

props.chargerAcOnline?一个":",

props.chargerUsbOnline?u ':',

props.chargerWirelessOnline?w ':);

}

healthd _ mode _ ops-battery _ update(props);//将数据传到上层的电池服务

回归道具。充电器a线|道具。充电器副线|//返回当前是否属于充电

道具。充电器无线在线;

}

接下来看看健康模式操作电池更新是怎么把数据传到上层的

void healthd _ mode _ Android _ battery _ update(

struct Android:电池属性*道具){

if (gBatteryPropertiesRegistrar!=空)

gBatteryPropertiesRegistrar-通知侦听器(* props);

返回;

}

上层会通过粘合剂通信,注册一个回调到电池属性注册器

void BatteryPropertiesRegistrar:register listener(const spIBatteryPropertiesListener listener){

{

if (listener==NULL)

返回;

互斥* Autolock _ l(mregistrionlock);

//检查这是否是重复的

for(size _ t I=0;我是听众。size();i ) {

if(m listeners[I]-as binder()==listener-as binder()){

返回;

}

}

m听众。添加(监听器);

listener-as binder()-linkToDeath(this);

}

healthd _ battery _ update();

}

而更新函数就是调用了通知监听器遍历各个听众传到上层电池服务

void电池属性注册:notify listeners(struct电池属性属性){

互斥* Autolock _ l(mregistrionlock);

for(size _ t I=0;我是听众。size();i ) {

m listeners[I]-电池属性改变(道具);

}

}

再来看看电池服务中,在onStart中通过ServiceManager,和电池属性这个服务通信,将电池监听器这个列表中心注册到电池属性中去

@覆盖

public void onStart() {

服务经理。获取服务(“电池属性”);

最终IBatteryPropertiesRegistrar电池属性注册=

IBatteryPropertiesRegistrar .存根。作为接口(b);

尝试{

batterypropertiesregistrar。注册监听器(新电池监听器());

} catch(远程异常e) {

//永远不应该发生。

}

publishBinderService('电池',new binder service());

publishLocalService(batterymanagerinternal。class,new local service());

}

再来看看电池监听器的电池属性已更改接口,当下面调这个接口,就会调用电池服务的更新函数,然后就是电池服务的一些主要流程就不分析了。

私有最终类电池监听器扩展了IBatteryPropertiesListener .存根{

@覆盖

公共无效电池属性已更改(电池属性属性){

最终长标识=活页夹。clearcallingidentity();

尝试{

电池服务. this.update(道具);

}最后{

粘合剂。restorecallingentity(identity);

}

}

}

电池服务接受健康d的数据都是被动的,健康d穿过来的。有没有主动去健康d查询的。

在电池管理员中就有主动去健康d查询的,代码如下

私有长查询属性(int id){

长浸水使柔软

if(mBatteryPropertiesRegistrar==null){

服务经理。获取服务(“电池属性”);//获取电池属性服务

mBatteryPropertiesRegistrar=

IBatteryPropertiesRegistrar .存根。作为接口(b);//接口转化下

if(mBatteryPropertiesRegistrar==null)

返回龙.最小值;

}

尝试{

电池属性prop=新电池属性();

if(mbatterypropertiesregistrar。getproperty(id,prop)==0)//prop是输出

ret=prop。getlong();

其他

ret=Long .最小值;

} catch(远程异常e) {

ret=Long .最小值;

}

返回浸水使柔软

}

再到

healthd看看对应的接口

status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) { return healthd_get_property(id, val); } status_t healthd_get_property(int id, struct BatteryProperty *val) { return gBatteryMonitor->getProperty(id, val); }

java的BatteryProperty对象对应到这边是指针

status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) { status_t ret = BAD_VALUE; val->valueInt64 = LONG_MIN; switch(id) { case BATTERY_PROP_CHARGE_COUNTER://根据不同ID,返回不同值 if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) { val->valueInt64 = getIntField(mHealthdConfig->batteryChargeCounterPath); ret = NO_ERROR; } else { ret = NAME_NOT_FOUND; } break; case BATTERY_PROP_CURRENT_NOW: if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { val->valueInt64 = getIntField(mHealthdConfig->batteryCurrentNowPath); ret = NO_ERROR; } else { ret = NAME_NOT_FOUND; } break; case BATTERY_PROP_CURRENT_AVG: if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { val->valueInt64 = getIntField(mHealthdConfig->batteryCurrentAvgPath); ret = NO_ERROR; } else { ret = NAME_NOT_FOUND; } break; case BATTERY_PROP_CAPACITY: if (!mHealthdConfig->batteryCapacityPath.isEmpty()) { val->valueInt64 = getIntField(mHealthdConfig->batteryCapacityPath); ret = NO_ERROR; } else { ret = NAME_NOT_FOUND; } break; case BATTERY_PROP_ENERGY_COUNTER: if (mHealthdConfig->energyCounter) { ret = mHealthdConfig->energyCounter(&val->valueInt64); } else { ret = NAME_NOT_FOUND; } break; default: break; } return ret; }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • windowsandroid手机版下载,windowsandroid安装apk
  • windowsandroid手机版下载,windowsandroid安装apk,WindowsAndroid 安装教程详解
  • android调用webview方法,androidwebview是什么,Android 中 WebView 的基本用法详解
  • android传感器高级编程,Android传感器,Android编程之光线传感器用法详解
  • android.app.Dialog,android自定义dialog对话框,Android开发笔记之-Dialog的使用详解
  • android 图片视频轮播框架,androidlayout轮播图,Android实现炫酷轮播图效果
  • android里的viewpager,安卓自定义view流程,Android自定义引导玩转ViewPager的方法详解
  • android里的viewpager,android viewpager详解
  • android里的viewpager,android viewpager详解,Android自定义超级炫酷的ViewPage指示器
  • android调用webview方法,androidwebview是什么
  • android设置控件宽度,android获取屏幕宽度和高度
  • android设置控件宽度,android获取屏幕宽度和高度,Android中获取控件宽高的4种方法集合
  • android蓝牙开发的基本流程,安卓蓝牙app开发教程
  • android蓝牙开发的基本流程,安卓蓝牙app开发教程,android蓝牙简单开发示例教程
  • android菜单栏,android菜单控件
  • 留言与评论(共有 条评论)
       
    验证码: