NETD

一、NETD解读

1.1、NETD的作用

Netd是Android系统中专门负责网络管理和控制的后台daemon程序,其功能主要分三大块:

  • 设置防火墙(Firewall)、网络地址转换(NAT)、带宽控制、无线网卡软接入点(Soft Access Point)控制,网络设备绑定(Tether)等。
  • Android系统中DNS信息的缓存和管理。
  • 网络服务搜索(Net Service Discovery,简称NSD)功能,包括服务注册(Service Registration)、服务搜索(Service Browse)和服务名解析(Service Resolve)等。

Netd的工作流程和Vold类似[1],其工作可分成两部分:
- Netd接收并处理来自Framework层中NetworkManagementService或NsdService的命令。这些命令最终由Netd中对应的Command对象去处理。
- Net接收并解析来自Kernel的UEvent消息,然后再转发给Framework层中对应Service去处理。

Netd位于Framework层和Kernel层之间,它是Android系统中网络相关消息和命令转发及处理的中枢模块。

1.2 NETD的工作流程

Netd进程由init进程根据init.rc的对应配置项而启动。通过命令行可以看到:

service netd /system/bin/netdclass mainsocket netd stream 0660 root systemsocket dnsproxyd stream 0660 root inetsocket mdns stream 0660 root systemsocket fwmarkd stream 0660 root inet
  • Netd启动时将创建三个TCP监听socket,其名称分别为”netd”,”dnsproxyd”,”mdns”和“fwmarked”。

  • Framework层中的NetworkManagementService和NsdService将分别和”netd”及”mdns”监听socket建立链接并交互。

  • 每一个调用和域名解析相关的socket API(如getaddrinfo或gethostbyname等)的进程都会借由”dnsproxyd”监听socket与netd建立链接。

  • fwmarkd 和底层kernel交互,防火墙firewall会对进来的包做标记。

1.2.1 main函数分析

Netd进程的入口函数是其main函数,代码如下所示:

int main() {CommandListener *cl;NetlinkManager *nm;DnsProxyListener *dpl;MDnsSdListener *mdnsl;ALOGI("Netd 1.0 starting");//为Netd进程屏蔽SIGPIPE信号blockSigpipe();//创建NetlinkManagerif (!(nm = NetlinkManager::Instance())) {ALOGE("Unable to create NetlinkManager");exit(1);};//创建CommandListener,它将创建名为"netd"的监听socketcl = new CommandListener();//设置NetlinkManager的消息发送者(Broadcaster)为CommandListener。nm->setBroadcaster((SocketListener *) cl);//启动NetlinkManagerif (nm->start()) {ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));exit(1);}//Netd设置环境变量ANDROID_DNS_MODE为"local"// Set local DNS mode, to prevent bionic from proxying// back to this service, recursively.setenv("ANDROID_DNS_MODE", "local", 1);//创建DnsProxyListener,它将创建名为"dnsproxyd"的监听socketdpl = new DnsProxyListener();dpl->startListener();//创建MDnsSdListener并启动监听,它将创建名为"mdns"的监听socketmdnsl = new MDnsSdListener();mdnsl->startListener();cl->startListener();while(1) {sleep(1000);}exit(0);
}

Netd的main函数非常简单,主要是创建几个重要成员并启动相应的工作,这几个重要成员分别是:
- NetlinkManager:它将接收并处理来自Kernel的UEvent消息。这些消息经NetlinkManager解析后将借助它的Broadcaster(也就是代码中为NetlinkManager设置的CommandListener)发送给Framework层的NetworkManagementService。

  • CommandListener、DnsProxyListener、MDnsSdListener:分别创建名为”netd”、”dnsproxyd”、”mdns”的监听socket,并处理来客户端的命令。
1.2.2 NetlinkManager分析

1、NetlinkManager主要负责接收并解析来自Kernel的UEvent消息。其核心代码在start函数中:

int NetlinkManager::start() {//创建接收NETLINK_KOBJECT_UEVENT消息的socket,其值保存在mUeventSock中//其中,NETLINK_FORMAT_ASCII代表UEvent消息的内容为ASCII字符串if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII, false)) == NULL)     {return -1;}//创建接收RTMGPR_LINK消息的socket,其值保存在mRouteSock中//其中,NETLINK_FORMAT_BINARY代表UEvent消息的类型为结构体,故需要进行二进制解析if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,RTMGRP_LINK |RTMGRP_IPV4_IFADDR |RTMGRP_IPV6_IFADDR |RTMGRP_IPV6_ROUTE |(1 << (RTNLGRP_ND_USEROPT - 1)),NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {return -1;}//创建接收NETLINK_NFLOG消息的socket,其值保存在mQuotaSock中if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {ALOGE("Unable to open quota socket");// TODO: return -1 once the emulator gets a new kernel.}//创建接收NETLINK_NETFILTER消息的socket,其值保存在mStrictSock中if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST, true)) == NULL) {ALOGE("Unable to open strict socket");// TODO: return -1 once the emulator gets a new kernel.}return 0;
}

