Android第一行代码
第1章 开始启程
1.1 Android系统架构
- Linux Kernel:为Android设备提供了各种硬件的底层驱动;
- Libraries:C/C++库为Android系统提供主要特性支持;还有Android Runtime(安卓运行时)库;
- Application Framwork:提供构建App时可能用到的各种API;
- Applications:所有应用程序都在这一层。
1.2 Android 应用开发特色
- 四大组件:
- Activity:所有应用程序的门面。凡是在App种看得到的东西,都是放在Activitiy中的;
- Service:在后台运行。即使用户退出应用,也可以继续运行;
- BroadcastReceiver:允许应用接受来自各处的广播;也可以向外发出广播信息;
- ContentProvider:帮助应用程序之间共享数据。例如想读取通讯录中的联系人,就需要ContentProvider来实现。
- 系统控件
- SQLite数据库
- 多媒体
1.3 创建Android项目
- Name:项目名称
- Package Name:项目的包名。Android系统通过不同的报名来区分不同的应用程序
1.4 分析Android项目结构
- .gradle和.idea:AS自动生成的文件,无需关心;
- app:包括项目中的代码、资源等内容;
- build:编译时自动生成的文件,无需关心;
- gradle:包含gradle wrapper的配置文件;gradle是用来自动构建各种项目的工具,gradle wrapper是根据缓存情况,自动将gradle下载好,免去因版本问题等需要手动配置的麻烦;
- .gitgnore:用来将指定的目录或文件排除在版本控制之外;(版本控制在第六章)
- build.gradle:项目全局的gradle构建脚本,通常不需要修改;(后面有详细介绍)
- gradle.properties:全局的gradle配置文件,其属性会影响项目中所有的gradle编译脚本;
- gradlew和gradle.bat:用来在命令行界面中执行gradle命令。前者is for mac/linux,后者for windows;
- .iml:所有IntelliJ IDEA项目都会自动生成这样一个文件(AS是基于IntelliJ IDEA开发的),用于标识,无需修改;
- local.properties:用于指定本机中的Android SDK路径,内容通常是自动生成的,无需修改;
- settings.gradle:用于指定项目中所有引入的模块。通常情况下模块的引入是自动完成的;
1.4.1 app目录下的内容
这部分内容才是工作重点,以下是详细分析。
- build:与外层的build目录类似,无需关心;
- libs:存储第三方jar包。此目录下的jar包会被自动添加到项目的构建路径里;
- androidTest:用来编写Android Test测试用例的,可以对项目进行一些自动化测试;
- java:放置所有java (Kotlin)代码的地方;
- res:存储项目中所有的图片(drawable)、布局(layout)、字符串(values)等资源;
- AndroidManifest.xml:整个Android项目的配置文件。注册组件、添加权限声明;
- test编写Unit Test测试用例,另一种自动化测试的方式;
- .gitnore:将app模块内指定的目录或文件排除在版本控制之外;
- app.iml:自动生成的文件,无需关心;
- build.gradle:app模块的gradle构建脚本;
- proguard-rules.pro:指定代码的混淆规则。当代码开发完成后打包成安装包文件,如果不希望被别人破解,通常会将代码进行混淆,从而使破解者难以阅读;
1.4.2 HelloWorld项目是如何运行起来的?
首先看AndroidManifest.xml中的这段代码:
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.MyHelloWorld">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
这段代码表示对MainActivity进行注册,必须先注册才能使用。其中intent-filter中的两行代码表示MainActivity是这个项目的主Activity,在手机上打开应用,首先启动的就是这个Activity。
MainActivity.kt中的这段代码:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyHelloWorldTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
}
}
}
}
}
书上的代码第一行是class MainActivity : AppCompatActivity() {
,意思是MainActivity
是继承自AppCompatActivity
的。这是AndroidX中提供的一种向下兼容的Activity。项目中所有自定义的Activity都必须继承它或它的子类才能拥有Activity的特性(AppCompatActivity
和ComponentActivity
都是Activity的子类)。
onCreate()
方法:一个Activity被创建时必定要执行的方法。
书上说“Android程序的设计讲究逻辑和视图分离,因此不推荐在Activity中直接编写界面”。书中的示例代码是通过setContentView()
方法给当前的Activity引入了一个activity_main布局,该布局文件位于res/layout目录下。
不过经过寻找,我并没有在res目录下找到layout文件夹。
1.4.3 详解res目录
- drawable:放图片的;
- mipmap:放应用图标的;
- values:放字符串、样式、颜色等配置的;
- layout:放布局文件的;
有很多mipmap开头的文件是为了让程序更好地兼容各种设备,drawabl也同理。(最主流的设备分辨率目录:drawable-xxhdpi)
如何使用res里的各种资源?
举个例子,在res/values/strings.xml文件中有如下代码:
<resources>
<string name="app_name">MyHelloWorld</string>
</resources>
可见其定义了一个应用程序名的字符串。有以下两种方式引用它:
- 在代码中通过
R.string.app_name
; - 在XML中通过
@string/app_name
。
其中的string部分是可以替换的,引用图片资源就换成drawable;引用应用图标就换成mipmap;引用布局文件就换成layout。例如在AndroidManifest.xml文件中,有如下代码:
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyHelloWorld"
tools:targetApi="31">
...
</application>
正是上文的XML引用方式。
1.4.4 详解build.gradle文件
Gradle是一种非常先进的项目构建工具。外层的gradle文件主要声明了gradle插件和kotlin插件,包括它们各自的版本。通常情况下并不需要修改外层的gradle文件。
书中的版本与现在的版本有区别。现在的gradle文件叫做build.gradle.kts,使用Kotlin编写,是目前默认的格式。书中的build.gradle文件是较旧版本。
内层的gradle文件就比较复杂了。
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
这部分应用了两个插件。第一个应用的插件一般有两种值可选:com.android.application
(表示这是一个应用程序模块)和com.android.library
(表示这是一个库模块)。前者可以直接运行,后者只可以作为代码库依附于别的应用程序模块运行。第二行代码应用了kotlin插件。这是用Kotlin开发Android项目所必需的插件。
在android
闭包中,又有以下几个闭包:(以下用ita程序中的代码作为实例,而不是MyHelloWorld)
-
defaultConfig
defaultConfig { applicationId 'com.bh.ita' minSdk 26 targetSdk 34 versionCode 8 versionName '1.0.8' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" }
defaultConfig可以对项目的更多细节进行配置,其中:
applicationID
是每一个应用的唯一标识符,绝不可重复;minSDKVersion
指定项目最低兼容的Android系统版本;targetSDKVersion
指定的值表示你在该目标版本上已做过了充分的测试,系统将会为你的应用程序启用一些最新的功能和特性;versionCode
和versionCode
:指定项目的版本号/版本名;testInstrumentationRunner
用于在当前项目中启用JUnit测试,你可以为当前项目编写测试用例,以保证功能的正确性和稳定性。
-
buildTypes
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } }
该闭包只会有两个子闭包:debud和release。前者用于指定生成测试版安装文件的配置,后者用于生 成正式版安装文件的配置。其中debug闭包是可以忽略不写的。在release闭包中:
- minifyEnabled:用于指定是否对项目的代码进行混淆;
- proguardFiles:用于指定混淆时使用的规则文件。'proguard-android-optimize.txt’在…/tools/proguard目录下,是所有项目通用的混淆规则;第二个proguard-rules.pro在当前项目根目录下,里面可以编写当前项目特有的混淆规则。
通过AS直接运行项目生成的都是测试版安装文件。如何生成正式版安装文件见第十五章。
接下里是dependencies
闭包,可以指定当前项目所有的依赖关系。通常AS项目一共有三种依赖关系:
- 本地依赖:对本地的jar包或目录添加依赖关系(implementation filetree);
- 库依赖:对项目中的库模块添加依赖关系(implementation project);
例如,implementation project(':helper')
就可以添加helper库的依赖关系了。 - 远程依赖:对jcenter仓库上的开源项目添加依赖关系(implementation)。
例如,implementation("androidx.core:core-ktx:1.9.0")
中,androidx.core
是域名部分,用于和其他公司的库做区分;core-ktx
是工程名部分,与同公司不同库工程做区分;1.9.0
是版本号,与同一个库不同版本做区分。加上这句声明后,Gradle就会在构建项目前首先检查一下本地是否已经有这个库的缓存,没有就会联网下载然后再添加道项目的构建路径中去。
1.5 日志工具
Android中的日志工具类是Log (andorid.util.Log)
,有以下五个方法来打印日志:
参数都是(‘tag’, ‘msg’)。前者一般传入当前的类名即可,主要用于对打印信息进行过滤;后者是想要打印的具体内容。
-
Log.v( )
: verbose,最为琐碎、意义最小; -
Log.d( )
: debug。用于打印一些调试信息; -
Log.i( )
: info。用于打印一些比较重要的数据; -
Log.w( )
: warn。用于打印警告信息,潜在风险; -
Log.e( )
: error。打印错误信息。当有错误信息被打印出时,一般意味着程序出现严重问题。
例如在onCreate函数中写Log.d("MainActivity", "onCreate execute")
,运行程序后即可在底部Logcat中查看到输出的日志。可以通过过滤器,查看不同条件下的日志,从而快速找出想要的信息。(比println好的原因之一)