Android学习笔记(一):Android基础_安卓学习笔记-程序员宅基地

技术标签: gradle  签名  android  学习笔记  android studio  Android  

1 Android发展和历史

Android是由Android公司创造的手机操作系统,公司创始人是Andy Rubin,后来被Google收购,Google于2007年11月发布了Android 1.0手机操作系统,在2009年发布了Android 1.5,此后Android发展迅速。目前Android已经超出类手机操作系统的范畴,已经被广泛应用于TV、手表以及各种可穿戴设备等等。

2 Android平台架构

Android系统的底层建立在Linux上,采用一种称为软件叠层的方式进行构建,这种方式使得层与层之间相互分离,保证了层与层之间的低耦合。

Android体系结构如下(图源):

在这里插入图片描述

可以看到主要由6部分组成:

  • 系统App
  • Java API框架层
  • 原生C/C++
  • Android运行时
  • 硬件抽象层(HAL
  • Linux内核

下面进行详细说明。

2.1 系统App

包含一系列核心App(电话拨号应用、电子邮件客户端、日历、相机等),这些应用程序通常使用Java编写。

2.2 Java API框架层

Java API框架提供了大量API供开发者使用,主要在这一层的上面进行App的开发。

2.3 原生C/C++

Android包含一套被不同组件所使用的C/C++库的集合,一般来说是通过Java API框架层去调用这些原生的C/C++库。

一些简单的原生C/C++库如下:

  • WebKit:一个Web浏览器引擎,为Android浏览器提供支持,也为WebView提供支持
  • OpenMAX:开放媒体加速层,目的在于使用统一的接口,加速处理大量多媒体资料
  • Libc:一个从BSD系统诞生的标准C系统库,并为嵌入式Linux调整过
  • Media Framework:基于PacketVideoOpenCORE,支持播放和录制许多流行的音频和视频格式
  • SGL:底层的2D图形引擎
  • OpenGL ES:基于OpenGL ES API实现的3D系统,可以使用硬件3D加速和软件3D加速
  • SQLite:供所有应用使用的功能强大的轻量级关系数据库

2.4 Android运行时

Android运行时由两部分组成:

  • Android核心库:核心库提供了Java语言核心库所能使用的绝大部分功能
  • ART:负责运行Android应用程序

2.5 硬件抽象层

硬件抽象层(HAL)提供了对Linux内核驱动的封装,可以向上提供驱动音频、蓝牙、摄像头、传感器等设备的编程接口,向下可以隐藏底层的实现细节。

Android把对硬件的支持分为两层:

  • 内核驱动层:处于Linux内核中,值提供简单的硬件访问控制逻辑,开源
  • 硬件抽象层:负责参数和流程控制,向上提供统一的编程接口,不开源,实现因厂家而异

2.6 Linux内核

Android是基于Linux的,Linux内核提供了安全性、内存管理、进程管理、网络协议栈和驱动模型等核心服务,也是系统硬件和软件叠层之间的抽象层。

3 Gradle

3.1 Gradle简介

GradleAndroid Studio采用的构建工具,GradleAntMaven相比,优势如下:

  • Gradle支持AntMaven的构建操作
  • Gradle提供了强大的依赖管理
  • Gradle使用Groovy编写构建文件,构建文件的功能更加灵活
  • 使用领域对象模型来描述构建
  • 支持多项目构建
  • 提供简单易用的自定义任务、自定义插件

3.2 目录介绍

以目前最新的7.5.1版本为例,下载之后解压会发现如下目录:

  • bin:包含Gradle的命令
  • docs:包含用户手册、DSL参考文档、API文档
  • lib:包含Gradle核心,以及依赖的JAR
  • init.d:初始化脚本,以.gradle结尾,比如test.gradle,构建时会执行,该文件夹默认为空
  • srcGralde源码

3.3 基础使用

Gradle解压后会有bin文件夹,其中包含gradlegradle.bat,根据系统的不同选择其中一个即可运行。

运行如果没有指定参数,会在当前目录下搜索build.gradle,如果想让其他文件作为构建文件,可以使用-b/--buildfile参数。

使用Gradle的关键就是编写构建文件,构建文件的主要作用就是定义构建项目的各种任务和属性。每个任务可以包含多个动作,Gradle每次可运行多个任务。

3.4 项目层次结构

一个典型的Gradle项目层次结构如下:

root:项目根目录,存放全部资源
--src:源文件+资源文件
----main:存放与项目相关的源文件和资源
------java:Java源文件
------resources:项目相关资源
----test:存放与测试相关的源文件和资源
------java:测试源文件
------resources:测试相关资源
--build:存放编译后的class文件,与src具有对应关系
--libs:存放第三方JAR包
--build.gradle:Gradle构建文件

如果使用gradle命令构建项目,项目根目录就会多出一个.gradle文件夹,存放的是Gradle构建信息,一般不需要手动修改。

3.5 构建文件

3.5.1 概要

Gradle构建文件本质上是一个Groovy源文件,该文件完全符合Groovy语法。Gradle采用领域对象模型的概念来组织构建文件,在整个构建文件中涉及如下API

  • Project:代表项目,通常一份构建文件代表一个项目,Project包含大量属性和方法
  • TaskContainer:任务容器,每个Project都会维护一个TaskContainer类型的tasks属性,ProjectTaskContainer具有一一对应的关系
  • Task:代表要执行的一个任务,允许指定它依赖的任务以及任务的类型,也可以通过configure()配置任务,还提供了doFirstdoLast方法来添加Action

3.5.2 构建文件结构

Gradle构建文件结构如下:

Task1:
--Action1
--Action2
--Action3

Task2:
--Action1
--Action2
--Action3

Task3:
--Action1
--Action2
--Action3

3.5.3 Task创建

Task创建有两种方式:

  • 调用Projecttask()
  • 调用TaskContainercreate()

无论哪一种方式都可以为Task指定如下属性:

  • dependsOn:指定该Task所依赖的其他Task
  • type:指定该Task的类型
  • 通过传入的代码块参数配置Task

一个简单的Task示例如下:

task hello1{
    
	println "配置的第一个Task"
}

然后通过gradle hello1可以看到输出如下:

在这里插入图片描述

3.5.4 Gradle构建过程

Gradle是一种声明式的构建工具,使用Gradle构建时,并不是直接按顺序执行build.gradle中的内容,构建过程可以分为两个阶段:

  • 配置阶段:Gradle读取build.gradle的全部内容来配置ProjectTask
  • 执行阶段:按照依赖关系执行指定Task

在创建Task时传入的代码用于配制该Task,因此上面的代码在配置阶段输出(Configure project),而不是在运行阶段输出。

3.5.5 Action添加

可以通过doFirst/doLastTask添加Action,代码示例如下:

task hello2{
    
    println "配置的第二个Task"
    doLast{
    
        for(i in 0..<5){
    
            println i
        }
    }

    doFirst{
    
        def s = "test str"
        println "str is:$s"
    }
}

运行结果:

在这里插入图片描述

可以看到,Gradle构建过程分为配置和运行,配置阶段会配置整个Project和所有Task,而运行阶段会运行对应参数指定的Task

3.5.6 通过TaskContainer创建Task

Project对象带有一个TaskContainer类型的tasks属性,可以在构建文件中通过它的create方法来创建task,示例如下:

tasks.create(name:'showTasks'){
    
    doLast{
    
        println 'tasks属性的类型: ${tasks.class}'
        tasks.each{
     e->
            println e
        }
    }
}

通过gradle showTasks输出如下:

在这里插入图片描述

3.5.7 Task依赖以及类型

在创建Task的时候可以通过dependsOn属性指定该Task锁依赖的Task,同时也可以通过type指定Task的类型(如果不指定默认是DefaultTask类)。示例如下:

tasks.create(name:'task3',dependsOn:'hello2',type:Copy){
    
    from 'src.txt'
    into 'targetDir'
}

上面定义了一个叫task3Task,依赖于hello2,类型为Copy(完成文件复制),from指定了复制的源文件,into指定了复制的目录位置(如果没有目录会新建)。

通过gradle tasks运行发现会首先运行hello2任务,同时会自动新建了into指定的文件夹,其中的内容是src.txt

另一方面,使用Projecttask方法同样可以指定type以及dependsOn属性,示例代码如下:

plugins {
    
	id 'java'
}

task compile(type:JavaCompile){
    
    source=fileTree('src/main/java')
    classpath=sourceSets.main.compileClasspath
    destinationDir=file('build/classes/main')
    options.fork=true
    options.incremental=true
}

task run(type:JavaExec,dependsOn:'compile'){
    
    classpath=sourceSets.main.runtimeClasspath
    main='Main'
}

上面的代码首先应用了java插件,然后定义了compilerun任务,其中后者依赖于前者。compile任务类型为JavaCompile(详细属性说明可查看官方文档),其中:

  • source指定了源代码路径
  • classpath指定类路径
  • destinationDir指定了编译后的字节码文件保存位置
  • options.fork表示是否编译之前创建一个新的进程,默认为false
  • options.incremental表示开启增量编译

run的类型是JavaExec(文档说明此处),指定了类路径以及主类。

注意在运行之前先在当前与build.gradle同级的文件夹下创建src/main/java/Main.java,里面内容示例如下:

public class Main{
    
    public static void main(String[] args){
    
        System.out.println("Hello world");
    }
}

然后可以直接gradle run运行。

但是笔者在运行的时候报错如下:

> Task :compileJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Could not find tools.jar. 

笔者环境是m1 Mac+JDK8,解决方法是通过

/usr/libexec/java_home -V

查看JDK路径,一般会有两个,其中一个在lib下带有tools.jar,而另一个没有,只需要使用cptools.jar复制到另一个没有的lib下即可。

修复之后,任务正常运行,输出如下:

在这里插入图片描述

3.5.8 属性定义

Gradle允许为ProjectTask指定或添加属性,也就是可以指定内置属性的属性值,也可以添加新的属性。

3.5.8.1 已有属性指定属性值

Project/Task本身具有内置属性,可以直接在构建文件制定属性值,示例:

version = 1.0
description = 'Project 描述'

task showProps{
    
    description = 'Task 描述'
    doLast{
    
        println version
        println description
        println project.description
    }
}

输出如下:

在这里插入图片描述

task外的就是为Project指定的属性,在task内的就是为该task指定的属性。

Project常用属性:

  • name:项目名字
  • path:项目绝对路径
  • description:项目描述信息
  • buildDir:项目构建结果存放路径
  • version:项目版本号
3.5.8.2 通过ext添加属性

Gradle中实现了ExtensionAware接口的API都可以通过ext添加属性,而ProjectTask都实现了ExtensionAware,可以通过ext为其添加属性,示例:

ext.prop1='project prop1'
ext.prop2='project prop2'
ext{
    
    prop3='project prop3'
    prop4='project prop4'
}
task showExtProps{
    
    ext.prop1='task prop1'
    ext.prop2='task prop2'
    ext{
    
        prop3='task prop3'
        prop4='task prop4'
    }
    doLast{
    
        println prop1
        println prop2
        println prop3
        println prop4
        println project.prop1
        println project.prop2
        println project.prop3
        println project.prop4
    }
}

通过gradle showExtProps输出如下:

在这里插入图片描述

3.5.8.3 通过-P添加属性

执行gradle命令可以通过-P添加项目属性,示例:

task showCmdProps{
    
    doLast{
    
        println("系统显卡:${graphics}")
        println("系统显卡:${project.graphics}")
    }
}

如果直接执行gradle showCmdProps会报错,提示找不到该属性。

需要使用-P指定属性,执行gradle -P graphics=RTX4090Ti showCmdProps后,输出如下:

在这里插入图片描述

3.5.8.4 通过JVM参数添加属性

执行gradle时可以通过-D设置JVM参数从而添加项目属性,示例:

task showJVMProps{
    
    doLast{
    
        println("JVM参数添加的属性:${p1}")
    }
}

执行gradle -D org.gradle.project.p1=111 showJVMProps后,输出如下:

在这里插入图片描述

3.6 增量式构建

对于一些执行时间长的任务,如果每次执行该任务时没有发生改变,那么就没有必要执行该任务。因此Gradle引入了增量式构建,如果任务的执行和前一次执行比较没有改变,Gradle不会重复执行该任务。

Gradle通过任务的输入和输出去判断任务有没有改变。Task使用如下属性表示输入和输出:

  • inputs:代表任务输入,是一个TaskInputs类型的对象
  • ouputs:代表任务输出,是一个TaskOutputs类型对象

两者都支持设置文件、文件集、目录、属性等,只要它们没有发生改变,Gradle就认为该任务的输入和输出没有改变。

示例代码:

task incrementalBuild{
    
    def source = fileTree('source')
    def dest = file('dist.txt')
    inputs.dir source
    outputs.file dest
    doLast{
    
        dest.withPrintWriter{
     writer->
            source.each{
     s->
                writer.write(s.text)
            }
        }
    }
}

其中输入的部分指定了目录是source,输出部分指定了文件为dist.txt,只要两者没有发生改变,那么就认为任务没有发生改变,就不会执行任务。下面是两次执行任务的结果:

在这里插入图片描述

可以看到第二次并没有执行,只是更新到最新。

3.7 插件介绍

为了简化开发人员从头开始编写每一个Task以及属性,Gradle提供了插件机制。

开发插件其实很简单,无非就是在插件中预先定义大量的任务类型、任务、属性,然后开发人员只需要在build.gradle中使用apply plugin应用插件即可。

应用插件就相当于引入了该插件包含的所有任务类型、任务和属性,这样Gradle就能执行插件中预先定义好的任务。

比如之前的java插件,引入之后,可以通过gradle tasks --all查看引入的任务:

在这里插入图片描述

可以看到java插件分成几类添加了很多的任务,另外,该插件还定义了大量属性,比如:

  • sourceCompatibility用于指定编译源文件时使用的JDK版本
  • archivesBaseName指定了打包生成的JAR包文件名

示例代码:

plugins{
    
    id 'java'
    id 'application'
}

sourceSets{
    
    utils
    main{
    
        compileClasspath=compileClasspath+files(utils.output.classesDirs)
    }
}

compileJava.dependsOn compileUtilsJava
mainClassName='Main'
run.classpath=sourceSets.main.runtimeClasspath+files(sourceSets.utils.output.classesDirs)

首先应用了java以及application插件,两个插件都定义了大量的属性以及任务。然后在其中的sourceSets添加了一个叫utils自定义的依赖,并在main中将utils编译生成的字节码添加到编译时的类路径中。接着通过配置compileJava的依赖任务compileUtilsJava(该任务是自动生成的,一个sourceSet对应了三个任务,比如此处的是utils,则会自动生成compileUtilsJavaprocessUtilsResourcesutilsClasses三个任务)。

最后两行是application插件定义的属性,mainClassName指定运行的主类,run.classpath指定运行时的类路径。

测试项目代码结构如下:

在这里插入图片描述

代码:

package utils;
public class Utils{
    
    public static void print(){
    
        System.out.println("utils test");
    }
}

import utils.Utils;
public class Main{
    
    public static void main(String[] args){
    
        Utils.print();
    }
}

运行gradle run可以看到输出如下:

在这里插入图片描述

3.8 依赖管理

通过Gradle配置依赖需要两步:

  • 配置仓库
  • 引入依赖

配置仓库在repositories中配置即可,例如:

repositories{
    
	// Maven默认中央仓库
	mavenCentral()
	// 通过URL自定义远程仓库或者本地仓库
	maven{
    
		// 定义远程仓库
		url "http://repo2.maven.org/maven2/"
		// 定义本地仓库
		url "/xxx/xx/xxx"
	}
}

然后可以通过配置组引入依赖。配置组的概念是由于项目编译时可能依赖一组JAR,而运行的时候又依赖另一组JAR,测试的时候可能又依赖另一组JAR,因此可以通过不同的组配置不同的依赖。

Gradle可以使用configurations来配置组,例如:

configurations{
    
	testConfig
}

定义配置组后就可以引入JAR包,Gradle使用dependencies来配置JAR包,配置方式与Maven相同,指定groupnameversion即可。示例代码:

dependencies{
    
    testConfig 'org.apache.commons:commons-lang3:3.12.0'
}

另外,在引入之后,如果需要提供额外的配置,可以使用闭包,示例如下:

testConfig('org.apache.commons:commons-lang3:3.12.0'){
    
	// 额外配置
}

如果需要添加多个JAR包,可以使用数组的形式:

dependencies{
    
    testConfig 'org.apache.commons:commons-lang3:3.12.0',
        'commons-io:commons-io:2.11.0'
}

定义好依赖后,可以在任务中使用该依赖,示例任务如下:

task showDependency{
    
    doLast{
    
        println configurations.testConfig.asPath
    }
}

执行任务之后就会输出依赖包在本地的路径,笔者输出是在Gradle的缓存目录下。

在实际开发中,通常不需要自己配置依赖组,应用java插件后,默认的依赖组有:

  • implementation:源代码依赖的组,最常用的一个依赖组
  • compileOnly:源代码编译时才依赖的组
  • runtimeOnly:源代码运行时才依赖的组
  • testImplementation:测试代码依赖的组
  • testCompileOnly:测试代码编译时依赖的组
  • testRuntimeOnly:测试代码运行时依赖的组
  • archives:打包时依赖的组

3.9 自定义任务

自定义任务就是一个实现Task接口的类,该接口定义了大量的抽象方法,因此一般自定义任务都会继承DefaultTask基类,自定义任务的累可以自定义多个方法,这些方法可作为Action使用。

自定义任务的Groovy可以直接定义在build.gradle中,或者定义在Groovy源文件中。示例:

class HelloWorldTask extends DefaultTask{
    
    @Internal
    def message = '测试str'
    @TaskAction
    def test(){
    
        println "test str: $message"
    }

    def info(){
    
        println "info: $message"
    }
}

task hello(type:HelloWorldTask){
    
    doLast{
    
        info()
    }
}

task hello1(type:HelloWorldTask){
    
    message = "测试 hello1"
}

上面定义了一个HelloWorldTask的任务类,自定义了一个message属性,且使用了@TaskAction修饰了test()方法作为Action。执行gradle hello hello1输出如下:

在这里插入图片描述

当需要定义大量的自定义任务时,直接写在build.gradle显然不是一个好办法,更好的办法是存放在buildSrc目录中。

buildSrc相当于另一个Gradle目录,该目录存放自定义任务的源代码。比如例子中的目录结构如下:

build.gradle
buildSrc
--src
----main
------groovy
--------com
----------company
------------TestTask.groovy

其中TestTask.groovy定义如下:

package com.company

import org.gradle.api.*;
import org.gradle.api.tasks.*;

class TestTask extends DefaultTask{
    
    @Internal
    File file = new File('dist.txt')

    @TaskAction
    def show(){
    
        println file.text
    }

    @TaskAction
    def multiShow(){
    
        println "==========="
        println file.text
        println "==========="
    }
}

build.gradle如下:

task show(type:com.company.TestTask)

task show1(type:com.company.TestTask){
    
    file = file("dist1.txt")
}

运行后会发现会输出文件的内容。

3.10 自定义插件

自定义插件其实就是实现一个Plugin<Project>接口的类,实现该接口要求必须实现apply()方法。插件的本质,就是为Project定义多个属性和任务,这样引入插件后即可直接使用这些属性和任务。示例:

class FirstPlugin implements Plugin<Project>{
    
    void apply(Project project){
    
        project.extensions.create("user",User)
        project.task('showName'){
    
            doLast{
    
                println '用户名:'+project.user.name
            }
        }
        project.tasks.create('showPass'){
    
            doLast{
    
                println '密码:'+project.user.pass
            }
        }
    }
}

class User{
    
    String name='test username'
    String pass='test password'
}

apply plugin:FirstPlugin

上面定义了一个叫FirstPlugin的插件,该插件重写了其中的apply()方法,通过project.extensions定义了一个扩展属性,同时定义了两个任务。

运行结果如下:

在这里插入图片描述

如果想独立定义插件,类似任务,需要放在buildSrc下。示例目录结构如下:

build.gradle
buildSrc
--build.gradle
--src
----main
------groovy
--------com
----------company
------------Item.groovy
------------ItemPlugin.groovy

build.gradle如下:

plugins{
    
    id 'item-plugin'
}
item.name = 'tets name'
item.price = 12345
item.discount = 0.5

buildSrc下文件内容如下:

//build.gradle
plugins{
    
    id 'java-gradle-plugin'
}

gradlePlugin{
    
    plugins{
    
        itemPlugin{
    
            id='item-plugin'
            implementationClass='com.company.ItemPlugin'
        }
    }
}

//Item.groovy
class Item{
    
    String name = 'name'
    double price = 10
    double discount = 1.0
}

//ItemPlugin.groovy
package com.company

import org.gradle.api.*;

class ItemPlugin implements Plugin<Project>{
    
    void apply(Project project){
    
        project.extensions.create("item",Item)
        project.task('showItem'){
    
            doLast{
    
                println '商品名:'+project.item.name
                println '商品销售价:'+project.item.price*project.item.discount
            }
        }
    }
}

运行结果:

在这里插入图片描述

4 Android环境搭建

4.1 Android Studio安装

安装就略过了,直接到官网下载安装即可。

下载之后,准备好SDKAVD等。

4.2 第一个程序

打开Android Studio选择创建应用:

在这里插入图片描述

接着设置包名并且选择位置即可:

在这里插入图片描述

然后等待依赖导入完成就可以直接运行了。

5 Android应用结构分析

5.1 Android项目结构分析

app目录代表一个模块,是一个典型的Gradle项目,目录树如下:

app
--build
--libs
--src
----androidTest
----main
------java
--------com/xxx/xxx/
------res
--------drawable
--------layout
--------mipmap-xxx
--------values
------AndroidManifest.xml
----test
--build.gradle
--gitignore

各部分含义如下:

  • build:存放的项目构建结果
  • libs:存放第三方依赖库
  • src:源代码和资源目录
  • build.gradle:项目的构建文件
  • androidTestAndroid测试项目
  • mainjava目录保存Java源文件(如果是Kotlin源文件则存放在kotlin中)
  • res:存放各种资源文件,比如layout存放布局文件,values存放各种XML格式的资源文件,比如字符串资源文件strings.xml、颜色资源文件colors.xmldrawable存放Drawable资源,比如drawable-ldpi等,与drawable对应的还有一个叫mipmap的目录,用于保存应用程序启动图标以及系统保留的Drawable资源
  • AndroidManifest.xml:系统清单文件,用于控制应用的名称、图标、访问权限等整体属性

5.2 R.java

R.java位于app/build/outputs/apk/debug/app-debug.apk中:

在这里插入图片描述

打开apk中的dex文件(dex是为Dalvik设计的一种格式),并打开对应的包路径即可以看到R.java

R.java是由AAPT工具根据应用中的资源文件自动生成的,规则如下:

  • 每类资源对应于R类的一个内部类,比如界面布局资源对应layout内部类,字符串资源对应string内部类
  • 每个具体的资源都对应于内部类的一个public static final int类型字段

5.3 AndroidManifest.xml

AndroidManifest.xml清单文件是每个Android项目所必须的,是整个Android应用的全局扫描文件,说明了该应用的名称、所使用的图标以及包含的组件等。

一般包含如下信息:

  • 包名
  • 包含的组件
  • 兼容的最低版本
  • 权限

6 基本组件介绍

6.1 ActivityView

ActivityAndroid应用中负责与用户交互的组件,View是所有UI控件、容器控件的基类,View组件需要放到容器中,或者使用Activity将它显示出来。显示可以通过ActivitysetContentView方法。

6.2 Service

Service也代表一个单独的Android组件,通常位于后台运行,一般不需要与用户交互。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。

6.3 BroadcastReceiver

BroadcastReceiverAndroid应用中另一个重要的组件,代表广播消息接收器。类似于事件编程中的监听器,与普通监听器不同的是,普通事件监听器监听的是程序中的对象,BroadcastReceiver监听的是其他组件,相当于一个全局的事件监听器。

使用时,实现BroadcastReceiver子类即可,并重写onReceive即可,其他组件通过sendBroadcast/sendStickyBroadcast/setOrderedBroadcast发送广播消息。实现了之后可以通过Context.registReceiver()或在AndroidManifest.xml中注册。

6.4 ContentProvider

ContentProvider可用于为跨应用数据交换,实现时,需要实现如下方法:

  • insert():插入数据
  • delete():删除数据
  • update():更新数据
  • query():查询数据

一般需要配合ContentResolver使用,一个应用使用ContentProvider暴露数据,另一个使用ContentResolver访问数据。

6.5 IntentIntentFilter

Intent可启动应用中的另一个Activity,也可以启动一个Service,还可以发送广播消息触发BroadcastReceiver。也就是说,ActivityServiceBroadcastReceiver之间的通信都以Intent作为载体,Intent封装了当前组件需要启动或触发的目标组件信息。

Intent可以分为隐式以及显式Intent,显式Intent明确指定了需要启动或触发的组件类名,隐式Intent只是指定启动或触发的组件应满足怎么的条件。对于隐式Intent,需要依靠IntentFilter来声明自己所满足的条件。

7 签名

7.1 简介

Android使用包名作为唯一的标识,如果安装包名相同的应用,后面安装的会覆盖前面的应用。签名主要有两个作用:

  • 确定发布者身份:由于可能存在包名相同的情况,使用签名可以避免相同包名的程序安装被替换
  • 确保应用完整性:确保程序包中的文件不会被替换

7.2 使用Android Studio签名

根据菜单中的Build->Generate Signed Build/APK即可创建签名apk

在这里插入图片描述

选择APK

在这里插入图片描述

如果没有创建数字证书,可以选择Create new...

在这里插入图片描述

随便输入即可:

在这里插入图片描述

完成后点击下一步,选择debugrelease

在这里插入图片描述

7.3 使用命令签名

7.3.1 创建key store

keytool -genkeypair -alias key -keyalg RSA -validity 400 -keystore key.jks

在这里插入图片描述

7.3.2 签名

使用apksigner签名:

apksigner sign --ks key.jks --ks-key-alias key --out sign.apk app-debug.apk

签完之后就会输出sign.apk

8 出处

本文大部分摘自《疯狂Android讲义(第四版)》,作者李刚。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_27525611/article/details/127116656

智能推荐

软件测试流程包括哪些内容?测试方法有哪些?_测试过程管理中包含哪些过程-程序员宅基地

文章浏览阅读2.9k次,点赞8次,收藏14次。测试主要做什么?这完全都体现在测试流程中,同时测试流程是面试问题中出现频率最高的,这不仅是因为测试流程很重要,而是在面试过程中这短短的半小时到一个小时的时间,通过测试流程就可以判断出应聘者是否合适,故在测试流程中包含了测试工作的核心内容,例如需求分析,测试用例的设计,测试执行,缺陷等重要的过程。..._测试过程管理中包含哪些过程

政府数字化政务的人工智能与机器学习应用:如何提高政府工作效率-程序员宅基地

文章浏览阅读870次,点赞16次,收藏19次。1.背景介绍政府数字化政务是指政府利用数字技术、互联网、大数据、人工智能等新技术手段,对政府政务进行数字化改革,提高政府工作效率,提升政府服务质量的过程。随着人工智能(AI)和机器学习(ML)技术的快速发展,政府数字化政务中的人工智能与机器学习应用也逐渐成为政府改革的重要内容。政府数字化政务的人工智能与机器学习应用涉及多个领域,包括政策决策、政府服务、公共安全、社会治理等。在这些领域,人工...

ssm+mysql+微信小程序考研刷题平台_mysql刷题软件-程序员宅基地

文章浏览阅读219次,点赞2次,收藏4次。系统主要的用户为用户、管理员,他们的具体权限如下:用户:用户登录后可以对管理员上传的学习视频进行学习。用户可以选择题型进行练习。用户选择小程序提供的考研科目进行相关训练。用户可以进行水平测试,并且查看相关成绩用户可以进行错题集的整理管理员:管理员登录后可管理个人基本信息管理员登录后可管理个人基本信息管理员可以上传、发布考研的相关例题及其分析,并对题型进行管理管理员可以进行查看、搜索考研题目及错题情况。_mysql刷题软件

根据java代码描绘uml类图_Myeclipse8.5下JAVA代码导成UML类图-程序员宅基地

文章浏览阅读1.4k次。myelipse里有UML1和UML2两种方式,UML2功能更强大,但是两者生成过程差别不大1.建立Test工程,如下图,uml包存放uml类图package com.zz.domain;public class User {private int id;private String name;public int getId() {return id;}public void setId(int..._根据以下java代码画出类图

Flume自定义拦截器-程序员宅基地

文章浏览阅读174次。需求:一个topic包含很多个表信息,需要自动根据json字符串中的字段来写入到hive不同的表对应的路径中。发送到Kafka中的数据原本最外层原本没有pkDay和project,只有data和name。因为担心data里面会空值,所以根同事商量,让他们在最外层添加了project和pkDay字段。pkDay字段用于表的自动分区,proejct和name合起来用于自动拼接hive表的名称为 ..._flume拦截器自定义开发 kafka

java同时输入不同类型数据,Java Spring中同时访问多种不同数据库-程序员宅基地

文章浏览阅读380次。原标题:Java Spring中同时访问多种不同数据库 多样的工作要求,可以使用不同的工作方法,只要能获得结果,就不会徒劳。开发企业应用时我们常常遇到要同时访问多种不同数据库的问题,有时是必须把数据归档到某种数据仓库中,有时是要把数据变更推送到第三方数据库中。使用Spring框架时,使用单一数据库是非常容易的,但如果要同时访问多个数据库的话事件就变得复杂多了。本文以在Spring框架下开发一个Sp..._根据输入的不同连接不同的数据库

随便推点

EFT试验复位案例分析_eft电路图-程序员宅基地

文章浏览阅读3.6k次,点赞9次,收藏25次。本案例描述了晶振屏蔽以及开关电源变压器屏蔽对系统稳定工作的影响, 硬件设计时应考虑。_eft电路图

MR21更改价格_mr21 对于物料 zba89121 存在一个当前或未来标准价格-程序员宅基地

文章浏览阅读1.1k次。对于物料价格的更改,可以采取不同的手段:首先,我们来介绍MR21的方式。 需要说明的是,如果要对某一产品进行价格修改,必须满足的前提条件是: ■ 1、必须对价格生效的物料期间与对应会计期间进行开启; ■ 2、该产品在该物料期间未发生物料移动。执行MR21,例如更改物料1180051689的价格为20000元,系统提示“对于物料1180051689 存在一个当前或未来标准价格”,这是因为已经对该..._mr21 对于物料 zba89121 存在一个当前或未来标准价格

联想启天m420刷bios_联想启天M420台式机怎么装win7系统(完美解决usb)-程序员宅基地

文章浏览阅读7.4k次,点赞3次,收藏13次。[文章导读]联想启天M420是一款商用台式电脑,预装的是win10系统,用户还是喜欢win7系统,该台式机采用的intel 8代i5 8500CPU,在安装安装win7时有很多问题,在安装win7时要在BIOS中“关闭安全启动”和“开启兼容模式”,并且安装过程中usb不能使用,要采用联想win7新机型安装,且默认采用的uefi+gpt模式,要改成legacy+mbr引导,那么联想启天M420台式电..._启天m420刷bios

冗余数据一致性,到底如何保证?-程序员宅基地

文章浏览阅读2.7k次,点赞2次,收藏9次。一,为什么要冗余数据互联网数据量很大的业务场景,往往数据库需要进行水平切分来降低单库数据量。水平切分会有一个patition key,通过patition key的查询能..._保证冗余性

java 打包插件-程序员宅基地

文章浏览阅读88次。是时候闭环Java应用了 原创 2016-08-16 张开涛 你曾经因为部署/上线而痛苦吗?你曾经因为要去运维那改配置而烦恼吗?在我接触过的一些部署/上线方式中,曾碰到过以下一些问题:1、程序代码和依赖都是人工上传到服务器,不是通过工具进行部署和发布;2、目录结构没有规范,jar启动时通过-classpath任意指定;3、fat jar,把程序代码、配置文件和依赖jar都打包到一个jar中,改配置..._那么需要把上面的defaultjavatyperesolver类打包到插件中

VS2015,Microsoft Visual Studio 2005,SourceInsight4.0使用经验,Visual AssistX番茄助手的安装与基本使用9_番茄助手颜色-程序员宅基地

文章浏览阅读909次。1.得下载一个番茄插件,按alt+g才可以有函数跳转功能。2.不安装番茄插件,按F12也可以有跳转功能。3.进公司的VS工程是D:\sync\build\win路径,.sln才是打开工程的方式,一个是VS2005打开的,一个是VS2013打开的。4.公司库里的线程接口,在CmThreadManager.h 里,这个里面是我们的线程库,可以直接拿来用。CreateUserTaskThre..._番茄助手颜色

推荐文章

热门文章

相关标签