Android学习笔记(一):Android基础

article/2025/10/24 11:12:31

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
--Action3Task2:
--Action1
--Action2
--Action3Task3:
--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.compileClasspathdestinationDir=file('build/classes/main')options.fork=trueoptions.incremental=true
}task run(type:JavaExec,dependsOn:'compile'){classpath=sourceSets.main.runtimeClasspathmain='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 FAILEDFAILURE: 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 versionprintln descriptionprintln 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 prop1println prop2println prop3println prop4println project.prop1println project.prop2println project.prop3println 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 sourceoutputs.file destdoLast{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{utilsmain{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{@Internaldef message = '测试str'@TaskActiondef 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.companyimport org.gradle.api.*;
import org.gradle.api.tasks.*;class TestTask extends DefaultTask{@InternalFile file = new File('dist.txt')@TaskActiondef show(){println file.text}@TaskActiondef multiShow(){println "==========="println file.textprintln "==========="}
}

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 = 10double discount = 1.0
}//ItemPlugin.groovy
package com.companyimport org.gradle.api.*;class ItemPlugin implements Plugin<Project>{void apply(Project project){project.extensions.create("item",Item)project.task('showItem'){doLast{println '商品名:'+project.item.nameprintln '商品销售价:'+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讲义(第四版)》,作者李刚。


http://chatgpt.dhexx.cn/article/aN8f1aSe.shtml

相关文章

dspic c语言教程,轻松学PIC之数码管篇 PIC单片机学习系列教程

轻松学PIC之数码管篇 PIC单片机学习系列教程 大家好&#xff0c;通过前一期的学习&#xff0c;我们已经对ICD2仿真烧写器和增强型PIC实验板的使用方法及学习方式有所了解与熟悉&#xff0c;学会了如何用单片机来控制发光管、继电器、蜂鸣器、按键等资源&#xff0c;体会到了学习…

PIC单片机之I/O控制

I/O控制对于单片机而言是最基础最核心的东西&#xff0c;其实单片机除了AD DA 转换之外的事情。其他大部分的事情I/O口都能做的到。I/O控制简单却能千变万化。 单片机在大部分应用中做的事情&#xff0c;莫过于 输入高低电平 &#xff0c;输出高低电平。就是通过这简单的控制电…

PIC单片机入门_中断系统详解

1.中断的基本概念 当单片机正在执行程序时&#xff0c;出现了某些特殊状况&#xff0c;例如定时时间到、有键盘信号输入等&#xff0c;此时CPU须要暂时停止当前的程序&#xff0c;而转去执行处理这些事件的程序&#xff0c;待执行完这些特定的程序之后&#xff0c;再返回到原先…

PIC单片机学习笔记

视频内容来自哔哩哔哩-【单片机】PIC单片机F877A视频教程&#xff08;郭天祥&#xff09; 1、简介 本视频使用的单片机是PIC16F877A &#xff08;公司用的的dsPIC33FJ128GP306&#xff09; dsPIC33——16位单片机 中档产品仅有35条汇编语言——意外之喜 哈佛总线结构、RI…

PIC单片机学习—USART串口

PIC单片机学习—USART串口 最近做了几个项目都用到了PIC单片机&#xff0c;在这几个项目之前我只用到了51单片机和STM32来做控制类的项目&#xff0c;用PIC单片机的时候发现使用的时候和51单片机还是有所区别&#xff0c;有许多小坑需要注意&#xff0c;所以决定写几篇针对新手…

PIC单片机应用开发实践教程(四): MPLAB X IDE Debug

源码基于 PIC16F15355开发板&#xff0c;想了解详情&#xff0c;请点 PIC16F15355开发板 当程序写好后&#xff0c;我们就可以使用在线调试功能&#xff0c;验证代码的正确性。 1、打开工程 File - Open Project &#xff0c;打开我们要调试的工程 2、打开工程属性&#xf…

PIC单片机

PIC芯片所用编译器是MPLAB X IDE&#xff0c;刚开始接触PIC单片机&#xff0c;也是一脸茫然&#xff0c;然后查阅资料逐渐了解、运用编译器&#xff0c;这里我就不说编译器怎么使用了&#xff0c;下面我将以PIC12LF1822芯片为例&#xff0c;简单来说说我见解&#xff0c;分别说…

PIC单片机应用开发实践教程(六): 代码配置器(MCC)

源码基于 PIC16F15355开发板&#xff0c;想了解详情&#xff0c;请点 PIC16F15355开发板 1、简介 MPLAB 代码配置器&#xff08;MPLAB Code Configurator&#xff0c;MCC&#xff09;通过图形用户界面&#xff08;Graphical User Interface&#xff0c;GUI&#xff09;生成驱…

【PIC】单片机基本概述

1. 公司 PIC 单片机是 Microchip&#xff08;微芯&#xff09; 公司的产品&#xff0c;始于1989年。 2. 精简指令集技术 传统的单片机采用复杂指令集&#xff08;Complex Instruction Set Computer, CISC&#xff09;结构&#xff0c;通常有50~110条。而 PIC 采用精简指令集&…

PIC单片机应用开发实践教程(二): 新建工程

源码基于 PIC16F15355开发板&#xff0c;想了解详情&#xff0c;请点 PIC16F15355开发板 1 打开MPLAB X IDE File---New Project Microchip Embedded --- Standalone Project 选择所用MCU对应型号 点击 Finish 后&#xff0c;工程就建好了 2 添加main.c文件 3 添加main.h文件 …

PIC单片机的入门认识(以PIC12为学习目标)

1.学习PIC12针脚定义 针脚1及针脚8可接电源5V和地线。针脚2.3可外接晶振。 针脚4为复位脚&#xff0c;当单片机正常运行时接高电平&#xff0c;若有一个低电平输入&#xff0c;单片机便复位。 针脚5则为单片机的I/O脚&#xff0c;可控制它为输入或输出&#xff0c;输入0&…

PIC单片机应用开发实践教程(七):PIC16F153xx——时钟使用

源码基于 PIC16F15355开发板&#xff0c;想了解详情&#xff0c;请点 PIC16F15355开发板 PIC16F15355 内部晶振框图如下&#xff0c;从框图中我们知道MCU时钟来源有3个&#xff1a;外部时钟&#xff0c;内部时钟&#xff0c;Timer1 内部时钟源&#xff08;INTERNAL CLOCK SOUR…

PIC单片机入门教程(二)—— 安装集成开发环境(MPLAB X IDE)

PIC单片机入门教程&#xff08;二&#xff09;—— 安装集成开发环境&#xff08;MPLAB X IDE&#xff09; 教程中使用的电脑运行Windows 10 专业版 64位系统 1、下载 MPLAB X IDE v4.05 历史版本&#xff1a;http://www.microchip.com/development-tools/pic-and-dspic-downl…

PIC单片机-Mplab的使用与PIC单片机介绍

内容包括Mplab IDE与Mplab X IDE的使用&#xff0c;触摸功能实践&#xff0c;PIC单片机C与汇编混合编程&#xff0c;PIC单片机介绍等。紫色文字是超链接&#xff0c;点击自动跳转至相关博文。持续更新&#xff0c;原创不易&#xff01; 目录&#xff1a; 一、Mplab IDE的使用…

PIC16F887 实战编程 单片机编程 基础实验教程

文章目录 2 PIC工程建立与仿真3 单片机基础寄存器操作&#xff1a;3.1 IO3.2 模拟输入电压读取3.3 外部中断3.4 定时器中断3.5 串口UART3.6 IIC通信 4 实际项目5 如何阅读代码6 如何把代码放到MPLAB V5.0xc8 v2.0上工作&#xff1f;6.1 短暂的回顾6.2 xc8 v2.0程序结构6.3 移植…

通俗易懂讲PIC单片机:从一窍不通到入门进步

单片机入门不难------谈PIC系列&#xff08;转自矿石收音机论坛---崂山&#xff09;十年前的老帖子&#xff0c;讲得通俗易懂&#xff0c;分享之。 请看图1 这个8条腿的小螃蟹就是我们的第一顿饭&#xff0c;只要把它吃下去&#xff0c;以后的大餐就好办了。第1、8条腿接电源 …

PIC单片机应用开发实践教程(五): 烧录器简介

源码基于 PIC16F15355开发板&#xff0c;想了解详情&#xff0c;请点 PIC16F15355开发板 ​​​​​​​ PIC单片机&#xff0c;无论是8位的10/12/16/18系列&#xff0c;还是16位PIC24/dsPIC33系列&#xff0c;常用的烧录器如下&#xff1a;PICkit3&#xff0c;PICkit4&#…

PIC单片机入门_C语言编程技术

1.为什么也是C语言&#xff1f; 用C 语言来开发单片机系统软件最大的好处是编写代码效率高、软件调试直观、维护升级方便、代码的重复利用率高等&#xff0c;因此C 语言编程在单片机系统设计中越来越广泛的运用。PIC 单片机的软件开发&#xff0c;同样可以用C 语言实现。 Micro…

PIC单片机与PIC单片机C语言编程简介

对于计算机学院与电子学院相关的同学来说&#xff0c;单片机一定不是一个陌生的概念。在大学的学习生涯中&#xff0c;经常用于教学的是MCS-51系列单片机。其实&#xff0c;除了MCS-51单片机外&#xff0c;还有一类单片机——PIC单片机。 PIC单片机&#xff0c;英文名为Periphe…

PIC单片机应用开发实践教程(三): MCU配置位与烧录

1 编译 工程建好并把相应的 .c和.h文件都加载到工程后&#xff0c;如下图 试试编译&#xff0c;如果没有语法错误&#xff0c;编译结果如下 到这里&#xff0c;是不是可以进行烧录了呢&#xff1f;不急&#xff0c;还有很重要的一个步骤&#xff0c;MCU配置位的设置&#xff0…