本文介绍cocos2d-android实现的Android植物大战僵尸,最后附源码 

内容介绍:

一.游戏最原始的开发框架。

主要会介绍

a)  SurfaceView+SurfaceHolder.CalllBack+Thread

b)  Canvas+Paint

c)  业务处理 

二.Cocos2d-android游戏引擎

a)  什么是游戏引擎

b)  为什么选择Cocos2d

c)  Cocos2d架构

d)  Cocos2d基础

i.  如何使用Cocos2d

1.  CCDirector

ii. 图形引擎Graphic

1.  CCNode

2.  CCScene

3.  CCLayer

4.  CCSprite

5.  CCLable

6.  CCMenu

7.  CCAction

iii.  声音引擎CocosDenshion

iv. Tiled工具使用

v.  粒子系统 

游戏开发一般流程 

三.植物大战僵尸

a)  大体功能

i.  连接服务器失败处理方式。(弹出对话框,引擎线程和UI线程交互)

ii. 根据等级选择植物,白天或黑夜进入游戏界面

iii.         可以种植向日葵收集阳光

iv. 可以种植植物攻击僵尸

v.  僵尸可以吃掉植物和向日葵

b)  架构

i.  MVC

c)  五个界面

i.  登陆界面LoginScene

ii. Home界面HomeScene

iii.         帮助界面HelpPop

iv. 选择界面SelectScene

v.  游戏界面GameScene

游戏开发框架原理

在Activity当中加载一个View,该View继承了SurfaceView并实现了SurfaceHolder.CallBack和Runnable接口。之后在线程中实现界面的不断刷新,在每次刷新当中通过SurfaceHolder取得Canvas对象,整个界面由Canvas绘制。

基础点:

①游戏开发框架 原理

SurfaceView+SurfaceHolder.CallBack和Runnable

②SurfaceHolder sfh = null;

sfh = this.getHolder();//拿到SurfaceView的控制者,SurfaceView在SurfaceHolder内,但是两者不直接打交道,而是通过lockCanvas和unlockCanvasAndPost。

sfh.addCallback(this);

③Canvas画布

canvas = sfh.lockCanvas();//拿到画布

draw…

sfh.unlockCanvasAndPost(canvas);

canvas.save();//保存之前的画布,继续往下绘制

canvas.restore();//还原到上一次画布保存的状态

介绍:

Canvas():创建一个空的画布,可以使用setBitmap()方法来设置绘制的具体画布;

Canvas(Bitmap bitmap):以bitmap对象创建一个画布,则将内容都绘制在bitmap上,bitmap不得为null;

Canvas(GL gl):在绘制3D效果时使用,与OpenGL有关;

drawColor:设置画布的背景色;

setBitmap:设置具体的画布;

clipRect:设置显示区域,即设置裁剪区;

isOpaque:检测是否支持透明;

rotate:旋转画布;

canvas.drawRect(RectF,Paint)方法用于画矩形,第一个参数为图形显示区域;第二个参数为画笔。

canvas.drawRoundRect(RectF, float, float,Paint) 方法用于画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。

canvas.drawLine(startX, startY, stopX, stopY,paint):前四个参数的类型均为float,最后一个参数类型为Paint。表示用画笔paint从点(startX,startY)到点(stopX,stopY)画一条直线;

canvas.drawArc(oval, startAngle, sweepAngle,useCenter, paint):第一个参数oval为RectF类型,即圆弧显示区域,startAngle和sweepAngle均为float类型,分别表示圆弧起始角度和圆弧度数,3点钟方向为0度,useCenter设置是否显示圆心,boolean类型,paint为画笔;

canvas.drawCircle(float,float, float, Paint)方法用于画圆,前两个参数代表圆心坐标,第三个参数为圆半径,第四个参数是画笔; 

④Paint画笔。在自定义绘图当中,可以通过其来改变该View的颜色,字体大小,样式,透明度等。

paint.setAntiAlias(true);//设置抗锯齿 

