写点什么

IBM 编程拉力赛中文指南

  • 2014-11-18
  • 本文字数:8228 字

    阅读完需:约 27 分钟

本次编程拉力赛为一项竞速赛事,但在具体规则上有所区别:不同于传统赛车竞技中的亲身驾驶方式,这一次参赛者需要利用自己编写的 AI(即人工智能)控制器引导车辆完成各条赛道。相关编码工作非常易于上手,即使大家并非资深程序员也能够参与其中——事实上,即使各位对于编码工作一无所知、也完全能够创建出具备一定水准的 AI 成果。

如果大家愿意为自己的赛车编写更为精妙的 AI 代码,那么了解 Java 或者其它与面向对象编程语言相关的基础知识将成为您良好的助力——如果不甚了解也无需灰心,编程拉力赛正是学习这方面技能的绝佳起点。

想要参加比赛并开始自己的速度竞逐?

遵循以下几个步骤以开启您的竞逐之路:

  • 注册报名并下载竞赛软件
  • 从零开始参加竞逐——只需 60 秒
  • 构建自己的第一款赛车 AI

在成功让您的车辆在赛道上跑起来之后,请点击此处阅读不同赛道之间的差别,从而确保自己的AI 方案能够正确指导车辆在每条赛道上顺利抵达终点。

第一步 注册并下载竞赛软件

要获取竞赛软件的具体安装说明以及安装器使用介绍,请点击此处查看安装说明页面。我们还在1.3 版本中加入了一些新鲜内容,大家可以点击此处了解相关详情。

请大家登陆IBM Code Rally中国赛区网站注册报名并下载竞赛软件。

第二步 从零开始参加竞逐——只需60 秒

编程拉力赛软件已经安装完成——接下来该做什么?

当编程拉力赛软件安装完成之后,参加比赛就变得非常简单了。下面大家需要完成的第一项任务就是启动编程拉力赛客户端。在客户端启动完毕后,大家应该看到以下界面。

点击“Welcome”选项卡上的“x”即可关闭当前欢迎界面。关闭后,大家应该看到以下界面。

在这一视图中,大家需要点击Windows > open perspective > other 选项并选择列表顶部的“code rally”项目。点选完成后,编程拉力赛客户端会显示以下界面。

现在大家看到的是编程拉力赛客户端的基础视图——其中面积广大且空空如也的区域正是各位编码的主要阵地。屏幕底部的部分(即‘my races’)将显示大家在游戏中已经完成的竞赛信息,而左手边的区域则显示大家已经创建完成的赛车列表外加所接入的服务器。在真正参与竞赛之前,大家需要首先接入一台服务器——最便捷的方式当然是直接连接IBM Cloud Server。IBM Cloud Server 是一套免费向大家开放的全球服务器,在这里任何参赛者都能加入竞逐并与来自世界各地的对手们进行圈速比拼!

如何接入IBM Cloud Server

为了与该云服务器相连接,我们需要点击界面左手边的“Servers”标签——这时对应区域的显示内容将会有所变化,具体如下图所示。

这时列表中应该显示出1 台可接入服务器——即“My Computer”或者“IBM Cloud”——无论实际显示条目是哪一种,大家都需要右键点击并选择“edit”从而打开以下界面。

IBM Cloud 信息

主机: http://www.coderallycloud.com

端口: 49030

注意: 如果您打算正式参加本次编程拉力挑战赛,请暂时不要使用这一服务器。适合您的服务器选项取决于您所在的具体区域:

北美: challenge-na.coderallycloud.com

欧洲: challenge-eu.coderallycloud.com

巴西: challenge-br.coderallycloud.com

印度: challenge-in.coderallycloud.com

中国: challenge-cn.coderallycloud.com

全部服务器使用同一端口: 49030

大家需要在上图所示界面中填写相关信息,其中主机与端口已经列出。您还需要为自己输入一个用户名——该用户名可供其他参赛者查看,如果您对其作出变更、原有竞赛历史记录也将丢失。如果您所选择的用户名已经存在,系统将给出对应之提示信息——如果您此前并没有使用过这一用户名,则需要另外选择一个新的用户名。

当用户名设置完成之后,各位就做好了参加竞赛的全部准备——接下来要做的就是创建一辆赛车并投身于赛道当中。

创建一辆基础赛车

