安卓开发(1)——安卓概述

安卓简介

安卓系统架构

Android系统结构

也就是常说的四层架构:linux内核层(linux kernel),系统运行库层(Library)、应用框架层(Application Framewordk)和应用层(Applications)。

Linux kernel

Linux内核层:这一层是为硬件提供底层的驱动,例如蓝牙,Wi-Fi驱动等等。

Libraries

系统运行库层:这一层通过C/C++库来为Android系统提供了特性支持,来为上层提供支持。

Application Framework

主要提供了建构应用程序时可能用到的各种API。

Application

应用层,所以安装在安卓手机上的应用程序都是属于这一层的,比如手机上的一些联系人,QQ,微信等应用。

安卓开发的四大组件

Activity,Service,BroadcastReceiver和ContentProvider。

Activity(活动):包含了所有在APP中可以看到的东西。

Service:是在后台运行的部分。

BroadcastReciver:广播接受者,可以接受各处的广播(如:短信,电话等等),也可以向外发出广播。

ContentProvider:应用程序之间共享数据。(比如:微信通过读取手机的联系人来添加好友)

Android的开发环境

所需的工具:

JDK:用于处理java代码。

AndroidSDK:是Google提供的Android开发工具包,提供Android要使用的API。

Android Studio:一个开发Android 的IDE。

如何搭建Android开发环境不是本博客的重点,想了解的,可以自行百度搜索。

第一个Android项目

创建项目

 

 

这里选择一个Empty Activity

 

 

这里的Name就是app的名字,Package name是项目的包名,Android系统就是通过包名来区分不同的应用程序(application)的,所以包名必须得有唯一性。

Save Location表示项目保存地址。

Language这里可以选择Kotlin和Java

Minimum SDK这里可以设置项目的最低兼容版本,也就是兼容Android的最低版本为多少。

点击Finish就完成了第一个项目的创建

使用项目

创建虚拟机

Android系统开发的app,需要在Android系统上才能跑,Android Studio提供了Android的虚拟机,可以给我们用来测试APP。

 

 

在菜单中找到这三个案件,中间那个就是用来创建和启动模拟器的。

然后开始创建模拟器:

 

 

这里根据自己的喜好来选就好(我用的是Pixei2)

 

 

然后选择系统镜像,推荐选择最新版本,也就是第一个,如果没有下载过该镜像,需要点击Download后才能点击下一步。

 

 

 

使用虚拟机

 

 

单机想要启动的虚拟机,就可以启动该虚拟机了。

在虚拟机上运行写好的项目app

先开启虚拟机,开启后,在该菜单栏里面选择要启动的虚拟机和项目:

 

 

然后再点击右边的运行按钮就可以运行了。(左边那个锤子是编译项目的意思)

 

 

 

 

第一个Android项目就搞定了。

分析第一个Android程序

查看项目文件:

 

 

当一个项目创建的时候默认是采用Android模式的项目结构来查看,但是这个步数真正的目录结构,而是被Android Studio转换了的,单击Android转换为Project(项目)版本来查看(中英文可以能有出入)。

 

 

切换为project版本下的项目结构:

 

 

这个才是真实的项目结构。

分析项目文件:

.gradle和.idea存放的都是AS自动生成的文件,这个不用处理。
app 项目中的代码和资源等主要内容都是存放在这个目录的,开发工作也主要是在这个目录下面。
build 包含了一些在编译时自动生成的文件。(不用关心)
gradle 这个目录包含了gradle wrapper的配置文件,使用gradle wrapper的方式不需要提前将gradle下载好,而是先根据有没有本地缓存的情况来决定要不要下载。可以点击File->Settings->Build,Execution,Deployment->Gradle。 整个安卓的项目都是采用gradle来全程把控
.gitignore 用来将指定的目录或文件排除在版本控制之外。
.build.gradle 全局的gradle构建脚本,通常是不用修改的。
gradle.properties 全局gradle的构建脚本,通常不需要修改。有点类似于makefile
gradlew和gradlew.bat 这两个文件是用来在命令行界面执行gradle命令的,其中gradlew是在linux上使用,gradlew.bat是用在windows系统上的。
HelloWorld.iml iml文件是所有的IDEA项目都会自动生成的一个文件(AS是基于IDEA开发的),该文件只是用来标识是IDEA的项目文件。
local.properties 该文件用来指定Android 的SDK的路径,通常是自动生成的,除非Android SDK位置发生变化,不然是不用修改的。
setting.gradle 用来指定项目中所有引入的模块,通常情况下模块的引入都是自动完成的,需要手动修改比较少。