⑤动手练习 

    publicvoid drawAndroid() {

       //将画笔的颜色设置为绿色

       paint.setColor(Color.GREEN);

       //绘制两根天线

       canvas.drawLine(113, 103, 100, 80, paint);

       canvas.drawLine(190, 100, 205, 80, paint);

       //绘制一个半圆,Android的小脑袋

       RectF oval = new RectF(90, 90, 220, 200);

       canvas.drawArc(oval, 0, -180, true, paint);

       //绘制身体

       canvas.drawRect(90, 150, 220, 315, paint);

       //绘制左手

       canvas.drawRect(50, 150, 80, 245, paint);

       //绘制右手

       canvas.drawRect(230, 150, 260, 245, paint);

       //绘制左脚

       canvas.drawRect(110, 320, 140, 400, paint);

       //绘制右脚

       canvas.drawRect(170, 320, 200, 400, paint);

       //绘制两个小圆,眼睛

       canvas.drawCircle(125, 120, 8, paint);

       canvas.drawCircle(180, 120, 8, paint);

    }

Cocos2d-android游戏引擎

游戏引擎是指一些已编写好的可编辑游戏系统或者一些交互式实时图像应用程序的核心组件。这些系统为游戏设计者提供各种编写游戏所需的各种工具,其目的在于让游戏设计者能容易和快速地做出游戏程式而不用由零开始。

Cocos2d-x

Cocos2d-iphone

Cocos2d-android

Cocos2d-html

本质区别:开发的语言不一样

共同点:api名字基本一致

1.概述

起源于iphone,后经翻译出现了java版

LGame 微云--- 

我们为什么要使用cocos2d 

1.一款开源的,跨平台,轻量级的2D游戏引擎。

2.性能良好

3.广泛的社区支持

4.拥护很多成功案例。比如捕鱼达人,三国塔防等

5.使用MIT授权协议。可以自由使用,修改源码,不需要像GPL一样公开源码,可以商用 

2.架构

帧率:每秒钟屏幕刷新的次数

1.图形引擎Cocos2D Graphic

View的层次

各个知识点介绍:

A)CCApplication,初始化程序,获取屏幕方向,系统语言等,不会直接操作

B)CCDirector,导演类,负责管理和切换场景。还负责初始化openGL各项参数。

CCDirector引擎的控制者,控制场景的切换,游戏引擎属性的设置

i.  单例模式,sharedDirector()取得对象

ii. attachInView()连接OpenGL的SurfaceView

iii.    setDeviceOrientation()设置横屏竖屏

iv. setDisplayFPS(true)显示fps帧率

v.  setAnimationInterval(1.0f/ 60)设置帧率,每秒刷新60次

vi. setScreenSize(480, 320)设置屏幕分辨率,高于设置的分辨率时,引擎会自动适配

vii.    runWithScene()运行场景

viii.   replaceScene()替换场景

ix. resume()进入运行状态

x.  pause()暂停

xi. end()结束

C)CCNode,引擎中最重要的元素,所有可以被绘制的东西都是派生于此。它可以包含其它CCNode,可以执行定时器操作,可以执行CCAtion。

D)CCScene,场景类,例如游戏的闪屏,主菜单,游戏主界面等。

E)CCLayer,图层类。

每个图层都有自己的触发事件,该事件只能对其拥有的元素有效,而图层之上的元素所包含的元素,是不受其事件管理的

查找第一个精灵位置与touch点位置相交的精灵。

//判断是否点中

if(CGRect.containsPoint(sprite.getBoundingBox(), point)){

CCNode有一个辅助属性叫做boundingBox,它返回精灵的边界矩形。

调用CCNode的一个辅助函数,convertTouchToNodeSpace。这个方法做了以下三件事:

1        计算touch视图(也就是屏幕)的touch点位置(使用locaitonInView方法)

2        转换touch坐标点为OpenGL坐标点(使用convertToGL方法

3        转换OpenGL坐标系为指定结点的坐标系(使用convertToNodeSpace方法)

注意:在实现事件处理的时候,①事件event的原点为屏幕的左上角②游戏中的position为屏幕的左下角.故在事件处理的时候一定要先将event点转化为游戏中的点,通过

convertToNodeSpace转化.

比如点击地图

CGPointpoint= tiledMap.convertTouchToNodeSpace(event);

点击当前Layer层的元素

CGPoint point = this.convertTouchToNodeSpace(event);

转化点后,开始判断是否点中了该元素

if(CGRect.containsPoint(sprite.getBoundingBox(),point)){

满足条件,则说明点中了

F)CCSprite,精灵类。

①锚点:设置在屏幕上显示的位置,原点为自身左下角为准,锚点的值乘以被设置锚点的元素宽或高,为移动的距离

a)setRelativeAnchorPoint和setAnchorPoint区别:当不设置setAnchorPoint只设置setRelativeAnchorPoint(false)时锚点为(0,0),如果设置setAnchorPoint,setRelativeAnchorPoint无效

b)和旋转动作的关系:旋转动作时以锚点(0.5,0.5)为中心点,向左向上为正方向,故锚点的原点(0,0)在精灵的左下角。而位置锚点向下向右为正方向。一个是设置为旋转的中心点,另一个是设置位置