如果大家希望了解更多与赛车创建以及编写首款 AI 方案相关的具体信息,请点击此处阅读相关博文,并按照其中的指示分步操作。不过如果您打算快速加入到竞赛当中,则可以直接遵循以下基础说明。

在编程拉力赛客户端中,大家需要对左侧面板中的“vehicles”标签做出变更。

现在大家需要点击绿色的“ +”来打开赛车创建窗口。

接下来大家需要为自己的车辆设定一个名称(请不要用数字作为名称的起始内容,而且名称内容必须由字母及数字构成)。接下来我们会看到几个车辆属性选框——在每个选框当中输入 1(要了解各个选框的含义以及如何对其进行合理利用,请点击此处查看我们的车辆创建指导博文)。接下来点击“AI Implementation”之下的“intermediate”选项。这时大家会看到五个新的下拉菜单——第一个为大家应当接入的服务器名称,我们不必管它。接下来的四个选项旨在选择大家的车辆在不同场景之下的行为方式——在四个选框中分别做出选择而后点击“提交”。现在,大家的车辆将会出现在界面左侧区域——右键点击其名称并在 race on… > your server name 当中输入服务器名称。而后,客户端会要求您选择一条竞赛赛道——赛道可以任意选择,同时指定“vehicle type”——所谓车辆类型是指您的赛车在竞赛当中的外观样式。当选择好了赛道以及车辆类型之后,即可点击 enter race 以开始比赛。

如何查看比赛过程回放

在用户界面底部,大家现在应该看到自己刚刚加入的竞赛入口——如果这时已经有其他参赛者加入到同一条赛道中来,我们就会直接参与到比赛当中。如果当前没有其他参赛者,那么系统会生成一场新的竞赛。大家在创建一条新赛道之后可以最多等待 30 秒,在此期间其他后续加入的参赛者将与您同场竞技。对手加入后竞赛即开始进行,整个比赛过程耗时在 10 到 30 秒之间。您无法在比赛过程中直接查看车辆行进状态,但当竞赛状态变更为“finished”(已完成)后,大家能够双击表格中的对应竞赛并在赛事回放窗口中查看具体过程,如以下截图所示。(提示:如果您的竞赛耗时超过 1 分钟才变更为‘finished’状态,请点击列表上方的同步按钮——该同步按钮图标为两条指向方向相反的黄色线条。)

在这里,大家可以点击屏幕下方的蓝色 play 按钮,竞赛也将就此展开——旁边注有名称的车辆来自其他参赛者——如果所显示的车辆旁边没有名称,则意味着其是由竞赛软件本身所生成的 bot 操控对象。每次竞赛需要跑完三圈赛道,且最高耗时为 5 分 30 秒(如果您在这一时间上限之内仍然未能完成比赛,则会被标记为‘did not finished’,即未完成状态)。

查看您的排名情况

如果大家希望了解自己与其他参赛者的圈速对比,请点击此处查看 IBM Cloud 排行榜——各位能够通过变更设置来根据平均圈速、最高圈速或者特定赛道最高圈速显示上榜名单。

下一步

现在大家已经了解到关于接入 IBM Cloud Server 以及进行竞赛的基础知识,如果希望获取更多创建高级 AI 或者学习如何对当前中级难度 AI 做出调整的信息,请点击此处阅读“如何创建更出色的车辆 AI”资料。

第三步 创建自己的第一款赛车 AI

在编程拉力赛的赛道上创建赛车 AI 非常简单——没错,非常非常简单!

大家可以创建两种不同类型的赛车 AI——其中 Intermediate AI(即中级 AI)是最容易创建的类型,而且无需任何编程技能作为前提。相比之下,打造出更为先进的高级 AI 则为大家提供了发挥个人能力的灵活空间,允许各位根据自己的思路构建圈速表现更出色的 AI 成果。高级 AI 最短只需三行代码即可完成,当然如果大家愿意、也可以编写出长达数百行的高复杂度代码——选择权完全掌握在您自己手中!

如果大家希望了解如何设置竞赛软件、加入竞赛并查看竞赛过程回放,请点击此处先行阅读“从零开始参加竞逐——只需 60 秒”章节。

创建自己的车辆