2、kernel消息上报流程:

graph TBA[kernel]-->B(NetLinkManager)    B-->C(NetlinkListener)C-->D(NetLinkEvent)D-->E(NetLinkHandle)E-->F(SocketListener)F-->G(NetworkManagementService)

3、NetlinkHandler的onEvent函数,由于其内部已针对不同属性的NetlinkEvent进行了分类处理。

void NetlinkHandler::onEvent(NetlinkEvent *evt) {const char *subsys = evt->getSubsystem();if (!subsys) {ALOGW("No subsystem found in netlink event");return;}//处理对应NETLINK_KOBJECT_UEVENT和NETLINK_ROUTE的信息if (!strcmp(subsys, "net")) {NetlinkEvent::Action action = evt->getAction();//查找消息中携带的网络设备名const char *iface = evt->findParam("INTERFACE");if (action == NetlinkEvent::Action::kAdd) {//添加NIC(Network InterfaceCard)的消息notifyInterfaceAdded(iface);} else if (action == NetlinkEvent::Action::kRemove) {//NIC被移除的消息notifyInterfaceRemoved(iface);} else if (action == NetlinkEvent::Action::kChange) {//NIC变化消息evt->dump();notifyInterfaceChanged("nana", true);} else if (action == NetlinkEvent::Action::kLinkUp) {//链路启用(类似插网线)notifyInterfaceLinkChanged(iface, true);} else if (action == NetlinkEvent::Action::kLinkDown) {//链路断开(类似拔网线)notifyInterfaceLinkChanged(iface, false);} else if (action == NetlinkEvent::Action::kAddressUpdated ||action == NetlinkEvent::Action::kAddressRemoved) {const char *address = evt->findParam("ADDRESS");const char *flags = evt->findParam("FLAGS");const char *scope = evt->findParam("SCOPE");if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {int resetMask = strchr(address, ':') ? RESET_IPV6_ADDRESSES : RESET_IPV4_ADDRESSES;resetMask |= RESET_IGNORE_INTERFACE_ADDRESS;if (int ret = ifc_reset_connections(iface, resetMask)) {ALOGE("ifc_reset_connections failed on iface %s foraddress %s (%s)", iface,address, strerror(ret));}}if (iface && flags && scope) {//ip地址改变notifyAddressChanged(action, address, iface, flags, scope);}} else if (action == NetlinkEvent::Action::kRdnss) {const char *lifetime = evt->findParam("LIFETIME");const char *servers = evt->findParam("SERVERS");if (lifetime && servers) {//DNS服务器改变notifyInterfaceDnsServers(iface, lifetime, servers);}} else if (action == NetlinkEvent::Action::kRouteUpdated || action == NetlinkEvent::Action::kRouteRemoved) {const char *route = evt->findParam("ROUTE");const char *gateway = evt->findParam("GATEWAY");const char *iface = evt->findParam("INTERFACE");if (route && (gateway || iface)) {notifyRouteChange(action, route, gateway, iface);}}} else if (!strcmp(subsys, "qlog")) {const char *alertName = evt->findParam("ALERT_NAME");const char *iface = evt->findParam("INTERFACE");//当数据量超过预警值,则会收到该通知notifyQuotaLimitReached(alertName, iface);} else if (!strcmp(subsys, "strict")) {const char *uid = evt->findParam("UID");const char *hex = evt->findParam("HEX");notifyStrictCleartext(uid, hex);} else if (!strcmp(subsys, "xt_idletimer")) {//这个和idletimer有关,用于跟踪某个NIC的工作状态,即是“idle”还是“active”//检测时间按秒计算const char *label = evt->findParam("INTERFACE");const char *state = evt->findParam("STATE");const char *timestamp = evt->findParam("TIME_NS");const char *uid = evt->findParam("UID");if (state)notifyInterfaceClassActivity(label, !strcmp("active", state),timestamp, uid);.......
}