c)通常设置位置时,建议锚点全部以左下点为准,即设置锚点为(0,0),用position改变坐标

d)加载Scene或Layer时的锚点,虽然默认他们的锚点是(0.5,0.5),但是被加载的时候用不上锚点。之所以Scene有锚点是因为Scene是CCNode的子类

②坐标

③镜像setFilpX(true)

④透明度setOpacity()0-255之间,值越小越透明,反之越不透明

⑤addChild添加子元素,三个参数

G)CCAction,动作类。 

①移动CCMoveByCCMoveTo

//加载图片资源

CCSprite s =CCSprite.spriteWithSpriteFrameName("nu.png");

s.setAnchorPoint(0,0);//设置锚点

s.setPosition(300,150);//设置坐标

//创建一个移动动作①动作执行所需时间②x或y方向的位移

CCMoveBy move =CCMoveBy.actionWithDuration(1, ccp(100,80));

CCMoveBy moveBack =move.reverse();//reverse返回一个反方向的动作

CCSequence seq =CCSequence.actions(move, moveBack);//按顺序执行相应动作

CCRepeatForever rf = CCRepeatForever.action(seq);//永远重复执行该动作

s.runAction(rf);//运行动作

②缩放CCScaleBy, 除了整体缩放外,还可以xy方向缩放不同的值

放大缩小

float mapWidth= tiledMap.getContentSize().width;

        CCScaleTo zoomin = CCScaleTo.actionWithDuration(1,480/mapWidth);

        CCDelayTime delay = CCDelayTime.actionWithDuration(0.3f);//该动作停留指定时间

        CCScaleTo st = CCScaleTo.actionWithDuration(1,1);

       CCDelayTimedelay2 = CCDelayTime.actionWithDuration(1);

        CCInstantAction call = CCCallFunc.actionWithTarget(this, "mapScaleCallback");

tiledMap.runAction(CCSequence.actions(zoomin,delay,st,delay2,call));

③旋转CCRotateBy,正反旋转

不断旋转精灵实现

xii.    代码:

//第一个参数为经历时间,第二个参数为要旋转的角度

CCRotateBy by =CCRotateBy.actionWithDuration(2, 360);

//设置该动作不断循环

CCRepeatForever seq =CCRepeatForever.action(by);

xiii.   注意点:以锚点为中心点来旋转。如要以中心点旋转,则应设置锚点(0.5,0.5),如果锚点为(0,0),则以左下点为中心点旋转 

④可见性CCHideCCShow

⑤弹跳CCJumpByCCJumpTo。三个参数的含义a)时间b)目标点c)回落高度d)跳动次数

⑥贝塞尔曲线运动

CCBezierConfig bezier = new CCBezierConfig();

       bezier.controlPoint_1 =ccp(10, 10); 起点

       bezier.controlPoint_2 =ccp(50, 100); 控制点,制高点

       bezier.endPosition =ccp(100, 20); 结束位置

    CCBezierBy b = CCBezierBy.action(2,bezier);

⑦闪烁

⑧播放序列帧

CCAnimation加载

如果要停止播放,可以采用stopAction

⑨渐快CCEaseIn。让动作渐快执行,两个参数a)被控制的动作b)加速度,如果为1就匀速了

⑩渐慢CCEaseOut

11.先渐快后渐慢

12.特效,先缓冲再加速CCEaseBlackIn