要创建自己的车辆,大家首先需要遵循以下步骤:

  1. 在 eclipse 中编程拉力赛客户端内点击屏幕左侧 vehicles 标签处的绿色“+”按钮,从而创建一辆新赛车。
  2. 在右上方的文本框内为您的车辆设定名称——请不要留空,务必记得填写!
  3. 在创建新车辆窗口的中部,大家会看到五个下方带有数字的选框——这些选框负责控制竞赛车辆的基本物理属性。
  4. 目前极速调整选项尚处于禁用状态。
  5. 加速选项控制车辆的提速能力。
  6. 重量选项控制车辆的自身重量——重量较大的车辆在与其它对手车辆或障碍物相撞时更不容易出现位移或者偏转,但在以高速通过急转弯时更容易因抓地力不足而打滑。
  7. 护甲调整选项目前尚处于禁用状态。
  8. 牵引力选项决定车辆自身的“掌控”能力——牵引力数值越高,车辆越不容易出现打滑状况。
  9. 转向选项负责控制车辆在转向时所能实现的最小弧形角度——这一数值越高,车辆就能以更小的弧形角度完成过弯。

大家能够在每种不同物理属性当中设定的数值是相对而非绝对的,且各项数值的占比总和为定值。最直观的理解方式就是假设当前车辆拥有 100“点”可分配数值,大家可以将其任意指派给上述每种属性,而每种属性的具体数值都会对车辆的实际表现产生影响。在每个选框下方都显示有一个百分比数值,这代表着当前各项数值在可分配总体数值当中所占权重,也便于大家快速查看当前车辆在哪种属性上较为偏重。有鉴于此,如果一辆赛车的各项属性数值均为 1,那么其实际运行效果与各项数值皆为 1000 的配置方案完全相同。提示:每个选框当中的数值至少需要为 1,否则车辆将无法正常运作。

中级与高级车辆二者在这方面并无区别,都需要添加上述属性分配信息。大家可以查看下一章节以了解如何创建属于自己的中级或者高级赛车。

编辑自己的赛车

大家可以随时在车辆列表当中右键点击对应车辆并选择“edit”来变更对应 AI 的具体设置——其中包括前面提到的各类选项以及中级 AI 条目。

中级 AI

要创建一套中级 AI,大家首先需要查看窗口左下方的“AI Implementation”区域。这里共提供两 2 上选项——“Intermediate”(中级)与“Advanced”(高级)。客户端会默认选择高级选项——如果大家将其变更为中级,则车辆 AI 会切换至中级模式、同时提供更多细节选项。

现在大家会在窗口左下方看到五个下拉式选框——其中每一个选框都标注为一种与 AI 方案相关的行为机制。对这些选框内容的修改会直接影响到您的车辆在赛道中的实际表现。

  1. API - 这一选项决定您从哪台服务器处获取 AI 选项。目前所有竞赛软件版本都配备同样的选项,因此我们可以姑且将其忽略。未来如果有更多选项被添加进来,大家可能需要选择其它备选服务器、同时注意不要选择当前竞赛服务器所不提供的相关选项。
  2. Race start - 这些选项用于控制车辆在竞赛起步阶段的运行方式。
  3. Off track - 这些选项负责确定在偏离赛道之外时,车辆将采取怎样的应对措施。
  4. Opponent in proximity - 此选项负责决定其它车辆靠近时,我们的赛车应该采取怎样的应对措施。
  5. Checkpoint - 此选项负责决定在抵达检查点并发现前方存在转变时,我们的车辆应该采取怎样的应对措施。

一旦大家为以上选框选择了合适的选项之后,点击“submit”提交设置结果,这样我们的中级 AI 就创建完成了——大家现在可以在车辆列表中右键点击对应车辆并驱动其加入竞赛当中!

高级 & 代理 AI

如果大家希望新手编写属于自己的 AI 或者代理方案,则可以将“AI Implementation”设置为高级模式,这样我们就可以自己动手从零编写或者对已经进行过竞赛的车辆 AI 代码作出修改。一旦大家选定了自己打算使用的 AI,请点击“submit”进行提交。完成上述步骤后,大家应该会在车辆列表当中看到刚刚加入进来的新 AI——这时双击对应车辆,客户端会直接打开 AI 文件、我们就从这里开始将自己的奇思妙想添加进去。

