Application应用程序

第1节 应用的基本概念

我们常说的“安卓应用”可能有三种不同的概念:

  1. 软件,能够完成一个特定功能的软件。例如“安装一个应用”、“这个应用可以帮助你寻找附近的美食”。

  2. 安装包,应用的实体,是我们摸得着、看得见的APK包,是一个很具体的东西。

  3. 应用运行起来后在内存中存在的形态。安装在设备上的APK文件在系统中运行起来就是Application。Android应用开发中,常说的Application就是它。它就是Android SDK中的Application类。

在讨论程序的开发、执行、存在周期的时候,我们说的Application就是第三种意思。

第2节 Android四大组件

ActivityServiceContentProviderBroadcastReceiver被称作Android应用的四大组件,它们分别对应着一个应用程序可能用到的表现形式。

2.1 Activity

这是应用的显示组件。通常来讲,我们能看到的设备上的内容,除了状态栏(statusbar)、导航栏(navigation bar)、锁屏界面,凡是显示出来的内容都属于Activity

(为了便于理解,这里讲的是大多数情况,一些窗口类型的界面–例如popupwindow,toast,输入法、壁纸等等,不应该称作Activity。)

开发者官网1

Activity中显示的是一个应用希望呈现给用户的内容。我们开发的应用基本上一定会有一个Activity,用来向用户展示我们希望呈现的内容。例如电话拨号界面,设置界面,日历界面。

开发者官网1 开发者官网1 开发者官网1

2.2 Service

这是应用可以在后台运行的服务组件,不需要有用户看得见的界面。它通常需要和Activity配合,借助Activity给用户提供控制的界面。

最典型的例子就是音乐播放器。音乐播放器的界面就是一个提供了音乐控制方式的Activity,当我们点击Activity上的播放按钮之后,Activity通知播放器的Service组件,让Service组件开始播放音乐;之后即使用户选择退出了音乐播放器的Activity界面,音乐仍然在被播放着,没有随着界面的退出而停止播放。

开发者官网1

2.3 ContentProvider

这是一个应用的数据存储组件。在Android系统中,出于数据安全的考虑,应用之间是被严格的隔离的,应用A不能访问到应用B的数据。但是如果应用B使用了ContentProvider组件,就能让应用A通过Android系统制定好的规则获取到应用B愿意提供的数据了。

开发者官网1

它就像是一个拥有某种数据的网站,安卓系统运行的其它组件可以通过“网址”访问这个网站,获取需要查询的数据。

  1. ContentProvider可以是私有的,只能为它所在的应用提供数据访问请求;
  2. ContentProvider也可以是公开的,为别的应用程序提供数据访问请求。

2.4 BroadcastReceiver

这是应用获取系统或者获取某些应用发出特定消息的组件。

例如,有个应用叫“换壁纸”,它希望设备开机完成后,自动更换桌面壁纸。可是开机后“换壁纸”还没有运行起来,用户也不会主动的告诉“换壁纸”“手机已经启动了,换一张壁纸吧”。

这时,就可以通过BroadcastReceiver组件获取开机完成后系统主动发出的“开机完成通知”。得到“通知”后,“换壁纸”就可以按照预想设计工作了。

BroadcastReceiver组件有个特点,就是包含它的应用不一定要正在运行,系统可以根据“通知”类型,先把这个“通知”告诉关注它的BroadcastReceiver,由BroadcastReceiver决定是否需要启动一个Activiy或者Service来做相应的处理。

开发者官网1

第3节 Intent初步

如果将ActivityServiceBroadcastReceiver这些组件比喻成一座一座的岛屿,那么Intent就是穿梭于这些小岛之间的小船。

开发者官网1
  • 从主界面启动Activity,需要通过Intent;
  • 在Activity里面启动另一个Activity,也需要通过Intent;
  • 启动Service需要通过Intent;
  • BroadcastReceiver被唤醒,也是通过Intent。

Intent不仅能将这些组件联系了起来。Intent也能穿梭于各个应用之间,把它们也联系起来。

