首页 > 技术文章 > Xamarin.Android开发

peterYong 2019-10-13 18:43 原文

使用 Visual Studio 生成第一个 Xamarin.Android 应用程序,并进一步了解使用 Xamarin 进行 Android 应用程序开发的基础知识。在此过程中,会介绍生成和部署 Xamarin.Android 应用程序所需的工具、概念和步骤。

链接

第 1 部分:快速入门

在本指南的第一部分,用户将创建一个应用程序,该应用程序可将用户输入的字母数字电话号码转换为数字电话号码,然后呼叫该号码。

第 2 部分:深入了解

在本文档的第二部分,将回顾生成的应用程序,并从根本上了解 Android 应用程序的工作原理。

查看项目中的项,查看每个文件夹及其用途:

  • Properties – 包含 AndroidManifest.xml 文件,该文件描述了对 Xamarin.Android 应用程序的所有要求(包括名称、版本号和权限),此外还包括.NET 程序集元数据文件 AssemblyInfo.cs,可以在此文件中填写一些应用程序相关的基本信息。

  • 引用 – 包含生成和运行应用程序所需的程序集。如果展开“引用”目录,可查看对 .NET 程序集(如 System、System.Core 和 System.Xml)的引用以及对 Xamarin 的 Mono.Android 程序集的引用。

  • Assets – 包含应用程序需要运行的文件(包括字体、本地数据文件和文本文件)。此处包括的文件可通过生成的 Assets 类访问。有关 Android 资产的详细信息,请参阅 Xamarin 使用 Android 资产指南。

  • Resources – 包含应用程序资源,例如字符串、图像和布局。可以通过生成的 Resource 类访问代码中的这些资源。 Android 资源指南提供有关“资源” 目录的更多详细信息。 应用程序模板在 AboutResources.txt 文件中还包含有“资源”的简明指南。

1、资源

“资源”目录包含 4 类文件夹(drawable、layout、mipmap 和 values),还有一个名为 Resource.designer.cs 的文件 。

  • drawable – 可绘制目录包含可绘制资源,如图像和位图。

  • mipmap – mipmap 目录包含适用于不同启动器图标密度的可绘制文件。 在默认模板中,drawable 目录包含应用程序图标文件“Icon.png” 。

  • layout – 布局目录包含 Android 设计器文件 (.axml),该文件定义每个屏幕或活动的用户界面。 该模板创建名为 activity_main.axml 的默认布局 。
  • values – 此目录包含存储简单值(如字符串、整数和颜色)的 XML 文件。 该模板创建名为 Strings.xml 的文件,用于存储字符串值。

  代码中使用:

string info = this.Resources.GetString(Resource.String.button_bundle_info);
  • Resource.designer.cs – 也称为 Resource 类,此文件是一个分部类,存放分配给每个资源的唯一 ID。它由 Xamarin.Android 工具自动创建,并在必要时重新生成。不应手动编辑此文件,因为 Xamarin.Android 将覆盖对其进行的任何手动更改。

更多查看:Android资源

2、应用基础知识和体系结构基础知识

Android应用程序不具有单一入口点;也就是说,应用程序中没有操作系统可调用来启动该应用程序的任何代码行。相反,当 Android 实例化应用程序的一个类时,会启动该应用程序,在此期间Android 将整个应用程序的进程加载到内存中。

设计复杂应用程序或与 Android 操作系统交互时,Android 的这一特有功能极其有用。 但是,这些选项也使 Android 在处理 Phoneword 应用程序等基本方案时变得复杂。 出于此原因,分两种情况来探索 Android 体系结构。本指南剖析使用 Android 应用最常见入口点(第一个屏幕)的应用程序。了解 Android 多屏幕中,讨论了以不同方式启动应用程序,全面探讨了 Android 体系结构的复杂性。