在开始编写自己的 AI 方案之前,请大家先行阅读以下几条基本原则:

  1. AI 方案需要利用 Java 进行编写。
  2. 如何实现车辆导航。在考虑如何设定车辆的导航轨迹时,最好的办法就是将其与奥运会中的滑雪比赛联系起来并等同看待——整条赛道当中分布着一系列检查点,而且不同路段的宽度也会有所区别。大家的车辆能够获取到与这些检查点相关的位置信息,并据此选择车辆通过检查点时经由的坐标。车辆必须经过全部检查点才会被视为完成一圈。如果大家错过了其中某个检查点而直接前往其它检查点了,也不需要过份担心——只需要让车辆重新返回错过的检查点再继续完成其余部分赛道即可。
  3. 如何引导车辆前进。大家无法对车辆的前进方向进行直接控制——当需要车辆转变时,您需要为其设置一个“目标”、而后车辆会转而驶向该目标(或者,如果该目标与车辆当前位置过于接近,您所设置的转向数值又过小而导致其转弯角度太大,那么车辆会不断转变直到最终经过设定好的目标)。
  4. 如何控制车辆速度。大家可以通过设置加速与制动百分比来控制车辆的加速与减速能力,但无法直接为车辆指定比赛当中的固定行进速度。大家可以首先了解车辆的极速水平,并在其达到极速时立即将加速能力设置为 0,这样能够相对直接地实现对车速的控制。
  5. 撞上地图边缘。如果大家的车辆撞上地图边缘,那么其需要在接下来的 6 秒中受到加速惩罚,即车辆有 6 秒钟无法实现快速加速。如果在此期间车辆再次撞上地图边缘,那么 6 秒钟惩罚时长将被重置。
  6. 尽管大家也可以对其它参赛车辆对象进行修改,但却无法加以控制——获取某位参赛对手的 carAI 类并调用 setAccelerationPercent(0); 或者 setBrakePercent(100); 并不会降低对方的速度——当然,这种恶劣的想法还是不错的!

拉力赛高级 AI 代码采取的则是事件驱动机制——这意味着当竞赛过程中出现特定状况时,AI 中的对应方法将得以调用、从而判断车辆应该采取怎样的应对措施。下面来看竞赛当中车辆会随时监听的九种事件:

  • onCarCollision - 当车辆主要撞击或者与被其它车辆撞击时,此事件将被调用。在这种情况下,客户端会显示与您相接触的车辆对象,这样大家就可以选择主动撞击以完成复仇。
  • onCheckpointUpdated - 当您的车辆通过赛道当中的下一个检查点时,此事件将会被触发——客户端会显示您刚刚经过的检查点信息。如果您希望将接下来的检查点设为目标,则可调用 call getCar().getCheckpoint();
  • onObstacleCollision - 当您的车辆撞击或者被撞击偏移而接触到非车辆对象时,此事件将会被触发。客户端会显示出撞击到的非车辆对象,您可以提前加以回避以避免由于反弹而再度与之接触。
  • onObstacleInProximity - 当您的车辆在竞赛中接近某个非车辆对象时,此事件将会被触发。客户端会显示出相关对象,您可以发现其确切位置并尝试进行回避。
  • onOffTrack - 当您的车辆在竞赛中脱离赛道时,此事件将会被触发。当脱离赛道时,车辆的牵引力将有所下降、而且您明显无法再通过任何后续检查点,但这种情况并不会带来其它额外惩罚。
  • onOpponentInProximity - 当您的车辆与其它参赛车辆距离较近时,此事件将会被触发——客户端会发出处理提示,您则能够通过其当前位置及时加以回避。
  • onRaceStart - 竞赛起始阶段,此事件将会被触发。提示:请在起始阶段保持车辆加速同时为其设定行驶目标,否则整场比赛将耗时甚久。
  • onStalled - 当您的车辆有 6 秒钟未进行移动时,此事件将会被触发。提示:当该事件被调用时,请让您的车辆立即开始前行。
  • onTimeStep - 竞赛过程中,每 100 毫秒该事件会被触发一次,因此您可以在其中添加代码以控制车辆的极速数值、检查车辆是否已经停止移动并完成其它任何您认为有利于提高圈速成绩的操作。

除了以上九种事件之外,代理 AI 还拥有另一种额外事件:

  • init - 每当您启用 / 更新自己的 AI 方案时,该方法将会被触发——如果您在竞赛开始之后的过程中对 AI 进行更新,onRaceStart 将不会被调用——因为其只适用于竞赛初始阶段。在这种情况下,建议大家利用 init 方法来确保自己的 AI 方案能够在更新之后得到完全初始化(更新操作会导致 AI 当中的全部条件信息丢失)。