3.1 创建Intent

在创建Intent的时候,可以使用下面的方式指定小船的目的地。

  1. 就像给某个机构发货,只需要发送给某个机构,但不需要知道这个机构中具体是哪个人。

    //action_name可以指定那一类组件可以收到这个Intent发出的请求
    Intent i = new Intent("action_name");
  2. 就像发货时写下接收方具体的名字和地址。

    Intent i = new Intent();
    i.setClassName("接收方的包名", "接收方的类名"); 

3.2 携带数据

小船可以携带货物,Intent也可以携带各种类型的数据。在创建Intent对象后,使用如下的方法,你就可以把想要在“岛屿”之间传递的数据放到小船上。

Intent i = new Intent();
i.putExtra("索引关键字",goods);

这些数据包括Stringcharlongintbooleanfloatdoublebyte等等常用的基本类型,也包括它们的数组。甚至还能传递自定义类封装的对象。

不过要传递自定义类封装的对象,需要这个类做特殊的处理,

  • 继承了Serializable接口,

    class data implements Serializable
    {
    
    }
    
    Intent i = new Intent();
    i.putExtra("DATA", new data());
  • 继承了Parcelable接口,

    class data implements Parcelable
    {
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
    
        }
    }
    
    Intent i = new Intent();
    i.putExtra("DATA", new data());

Parcelable的性能要比Serializable好很多,传输的效率要高上10倍左右;但是Parcelable需要开发者自己定义数据序列化和反序列化的规则,所以编码量比使用Serializable要大很多。

在实际的开发场景中要根据实际的情况作选择。

第4节 Manifest初步

在应用工程的源码目录下,都有一个AndroidManifest.xml文件,它是这个应用的配置文件,当应用安装到设备上后,Android系统会从这个文件中获取很多关于这个应用的配置信息。

Manifest文件内容,大体如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.anddle.activitytest">

    <uses-permission />
    <permission />
    <permission-tree />
    <permission-group />
    <instrumentation />
    <uses-sdk />
    <uses-configuration />  
    <uses-feature />  
    <supports-screens />  
    <compatible-screens />  
    <supports-gl-texture />  

    <application
        android:icon="@mipmap/ic_launcher"
        android:name=".MyApplication">

        <activity android:name=".MainActivity"/>

        <service android:name=".MyService"/>

        <provider android:name=".MyContentProvider"
            android:authorities="com.anddle.authoriteies"/>

        <receiver android:name=".MyReceiver"/>

    </application>
</manifest>

不是所有的项都需要在Manifest文件中使用,通常只会用到最常用的。

4.1 application标签

应用使用了哪些组件,都要在这个配置文件中登记。不然在程序启动这些组件的时候,系统会找不到它们,导致程序崩溃。

<application
   android:icon="@mipmap/ic_launcher"
   android:name=".MyApplication">

   <activity android:name=".MainActivity"/>

   <service android:name=".MyService"/>

   <provider android:name=".MyContentProvider"
       android:authorities="com.anddle.authoriteies"/>

   <receiver android:name=".MyReceiver"/>

</application>

4.2 uses-permission标签

这个应用要使用系统的哪些权限,也要在这个配置文件中声明。这样在安装的时候,系统会根据填写的内容,告诉用户,让用户判断是否接受应用使用这些系统的功能。

例如,一个应用要读取磁盘存储器上的内容,就需要申请权限,如果用户同意,才能成功读取磁盘。

<uses-permission 
    android:name="android.permission.READ_EXTERNAL_STORAGE" />

第5节 Task、Application与四大组件的关系

5.1 Task

在Android系统中,很多模块是互用的。例如当我们启动“相机”拍摄了一张照片,然后通过“图库”浏览刚拍的照片,最后选择“微信”将照片分享出去。为了实现这一些列的操作,达到我们希望的目的,整个过程就利用了3个不同应用,分别使用了它们对应的Activity。