在仿真器或设备中首次打开 Phoneword 应用程序时,操作系统会创建第一个Activity,Activity是特殊的 Android 类,对应于单个应用程序屏幕,负责绘制和支持用户界面 Android 创建应用程序的第一个Activity时,会加载整个应用程序

活动负载

由于 Android 应用程序中没有线性发展(可以通过多个点启动应用程序),Android 采用一种独特方式来跟踪哪些类和文件组成应用程序。 在 Phoneword 示例中,将向名为“Android Manifest” 的特殊 XML文件注册组成应用程序的所有部分。 “Android Manifest” 的作用是跟踪应用程序的内容、属性和权限,并将这些信息告知 Android 操作系统。 可以将 Phoneword 应用程序当作单一活动(屏幕)和由 Android 清单文件捆绑在一起的资源文件和帮助程序文件的集合,如以下关系图所示:

资源帮助程序

以下几个部分将探索 Phoneword 应用程序各部分的关系;使你能更好地理解上面的关系图。 此探索先从用户界面开始,会讨论 Android 设计器和布局文件。

3、用户界面

activity_main.axml 是应用程序中第一个屏幕的用户界面布局文件 。 .axml 指示这是 Android 设计器文件(AXML 表示 Android XML,名称 Main 对 Android 而言是任意的 – 可将布局文件命名为其他名称。在IDE中打开activity_main.axml 时,会显示名为“Android Designer”的 Android 布局文件的可视编辑器 :

TranslateButton 的 ID 设置为 @+id/TranslateButton

设置 TranslateButton 的 id 属性时,Android Designer 会将 TranslateButton 控件映射到 Resource 类,并为其分配 TranslateButton 的资源 ID 。 通过将可视控件映射到类,可以找到并使用 TranslateButton 和应用代码中的其他控件。 当你剖析为控件提供支持的代码时,会更详细地了解这一内容。 此时,只需知道控件的代码表示形式是通过 id 属性链接到设计器中控件的可视表示形式即可

源视图

在设计界面上定义的所有内容都会转换成 XML,以供 Xamarin.Android 使用。 Android 设计器提供源视图,此源视图包含从可视化设计器生成的 XML,可以切换到设计器视图左下角的“源” 面板以查看此 XML

4、Activities和活动生命周期

Activity 类包含为用户界面提供支持的代码,activity负责响应用户交互,并创建动态用户体验

  • Activity 类

Phoneword 应用程序只有一个屏幕(活动)。为屏幕提供支持的类称为MainActivity,位于MainActivity.cs 文件中,名称 MainActivity 在 Android 中没有特别的意义 – 虽然约定是命名应用程序 MainActivity 中的第一个活动,但 Android 并不在意将其命名为其他名称。

打开 MainActivity.cs 时,可以看到,MainActivity 类是 Activity 类的子类 并且活动标有 Activity特性:向 Android 清单注册Activity,这能让 Android 知道此类是该清单所管理的Phoneword 应用程序的一部分

特性中:Label 属性设置将显示在屏幕顶部的文本【在values文件夹下的string.xml中管理】;MainLauncher属性告知 Android 在启动应用程序时显示此活动。 了解 Android 多屏幕指南中所述,当你向应用程序添加更多活动(屏幕)时,此属性会变得很重要。

  • 活动生命周期

在 Android 中,活动会根据与用户的交互经历生命周期的不同阶段。可以对活动进行创建、启动和暂停、恢复和销毁等操作。Activity 类包含这些方法,系统会在屏幕生命周期的特定时间点调用这些方法。 

通过重写 Activity 生命周期方法,可以控制活动的加载方式和与用户的互动方式,甚至还可以控制活动从设备屏幕消失后会发生的情况。 例如,可以重写生命周期方法,以执行以下重要任务:

  • OnCreate – 创建视图、初始化变量,并执行在用户能看到活动之前其他必须完成的准备工作。只有将活动加载到内存时,才会调用此方法一次。【向用户显示屏幕之前】

  • OnResume – 执行每当活动返回到设备屏幕时必须发生的任何任务。【从主屏幕再次回到app也会调用此方法】

  • OnPause – 执行每当活动离开设备屏幕时必须发生的任何任务。【单击home键,离开app时】

在“Activity”中将自定义代码添加到生命周期方法时,您将覆盖该生命周期方法的基本实现。 您可以利用现有的生命周期方法(已附加一些代码),然后使用自己的代码扩展该方法。 您可以从方法内部调用基本实现,以确保原始代码(base.OnCreate())在新代码之前运行。 下一部分将说明一个示例。

活动生命周期是Android的重要组成部分。 完成入门系列后,如果您想了解有关活动的更多信息,请阅读活动生命周期指南。 

OnCreate

protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);

    // Set our view from the "main" layout resource
    SetContentView (Resource.Layout.activity_main);
    // Additional setup code will go here
}