除了前面提到的项目之外,您无法为自己的车辆添加更多事件,但却可以在事件当中调用更多由您自己创建的方法。

一旦您完成了车辆 AI 的编写工作之后,请右键点击对应车辆并选中“enter race”选项从而加入到比赛当中。竞赛将随即进入排队阶段并开始进行。如果大家使用的是高级 AI 方案,那么竞赛将以速度高于实时方式的模拟方式进行,您需要等等竞赛模拟完毕之后方能查看过程回放、而且在在此之前您向 AI 提交的任何代码变更都不会被账户接受。如果您选择使用代理 AI,那么竞赛将以实时方式进行——除非有多位其他参赛者所使用 AI 代码质量糟糕或者服务器延迟过高,从而导致服务器耗费大量时间进行比赛过程回放。在这种情况下,所有参赛者都会感受到显著的处理时耗提升。在使用代理 AI 时,您可以双击“my races”视图下的对应竞赛以查看车辆的竞速过程——当前查看到的状态可能与实际模拟情况存在数秒种延时,因此您对于 AI 所做出的任何代码变更都需要在几秒钟之后才能切实反映在车辆行为之上,这是因为回放窗口自身存在一定延时。为了让您对代码的变更快速起效,最好的办法是在竞赛过程中保存对应 AI 文件——您的 AI 将由 WebSphere Application Server Liberty Profile 以极高速度进行重新启用(所谓极高速度,通常指 1 秒以内),而竞赛模拟机制随后会与更新版本进行通信。

以下所示为大家可以创建的最简单高级 / 代理 AI 代码:

@Override

public void onCheckpointUpdated(CheckPoint cp) {
getCar().setTarget(AIUtils.getClosestLane(getCar().getCheckpoint(), getCar().getPosition()));

}

@Override

public void onRaceStart() {

getCar().setAccelerationPercent(100);
getCar().setTarget(AIUtils.getClosestLane(getCar().getCheckpoint(), getCar().getPosition()));

}

通过以上代码,您的车辆会在竞赛起始阶段将加速数值提升为 100%,并将下一个检查点的中心设定为前进目标。当车辆通过该检查点时,继续将接下来的检查点设为目标。这是一套非常基础的 AI 方案——而且在一条直道上应该可以取得很好的成绩。然而实际赛道中存在着众多需要减速通过的转弯区域、限制车速拉升的路段以及需要回避的障碍物,因此大家需要加入更多构思才能取得良好的比赛成绩。

高阶技巧

  • 通过使用“getCar().getVelocity().normalize();”,您将能够以英里每小时为单位了解车辆的行驶速度——如果大家希望对车速加以限定,那么这种方法将极具实用性,这样我们就能在检测到当前车速达到或者超过限定速度时将加速属性降低为 0、甚至是利用制动方式来实现硬性减速。大家还可以利用它来检查自己的车辆是否行驶得过慢,并在适合的条件下松开刹车转而进行加速。注意:目前的 jbox2d 库尚未被添加到您的 AI 类路径当中,因此对应代码会显示为编译错误——大家可以忽略这部分提示,在竞赛正式开始时服务器将把类路径部署到位。
  • 您可以通过调用“getTrack().getCheckpoints();”来获取一份当前赛道上全部检查点的信息列表——如果大家打算纵览全部检查点并计算出最理想的行进路线,那么这些信息将极具参考价值。
  • 如果需要检查您的车辆是否需要在在下一个检查点之前进行转向,一大简单方式在于使用“getCar().getCheckpoint().getIntersectionPoint(getCar().getRotation(), getCar().getPosition());”——如果您的车辆无需转向即可通过下一个检查点,那么其会返回一条位置信息 ; 如果您的车辆需要转向才能通过下一个检查点,那么其会返回 null 结果。如果大家获得的返回结果为 null,则可以选择降低车速并从容检查车辆如何才能正确经过接下来的检查点,而后停止减速、重新开始加速。
  • 您无法创建自己的子类、额外类或者使用反射机制,这是受到竞赛软件自身的架构模型所限。我们已经意识到应该对这一问题做出调整,但就目前而言我们还不允许参赛者在车辆中使用更多初始类。
  • 您无法使用竞赛服务器代码包含范畴之外的任何库——默认情况下不包含在 AI 编辑器当中,但却仍然可用的惟一例外为 Jbox2d——如果有必要,请随意将其添加到自己的类路径当中。
  • 您无法从自己的 AI 中获取到 system.out/system.err 信息,也无法将其运行在调试模式之下。我们已经意识到这个问题,也理解这种修正机制能够帮助大家更好地理解整个竞赛体系。
  • 我们还准备了更多关于车辆 AI 的细节信息以及关于编写 AI 的说明文档——大家可以点击此处下载这方面材料。