我们将使用到的这些Activity串在一起,称作Task。它们就像一个叠叠乐,后启动的放在另一个的上面–我们给这个叠叠乐一个专业的术语–栈。Task中包含的Activity是以栈的形式存放的。

开发者官网1

假如此时,我们按返回按钮一步一步返回,Task中位于顶部的Activity就被一步一步退出,直到退出这个Task中所有的Activity,然后回到了系统主界面。

假如此时,我们按home按钮,就会返回到系统主界面,同时将这个Task保存下来。如果你再启动主界面上的另一个应用,一个新的Task就会被建立起来。

开发者官网1

通过上面的描述,我们可以知道:Task只和Activity有关。

另外,Task可以在系统中存在多个,例如启动应用A,创建Activity A,这里就有一个Task A了;然后按“home”键返回,再启动应用B,创建Activity B,这里就有一个Task B了;以此类推,会有Task C Task D。

不过运行在最前台的只有一个Task,这就是前台Task。其他的Task都是后台Task。

5.2 Application与组件的关系

站在系统的角度看,Application就是运行着组件的那个线程环境。这个线程环境中运行着正在工作的组件---即那些在它Manifest中声明了的、正运行着的组件。以上面微信分享照片的task作为例子。

相机应用:虽然没有被看见,但它还有Activity存在着,它运行的线程环境还存在着,所以这个application正在运行着;

图片浏览应用:虽然没有被看见,但它还有Activity存在着,它运行的线程环境还存在着,所以这个application正在运行着;

微信应用:很明显,它有Activity运行着,它运行的线程环境存在,所以这个application正在运行着;可以想象微信一定有个正在后台接受网络发送了消息的Service 。这个Service也是application的一部分--因为他们在同一个线程当中(这种说法并不严谨,如果Service被指定了在单独的进程中运行,那么application就不是同一个了。不过这里为了举一个贴近实际场景的例子,就没有去做详细的区分了);

Application可以看做是这些组件的集合,是这些组件对外共同的名字。

开发者官网1

Application被运行起来的情况有:

  1. Activity被启动,显示出一个界面;
  2. service被启动,在后台默默的运行;
  3. 其他应用开始访问自己提供的ContentProvider;
  4. BroadcastReceiver收到了一个“消息”;

站在系统的角度看,当应用运行起来时,系统为这个应用创造了一个线程环境,这个线程环境就是Application,那些组件运行的环境都在这个线程当中。

由此可知,Application是一个安装应用的入口。不过通常来讲,在通过Android Studio创建工程的时候,并没有给我们指出这个Application,而是使用了系统默认的Application。

另外Android Studio还为我们创建了一个Activity。这多少会让我们误以为Activity才是应用程序被最早启动的地方。

要自己定义应用的Application环境,首先要在代码中继承Application类。这里列出了一部分可以被覆盖的Application类方法。

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
    }
    @Override
    public void onLowMemory() {
        super.onLowMemory();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
    }   
}

这里的onCreate()函数才是一个应用被启动的第一个地方(而非Activity的onCreate())。

创建Application类后,我们要再manifest文件中,指明我们的应用要使用这个自定义的Application:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.anddle.activitytest">
    ......   
    <application
        android:icon="@mipmap/ic_launcher"
        android:name=".MyApplication">      -->指定我们自定义的Application
   </application>
</manifest>

如果这里不指定android:name,那么系统将使用默认的Application。

不是所有的应用都需要使用自定义的Application,对于那些有很多组件(Activity Service等等)都需要做相同初始化都应用,就可以使用它。

例如,有个应用包含一个Activity和一个Service,无论是哪个先启动,都首先要去做一个共同的操作。这种情况就可以考虑把那个共同的操作放到Application中进行。

Application还可以作为各个组件共享变量和数据的仓库。

Activity和Service都是有生命周期的,一旦生命结束,它们保存的一些数据就会被释放掉。但是它们的生命结束了,不代表Application的生命结束了(Application是整个应用的生命)。为了其他组件还能继续使用这些数据,我们就可以把数据放着这个生命更长的Application中。

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值