3、大致流程

  • NM创建NetlinkHandler后,工作便转交给NetlinkHandler来完成,而每个NetlinkHandler对象均会单独创建一个线程用于接收Socket消息。
  • 当Kernel发送UEvent消息后,NetlinkHandler便从select调用中返回,然后调用其onDataAvailable函数,该函数内部会创建一个NetlinkEvent对象。
  • NetlinkEvent对象根据socket创建时指定的解析类型去解析来自Kernel的UEvent消息。
  • 最终NetlinkHandler的onEvent将被调用,不同的UEvent消息将在此函数中进行分类处理。
  • NetlinkHandler最终将处理结果经由NM内部变量mBroadcaster转发给NetworkManagementService。
1.2.3 CommandListener分析

CommandListener的作用主要是接收Framework层NetworkManageService的命令。它定义了多个和网络相关的Command类。还定义了多个控制类(即server目录下的xxxControl.cpp/.h),这些控制类将和命令类共同完成相应的命令处理工作。

//构造函数
CommandListener::CommandListener() :FrameworkListener("netd", true) {registerCmd(new InterfaceCmd());registerCmd(new IpFwdCmd());registerCmd(new TetherCmd());registerCmd(new NatCmd());registerCmd(new ListTtysCmd());registerCmd(new PppdCmd());registerCmd(new SoftapCmd());registerCmd(new BandwidthControlCmd());registerCmd(new IdletimerControlCmd());registerCmd(new ResolverCmd());registerCmd(new FirewallCmd());registerCmd(new ClatdCmd());registerCmd(new NetworkCommand());registerCmd(new StrictCmd());registerCmd(getQtiConnectivityCmd(this));if (!sNetCtrl)sNetCtrl = new NetworkController();if (!sTetherCtrl)sTetherCtrl = new TetherController();if (!sNatCtrl)sNatCtrl = new NatController();if (!sPppCtrl)sPppCtrl = new PppController();if (!sSoftapCtrl)sSoftapCtrl = new SoftapController(this);if (!sBandwidthCtrl)sBandwidthCtrl = new BandwidthController();if (!sIdletimerCtrl)sIdletimerCtrl = new IdletimerController();if (!sResolverCtrl)sResolverCtrl = new ResolverController();if (!sFirewallCtrl)sFirewallCtrl = new FirewallController();if (!sInterfaceCtrl)sInterfaceCtrl = new InterfaceController();if (!sClatdCtrl)sClatdCtrl = new ClatdController(sNetCtrl);if (!sStrictCtrl)sStrictCtrl = new StrictController();
}

它每个Command类都定义了一个runCommand方法,用于接受上层发下来的消息,并调用对应的xxxControl去执行。

1.2.4 NetworkManagementService分析

1、NetworkManagementService作用有两个:

[1] 用于接受上层模块发下来的命令,并将它发送到CommandListener处理;
[2] 用于接收底层传上来的一些notify消息,并做出相应处理。

