Start a new Android Studio project:
Name:表示应用名称,此应用安装到手机之后会在手机上显示该名称
Package name:表示项目的包名,Android系统就是通过包名来区分不同应用程序的,因此包名一定要具有唯一性。
下面是安卓的创建活动界面,可以看到,Android Studio提供了很多种内置模板,不过由于我们才刚刚开始学习,用不着这么多复杂的模板,这里直接选择Empty Activity来创建一个空的活动就可以了。
锤子按钮:编译项目
下拉列表:选择运行哪一个项目,通常app就是当前的主项目
三角形按钮:运行项目
单词/缩写 | 含义 |
---|---|
AVD:Android Virtual Device | Android运行的虚拟设备 |
select a system image | 选择系统映像/差不多就是系统版本文件的意思 |
portrait | 纵向的 |
lanscape | 横向的 |
emulated performance | 仿真性能 |
graphics | 图像 |
device frame | 设备框架 |
advanced setting | 高级设置 |
default orientation | 默认方向,指的是初次进入系统时的竖屏还是横屏 |
instant run | 即时运行 |
任何一个新建的项目都会默认使用Android模式的项目结构,但这并不是项目真实的目录结构,而是被Android Studio转换过的。这种项目结构简洁明了,适合进行快速开发,但是对于新手来说可能并不易于理解。
Project是真实的项目目录结构,而Android是经过转化的项目目录结构。
现在整个项目的外层目录结构已经介绍完了。你会发现,除了app目录之外,大多数的文件和目录都是自动生成的,我们并不需要进行修改。想必你已经猜到了,app目录下的内容才是我们以后的工作重点,展开之后结构如下图所示。
下图是谷歌官方文档中提供的AS工程结构图:
可能很多人刚开始接触studio时,对于BuildScript下面的两个build.gradle文件不太明白,一模一样的文件名放在同一个目录下给人一种混乱的感觉(至少我当时是这么头疼的)
一个工程中 build.gradle文件个数 = module个数(代表模块的garadle配置文件) + 1(代表整个工程的配置文件)
接下来我们来查看AndroidManifest.xml
文件,也就是整个安卓项目的配置文件。
这段代码表示对HelloWorldActivity(MainActivity)这个活动进行注册。没有在AndroidManifest.xml里注册的活动是不能使用的。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
//使用<intent-filter>括起来的以上两段代码很重要,其代表HelloWorldActivity是这个项目的主活动在手机上点击应用图标,首先启动的就是这个活动。
</intent-filter>
</activity>
下面我们打开MainActivity来一探究竟,查看器其究竟是如何运行的:
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);//创建活动时必须要执行的方法
setContentView(R.layout.activity_main);//将布局文件直接引入到活动中来
}
}
首先我们可以看到,MainActivity
是继承自AppCompatActivity
的,这是一种向下兼容的Activity
,可以将Activity
在各个系统版本中增加的特性和功能最低兼容到Android 2.1
系统。Activity
是Android
系统提供的一个活动基类,我们项目中所有的活动都必须继承它或者它的子类才能拥有活动的特性(AppCompatActivity
是Activity
的子类)。然后可以看到MainActivity
中有一个onCreate()
方法,这个方法是一个活动被创建时必定要执行的方法,其中只有两行代码,并且没有Hello
World!的字样。那么在应用中显示的Hello World!
是在哪里定义的呢?
其实Android程序的设计讲究逻辑和视图分离,因此是不推荐在活动中直接编写界面的,更加通用的一种做法是:
可以看到,在onCreate() 方法的第二行调用了setContentView()
方法,就是这个方法给当前的活动引入了一个hello_world_layout
布局,那Hello World!
一定就是在这里定义的了!我们快打开这个文件看一看。
布局文件都是定义在res/layout目录下的,当你展开layout目录,你会看到hello_world_layout.xml这个文件。打开该文件并切换到Text视图,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
现在还看不懂?没关系,后面我会对布局进行详细讲解的,你现在只需要看到上面代码中有一个TextView
,这是Android系统提供的一个控件,用于在布局中显示文字的。然后你终于在TextView中看到了Hello World!的字样!哈哈!终于找到了,原来就是通过android:text="Hello World!"
这句代码定义的。
目录文件夹名 | 作用 |
---|---|
以drawable开头的文件夹 | 放图片 |
以mipmap开头的文件夹 | 放应用图标 |
以values开头的文件夹 | 用来放字符串、样式、颜色等配置 |
layout文件夹 | 放布局文件 |
之所以有这么多mipmap开头的文件夹,其实主要是为了让程序能够更好地兼容各种设备。Android机型尺寸各种各样,于是屏幕适配就成了Android开发中很重要的一环。
drawable文件夹也是相同的道理,由于我选择的设备是"Nexus 5X API 24",所以Android Studio自动替我们生产了此文件夹,但是我们应该自己创建drawable-hdpi、drawable-xhdpi、drawable-xxhdpi等文件夹。在制作程序的时候最好能够给同一张图片提供几个不同分辨率的版本,分别放在这些文件夹下,然后当程序运行的时候,会自动根据当前运行设备分辨率的高低选择加载哪个文件夹下的图片。当然这只是理想情况,更多的时候美工只会提供给我们一份图片,这时你就把所有图片都放在drawable-xxhdpi文件夹下就好了。
知道了res目录下每个文件夹的含义,我们再来看一下如何去使用这些资源吧。打开res/ values/strings.xml文件,内容如下所示:我们以程序名字符串为例进行引用方式的说明:
<resources>
<string name="app_name">MyHelloWorld</string>
</resources>
可以看到,这里定义了一个应用程序名的字符串,我们有以下两种方式来引用它。
R.string.app_name
可以获得该字符串的引用。@string/app_name
可以获得该字符串的引用。 基本的语法就是上面这两种方式,其中string 部分是可以替换的,如果是引用的图片资源就可以替换成drawable ,如果是引用的应用图标就可以替换成mipmap ,如果是引用的布局文件就可以替换成layout ,以此类推。比如:我在所有的以mipmap开头的文件夹中都加入了“cloud.png”文件,然后在AndroidManifest.xml
文件中将android:icon="@mipmap/ic_launcher"
语句修该为android:icon="@mipmap/cloud"
语句,那么就出现了以下的图标:
这样就实现了更改应用程序图标的功能,所以可见修改一个图标只需在AndroidManifest.xml
文件中修改简单的一局代码,可以见得Andrioid Studio
IDE果然已经将安卓开发简化到“一步到位”。
Gradle是一个构建工具,它是用来帮助我们构建app的,构建包括编译、打包等过程。我们可以为Gradle指定构建规则,然后它就会根据我们的“命令”自动为我们构建app。Android Studio中默认就使用Gradle来完成应用的构建。有些同学可能会有疑问:”我用AS不记得给Gradle指定过什么构建规则呀,最后不还是能搞出来个apk。“ 实际上,app的构建过程是大同小异的,有一些过程是”通用“的,也就是每个app的构建都要经历一些公共步骤。因此,在我们在创建工程时,Android Studio自动帮我们生成了一些通用构建规则,很多时候我们甚至完全不用修改这些规则就能完成我们app的构建。
有些时候,我们会有一些个性化的构建需求,比如我们引入了第三方库,或者我们想要在通用构建过程中做一些其他的事情,这时我们就要自己在系统默认构建规则上做一些修改。这时候我们就要自己向Gradle”下命令“了,这时候我们就需要用Gradle能听懂的话了,也就是Groovy。Groovy是一种基于JVM的动态语言。
Gradle的基本组成:
组成名词 | 作用 |
---|---|
Project与Task | Project:每一个待构建的工程;Task:构建一个Project需要执行一系列的任务,比如:Java源码编译、资源文件编译、Lint检查、打包以生成最终的apk文件等等任务; |
插件 | 两个核心:1.定义Task;2.执行Task。为了让Gradle能正常工作,完成整个构建流程中的一系列Task的执行,必须导入合适的插件,这些插件中定义了构建Project中的一系列Task,并且负责执行相应的Task。 |
Gradle配置文件 | Android Studio中的一个Module即为Gradle中的一个Project。app目录下的build.gradle文件,代表了app Module的构建脚本,它定义了应用于本模块的构建规则。工程根目录下也存在一个build.gradle文件,它代表了整个工程的构建,其中定义了适用于这个工程中所有模块的构建规则。 |
构建脚本buildscript | 用于Gradle下的脚本构建,定义了项目的构建规则 |
插件功能详细介绍:
在新建工程的app模块的build.gradle文件的第一行,往往都是如下这句:
apply plugin: 'com.android.application'
这句话的意思就是应用“com.android.application“这个插件来构建app模块,app模块就是Gradle中的一个Project。也就是说,这个插件负责定义并执行Java源码编译、资源文件编译、打包等一系列Task。实际上"com.android.application"整个插件中定义了如下4个顶级任务:
当我们执行一个任务时,会自动执行它所依赖的任务。比如,执行assemble任务会执行assembleDebug任务和assembleRelease任务,这是因为一个Android项目至少要有debug和release这两个版本的输出。
Gradle配置文件的详细介绍:
不同于Eclipse,Android Studio是采用Gradle来构建项目的。Gradle是一个非常先进的项目构建工具,它使用了一种基于Groovy的领域特定语言(DSL)来声明项目设置,摒弃了传统基于XML(如Ant和Maven)的各种烦琐配置。
在之前对项目的目录结构介绍中我们提到:有两个build.gradle文件,一个是在最外层目录下的,一个是在app目录下的。这两个文件对构建Android Studio项目都起到了至关重要的作用,下面我们就来对这两个文件中的内容进行详细的分析。项目中一般会出现2个或者多个 build.gradle 文件,一个在根目录下,一个在 app 目录下。如果切换到 Android 模式下则全部在 Gradle Scripts:
先来看一下最外层目录下的build.gradle文件,代码如下所示:
buildscript {
repositories {//第一处:repositories
jcenter() //构建脚本中所依赖的库都在jcenter仓库下载
}
dependencies {
//指定了gradle插件的版本
classpath 'com.android.tools.build:gradle:1.5.0'
}
}
allprojects {
repositories {//第二处:repositories
//当前项目所有模块所依赖的库都在jcenter仓库下载
jcenter()
}
}
这些代码都是自动生成的,虽然语法结构看上去可能有点难以理解,但是如果我们忽略语法结构,只看最关键的部分,其实还是很好懂的。
jcenter
的含义:它是一个代码托管仓库,很多Android开源项目都会选择将代码托管到jcenter上,声明了这行配置之后,我们就可以在项目中轻松引用任何jcenter上的开源项目了。
dependencies 闭包中使用classpath 声明了一个Gradle插件。声明这个插件的原因是:因为Gradle并不是专门为构建Android项目而开发的,Java、C++等很多种项目都可以使用Gradle来构建。因此如果我们要想使用它来构建Android项目,则需要声明com.android.tools.build:gradle:2.2.0 这个插件。其中,最后面的部分是插件的版本号,我在写作本书时最新的插件版本是2.2.0。
这样我们就将最外层目录下的build.gradle文件分析完了,通常情况下你并不需要修改这个文件中的内容,除非你想添加一些全局的项目构建配置。
下面我们再来看一下app目录下的build.gradle文件,代码如下所示:
// 声明是Android程序,
// com.android.application 表示这是一个应用程序模块,可直接运行
// com.android.library 标识这是一个库模块,是依附别的应用程序运行
apply plugin: 'com.android.application'
android {
compileSdkVersion 29 //编译Sdk版本为29
buildToolsVersion "29.0.0"//指定项目的构建版本
defaultConfig {
applicationId "com.example.myhelloworld"//用于指定项目包名,初始化之后的修改在此进行
minSdkVersion 15//当前项目的最低兼容的安卓系统版本
targetSdkVersion 29//目标版本号,意味着此模块所有特性在此安卓版本上最好
versionCode 1//指定项目版本号
versionName "1.0"//指定项目版本名
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"//指定当前测试单元类型
}
buildTypes {
release {
minifyEnabled false //是否对代码进行混淆,此处为不进行混淆
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'//用于指定混淆时使用的规则文件
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
各个部分的作用:
android闭包的内容分析:
dependencies内容分析闭包:
Android中的日志工具类是Log(android.util.Log)
,这个类中提供了如下5个方法来供我们打印日志。
方法名 | 作用 |
---|---|
Log.v() | 用于打印那些最为琐碎的、意义最小的日志信息。对应级别verbose,是Android日志里面级别最低的一种。 |
Log.d() | 用于打印一些调试信息,这些信息对你调试程序和分析问题应该是有帮助的。对应级别debug,比verbose高一级。 |
Log.i() | 用于打印一些比较重要的数据,这些数据应该是你非常想看到的、可以帮你分析用户行为数据。对应级别info,比debug高一级。 |
Log.w() | 用于打印一些警告信息,提示程序在这个地方可能会有潜在的风险,最好去修复一下这些出现警告的地方。对应级别warn,比info高一级。 |
Log.e() | 用于打印程序中的错误信息,比如程序进入到了catch语句当中。当有错误信息打印出来的时候,一般都代表你的程序出现严重问题了,必须尽快修复。对应级别error,比warn高一级。 |
其实很简单,一共就5个方法,当然每个方法还会有不同的重载,但那对你来说肯定不是什么难理解的地方了。我们现在就在HelloWorld项目中试一试日志工具好不好用吧。
打开HelloWorldActivity,在onCreate()
方法中添加一行打印日志的语句,如下所示:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("MainActivity","onCreate execute");
}
}
Log.d()
方法中传入了两个参数:第一个参数是tag
,一般传入当前的类名就好,主要用于对打印信息进行过滤;第二个参数是msg
,即想要打印的具体的内容。
09/08 20:24:53: Launching app
$ adb shell am start -n "com.example.myhelloworld/com.example.myhelloworld.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Connected to process 8188 on device emulator-5554
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
D/EGL_emulation: eglMakeCurrent: 0x9c1613c0: ver 2 0 (tinfo 0xa80f6720)
D/MainActivity: onCreate execute
D/EGL_emulation: eglMakeCurrent: 0x9c1613c0: ver 2 0 (tinfo 0xa80f6720)
而随着我在模拟器上点击,做更多的相关操作,日志的内容也变得越来越多。
快速在AS调用日志方法可以使用快捷操作:logi/ligw…
点击Edit Filter Configuration
,会弹出一个过滤器配置界面。我们给过滤器起名叫data,并且让它对名为data的tag进行过滤:
logcat中的日志级别:
当前我们选中的级别是verbose,也就是最低等级。这意味着不管我们使用哪一个方法打印日志,这条日志都一定会显示出来。而如果我们将级别选中为debug,这时只有我们使用debug及以上级别方法打印的日志才会显示出来,以此类推。你可以做一下试验,当你把logcat中的级别选中为info、warn或者error时,我们在onCreate() 方法中打印的语句是不会显示的,因为我们打印日志时使用的是Log.d() 方法。
日志级别控制的好处就是,你可以很快地找到你所关心的那些日志。相信如果让你从上千行日志中查找一条崩溃信息,你一定会抓狂的吧。而现在你只需要将日志级别选中为error,那些不相干的琐碎信息就不会再干扰你的视线了。
关键字过滤输入框:
我们可以在输入框里输入关键字的内容,这样只有符合关键字条件的日志才会显示出来,从而能够快速定位到任何你想查看的日志。另外还有一点需要注意,关键字过滤是支持正则表达式的,有了这个特性,我们就可以构建出更加丰富的过滤条件。
你现在一定会觉得很充实,甚至有点沾沾自喜。确实应该如此,因为你已经成为一名真正的Android开发者了。通过本章的学习,你首先对Android系统有了更加充足的认识,然后成功将Android开发环境搭建了起来,接着创建了你自己的第一个Android项目,并对Android项目的目录结构和执行过程有了一定的认识,在本章的最后还学习了Android日志工具的使用,这难道还不够充实吗?
不过你也别太过于满足,相信你很清楚,Android开发者和出色的Android开发者还是有很大的区别的,你还需要付出更多的努力才行。即使你目前在Java领域已经有了不错的成绩,我也希望在Android的世界你可以放下身段,以一只萌级小菜鸟的身份起飞,在后面的旅途中你会不断地成长。