分析app目录

经过前面分析的项目文件可以知道,除了app目录,大部分文件都是AS自动生成的,所以app文件下的内容才是重点。

 

 

build和外层的build一样,包含的是一些编译时自动生成的文件。
libs 如果项目中包含了第三方的jar包,就需要把这些jar包放到libs文件夹下
androidTest 用来写Android Test测试用例的
java 放置所有java(Kotlin)代码的,展开可以看到,AS自动生成了一个MainActivity文件
res 在项目里使用的所有图片、布局、字符串等资源都要存放到该目录下。图片放到drawable目录下,布局放到layout目录下,字符串放到value目录下。
AndroidManifest.xml 整个项目的配置文件,在程序中定义的所有四大组件都需要在这个文件里面注册,还可以在这里面给应用程序添加权限声明。
test 用来编写Unit Test测试用例,对项目进行自动化测试的一种方式。
.gitignore 用于将app模块内指定的目录或文件排除在版本控制之外。
app.iml IDEA自动生成的文件,仅仅来标识是个IDEA文件。
build.gradle app模块内部的gradle构建脚本,指定很多项目构建相关的配置。
proguard-rules.pro 用于指定项目代码的混淆规则,当代码开发完成后,不希望被别人破解,通常会将代码进行混淆,

详解app中的res

 

 

将res文件夹展开可以看到里面的文件还是挺多的,但是归纳一下就还好。

图片所有以drawable开头的目录都是用来存放图片的
应用图标 所有以mipmap开头的存放的都是应用图标
配置 所有以values开头的都是用来存放字符串、样式、颜色等配置的。
布局 所有以layout开头的目录都是用来存放布局文件的。

之所以有这么多mipmap开头的目录,主要是为了兼容各种设备,drawable也是一样,虽然AS没有自动生成,但是为了更好兼容,我们也应该自己创建drawable-hdpi ... drawable-xxxhdpi等目录。在写程序的时候最好对同一个图片能提供几个不同分辨率的版本,分别放在这些目录下面,但是大多数时候只会有一个图片,就把所有的图片放到drawable-xxxhdpi里面就好了,这个目录对应的是最主流的设备分辨率。

 

 

打开values下的string,xml:

<resources>
  <string name="app_name">HelloWorld</string>
</resources>

可以看到这里定义了一个应用程序名字的字符串,有两种方式可以拿来引用它:

1:在代码里面通过R.string.app_name可以获得该字符串的引用

2:在X M L中通过 @string/app_name可以获得该字符串的引用

其中的string部分是可以替换的,如果用的是图片资源就可以替换成drawable,如果是应用图标就可以替换成mipmap,布局文件就可以替换成layout

例如:AndroidManifest.xml文件中的内容

    <application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:supportsRtl="true"
      android:theme="@style/Theme.HelloWorld">
....
      </activity>
  </application>

 

 

详解build.gradle文件

AS采用gradle来构建项目,gradle是一个比较先进的项目构建工具,基于Groovy领域特点语言来进行项目设置,抛弃了基于传统的XML的各种繁琐配置。(反正比较先进就行了。)

在我们这个HelloWorld项目中,有两个build.gradle文件,一个是整体的build.gradle,一个是app目录下的build.gradle文件。

详解整体项目的build.gradle文件

先看整体项目的build.gradle文件:

buildscript {
  ext.kotlin_version = "1.3.72"
  repositories {
      google()
      jcenter()
  }
  dependencies {
      classpath "com.android.tools.build:gradle:4.1.1"
      classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  }
}

allprojects {
  repositories {
      google()
      jcenter()
  }
}

这些代码都是自动生成的,看似复杂但是忽略语法查看关键代码还是挺简单的。