Bundle:从字符串值到各种可打包类型的映射

OnCreate中加载在 Android Designer中创建的用户界面:请调用 SetContentView 并向其传递布局文件的资源布局名称:activity_main.axml ,布局位于 Resource.Layout.activity_main

当 MainActivity 开始运行后,会基于 activity_main.axml 文件的内容创建一个视图 。

准备好布局文件后,可以开始查找控件,若要查找控件,请调用 FindViewById,并传入控件的资源 ID。

 // Get our UI controls from the loaded layout
            EditText phoneNumberText = FindViewById<EditText>(Resource.Id.PhoneNumberText);
            TextView translatedPhoneWord = FindViewById<TextView>(Resource.Id.TranslatedPhoneword);
            Button translateButton = FindViewById<Button>(Resource.Id.TranslateButton);

现在布局文件中已具有对控件的引用,可以开始对其进行编程,以响应用户交互。

响应用户交互

在 Android 中, Click 事件侦听用户的触控,在此应用中,Click 事件将由 lambda 处理,不过也可改用委托或命名事件处理程序。

translateButton.Click += (sender, e) =>
            {
                // Translate user's alphanumeric phone number to numeric
                string translatedNumber = PhoneTranslator.ToNumber(phoneNumberText.Text);
                if (string.IsNullOrWhiteSpace(translatedNumber))
                {
                    translatedPhoneWord.Text = string.Empty;
                }
                else
                {
                    translatedPhoneWord.Text = translatedNumber;
                }
            };

5、为不同的屏幕密度设置图标

Android 设备具有不同的屏幕大小和分辨率,不是所有图像都能清晰显示在屏幕上。

考虑到这一点,最好将不同分辨率的图标添加到 Resources文件夹。 Android 提供了不同版本的 mipmap 文件夹来处理不同密度的启动器图标,包括:

针对中等密度屏幕的 mdpi、针对高密度屏幕的 hdpi,以及针对超高密度屏幕的 xhdpi、xxhdpi 和 xxxhdpi 。 不同大小的图标存储在相应的 mipmap- 文件夹中 。

各文件夹下:

ic_launcher:桌面图标【可以在设置->更多->应用程序中查看】,

ic_launcher_background:背景图,

ic_launcher_foreground:前景图,

ic_launcher_round:圆形图【屏幕上的】,

并非每个人都具有可用于创建自定义图标和启动图像(让应用与众不同)的设计器。下面是几种生成自定义应用图像的备选方法:

  • Android Asset Studio – 是一个基于 Web 的浏览器生成器,针对所有类型 Android 图标,带有其他有用社区工具的链接。 在 Google Chrome 中性能最佳。

  • Visual Studio – 可以用于直接在 IDE 中为应用创建简单图标集。

  • Glyphish – 可免费下载和购买的高质量预生成图标集。

  • Fiverr – 从各种设计器中进行选择以便为你创建图标集(最低 5 美元)。 可以漫无目标,不过如果需要动态设计的图标,这是很好的资源。

有关图标大小和要求的详细信息,请参阅 Android 资源指南。

