Android项目基本结构详解
一、简介
第3章虽然通过百度地图应用展示了你可能感兴趣的内容,但是,如果你是一个初学者,一开始就看懂和理解代码可能会非常费劲。为了解决此问题,从这一章开始,本模块将从最基本的内容讲起,带你逐步进入用C#进行Android应用开发的乐园。
二、AndroidApp入口
要用C#开发Android应用程序,首先需要对项目的基本结构有一个感性认识。如下图所示:
Android应用程序使用的是单一入口,源程序中并不能一眼看出程序从哪开始运行,当应用程序加载到内存中时,Android操作系统会自动从内部自定义的入口处开始运行该应用程序。这种独特的架构可让程序员设计的代码直接和Android操作系统交互,但其缺点也很明显:即使一个非常简单的程序,也会变得很复杂。
为了方便找到程序入口点,当新建一个Android项目时,默认情况下,在MainActivity.cs文件的特性声明中通过“MainLauncher = true”表示程序从这个Activity开始执行:
[Activity(Label = "myDemos", MainLauncher = true)]
public class MainActivity : Activity
{
//……
}
注意MainActivity.cs的前缀,一般表示它控制的是布局文件Main.axml(但不是必须这样做)。比如,如果把Main.axml改为First.axml,那么它对应的Activity类最好命名为FirstActivity.cs,换言之,前缀只是为了方便查看它和界面的对应关系而已,这仅仅是一种命名习惯,而不是对文件命名的必然要求。
实际上,你可以把“MainLauncher = true”放到任何一个继承自Activity的类中。总之,你将其放到哪个类的特性声明中,程序就从哪个Activity开始执行。
三、资源文件(Resources文件夹下的各种文件)
资源文件是指在代码中使用的并且在编译时就被打包到应用程序中的附加文件。根据资源的类型,这些文件分别被保存在项目中的不同文件夹下。
在项目的Resources文件夹下,根据资源类型,可将其分为多个子文件夹。这些资源包括图片,音频文件,以及任何与程序可见内容相关的东西。例如,自定义的动画、菜单、风格、颜色以及由.axml文件(带设计界面)或.xml文件(不带设计界面)描述的用户交互页面的布局等。
1、基本概念
Resources文件夹下保存的资源不包括代码文件,而是指随项目一起编译和打包的除了代码文件之外的其他文件,如声音、视频、图像、字符串、布局文件等。
将资源单独组织的优点是:代码分离,这样就可以适用于多平台,同时也能在编译时进行检查,并在编译通过后为键入C#代码提供智能提示。
使用资源可使修改程序的特征变得很容易,同时不必修改代码。而且通过可选择的资源集合,能为不同的设备配置优化程序(例如不同的国家语言和不同的屏幕尺寸等)。
系统搭建工具会自动为Android项目使用的每一个资源都定义一个唯一的整型ID,程序通过ID获得代码中的资源或者.xml或.axml中的其它资源的引用。例如,程序包含了一个名叫logo.png (保存在 Resources/drawable/ 目录下)的图片文件,工具就会生成一个叫做Resource.drawable.logo的资源ID,程序中可以用这个ID引用该图片。
分隔资源的一个重要能力是为不同的设备配置提供可选择的资源文件。例如,在XML中定义UI字符串,就可以把这些字符串翻译成其它的国家语言,并在分隔的文件中分别保存。然后,根据追加到资源目录名中的国家语言修饰符(例如res/values-fr/是法语字符串的值)和用户对国家语言的设置,Android系统就会提供适当的国家语言字符串到UI中。
Android支持很多不同的修饰符以支持可选的资源。这些修饰符都是被加入到资源目录名中的简短字符串。这样定义是为了定义设备的特定配置,以保证这个目录下的资源可能会被使用到。
2、基本的资源文件夹
基本的资源文件夹用于保存项目中常用的资源文件。
(1)Drawable文件夹
Drawable文件夹:保存通用的图片文件。
Drawable resources(可绘制资源)是编译到应用程序中的图像资源,这些资源可通过API去调用或引用其他的XML资源。例如,位图文件 (.png、.gif、.jpg)、可调整大小的9-Patch格式的图像、在XML文件中定义的状态列表、通用的形状定义、……等。
通过Resources.Drawable获取该文件夹下的这些文件资源。
说明:9-Patch图像(*.9.png)的含义及用法见本博客第5章介绍的图像示例。
(2)其他与分辨率对应的图片资源文件夹
其他与分辨率对应的相关文件夹还有:
drawable-ldpi文件夹:保存低分辨率手机专用的图片文件。
drawable-mdpi文件夹:保存中等分辨率手机专用的图片文件。
drawable-hdpi文件夹:保存高分辨率手机专用的图片文件。
drawable-xdpi文件夹:保存超高分辨率手机专用的图片文件。
drawable-xxdpi文件夹:保存超超高分辨率手机专用的图片文件。
(3)layout文件夹
layout文件夹用于保存所有界面文件(包括带设计界面的.axml文件和不带设计界面的.xml文件),如果你高兴,可随时将.xml换名为.axml,或者随时将.axml换名为.xml。
一般用Main.axml作为默认的App主页布局文件,有Design和Source两种模式。
该文件夹下的文件都是可编译为屏幕布局的XML文件,可通过Resources.Layout获取这些文件资源,通过Resources.Id获取这些文件中用id定义的资源。
(4)Values文件夹
Values文件夹存放用字符串描述的XML文件,该文件夹下的这些文件中包含的都是一个或多个简单的字符串类型的值或列表(strings、integers、colors等)。比如在一个XML文件中用字符串列表定义一系列颜色。
Values文件夹资源文件都是文本资源,在文件夹中有一些约定的文件名称。另外,该目录下的XML文件中的值可定义多个资源而不是只能定义单个资源,例如:
String.xml:保存字符串值列表,通过Resources.String获取这些资源。Colors.xml:保存颜色值列表,通过Resources.Color获取这些资源。Arrays.xml:定义数组,通过Resources.Array获取这些资源。Dimens.xml:保存尺度值,通过Resources.Dimens获取这些资源。
(5)animator文件夹
该文件夹下保存的是用属性动画描述对象的动画的XML文件。其用法和WPF的属性动画相似,是一种非常灵活而强大的描述动画的方式,可描述各种类型的对象。
“属性动画”是在API level 11 (Android 3.0)开始提供的。
注:Android的属性动画和WPF的属性动画的用法非常相似,只要学会其中一种,很快就会另一种的用法。
(6)anim文件夹
该文件夹用于保存描述补间动画(tween animations)的XML文件。
补间动画用一系列的动画指令对视图对象的内容执行转换,用于将View对象转换为动画,比如边旋转边增大图像或文本的大小。
由于旋转图像或文本会导致占用的容量快速膨胀,因此补间动画只能用于View对象。
(7)color文件夹
该文件夹用于保存描述颜色状态列表的XML文件。颜色状态列表是指用颜色表示每个状态的变化,比如按钮按下、禁用等。要让按钮的随着状态的变化而引起颜色的改变,就需要定义这样一个颜色状态列列表,用它来描述这些颜色状态的变化。
(8)menu文件夹
该文件夹保存的是描述菜单的XML文件,如【应用程序选项】菜单、弹出菜单、上下文菜单(快捷菜单)、子菜单等。
通过Resources.Menu获取该文件夹下的这些文件资源。
(9)raw文件夹
用原始的二进制形式保存的任意文件。这些文件仍然以二进制文件的形式被编译到Android应用程序中。
通过Resources.Raw可获取该文件夹下的这些文件资源。
(10)xml文件夹
该文件夹下保存的XML文件类似于.NET的配置文件,即:该文件夹下保存的都是可以由应用程序在运行时读取的任意XML文件。
通过Resources.Xml获取这些资源。
4、引用资源的方式
当将资源文件添加到项目中时,管道会自动将该资源描述添加到资源中,并自动生成对应的类(保存在Resource.designer.cs文件中),自动生成的类会为每个资源自动分配一个唯一的ID。
有两种存取和管理资源的方式:
-----在C#代码中引用资源。
-----在.xml或者.axml布局文件中引用资源。
(1)方式1--在C#代码中引用资源
格式:@[<PackageName>.]Resource.<ResourceType>.<ResourceName>
例如:
SetContentView(Resource.Layout.Main);EditText phoneNumberText = FindViewById<EditText>(Resource.Id.PhoneNumberText);
(2)方式2--在xml(或者axml)布局文件中引用资源
格式:@[<PackageName>:]<ResourceType>/<ResourceName>
例如(见Main.axml文件):
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:id="@+id/myImage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/flag" /></LinearLayout>
5、其他备用的资源文件夹
备用资源也是保存在Resources文件夹下,子文件夹的格式为:
<ResourceType>-<Qualifier>
即:<资源类型>-<限定标识符>
例如:drawable-hdpi
当有多个限定符时,各限定符之间用短划线分隔。
限定符必须按下表列出的顺序出现(该表实际是按优先级从高到低排列):
例如,v11的目标是API level 11 (Android 3.0)设备。
下面是截图表格中各限定符的含义。
MCC、MNC
手机的国家码(MCC):手机的SIM卡提供商。
移动网络码(MNC):手机连接到的网络供应商。
尽管本地化目标可以使用用于移动设备的国家码,但是仍建议使用资源限定符来限定它。例如:美国(U.S)的资源限定符是 mcc310-mnc026
MCC的完整编码详见下面的网站:http://mcclist.com/
Language
两字母的ISO 639-1国家语言编码以及可选的两字母的ISO-3166-alpha-2区域编码。 如果同时提供这两个限定符,用“-r”分隔。
例如:对于French-Canadian locales,使用的资源限定符为:fr-rCA
完整的语言和区域编码见“Codes for the Representation of Names Of Languages and Country names and code elements”。
Smallest Width
限定屏幕执行时使用的最小宽度。例如:sw320dp 表示目标设备的高和宽最小说320dp。
Available Width、Available Height
Available Width:屏幕最小宽度,格式:w<N>dp,其中N表示与像素无关的宽度。当用户旋转屏幕时会改变该值。例如:w720dp 表示目标设备的最小宽度为720dp
Available Height:屏幕最小高度,格式:h<N>dp,其中N表示与像素无关的宽度。当用户旋转屏幕时会改变该值。例如:h720dp 表示目标设备的最小高度为720dp
Screen aspect
指屏幕的宽高比,而不是屏幕的放置顺序(纵向和横向)。其值有两个:long(表示宽屏幕)、notlong(表示非宽屏幕)。
Screen Orientation
屏幕放置顺序,Portrait(纵向放置)或者landscape(横向放置)。该值可在应用程序的生命周期中改变。取值有:port、land
Dock mode
设备在 car dock或者 desk dock 中。API 8(Android 2.2.x)开始提供。取值有:car、desk
Night mode
应用程序是否在夜间模式下运行。此限定可给开发者在应用程序生命周期中提供优化选项。API level 8 (Android 2.2.x)开始提供。
可能的值:night、notnight
Screen pixel density (dpi)
物理屏幕的像素数,通常用dpi表示。可能的值有:
ldpi – 低分辨率屏幕
mdpi – 中等分辨率屏幕
hdpi – 高分辨率屏幕
xhdpi – 超高分辨率屏幕
nodpi – 资源不可缩放
tvdpi – 在mdpi和hdpi之间
Touchscreen type
限定设备是哪种触摸屏。可能的值有:notouch (无触摸功能)、stylus(适用于手写笔的电阻式触摸屏)、finger(手指触摸)
Keyboard availability
限定哪种键盘可用。可在应用程序的生命周期中改变它。可能的值有:
keysexposed – 该设备有可用的键盘。如果软键盘不可用,则当【硬件键盘】设置打开时仅使用硬键盘。
keyshidden – 设备没有硬键盘,而且软键盘也隐藏。即:软件没有可以用的键盘。
keyssoft –设备有可用的软键盘。
Primary text input method
限定哪种硬件键盘可用于输入。可能的值有:
nokeys – 无可用的用于输入文本信息的硬件键盘。
qwerty – 有可用的qwerty键盘。
12key – 有可用的12-键(12-key)硬件键盘。
Navigation key availability
用于当“5-way”或者“d-pad (directional-pad)”导航键可用的情况。可在应用程序生命周期中改变。可能的值有:
navexposed – 用户可以使用导航键。
navhidden – 导航键不可用。
Primary non-touch navigation method
设备提供的可用导航键。可能的值有:
nonav – 仅能通过触摸屏幕导航。
dpad – 有可用的“d-pad (directional-pad)”导航。
trackball – 设备有一个“trackball”用于导航。
wheel - (不常见)有一个或多个可用的定向轮
Platform version (API level)
设备支持(supported)的API级别。格式:v<N>
下图列出了Android的判断规则:
四、附件(Assets文件夹下的各种文件)
在Android App中,Asset文件夹及其子文件夹下可保存任意类型的原始文件:text、xml、fonts、music、video、……等(WPF的内容文件类型也是如此,换言之,如果你知道如何用C#读写WPF项目中的内容文件,也就知道如何用C#读写Android项目中Asset文件夹下的原始文件了)。
与资源文件不同,Asset文件夹下的文件不是被编译到Android应用程序内,而是以单独的原始(raw)文件的形式作为附件随Android App一起打包到.apk压缩文件包中发布出来。
和WPF的比较:在WPF中,将这种文件称为内容文件,WPF是通过文件属性来指定编译器如何处理这种文件的(复制到输出目录:总是复制、如果较新则复制等),但是WPF并没有规定必须将这种类型的文件保存到项目中的哪个文件夹及其子文件夹下。而Android规定必须将这种类型的文件保存到项目中的Asset文件夹及其子文件夹下,而且规定只能通过Assets类来打开该文件夹下的文件。
下面的代码演示了如何读取Assets下的文本文件并将其显示到界面上:
using Android.App; using Android.OS; using Android.Widget; using System.IO; namespace MyTestProj { [Activity(Label = "Activity1")] public class Activity1 : Activity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState); var textView1 = new TextView(this); string content; using (StreamReader sr = new StreamReader(Assets.Open("readme.txt"))) { content = sr.ReadToEnd(); } textView1.Text = content; SetContentView(textView1); } } }
在Android应用程序中,每个组件都是一个独立的模块,但并不是所有组件都是用户进入程序的真实入口,其中一些要依赖于其它组件。
Android内置了四种不同的应用程序组件:Activity、Service、Content Provider和Broadcast recevicer。除此之外,还可以自定义组件并将其保存到项目的Components文件夹下。
在后面的章节中,还会专门介绍这些内容,这里你只需要记住真正掌握Android应用开发技术,必须理解这些组件的含义和具体使用场合。
六、权限设置与管理(AndroidManifest.xml)
清单(Manifest)可以定义应用程序及其组件的结构和元数据。
AndroidManifest.xml文件包含在每个安卓应用程序中,该文件向系统描述了本程序所包括的组件、所实现的功能、所能处理的数据、要请求的资源等。
该文件的功能类似于网站中的Web.conig文件。
在AndroidManifest.xml文件中添加或删除权限时,最常用的是通过VS2015主菜单下的项目属性来设置与权限(Permissions)相关的属性:
也可以在【解决方案资源管理器】中双击项目的【Properties】文件夹直接进入该界面。
设置后,它会自动更新项目中Properties文件夹下的AndroidManifest.xml文件。如果项目的Properties文件夹下不存在该文件,系统会自动创建它。
在Android系统能够启动一个程序组件之前, 系统必须通过读取程序的AndroidManifest.xml文件来知道这个组件是否存在。因此对于Java开发者要求程序必须在这个文件中声明它所有的组件。
除了声明程序组件外,这个配置文件还做一些其它的工作,例如:
确定程序需要哪些用户权限,例如网络访问或者读取用户的联系人。声明程序需要的最小的 API Level 这个要参照程序都使用了哪些API。声明程序使用或要求的硬件和软件特性,例如相机,蓝牙服务,或者多点触屏。程序需要链接的API类库(除Android framework API之外的类库),例如 Google Map类库。
但是,如果用C#来开发,“直接在AndroidManifest.xml文件中声明”并不是必需的,因为用带有智能提示的特性声明来声明它,然后让编译器自动生成合适的代码添加到这个文件中更方便。
当然,如果你希望直接在该文件中添加声明,也可以这样做。换言之,C#开发给你提供了多种声明清单的方式,你只需要按自己的习惯去选择一种合适的方式即可。这些方式有:
在AndroidManifest.xml文件中直接添加声明。通过项目的属性窗口选择,然后让编译器自动将合适的代码添加到AndroidManifest.xml文件中。在AssemblyInfo.cs文件中声明,然后让编译器自动自动将合适的代码添加到AndroidManifest.xml文件中。通过在类上方添加特性声明来声明,然后让编译器自动自动将合适的代码添加到AndroidManifest.xml文件中。
通过这些方式,你就拥有了最大的灵活性,同时也是代码看起来简洁而直观。
七、CPU架构(CPU Architectures)
VS2015调试模式(Debug)使用的是可适应多种CPU架构的模式,发布模式(Release)用于选择某种具体型号的手机,然后部署。
(1)如何指定支持的CPU架构
在调试状态(Debug)下,下面的两个选项默认是选中的:
这两个选项选中后。可防止调试时指定某种具体的CPU架构(CPU Architectures),即在下面的配置中,下面这些选项是不可更改的:
通常情况下,实际发布应用程序时需要明确配置使用的CPU体系结构或架构。当更改为发布(Release)模式时,它会自动不勾选“Use Shared Runtime”和“Use Fast Delployment…”复选框,这样一来,上图中的选项就可选了。
Xamarin.Android提供下面的架构:
armeabi – 基于ARM的CPU系列,提供了不低于ARMv5TE结构的指令集。注意armeabi不是线程安全的而且不能用于多CPU的设备。armeabi-v7a – 这种CPU支持硬件浮点运算和多CPU(SMP)设备。注意armeabi-v7a的机器码无法在ARMv5设备上运行。arm64-v8a –这是一种基于64-bit ARMv8架构的CPU。x86 – 这种CPU提供x86 (or IA-32)指令集。该指令集等价于奔腾Pro(Pentium Pro),包括MMX, SSE, SSE2,以及SSE3指令集。x86_64 CPUs提供64-bit的x86指令集。
在Release模式下,Xamarin.Android默认指定的是armeabi-v7a,其性能比armeabi高。如果发布目标是64-bit ARM平台(例如Nexus 9),此时需要选择arm64-v8a。如果发布到x86设备,就选择x86,如果发布目标是64-bit的CPU架构,就选择x86_64。
注意:由于在64位架构上可运行32位程序(例如Nexs 9就是64-bit ARM设备),因此,发布到64位手机设备上的时候,不是必须选择arm64-v8a或x86_64,仍然可以继续选择32位架构,这样做的优点是可避免应用程序占用过多的内存。
(2)多平台目标(Targeting Multiple Platforms)
面向多平台的优点是可同时调试多种CPU,缺点是这会导致.apk文件很大,如果希望减少.apk文件的大小,发布时只选择一个平台就行了。
注意:虽然在Xamarin.Android 5.1.x或更高版本中也可以体验64位的功能,但实际发布时并不使用它。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。