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

智能推荐

快速排序_qsort排序结构-程序员宅基地

文章浏览阅读3.2k次。 快速排序(Quicksort)是一種眾所周知的排序算法,由C. A. R. Hoare所發展的,以平均效能來說,排序 n 個項目要Θ(n log n)次比較。然而,在最壞的效能下,它需要Θ(n2)次比較。一般來說,快速排序實際上明顯地比其他Θ(n log n) 演算法更快,因為它的內部回圈(inner loop)可以在大部分的架構上很有效率地被實作出來,且在大部分真實世界的資料,可以決定設計的選_qsort排序结构

清除win7或win10系统垃圾文件2023.7.12_win7 professional版本 垃圾文件清理-程序员宅基地

文章浏览阅读102次。清除系统垃圾文件_win7 professional版本 垃圾文件清理

【驱动】网卡驱动·linux内核网络分层结构-程序员宅基地

文章浏览阅读133次。原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处、作者信息和本声明。否则将追究法律责任。http://liucw.blog.51cto.com/6751239/1221140Preface Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计。 Linux内核采用分层结构处理网络数据包。分层结构与网络协议的结构匹..._网卡驱动程序可以有内核实现 也可以有用户层实现的 但只能同时有一个接管网卡

SpringBoot整合Shiro-程序员宅基地

文章浏览阅读133次。最近做的项目整合了SpringBoot+Shiro,自己也不会,就就在网上现学现用,然后发现也有一篇满足的我需要的一篇完整帖子,所以有了这篇。废话少说。还是先上一张图,大概了解一下shiro框架,有理论有实践。对上图简单的进行说明三个核心组件:Subject, SecurityManager 和 Realms.Subject: 即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前

vue之elementUi的el-select同时获取value和label的两种方法_vue中elementui的el-select同时获取value和label-程序员宅基地

文章浏览阅读1.2k次。【代码】vue之elementUi的el-select同时获取value和label的两种方法。_vue中elementui的el-select同时获取value和label

仿真软件LTspice之《第三方spice模型导入方法》_ltspice怎么导入模型.cir-程序员宅基地

文章浏览阅读3.6w次,点赞16次,收藏99次。1. 将下载的spice模型(.lib文件)拷入C:\Program Files\LTC\LTspiceXVII\lib\sub;2. 打开LTspice软件,将上述.lib文件拖入软件中,此时文件以txt形式打开;3. 右键点击.subckt后的元件名称,然后点击create symbol,创建模型;4. 完成上述操作后,点component,搜索元件名称即可放置元件。_ltspice怎么导入模型.cir

随便推点

FileUploadException: Stream closed-程序员宅基地

文章浏览阅读3.5k次,点赞4次,收藏5次。springboot升级到2.3.4.RELEASE后,上传文件报FileUploadException,因使用logback过滤器引起,解决方案,增加以下配置。spring:  mvc: hiddenmethod: filter: enabled: true _fileuploadexception: stream closed

ext4文件系统镜像制作教程_3519dv500 emmc 使用ext4格式的镜像制作-程序员宅基地

文章浏览阅读47次。ext4文件系统镜像制作教程 - 小蓝博客dd if=/dev/zero of=image.img bs=1M count=1024mkfs.ext4 image.imgmkdir ~/workmount -o loop image.img $HOME/work_3519dv500 emmc 使用ext4格式的镜像制作

大模型中的数据_大模型样本数据质量 csdn-程序员宅基地

文章浏览阅读70次。数据是基础大模型的生命线;这些模型的训练数据在很大程度上决定了这些模型可以获得哪些功能。数据的中心性并不是基础大模型所独有的;最近对以数据为中心的人工智能的呼吁表明了管理、理解和记录用于训练机器学习模型的数据的普遍重要性。具体而言,对于基础大模型,目前的做法是使用未指定或不明确的原则选择训练数据,并且训练数据的性质普遍缺乏透明度。我们认为,需要一种替代方法来重新构想围绕基础模型的数据生态系统:我们利用数据可视化和管理方面的工作,为基础模型提出一个数据中心。_大模型样本数据质量 csdn

Error parsing XML: junk after document element这样的错误_invalid roslaunch xml syntax: junk after document -程序员宅基地

文章浏览阅读4.1k次,点赞2次,收藏2次。很多开发者可能在编写XML布局文件时提示Error parsing XML: junk after document element这样的错误,这里Android123提示大家一般合法的XML文件只有一个主根节点,比如&lt;android12..._invalid roslaunch xml syntax: junk after document element: line 29, column 0

linux系统时区、日期、时间的查看和修改_linux 获取时区时间差-程序员宅基地

文章浏览阅读3.4k次。修改时区$ sudo cp /usr/share/zoneinfo/$主时区/$次时区 /etc/localtime#在中国可以使用:$ sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime查看日期和时间$ date -R #Fri, 09 Dec 2016 14:24:07 +0800$ date -R "+%Y-%m-%d %H:_linux 获取时区时间差

Throwable异常和处理异常的方案(try……catch……finally;throws)_try catch throwable-程序员宅基地

文章浏览阅读877次。Throwable异常的概念java程序出现了不正常的现象,异常主要分为两大类Exception和ErrorError表示非常严重的错误,一般情况下我们无法自己处理,那就不处理。Exception异常有两类,一类是RuntimeException,表示运行时期异常,这样的异常一般是由于代码逻辑不够严谨导致的,可以按照编译时期异常的处理方式去处理,另外一种是非RuntimeException异常,叫做编译时期异常,一定要处理,如果不处理,编译不通过,程序无法运行处理异常的方式1.try……_try catch throwable