一.Android应用的基本组件介绍
Android应用通常由一个或多个基本组件组成,前面我们看到Android应用中最常用的组件就是Activity。事实上Android应用还可能包括Service、BroadcastReceiver、ContentProvider等组件。本节先让读者对这些组件建立一个大致的认识,后面的章节还会对这些组件做更详细的介绍。
1. Activity和View
Activity是Android应用中负责与用户交互的组件——大致上可以把它想象成Swing编程中的JFrame控件。不过它与JFrame的区别在于:JFrame本身可以设置布局管理器,不断地向JFrame中添加组件,但Activity只能通过setContentView (View)来显示指定组件。
View组件是所有UI控件、容器控件的基类,View组件就是Android应用中户实实在在看到的部分。但View组件需要放到容器组件中,或者使用Activity将它显示出来。如果需要通过某个Activity把指定View显示出来,调用Activity的setContentView ()方法即可。
setContentView()方法可接受一个View对象作为参数,例如如下代码:
//创建一个线性布局管理器
LinearLayout layout = new LinearLayout(this);
//设置该Activity显示layout
setContentView(layout) ;
上面的程,序通过代码创建了一个LinearLayout对象 (它是ViewGroup的子类,ViewGroup又是View的子类), 接着调用Activity的setContentView (layout) 把这个布局管理器显示出来。
setContentView ()方法也可接受一个布局管理资源的ID作为参数,例如如下代码:
//设置该Activity显示main.xml文件定义的View
setContentView(R.layout.main);
实际上Activity是Window的容器,Activity包含一 个getWindow()方法,该方法返回该Activity所包含的窗口。对于Activity而言,开发者一般不需要关心Window对象。如果应用程序不调用Activity的setContentView ()来设置该窗口显示的内容,那么该程序将显示一个空窗口。
Activity为Android应用提供了可视化用户界面,如果该Android应用需要多个用户界面,那么这个Android应用将会包含多个Activity,多个Activity组成Activity栈,当前活动的Activity位于栈顶。
Activity包含了一个setTheme (int resid) 方法来设置其窗口的风格。例如,我们希望窗口不显示ActionBar、以对话框形式显示窗口,都可通过该方法来实现。
2.Service
Service与Activity的地位是并列的,它也代表一个单独的Android组件。Service与Activity的区别在于: Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。
与Activity组件需要继承Activity基类相似,Service组件需要继承Service基类。一个Service组件被运行起来之后,它将拥有自己独立的生命周期,Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。
3.BroadcastReceiver
BroadcastReceiver是Android应用中另一个重要的组件,顾名思义,BroadcastReceiver代表广播消息接收器。从代码实现角度来看,BroadcastReceiver非常类似于事件编程中的监听器。与普通事件监听器不同的是,普通事件监听器监听的事件源是程序中的对象;而BroadcastReceiver监听的事件源是Android应用中的其他组件。
使用BroadcastReceiver组件接收广播消息比较简单,开发者只要实现自己的BroadcastReceiver子类,并重写onReceive(Context context, Intent intent) 方法即可。当其他组件通过sendBroadcast ()、sendStickyBroadcast ()或sendOrderedBroadcast()方法发送广播消息时,如果该BroadcastReceiver也对该消息“感兴趣”(通过IntentFilter配置),BroadcastReceiver的onReceive (Contextcontext, Intent intent) 方法将会被触发。
开发者实现了自己的BroadcastReceiver之后,通常有两种方式来注册这个系统级的“事件监听器”。
- ➢在Java代码中通过Context.registReceiver () 方法注册BroadcastReceiver.
- ➢在AndroidManifest.xmI文件 中使用<receiver../>元素完成注册。
读者此处只要对BroadcastReceiver有一个大致的印象即可,本书后面的章节还会详细介绍如何开发、使用BroadcastReceiver组件。
4.ContentProvider
对于Android应用而言,它们必须相互独立,各自运行在自己的进程中,如果这些Android应用之间需要实现实时的数据交换——例如, 我们开发了一个发送短信的程序,当发送短信时需要从联系人管理应用中读取指定联系人的数据——这就需要多个应用程序之间进行数据交换。
Android系统为这种跨应用的数据交换提供了一个标准: ContentProvider。 当用户实现自己的ContentProvider时,需要实现如下抽象方法。
➢insert (Uri,ContentValues) :向ContentProvider插入数据 。
➢delete (Uri, ContentValues) :删除ContentProvider中指定数据。
➢update (Uri, ContentValues, String, String[]) :更新ContentProvider中指定数据。
➢query (Uri, String[], String, String[], String) :从ContentProvider查询数据。
通常与ContentProvider结合使用的是ContentResolver,一个应用程序使用ContentProvider:暴露自己的数据,而另一个应用程序则通过ContentResolver来访问数据。
5. Intent和IntentFilter
Intent并不是Android应用的组件,但它对于Android应用的作用非常大——它是Android应用内不同组件之间通信的载体。当Android运行时需要连接不同的组件时,通常就需要借助于Intent来实现。Intent可以启动应用中另一个Activity,也可以启动一个Service组件,还可以发送一条广播消息来触发系统中的BroadcastReceiver。也就是说,Activity、Service、 BroadcastReceiver三种组件之间的通信都以Intent作为载体,只是不同组件使用Intent的机制略有区别而已。
- 当需要启动一个Activity 时,可调用Context的startActivity (Intent intent) 或startActivityForResult (Intent intent, int requestCode) 方法,这两个方法中的Intent参数封装了需要启动的目标Activity的信息。
- 当需要启动一个Service 时,可调用Context 的startService (Intent intent)方法或bindService (Intent service, ServiceConnection conn,int flags) 方法,这两个方法中的Intent参数封装了需要启动的目标Service的信息。
- 当需要触发一个BroadcastReceiver时, 可调用Context的sendBroadcast (Intentintent)、 sendStickyBroadcast (Intent intent)或sendOrderedBroadcast (Intent intent, String receiverPermission) 方法来发送广播消息,这三个方法中的Intent 参数封装了需要触发的目标BroadcastReceiver的信息。
通过上面的介绍不难看出,Intent封装 了当前组件需要启动或触发的目标组件的信息,因此有些资料也将Intent翻译为“意图”。实际上Intent对象里封装了大量关于目标组件的信息,.本书后面还会更详细地介绍Intent所封装的数据,此处不再深入讲解。
当一个组件通过Intent表示了启动或触发另一个组件的“意图"之后,这个意图可分为两类。
- ➢显式Intent:显式Intent明确指定需要启动或者触发的组件的类名。
- ➢隐式Intent: 隐式Intent只是指定需要启动或者触发的组件应满足怎样的条件。
对于显式Intent而言,Android系统无须对该Intent做任何解析,系统直接找到指定的目标组件,启动或触发它即可。
对于隐式Intent而言,Android系统需要对该Intent进行解析,解析出它的条件,然后再去系统中查找与之匹配的目标组件。如果找到符合条件的组件,就启动或触发它们。
那么Android系统如何判断被调用组件是否符合隐式Intent呢?这就需要靠IntentFilter来实现了,被调用组件可通过IntentFilter来声明自己所满足的条件一也就是声明自己到底能处理哪些隐式Intent。关于Intent和IntentFilter本书后面还会有进一步阐述,此处不再深入讲解。
二.签名APK的两种方法
前面已经介绍过,Android项 目以它的包名作为唯一标识。 如果在同一台手机上安装两个包名相同的应用,后面安装的应用就可以覆盖前面安装的应用。为了避免这种情况发生,Android要求对作为产品发布的应用进行签名。
签名主要有如下两个作用:
➢确定发布者的身份。由于应开发者可以通过使用相同包名来替换已经安装的程序,
因此使用签名可以避免发生这种情况。
➢确保应用的完整性。签名会对应用包中的每个文件进行处理,从而确保程包中的文
件不会被替换。
通过以上介绍不难看出,Android应用签名的作用类似于现实生活中的签名。当开发者对Android应用签名时,相当于告诉外界:该应用程序是由“我”开发的,“我”会对该应用负责——因为有签名(签名有密钥),别人无法冒名顶替“我”;与此同时,“我” 也无法冒名顶替别人。
签名后的APK就可以上传到各大应用商城,既可供广大用户免费使用,也可以用于盈利。
提示:
在应用的开发、调试阶段,Android Studio或Gradle工具会自动生成调试证书对Android应用签名,因此部署、调试前面两个示例并没有经过签名。需要指出的是,如果要正式发布一个Android应用,必须使用合适的数字证书来给应用程序签名,不能使用AndroidStudio或Gradle具生成的调试证书来发布。
1.使用Android Studio对Android应用签名
大部分时候,开发者会直接使用Android Studio对 Android应用签名。使用AndroidStudio对Android应用签名的步骤如下。
- 单击Android Studio主菜单中的“Build”-→"Generate Signed APK..."菜单项,Android Studio弹出如图1. 41所示的对话框。
- 如果系统中还没有数字证书,则可以在图1.41所示窗口中单击“Create new..." 按钮,并按图1.42所示格式填写数字证书的存储路径和密码。
- 填写完成后单击“OK”按钮,Android Studio返回图1.41所示的对话框,并在该对话框
中使用刚刚创建的数字证书,如图1.43所示。
- 单击“Next"”按钮,Android Studio将 会显示如图1.44所示的对话框,该对话框用于指
定生成签名后的APK安装包的存储路径。选择签名版本,其中V1版本只对JAR包签名,而V2版本对整个APK签名,通常建议选择V2版本。
单击“Finish”按钮,签名完成。Android Studio将 会在指定路径下生成一个签名后的APK安装包。
上面步骤的第2步用于制作新的数字证书,一旦数字证书制作完成,以后就可以直接使用该数字证书签名了。
利用已有的数字证书进行签名,请按如下步骤进行。
- 在图1.41所示窗口中单击“Choose existing..."按钮浏览已有的数字证书。
- 浏览到已有的数字证书之后,在该对话框下面的Key store password、 Key alias、Key password文本框中输入已有的数字证书对应的信息,这样将会看到如图1.43所示的效果。剩下的事情同样是单击"Next”按钮,并选择签名APK的存储路径即可。
2.使用 Android 9 的命令对APK签名
如果不想借助于Android Studio对Android应 用程序签名,或者在某些场合下,需要对一个“未签名”的APK包(比如已有一个未签名的APK包,或者委托第三方公司开发的API包)进行签名,则可通过“命令”对Android应用进行手动签名。
使用命令对Android应用签名的步骤如下。
- 创建Key store库。JDK安装目录下的bin子目录下提供了keytool.exeI具来生成数字证书。在命令行窗口输入如下命令:
输入上面命令后按回车键,接下来将会以交互式方式让用户输入数字证书keystore的密码、作者、公司等详细信息,如图1.45所示。
第1步的作用是生成属于你们公司、你的数字证书,这个步骤只要做一次即可。一旦数字证书创建成功之后,只要在该证书有效期内,就可以一直重复使用该证书。
- 如果Android项目没有错误,在Android Studio的“Build" →"Build APK(s)" 即可生成未签名的APK安装包。在Android Studio项 目的app\build\outputs\apk\debug路径下即可找到一个app-release-unaligned.apk文件,该文件就是未签名的APK安装包。
第2步的作用是生成一个未签名的APK安装包,如果本来已有这个未签名的安装包,或者该安装包是你们委托第三方公司开发的、第三方公司负责提供该未签名的安装包,那么这个步骤是可以省略的。- 使用 Android 9 的 apksigner.bat 命令对未签名的APK安装包进行签名。apksigner.bat命令位于Android SDK 的 build-tools目录下。比如Android 9.0 的 build-tools 位于SDK\ build-tools\28.0.3目录下,为了方便使用该目录下的命令,可以考虑将该项目添加到PATH环境变量中。
apksigner命令对应于前面介绍的V2版本的签名。
在命令行窗口中输入如下命令:
apksigner sign --ks crazyit.jks --ks-key-alias crazyit
--out HelloWorld_crazyit.apk app-debug.apk
上面命令中各选项说明如下。
➢sign: 指定使用apksigner命令执行签名。
➢--ks:指定数字证书的存储路径。
➢--ks-key-alias: 指定数字证书的别名。
➢--out<文件名>: 指定签名后的APK文件的文件名。
➢最后一个参数则代表未签名的APK文件。
输入上面命令后按回车键,接下来将会以交互式方式让用户输入数字证书keystore的密码、数字证书的密码。如果签名成功,该命令不会有任何提示,但会在指定目录下生成一个签名后的APK文件。
apksigner还提供了verify 子命令来验证签名是否有效。例如,在命令行窗口中输入如下命令:
apksigner verify -v HelloWorld_crazyit.apk
上面命令将会对APK安装包的签名状态执行检查,检查结果通常会产生如下输出。
上面的输出提示用户:签名检查已经得到确定,其中V1版本的签名为false, V2版本的签名为true。上面两条命令的执行结果如图1.47所示。