应用程序基础知识

可访问性

API级别

1、Android资源

新增加资源:eg,my_image_name .png【约定用下划线,小写】,并将其生成操作设置为:Android资源

2、活动生命周期方法

一个Activity活动就是一个界面的布局。参考:onResume什么时候执行,执行几次的问题

程序正常启动时:onCreate()->onStart()->onResume();
onCreate()在活动第一次创建时被调用,主要用于加载布局。
onStart()这个方法在活动由不可见变为可见的时候调用。
onResume()这个方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。

三种调用的场景:
一个Activity启动另一个Activity: onPause()->onStop(),    再返回:onRestart()->onStart()->onResume()
程序按back 退出: onPause()->onStop()->onDestory(),再进入:onCreate()->onStart()->onResume();
程序按home 退出: onPause()->onStop(),                    再进入:onRestart()->onStart()->onResume();

OnCreate

OnCreate是创建活动时要调用的第一个方法,OnCreate始终需要重写,以执行活动可能需要的任何启动初始化,如:

  • 创建视图
  • 初始化变量
  • 将静态数据绑定到列表

OnCreate采用一个绑定参数,该参数是在绑定不为 null 时用于存储和传递状态信息和活动之间的对象的字典,这表示活动正在重新启动,应从上一个实例还原其状态。

OnStart

OnCreate 完成后 , 系统始终会调用 OnStart。 如果活动在活动可见之前需要执行任何特定的任务,例如刷新活动中视图的当前值,则活动可能会重写此方法,调用此方法之后Android 将立即调用OnResume

OnResume

当活动准备好开始与用户交互时,系统将调用OnResume 【Activity 开始跟用户交互之前会调用 onResume()】。 活动应重写此方法以执行如下任务:

  • 斜向上帧速率(游戏开发中的常见任务)
  • 开始动画
  • 侦听 GPS 更新
  • 显示任何相关的警报或对话框
  • 连接外部事件处理程序

OnPause

当系统要将活动放入背景中或活动被部分遮盖时,将调用OnPause 。 如果活动需要,则应重写此方法:

  • 提交永久性数据的未保存更改

  • 销毁或清理使用资源的其他对象

  • 增加帧速率和暂停动画

  • 注销外部事件处理程序或通知处理程序(即绑定到服务的程序)。 必须执行此操作以防止活动内存泄露。

  • 同样,如果活动显示了任何对话框或警报,则必须用.Dismiss()方法对其进行清理。

在调用OnPause后,可能会调用以下两个方法:

1、OnResume:if the Activity is to be returned to the foreground.

2、OnStop:if the Activity is being placed in the background

OnStop

当用户不再显示该活动时,将调用OnStop 。 发生以下情况之一时,会发生这种情况:

  • 正在启动新活动,并覆盖此活动。
  • 现有活动将进入前台。
  • 正在销毁活动。

OnDestroy

OnDestroy是在将其销毁并从内存中完全删除之前,在活动实例上调用的最终方法。

大多数活动将不会实现此方法,因为大多数清理和关闭操作都是通过OnPause和OnStop方法完成的;而需要重写OnDestroy方法一般是用来清理长时间运行的可能会泄漏的资源,例如 在OnCreate中启动的后台线程。

OnRestart

对于应该在OnRestart中实现哪种逻辑,没有通用的指导原则。这是因为无论正在创建活动还是重新启动活动,都始终调用OnStart,因此活动所需的任何资源都应该在OnStart中初始化,而不是在OnRestart中初始化。

3、Back vs. Home

程序按back 退出: onPause()->onStop()->onDestory(),再进入:onCreate()->onStart()->onResume();
程序按home 退出: onPause()->onStop(),                    再进入:onRestart()->onStart()->onResume();

按Back键会销毁程序,home不会,只是退到后台线程。

4、Managing State Throughout the Lifecycle