首先两处的repositories都声明了google()和jcenter(),这两个分别对应两个代码仓库,google()主要是包含谷歌自己的扩展依赖库,jcenter()主要是包含一些开源的第三方库,声明了之后就可以使用它们所包含的依赖库了。

在dependencies中声明了gradke和kotlin的两个插件,申明gradle是因为gradle并不是专门给Android开发出来的,如果要使用的话,肯定需要声明gradle的插件,gradle后面的版本通常是和AS的版本相对应的,申明kotlin是因为这个Android项目是用的kotlin语言,如果采用的是java的话就不会有这个了。

详解app下的build.gradle文件

查看app下的build.gradle源代码:

plugins {
  id \'com.android.application\'
  id \'kotlin-android\'
}
android {
  compileSdkVersion 30
  buildToolsVersion "30.0.3"
  defaultConfig {
      applicationId "com.example.helloworld"
      minSdkVersion 21
      targetSdkVersion 30
      versionCode 1
      versionName "1.0"

      testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  }
  buildTypes {
      release {
          minifyEnabled false
          proguardFiles getDefaultProguardFile(\'proguard-android-optimize.txt\'), \'proguard-rules.pro\'
      }
  }
  compileOptions {
      sourceCompatibility JavaVersion.VERSION_1_8
      targetCompatibility JavaVersion.VERSION_1_8
  }
  kotlinOptions {
      jvmTarget = \'1.8\'
  }
}
dependencies {
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
  implementation \'androidx.core:core-ktx:1.2.0\'
  implementation \'androidx.appcompat:appcompat:1.1.0\'
  implementation \'com.google.android.material:material:1.1.0\'
  implementation \'androidx.constraintlayout:constraintlayout:1.1.3\'
  testImplementation \'junit:junit:4.+\'
  androidTestImplementation \'androidx.test.ext:junit:1.1.1\'
  androidTestImplementation \'androidx.test.espresso:espresso-core:3.2.0\'
}

第一个闭包plugins:

com.android.application:表示是一个应用程序模块。 kotlin-android:就是一个kotlin的插件,如果要用kotlin来开发,这个插件是必须要的。

第二个闭包是一个很大的闭包 Android,该闭包中嵌套了defaultConfig,buildTypes,compileOptions,kotlinOptions 四个闭包。在Android闭包里面可以配置项目构建的各自属性,其中compileSdkVersion用来指定项目的编译版本,指定Android项目的SDK版本,buildToolsVersion用来指定项目构建工具的版本。

Android闭包里的defaulltConfig闭包:

        applicationId "com.example.helloworld"  //每个应用的唯一标识符,也就是在创建项目时的包名
      minSdkVersion 21 //项目最低兼容的Android版本
      targetSdkVersion 30 //该字段指定的值用来表示在当前目标版本上已经做好了充分的测试
//指定的值表示在哪一个sdk版本以及之前已经做好了充分的测试。
      versionCode 1 //指定项目的版本号
      versionName "1.0" //指定项目的版本名

      testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
      //用来在当前项目中七点JUnit测试

Android闭包里的buildTypes闭包:

buildTypes闭包里通常会有两个子闭包,release和debug,就相当于项目的两个版本,debug默认可以不用写。

    buildTypes {
      release {
          minifyEnabled false //用来指定是否对项目的代码进行混淆
          proguardFiles getDefaultProguardFile(\'proguard-android-optimize.txt\'), \'proguard-rules.pro\' //proguardFiles用来指定代码的混淆规则
//proguard-android-optimize.txt是在<AndroidSDK>/tools/proguard目录下的一个通用混淆规则
//proguard-rules.pro是在当前项目的根目录下的,里面可以编写当前项目特有的代码混淆规则
      }
  }

Android剩下的两个闭包:compileOptions,kotlinOptions都是用来指定java和jvm版本的,没啥影响。

 

最后一个闭包:dependencies,这个闭包指定了项目的所有依赖关系。

在Android中的依赖有三种方式:本地依赖,库依赖,远程依赖。本地依赖可以对本地的jar包添加,库依赖可以对项目中的库模块添加依赖,远程依赖则是对于jcenter仓库上的开源项目进行添加依赖。

implementation fileTree 是本地依赖申明
implementation 表示是一个远程依赖
implementation project 表明是一个库依赖

 

dependencies {
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
  implementation \'androidx.core:core-ktx:1.2.0\'
  implementation \'androidx.appcompat:appcompat:1.1.0\'//一个标准的远程依赖
  //androidx.appcomat是域名用来区分其他公司的库,appcompat是工程名,1.1.0是版本号。
  implementation \'com.google.android.material:material:1.1.0\'
  implementation \'androidx.constraintlayout:constraintlayout:1.1.3\'
  testImplementation \'junit:junit:4.+\'
  androidTestImplementation \'androidx.test.ext:junit:1.1.1\'
  androidTestImplementation \'androidx.test.espresso:espresso-core:3.2.0\'
}

 

分析一个项目的运行流程

分析一下,这里写的Helloworld项目是如何运行起来的。

首先查看AndroidManifest.xml文件,找到如图所示的代码:

 

 

        <activity android:name=".MainActivity">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>

这段代码表示对MainActivity的注册(没有再AndroidManifest.xml文件中注册的四大组件是无法使用的),其中intent-filter代码段里面的两行代码最为重要,其作用就是表示MainActivity是这个项目的主Activity,在手机上点击应用图标,首先启动的就是该Activity。

凡是在APP中可以看到的东西,都是Android中的四大组件的Activity中的内容,因此在该HelloWorld项目中看到的弹出来的界面就是MainActivity,查看MainActivity的代码:

 

 

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)
  }
}