13.正弦曲线速度变化CCEaseSineInCCEaseSineOut CCEaseSineInOut

14.CCTintTo颜色渐变动作

CCLabellabel label= CCLabel.labelWithString("那些年,我们在传智播客学习的日子", font, 20);

label.setAnchorPoint(ccp(0.5,0.5));

label.setPosition(150,150);

label.setColor(ccc3(255,228, 0));

CCTintTo tint = CCTintTo.actionWithDuration(0.6,285, 182, 0);

CCTintTo back = CCTintTo.actionWithDuration(0.6,255, 230, 0);

CCSequence seq = CCSequence.actions(tint,back);

label.runAction(CCRepeatForever.actionWithAction(seq));

a)CCSequence:按顺序执行动画

b)CCCallFunc:执行指定函数的动作,回调动作

c)CCDelayTime:逗留指定时间的动作

d)CCSpawn:同时执行要被执行的动作

注意:By在执行重复性动画的时候用,To在一次性动画的时候调用,换句话说:To执行完了就结束释放掉了,如果要执行该动画必须重新run该动作,而By是可以还原回到原位置,然后继续循环执行动作。

By传入的CGPoint为x或者y方向的位移差

To传入的CGPoint为目标坐标,该坐标是以屏幕左下角为原点的

I)CCMenu菜单

CClayer的子类

代码:

//初始化

CCMenu menu = CCMenu.menu();

menu.setPosition(0,0);

//加载图片

CCSpritenight=CCSprite.spriteWithSpriteFrameName("item.png");

//添加菜单item

CCMenuItem item =CCMenuItemSprite.item(night,night,this, menuCall);

item.setAnchorPoint(0,0);

item.setPosition(20,160);

//添加item到菜单

menu.addChild(item, TagNight,TagNight);

//添加菜单到界面

this.addChild(menu,TagMenu,TagMenu);

注意:CCMenu默认锚点(0.5,0.5),默认Position为屏幕的中点

J)CCLabel文本 与CCLabelAtlas

//①文本内容;②字体;③文本字体大小

CCLabel label = CCLabel.makeLabel(name,Const.textFont, fontsize);

//①要显示的字符位置;②图片资源;③单个资源的宽;④单个资源的高;⑤起始字符

CCLabelAtlas atlas = CCLabelAtlas.label("12","seedpackets.png", 57, 80,'1');

K)schedule()定时器

X)附加

①帧动画:编辑工具有Zwoptex,TexturePacker

②瓦块地图TiledMap:编辑工具Tiled

③粒子系统:编辑工具PracticalDesigner

2.声音引擎CocosDenshion

a)SoundEngine.sharedEngine().playSound(CCDirector.theApp,  R.raw.bg, ture);最后一个参数是setLooping(true),ture表示要循环播放资源,反之false则只播放一遍

b)对外提供了设置音量,媒体音量,是否要静音,暂停,继续播放,释放内存功能等常用功能

物理引擎

a)  Box2d

b)  Chipmunk 

网络库

a)  libCurl

脚本库Lua

Lua 是一个小巧的脚本语言。是巴西里约热内卢天主教大学(PontificalCatholic University of Rio de Janeiro)里的一个研究小组,由RobertoIerusalimschy、WaldemarCeles 和Luiz Henrique de Figueiredo所组成并于1993年开发。 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。

3.补充

①坐标:

a)  屏幕坐标系:以屏幕的左上角为原点,向右,向下为正方向

b)   GL坐标系:以屏幕的左下角为原点,向右,向上为正方向

注意:在CCLayer的触摸事件处理中,注意两个坐标系的转换

②游戏速度

针对塔防游戏,有时候需要加快游戏速度。可以通过

CCScheduler.sharedScheduler().setTimeScale(ts);

来设置,ts>1表示加速,ts<1表示减速,设置为0则游戏处于静止状态

③使用Handle

④锚点与Position锚点

①CCNode默认锚点(0.5,0.5)

②锚点的原点是相对自身CCNode的左下角,不是屏幕的左下角

       正方向为右或者上

③能够改变自身CCNode的锚点值(x,y)

       X方向的位移:x*CCNode.w

       Y方向的位移:y*CCNode.h