//构造函数
private NetworkManagementService(Context context, String socket) {mContext = context;......//通过NativeDaemonConnector创建一个socket来通信mConnector = new NativeDaemonConnector(new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl, FgThread.get().getLooper());mThread = new Thread(mConnector, NETD_TAG);.......}

2、NetworkManagementService 工作流程

2.1 NetworkManagementService通过onEvent发放处理底层上来的消息。

public boolean onEvent(int code, String raw, String[] cooked) {String errorMessage = String.format("Invalid event from daemon (%s)", raw);switch (code) {case NetdResponseCode.InterfaceChange:/** a network interface change occured* Format: "NNN Iface added <name>"*         "NNN Iface removed <name>"*         "NNN Iface changed <name> <up/down>"*         "NNN Iface linkstatus <name> <up/down>"*/if (cooked.length < 4 || !cooked[1].equals("Iface")) {throw new IllegalStateException(errorMessage);}if (cooked[2].equals("added")) {notifyInterfaceAdded(cooked[3]);return true;} else if (cooked[2].equals("removed")) {notifyInterfaceRemoved(cooked[3]);return true;} else if (cooked[2].equals("changed") && cooked.length == 5) {notifyInterfaceStatusChanged(cooked[3],cooked[4].equals("up"));return true;} else if (cooked[2].equals("linkstate") && cooked.length == 5) {notifyInterfaceLinkStateChanged(cooked[3],cooked[4].equals("up"));return true;}throw new IllegalStateException(errorMessage);// break;......
}

2.2 NetworkManagementService通过NativeDaemonConnector的对象mConnector执行execute方法将命令通过socket发送到底层。

//NetworkManagementService.java
//firewall对应之前CommandListener的一个command类,后面是参数
mConnector.execute("firewall", "zeusis_instrument_test", rule);
//CommandListener.cpp
CommandListener::FirewallCmd::FirewallCmd() ://对应匹配firewallNetdCommand("firewall") {
}
......
int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,char **argv) {......//对参数进行匹配if (!strcmp(argv[1], "zeusis_instrument_test")) {if (argc != 3) {cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage: firewall zeusis_dormant_mode<enable|disable>",false);return 0;}ALOGE("setInstrumentTesting: %s",argv[2]);int res = sFirewallCtrl->setInstrumentTesting(!strcmp(argv[2],"enable"));return sendGenericOkFail(cli, res);}

二、iptable解读

2.1、iptable原理

iptables是Linux系统中最重要的网络管控工具。它与Kernel中的netfilter模块配合工
作,其主要功能是为netfilter设置一些过滤(filter)或网络地址转换(NAT)的规则。当Kernel收到网络数据包后,将会依据iptables设置的规则进行相应的操作。

  • iptables内部(其实是Kernel的netfilter模块)维护着四个Table,分别是filter、
    nat、mangle和raw,它们对应着不同的功能,稍后将详细介绍它们的作用。
  • Table中定义了Chain。一个Table可以支持多个Chain,Chain实际上是Rule的集
    合,每个Table都有默认的Chain。例如filter表默认的Chain有INPUT、OUTPUT、
    FORWARD。用户可以自定义Chain,也可修改Chain中的Rule。稍后将介绍不同Table中
    默认Chain方面的知识。
  • Rule就是iptables工作的规则。首先,系统将检查要处理的数据包是否满足Rule设
    置的条件,如果满足则执行Rule中设置的目标(Target),否则继续执行Chain中的下一
    条Rule。
2.2、iptable命令
2.2.1.基本格式
  1. iptable [-t 表] 命令选项 [连名] 匹配条件 [-j 动作]
  2. 常用命令选项如下:

    • -A 【append】 在指定的连的结尾添加规则

    • -D 【delete】删除指定连中的规则,可以按规则号或规则内容匹配

    • -I 【insert】插入一条新规则,默认是在最前面

    • -R 【replace】 替换某一条规则

    • -L 【list】列出所有规则

    • -F 【flush】清空所有规则

    • -N 【new】自定义一条规则连

    • -X 【–delete-chain】 删除用户自定义规则连

    • -P 【policy】设置默认策略

    • -n 【numeric】以数字方式显示,如:显示ip,但不显示主机名

    • -v 【verbose】显示详细信息

    • -V 【version】查看iptable的版本信息

    • -Z 清空计数器值

2.2.2.举例
  • iptable -t filter -F【清空filter表中所有规则】
  • iptable -t filter -Z【清空filter表中的计数器值】
  • iptable -t filter -X 【清除filter表中自定义连】
  • iptable -t filter -P INPUT DROP 【设置INPUT连默认策略为DROP】
  • iptable -t filter -P OUTPUT DROP
  • iptable -t filter -P FORWROD DROP
  • iptable -t filter -A INPUT -p tcp -j ACCEPT 【在INPUT连最后添加一条允许tcp协议的数据包进入的规则】
  • iptable -t filter -R INPUT 1 -p tcp -j DROP 【替换INPUT连的第1条规则为拒绝tcp数据包进入】
  • iptable -t nat -vnL –line-number 【以详细的、数字的格式列出nat表中的所有规则】
  • iptable -t nat -D POSTROUTING 1 【删除nat表POSTROUTING 连中的第1条规则】

三.条件匹配
1. 协议匹配:用于检查数据包使用的协议,符合规则就允许,反之拒绝。允许使用的协议名在/etc/protocols文件中。

常用的协议有tcp,udp,icmp,ip 和all。【 -p 协议名 】
- iptable -I INPUT -p icmp -j REJECT 【拒绝进入防火墙本身的icmp数据包】
- iptable -A FORWARD -p udp -j ACCEPT 【允许转发udp的所有数据包】
2. 地址匹配:用于检查数据包的地址是否符合规则,包括源地址和目的地址。【-s 源地址, -d 目的地址】
- iptable -A FORWARD -s 10.0.0.0/8 -j DROP 【拒绝转发来自10.0.0.0/8 网段的数据包】

  • iptable -A FORWARD -d 80.0.0.0/8 -j DROP 【 拒绝转发目的是80.0.0.0/8 网段的数据包】

3.端口匹配:用于检查数据包的tcp或udp端口,需要和 “-p 协议类型” 一起使用【-sport 源端口,-dport 目的端口】
- iptables -A FORWARD -s 10.0.0.0/8 -p tcp –dport 80 -j ACCEPT 【允许转发来自10.0.0.0/8网段,目的端口是80的数据包】

  • iptables -I FORWARD -s 10.0.0.0/8 -p tcp –sport 21 -j ACCEPT【允许转发来自10.0.0.0/8网段,源端口是21的数据包】

4.接口匹配:用于检查数据包从防火墙那个接口进入或出去,来判断是否允许。
- iptables -A FORWARD -i eth0 -s 10.0.0.0/8 -p tcp –dport 80 -j ACCEPT
【允许转发从eth0进入,来自10.0.0.0/8网段,使用tcp 协议,目的端口椒80的数据包】

  • iptables -A INPUT -i eth0 -s 80.0.0.0/8 -j DORP 【拒绝从eth0进入,来自80.0.0.0/8的数据包】

5.SNAT转换:一般linux充当网关服务器时使用
SNAT只能用在nat表的POSTROUTING连,用于对源地址进行转换。要结合 –to 使用。
- iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -j SNAT –to 202.106.1.1
【将来自10.0.0.0/8网段的所有数据包的源地址转为202.106.1.1】

  • iptables -t nat -A POSTROUTING -i eth0 -s 80.0.0.0/8 -p tcp –dport 25 -j SNAT –to 202.106.1.1

6.DNAT转换:只能用在nat表中的PREROUTING连,用于对目的地址或端口进行转换。
- iptables -t nat -A PREROUTING -i eth1 -d 202.106.1.1 -p tcp –dport 80 -j DNAT –to 10.0.0.10
【将从eth1 进入,目的地址是202.106.1.1,使用tcp 协议,目的端口是80的数据包的目的地址转为10.0.0.1】

7.MASQUERADE:伪装,是SNAT的特例。
- iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -o eth1 -j MASQUERADE
【将来自10.0.0.0/8网段,从eth1出去的数据包的源地址伪装为eth1接口地址】

2.2.3.拓展模块

1.按来源MAC地址匹配
- iptables -t filter -A FORWARD -m –mac-source 00:02:b2:03:a5:f6 -j DROP
【拒绝转发来自该MAC地址的数据包】

2.按多端口或连续端口匹配
20: 表示20以后的所有端口
20:100 表示20到100的端口
:20 表示20之前的所有端口
-m multiport [–prots, –sports,–dports]
- iptables -A INPUT -p tcp -m multiport –dports 21,20,25,53,80 -j ACCEPT 【多端口匹配】
- iptables -A INPUT -p tcp -dport 20: -j ACCEPT
- iptables -A INPUT -p tcp -sport 20:80 -j ACCEPT
- iptables -A INPUT -p tcp -sport :80 -j ACCEPT
3.还可以按数据包速率和状态匹配
- -m limit –limit 匹配速率 如: -m limit –limit 50/s -j ACCEPT
- -m state –state 状态 如: -m state –state INVALID,RELATED -j ACCEPT

2.3、CommandListener运用
CommandListener::CommandListener() :FrameworkListener("netd", true) {/** This is the only time we touch top-level chains in iptables; controllers* should only mutate rules inside of their children chains, as created by* the constants above.** Modules should never ACCEPT packets (except in well-justified cases);* they should instead defer to any remaining modules using RETURN, or* otherwise DROP/REJECT.*/// Create chains for children modulescreateChildChains(V4V6, "filter", "INPUT", FILTER_INPUT);createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD);createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT);createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING);createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING);createChildChains(V4, "mangle", "FORWARD", MANGLE_FORWARD);createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING);createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING);// Let each module setup their child chainssetupOemIptablesHook();// Support enhanced firewall @{createChildChains(V4V6, "filter", "firewall", FILTER_FIREWALL);///@}/* When enabled, DROPs all packets except those matching rules. */sFirewallCtrl->setupIptablesHooks();sFirewallCtrl-> initInstrumentTesting();/* Does DROPs in FORWARD by default */sNatCtrl->setupIptablesHooks();/** Does REJECT in INPUT, OUTPUT. Does counting also.* No DROP/REJECT allowed later in netfilter-flow hook order.*/sBandwidthCtrl->setupIptablesHooks();/** Counts in nat: PREROUTING, POSTROUTING.* No DROP/REJECT allowed later in netfilter-flow hook order.*/sIdletimerCtrl->setupIptablesHooks();sBandwidthCtrl->enableBandwidthControl(false);if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {ALOGE("failed to initialize RouteController (%s)", strerror(-ret));}
}

Android - NETD解读相关推荐

  1. android系统(106)---Android Netd ndc

    Android Netd ndc (Native Daemon Connector) http://blog.chinaunix.net/uid-23381466-id-5112474.html Ne ...

  2. android 内核 netlink 上报,Network Daemon(Android Netd)架构和源码分析

    平台: RK3066 ARM9双核 Android4.1 一 Network Daemon(netd)功能概述: Netd是Android的网络守护进程.NetD是个网络管家,封装了复杂的底层各种类型 ...

  3. android netd守护进程机制 --- netd分析

    3 netd分析 3.1 CommandListener初始化 CommandListener的构造方法分为3大步骤: 1,父类初始化,传入netd socket FrameworkListener( ...

  4. android netd和kernelframeworks的通信逻辑

    在应用到Linux内核之间需要一个桥梁,这个桥梁就是Netd守护进程,我们就从Netd守护进程开始去了解一些Android网络系统的工作流程. Netd进程是通过init进程启动的,我们来看看它在in ...

  5. Android——adapter解读

    文章目录 前言 一.adapter是什么? 二.适配器解读 1.继承 2.对象 3.实现方法 4.实现原理 5 .方法联系 6.创建构造方法 三.总结 前言   本篇文章将以常用的RecyclerVi ...

  6. Android内核解读-应用的安装过程

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/19578947 前言 我们知道,在android手机上安装一个apk很简单,只 ...

  7. android netd分析

    1.NetlinkListener.cpp 是 NetlinkHandler.cpp的父类 应该都用来接收内核过来的消息的

  8. Android APP完整基础教程(13)应用资源-动画

    动画模式在android系统中被分为三类,分别为: tween(view) animation:补间动画 frame(drawable) animation:逐帧动画 property animati ...

  9. 专题总纲目录 Android Framework 总纲

    专题总纲说明: 本系列文章虽说是 Android 的知识体系专题,同时也是学习Android Framework 系统的一个思路,尤其是当我们对Android 框架层 一点都不了解的时候,但前提是要有 ...

  10. 学习使用安卓scroller

    Android Scroller 解读编写Demo Scroller是一个专门用于处理滚动效果的工具类,大多数情况下,我们直接使用Scroller的场景并不多,但是很多大家所熟知的控件在内部都是使用S ...

最新文章

  1. 对简单单元格的增删改
  2. mybatisplus 结果_springboot整合mybatisPlus 乐观锁的实现
  3. 多点积分又改规则了_2020年落户广州积分入户名额有多少个?如何加分?
  4. 6 道 BATJ 必考的 Java 面试题
  5. undefined reference to `vtable for XX::XX'
  6. PHP调整图片饱和度,window_Win10系统电脑屏幕的饱和度如何调整?,什么是饱和度? 对电脑来说 - phpStudy...
  7. 如何卸载zabbix且删除
  8. 线性代数及其应用 知识整理
  9. nginx工作原理:
  10. 计算机专业论文答辩ppt,计算机行业毕业论文答辩PPT.pptx
  11. winpe装双系统linux_winPE+ubuntu双系统U盘制作
  12. 【VBA研究】利用DateAdd函数取上月或上年同期的日期
  13. CSS 3.0实现八卦图
  14. Java如何处理参数中带特殊符号的请求?
  15. 2016年9个最受欢迎的H5页面制作工具
  16. 前端实现文字竖向排版
  17. Android WebRtc 桌面投屏、视频源码
  18. 网络安全学习第15篇 - 游戏内存修改
  19. WebRTC Native M96音频基础知识介绍--使用Opus
  20. C++——HIS排班系统for Neuedu

热门文章

  1. android apr分析,APR分析-设计篇
  2. python图像锐化_(python 图像锐化教程)C 实现bmp图像锐化后,锐化的效果很差,求大神帮忙啊...
  3. ​​​​​​​Carryon 数数字
  4. php oop思想
  5. Android的一个登陆注册页面
  6. 在html中加入中文字体,css设置中文字体
  7. 5分钟带你了解Prosody XMPP Server
  8. 只需几步教你学会域名的使用方法
  9. 用Python统计字符串个数
  10. Matlab画堆叠柱状图(颜色设置,x轴外部标注,y轴标注,颜色设置)