可以很清楚地看到MainActivity是继承了AppCompatActivity这个类的。AppCompatActivity是AndroidX提供的一个向下兼容的Activity,可以让Activity在不同系统版本中的功能保持一致性。而Activity是Android提供的一个Activity基类,所有自己自定义的Activity都需要继承于它或它的子类(AppCompatActivity就是它的子类)。然后可以看到该MainActivity类有一个onCreate()函数,这个方法是Activity组件创建时必须要执行的方法,而且只有两行代码,可以看到这里并没有我们使用该APP显示的HelloWorld字符串,那么显示的HelloWorld到底在哪里呢?

Android程序设计讲究逻辑和视图分离, 因此一般不要再Activity里面直接编写界面加逻辑的,更加通用的办法是:在布局文件中编写界面,然后再在Activity中引入进来

在MainActivity类中的onCreate()方法中的第二行调用了setContentView()方法,就是这个方法给当前的Activity引入了一个activity_main布局,那么“Hello World!”就一定在该R.layout.activity_main里面定义的,前面有提到所有的资源在res下,所有的布局文件又在res下的layout里面,所以直接去查看布局文件:

 

 

切换成code(代码)视角:

 

 

<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:text=".....",就是这里我们想要的Hello World。

总结一个项目的运行流程:进入一个app首先是获取AndroidManifest.xml文件中注册的Activity组件,然后看到的是Activity里面的MainActivity,就好比一个C语言程序里面的main函数,然后根据MainActivity里面的代码逻辑获取得到布局文件最后展示出来。

 

 

如何使用日志工具

在Android里面的日志工具是非常重要的,特别是对于开发者来说,是非常重要的。

Android中的日志工具类是Log(android.util.Log),提供了5个方法来打印日志:

Log.v()打印最低级别的日志,对应级别是verbose
Log.d() 打印一些调试信息,对应级别是debug,比verbose高一级
Log.i() 打印一些比较重要的信息,可以分析用户行为的数据,对应级别为info,比debug高一级
Log.w() 打印警告信息,对应级别为warn比info高一级
Log.e() 打印错误信息,对应级别为error

在helloworld项目中使用日志工具:在MainActivity.kt里面添加Log.d("MainActivity","onCreate execute")

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)
      Log.d("MainActivity","onCreate execute")
  }
}

 

 

再在Logcat下面就可以看到日志信息了。

通过过滤器来查看不同的Log信息。

 

总结

使用kotlin来开发Android已经大势所趋了,安卓开发的细节,以及配置文件很多,比较麻烦,这一章节看完了相当于有一个大概的轮廓了。