④当x或者y为正值,移动的方向为左边,或者下边

  当x或者y为负值,移动的方向为右边,或者上边

什么地方用到锚点

改变自身CCNode坐标,在执行某些动作的时候会用到

位置Position 

①默认(0,0)为屏幕的左下角或者父CCNode的左下角

②正方向为屏幕的右边或者上边 

一般锚点都设置为(0,0)

只改变Position来调整坐标

===========================================

⑤粒子系统

CCParticleSystem  ps= CCParticleSystemPoint.particleWithFile("demo.plist");

加载plist文件,添加到当前层就偶了

3.瓦片地图TiledMap

1.编辑工具:Tiled

2.输出文件为tmx格式

3.关键字

a)图层Layer

b)对象Object

c)瓦片Tile

4.工具的使用流程

a)创建new一个地图,可以设置:

①地图方向:是否成45°角,成45°角能有一个倾斜视角

②地图大小:单位块的个数

③块大小:单位像素

b)在地图中选择新图块来创建瓦片,瓦片的数量根据需求自定义

c)在地图上根据需求创建Object对象:在图层区域右键添加

d)添加图块属性

5.cocos2d-android提供了CCTMXTiledMap专门用来解析地图文件

a)  其中CCTMXObjectGroup专门用来储存Object对象

CCTMXObjectGroup group =tiledMap.objectGroupNamed("road");

tiledMap.touchMove(event,tiledMap);地图就能滑动起来

//地图上人物行走①实现匀速②添加动作结束后调用相应函数提示③执行动作

float duration =CGPoint.ccpDistance() / 30;

CCMoveTo moveto =CCMoveTo.actionWithDuration(duration,);

CCCallFunc call =CCCallFunc.actionWithTarget(this,"WalkCallback");

CCSequence move = CCSequence.actions(moveto,call);

this.runAction(move);

//播放序列帧①加载array②添加动作③执行

CCAnimation animation =CCAnimation.animation("walk", 0.1f,array);

CCAnimate animate =CCAnimate.action(animation);

CCRepeatForever repeat =CCRepeatForever.action(animate);

this.runAction(repeat);

6.注意

a)  地图编辑好后可选保存参数:zlib或gzip压缩。部分引擎可能只支持一种压缩格式解析,如果引擎不支持可考虑换另一种压缩格式保存地图文件

b)  瓦片引入的图片路径:有时候需要去编辑tmx文件,修改图片路径

c)  Object绘制按先后循序插入对象

i.  生成的对象x,y以左下角点为准

CGSize mapSize = tiledMap.getContentSize();

       CGSizewinSize = CCDirector.sharedDirector().winSize();

    finalint N = event.getHistorySize() - 1;

    if (N < 0)

        return;

        CGPointtouchLocation = CGPoint.make(event.getX(), event.getY());

        CGPointprevLocation = CGPoint.make(event.getHistoricalX(N),event.getHistoricalY(N));

        touchLocation  = CCDirector.sharedDirector().convertToGL(touchLocation);

       prevLocation   = CCDirector.sharedDirector().convertToGL(prevLocation);

       if (prevLocation.x != 0 &&prevLocation.y != 0) {

           if (mapSize.width *tiledMap.getScale()/2 >= tiledMap.getPosition().x &&winSize.width-mapSize.width * tiledMap.getScale()/2 <= tiledMap.getPosition().x) {

               CGPoint mp = tiledMap.getPosition();

               mp.x += touchLocation.x-prevLocation.x;

               tiledMap.setPosition(mp);

           }

           if (mapSize.height *tiledMap.getScale()/2 >= tiledMap.getPosition().y &&winSize.height-mapSize.height * tiledMap.getScale()/2 <= tiledMap.getPosition().y) {

               CGPoint mp = tiledMap.getPosition();

               mp.y += touchLocation.y-prevLocation.y;

               tiledMap.setPosition(mp);

           }

       }

       CGPointmpoint = tiledMap.getPosition();

        if (mapSize.width *tiledMap.getScale()/2 < tiledMap.getPosition().x) {

            mpoint.x = mapSize.width *tiledMap.getScale()/2;

        }

        if (winSize.width-mapSize.width * tiledMap.getScale()/2> tiledMap.getPosition().x) {

            mpoint.x = winSize.width-mapSize.width *tiledMap.getScale()/2;

        }

        if (mapSize.height *tiledMap.getScale()/2 < tiledMap.getPosition().y) {

            mpoint.y = mapSize.height * tiledMap.getScale()/2;

        }

        if (winSize.height-mapSize.height *tiledMap.getScale()/2 > tiledMap.getPosition().y) {

            mpoint.y = winSize.height-mapSize.height *tiledMap.getScale()/2;

        }

        tiledMap.setPosition(mpoint);