在停止或者销毁时,系统提供一种保存状态(实例状态)的机会,三种方式:

  • android采用称之为Bundle的字典(key/value) 来保存原始值
  • Creating a custom class that will hold complex values such as bitmaps.
  • 绕过配置更改生命周期,并承担维护活动状态的全部责任。

4.1、Bundle介绍

Bundle不适合保存复杂数据(如 位图)

活动提供一些方法来帮助在捆绑包中保存和检索实例状态:

  • OnSaveInstanceState–此操作由 Android 在活动被销毁时调用。 如果活动需要保留任何键/值状态项,则这些活动可以实现此方法。

  • OnRestoreInstanceState–在OnCreate方法完成后将调用此方法,并为Activity提供了另一个在初始化完成后恢复其状态的机会。

1.OnSaveInstanceState

当活动正在停止时,将调用OnSaveInstanceState 。 它将接收一个捆绑参数,活动可在其中存储其状态。 当设备遇到配置更改时【例如 改变屏幕方向,横屏竖屏】,活动可以使用Bundle传入的对象通过重写OnSaveInstanceState来保留活动状态。

保存活动中的暂时性数据【eg 计数器】,而控件的Text值不会改变。

参考:onSaveInstanceState()和onRestoreInstanceState()使用详解

 

 当系统开始停止您的Activity时,它会调用onSaveInstanceState()(1),以便您可以指定要保存的其他状态数据,以防Activity必须重新创建实例。如果Activity被破坏并且必须重新创建相同的实例,则系统将(1)中定义的状态数据传递给onCreate()方法(2)和onRestoreInstanceState()方法(3)。

注意
1、如果是用户自动按下Home键,是不会触发onSaveInstanceState()和onRestoreInstanceState()的。
2、每次用户旋转屏幕时,您的Activity将被破坏并重新创建。当屏幕改变方向时,系统会破坏并重新创建前台Activity,因为屏幕配置已更改,您的Activity可能需要加载替代资源(例如布局)。即会执行onSaveInstanceState()和onRestoreInstanceState()的。

2.OnRestoreInstanceState

还原状态,一般在OnCreate中还原状态,而不用重写OnRestoreInstanceState。

4.2 保存复杂数据

通过重写OnRetainNonConfigurationInstance并返回包含要保存的数据的Java.Lang.Object实例来保存数据。 使用OnRetainNonConfigurationInstance保存状态有两个主要优点:

  • OnRetainNonConfigurationInstance返回的对象对更大、更复杂的数据类型执行良好的工作,因为内存会保留此对象。

  • OnRetainNonConfigurationInstance方法将按需调用,并且仅在需要时调用。 这比使用手动缓存更经济。

 

5、Service

  • 绑定服务

实现和使用绑定服务

为了使 Android 应用程序使用绑定的服务,必须实现以下

  1. 新建服务。扩展Service类并实现生命周期回调方法 –此类将包含执行服务所需工作的代码。【eg:一个告诉用户服务启动的时间和运行时间的方法api】
  2. 创建一个实现IServiceConnection接口的类,这个接口提供供Android 调用的回调方法(OnServiceConnected),以便在服务连接发生更改时通知客户端,eg:客户端已连接或断开连接到服务.。服务连接还将提供对对象(Binder)的引用,客户端可使用该对象直接与服务交互。 
  3. 创建一个实现IBinder接口的类, IBinder接口提供了客户端用来与服务进行通信的 API。 Binder 可以提供对绑定服务的引用、允许直接调用方法或Binder 可以提供一个客户端 API,用于封装和隐藏应用程序中的绑定服务。 IBinder必须提供远程过程调用所需的代码。 不需要(或建议)直接实现IBinder接口。 相反,应用程序应该扩展Binder类型,它提供IBinder所需的大部分基本功能。
  4. 启动Activity并绑定到服务。一旦 service connection, binder, and service 被创建,Android 应用程序负责启动该服务并将其绑定到它。

 

推荐阅读