代理技巧

  • 请确保使用 init() 方法来设置车辆的速度、目标以及其它任何 AI 所需要的状态信息,在对 AI 代码进行更新后、之前保存在其中的全部变更都将丢失且需要重新加以填写。最理想的更新实施方式就是使用 init 方法。
  • 针对高级 AI 所编写的代码在理论层面上也能够作用于代理 AI,因此大家可以将现有逻辑直接从高级 AI 当中复制到新的代理 AI 处。

仍有疑问?需要更多帮助?

点击此处查看我们的常见问题页面,其中提供的一份参赛者最常遇到的问题列表,当然也包括解决这些问题的办法。如果您的问题在那里仍然得不到解决,请点击此处通过论坛获取更多帮助! 本文原载于 developerWorks

2014-11-18 20:061307
用户头像

发布了 501 篇内容, 共 254.8 次阅读, 收获喜欢 59 次。

关注

评论

发布
暂无评论
发现更多内容

Android开发3年,4个月面试,终于拿到小米、京东

android 程序员 移动开发

android之Fragment(官网资料翻译)

android 程序员 移动开发

基于 Istio 的全链路灰度方案探索和实践

阿里巴巴云原生

阿里云 云原生 istio 灰度 全链路

Android刘海屏、水滴屏全面屏适配详解

android 程序员 移动开发

Android动画之补间动画

android 程序员 移动开发

android各种提示Dialog 弹出框

android 程序员 移动开发

Android岗高频面试题二集,看你能答出几题?(含答案

android 程序员 移动开发

Android开发 申请Mob的SMSSDK的短信验证码功能中获取MD5签名(更新中)

android 程序员 移动开发

Android中自定义下拉样式Spinner

android 程序员 移动开发

Android低版本上APP首次启动时间减少80%(一)

android 程序员 移动开发

Android命令Monkey压力测试,详解

android 程序员 移动开发

Android体系化进阶学习图谱:我们究竟还要学习哪些Android知识?(某大厂内部资料

android 程序员 移动开发

Android使用ViewPager实现图片轮播系列之四:手动滑动 + 左右箭头 + 删除数据

android 程序员 移动开发

android各种提示Dialog 弹出框(1)

android 程序员 移动开发

Android屏幕适配方案

android 程序员 移动开发

Android平台HTTPS抓包全方案

android 程序员 移动开发

Android仿QQ锁屏状态下消息提醒(震动+提示音)

android 程序员 移动开发

Android刘海屏、水滴屏全面屏适配方案

android 程序员 移动开发

Android厂商推送冲突了。。。

android 程序员 移动开发

android图片加载库Glide4使用教程(项目中如何快速将Glide3替换成Glide4)

android 程序员 移动开发

Android备忘录《内存泄漏》

android 程序员 移动开发

Android开发3年,九月份面试12家大厂跳槽成功,我有一些面试经验想分享给你们

android 程序员 移动开发

Android事件分发机制三:事件分发工作流程

android 程序员 移动开发

Android修炼系列(十二),自定义一个超顺滑的回弹RecyclerView

android 程序员 移动开发

消息队列RocketMQ应对双十一流量洪峰的“六大武器”

阿里巴巴云原生

阿里云 RocketMQ 云原生 消息队列 流量

Android企业级实战-界面篇-2

android 程序员 移动开发

基于 OpenYurt & EdgeX Foundry 的云边端一体化解决方案

阿里巴巴云原生

云原生 边缘计算 openyurt EdgeX Foundry

Android应用进程间通信之Messenger信使使用及源码浅析

android 程序员 移动开发

Android内存泄漏问题

android 程序员 移动开发

Android属性动画——ObjectAnimator类及浮动菜单的实现

android 程序员 移动开发

Android常见问题及开发经验总结(一)

android 程序员 移动开发

IBM编程拉力赛中文指南_IBM_崔康_InfoQ精选文章