4.游戏应用——植物vs僵尸

4.1概述

1.游戏类型

a)        RPG=Role-playing Game:角色扮演游戏

b)       ACT=Action Game:动作游戏

c)        AVG=Adventure Game:冒险游戏

d)       SLG=Simulation Game:策略游戏

e)        RTS=Real-Time Strategy Game:即时战略游戏

f)        FTG=Fighting Game:格斗游戏

g)       STG= Shooting Game:射击类游戏

h)       FPS=First Personal Shooting Game:第一人称视角射击游戏

i)         PZL=Puzzle Game:益智类游戏

j)         RCG=Racing Game:竞速游戏[也有称作为RAC的

k)       CAG=Card Game:卡片游戏

......了解游戏类型是为了在交流的过程中,当别人提到一种类型如rpg游戏,你能立刻知道他说的是角色扮演类型游戏

 2.游戏中的对象

a)        植物

i.         ID

ii.       名称

iii.     建造价格

iv.      类型(攻击类型和增益类型)

v.        CD时间

vi.      伤害值

vii.    金币值

viii.  生命值

b)       僵尸

i.         ID

ii.       名称

iii.     血量

iv.      行走速度

v.        伤害值

vi.      CD时间 

4.2工程配置

1.开发工具eclipse

2.引入jar:cocos2d-android.jar

3.UTF-8编码

4.4基础功能

2.欢迎界面

a)  加载背景

b)  等待效果

c)  获取累计天数业务

d)  右上角显示当前版本号PackageManagerPackageInfo

e)  如果连接失败,自动弹出一个layer提示2.主界面

a)  加载背景

b)  底部栏

c)  菜单

d)  帮助pop

 

 

    /**

     * @param context

     * @return返回一般的对话框

     */

    publicstatic Dialog getDialog(Context context, float f){

       Dialog d = new Dialog(context,R.style.init_game);

       d.requestWindowFeature(Window.FEATURE_NO_TITLE);

       Window window =d.getWindow();

       WindowManager.LayoutParamslp=window.getAttributes();

       lp.dimAmount=f;

       window.setAttributes(lp);

       window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

       window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

              WindowManager.LayoutParams.FLAG_FULLSCREEN);

 

       return d;

    }

//彻底退出应用

android.os.Process.killProcess(android.os.Process.myPid());

Intent intent = new Intent();

       intent.setClass(context,A.class);

       intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

       context.startActivity(intent);

       context.finish();

==========================================================================================

效果图

Android植物大战僵尸附源码_第1张图片

Android植物大战僵尸附源码_第2张图片

==========================================================================================

完整源码

http://download.csdn.net/detail/codehxy/9708531

 

更多相关文章

  1. android animation中的参数interpolator详解
  2. Android游戏开发实践指南(华章程序员书库)
  3. Android 实现一个http 带参数登录
  4. Android那些疑惑(2)-LayoutInflater中inflate方法参数的意义
  5. android 跳转并传递参数
  6. 谷安: 两分钟内呈现 6 小时的 Android 游戏开发过程… [视频]
  7. android动作传感器
  8. [置顶] 我的Android进阶之旅------>Android疯狂连连看游戏的实现

随机推荐

  1. Android全埋点解决方案-认识
  2. Android里Context的使用
  3. Android异步处理常用方法
  4. android上实现Junit单元测试
  5. .net平台借助第三方推送服务在推送Androi
  6. android中MessageQueue,Message,Looper,h
  7. Android,Thread+Handler 线程 消息循环
  8. 《Android移动应用基础教程》(Android Stu
  9. Android系统源码阅读(10):Android 应用程序
  10. 在Android 9 中移植自己的app,用到automov