Android四大组件

Activity

Activity作为四大组件中使用最频繁的组件,我们在使用的过程中应该要知道它的生命周期和启动模式。

生命周期

典型情况下的生命周期

Activity生命周期如下图所示(图片摘自《Android开发艺术探索》):
Android四大组件_第1张图片

  • onCreate():onCreate()作为Activity七大生命周期里的第一个生命周期表示Activity的创建,在这个生命周期中,一般进行一些数据和对象初始化的操作。在这个生命周期时,Activity处于不可见的状态。
  • onStart():当Activity的onCreate方法执行完毕后,系统会调用Activity的onStart()方法,在此生命周期,Activity处于可见但无法与用户进行交互的状态。
  • onResume():在这个生命周期时,Activity已经可见并且可以与用户进行交互了。
  • onPause():onPause()方法与onStart()方法相对应,此时Activity处于可见状态,但是不可交互。在这个方法中可以进行一些数据的存储,但是不能做太过耗时的操作,因为这会影响新的Activity的显示,onPause()方法执行完毕后才会执行新Activity的onResume()方法。特别注意的是:当Activity中弹出Dialog对话框时,onPause()方法不会被回调,而当Activity启动Dialog风格的Activity时,onPause()方法会回调。
  • onStop():与onResum()方法相对应,此时Activity处于完全不可见状态,可做一些资源释放回收工作。
  • onRestart():当Activity未被销毁而从其他Activity重新回到该Activity的时候,onRestar()方法会被回调,接着回调onStart()与onResume()方法。
  • onDestroy():Activity被销毁的时候会回调此方法,在此方法中我们要做资源回收与释放的工作。
异常情况下的生命周期分析:
  • 情况1:资源相关的系统配置发生改变导致Activity被杀死并重建
    当手机横竖屏切换或应用分屏显示再切换回全屏模式时(Android7.0后的特性),由于系统的资源配置发生了改变,在默认的情况下Activity就会被销毁并且重新创建,当然我们也可以阻止系统重新创建我们的Activity。如下图(图片摘自《Android开发艺术探索》)
    Android四大组件_第2张图片
    当系统配置发生变化后,Activity就会被销毁,其onPause、onStop、onDestroy方法均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstance来保存当前Activity的状态,这个方法在onStop方法之前被调用,与onPause方法没有既定的时序关系,当Activity重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法,onRestoreInstanceState方法是在onStart方法之后被回调的。
    同时,在onSaveInstanceState和onRestoreInstanceState方法中,系统自动为我们做了一些恢复工作,如:EditeText中用户输入的数据,ListView的滚动位置等,这些view相关的状态系统都能为我们默认恢复。可以查看view的源码,和Activity一样,每个view都有onSaveInstanceState方法和onRestoreInstanceState方法。
  • 情况2:资源内存不足导致低优先级别的Activity被杀死
    这种情况的数据存储与恢复的过程与情况1完全一致,Activity按照优先级从高到低,可分为以下三种:
    (1)前台Activity——正在和用户交互的Activity,优先级最高。
    (2)可见但非前台Activity——比如:Activity弹出一个对话框,导致Activity可见但是位于后台无法和用户直接交互。
    (3)后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。

为了防止重建Activity,可以给Activity指定configChanges属性,例如不想让Activity在屏幕旋转时重建就可以给configChanges添加orientation属性,不想让Activity在分屏后恢复全屏时重建,可以给configChanges添加screenLayout属性,如果想指定多个值,可以用“|”连接起来,如下所示:

android:configChanges="screenLayout|orientation"

启动模式

Activity目前有四种启动模式:standard、singleTop、singleTask和singleInstance:

  • standard:标准模式,这是系统默认的模式。在这种模式下,每次启动一个Activity都会创建一个新的实例压入栈中,不管这个实例是否存在。需要注意的是,在此模式下的Activity默认会进入启动它的Activity所属的任务栈中,而非Activity类型的Context对象没有所谓的任务栈,用此类Context启动standard模式下的Activity会抛出异常,解决这个问题的方法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,但这时待启动的Activity实际上是以singleTask模式启动的。
  • singleTop:栈顶复用模式。在这种模式下,如果新的Activity已经位于栈顶,那么Activity不会被重新创建,此时Activity的onNewIntent方法会被调用,通过此方法可以获取请求信息,此时Activity的onCreate与onStart方法不会被回调。如果新的Activity不在栈顶,那么仍会创建新的实例压入栈中。
  • singleTask:栈内复用模式。在这种模式下,如果Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,而是直接复用已存在的实例,并且把与它处在同一个栈中、在它之上的其他Activity弹出栈中,此时会调用Activity的onNewInten方法。
  • singleInstance:单实例模式,它是加强版的singleTask模式,它除了具有singleTask模式的所有特性外,还加强了一点,那就是此种启动模式的Activity只能单独存在于一个任务栈中,后序启动此Activity都不会再创建新的实例,除非这个独特的任务栈被系统销毁了。

BroadcastReciever

广播的分类

  • 普通广播是异步的,通过Context.sendBroadcast()发送,逻辑上可以在同一时间被所有接收者接收到,消息传递的效率比较高,缺点是:接收者不能将处理结果传递给下一个接收者,普通广播是无法被中止的;
  • 有序广播是按照接收者声明的优先级别priority属性进行传播的,priority属性取值范围从-1000到1000,数值越大优先级越高,越早收到广播,通过Context.sendOrderedBroadcast()发送,前面的接收者可以通过BroadcastReciever.abortBroadcast()中止广播的传递,也可以通过setResultExtras(Bundle)将数据传递给下一个接收者,下一个接收者可通过getResultExtras(true)获取上一个接收者传递过来的数据。

生命周期

BroadcastReciever的生命周期很短,当onRecieve()方法执行完毕之后即会被finish,因此不推荐在onRecieve方法中执行耗时操作。不应该在onRecieve方法中开启线程处理耗时操作,因为当onRecieve方法执行完毕后BroadcastReciever就会退出,而线程还在,此时该线程会被标记为空线程,在系统内存紧张的时候会被回收而导致线程无法保证被执行完毕,因此建议启动一个service,由service去执行耗时操作。另外,如果onRecieve方法超过10秒未执行完毕,会导致系统抛出ANR。

注册

广播的注册分为静态注册与动态注册两种。静态注册在清单文件中进行注册,而动态注册则在代码中通过Context.registerReciever()进行注册。

有序广播的传递规则

  • 优先级高的广播接收者先接收
  • 同优先级的广播接收者,动态注册优先于静态注册
  • 同优先级的动/静态注册广播接收者,先注册的优先于后注册的

Service

Service有两种启动方式

  • 通过startService()的方式启动Service:这种启动方式启动的Service其生命周期为onCreate()——>onStartCommand()——>onDestroy(),当Service已经启动后,再次启动此Service不会再调用onCreate()方法,而是调用onStart()(已过期)或者onStartCommand()方法。停止服务需要调用stopService()方法,Service停止时onDestroy()方法被调用。此启动方式一旦Service启动,则Service与启动者就没有关联了,启动者退出后,Service仍然在后台运行着,直到有Context对象调用stopService()停止它或者它自己调用stopSelf()停止自己,启动者无法调用到Service内的方法。特别需要注意的是,通过此种方式启动Service后,如果再调用bindService()绑定此Service,则Service的bind()方法会被调用,此时如果不执行unBindService()方法,调用stopService()方法是无效的。生命周期如下所示:
2018-12-06 16:43:59.324 23816-23816/com.anyidc.service_study E/tag: --------Service-------->>onCreate
2018-12-06 16:43:59.325 23816-23816/com.anyidc.service_study E/tag: --------Service-------->>onStartCommand
2018-12-06 16:44:02.992 23816-23816/com.anyidc.service_study E/tag: --------Service-------->>onBind
2018-12-06 16:44:04.278 23816-23816/com.anyidc.service_study E/tag: --------Service-------->>onUnbind
2018-12-06 16:44:06.090 23816-23816/com.anyidc.service_study E/tag: --------Service-------->>onDestroy
  • 通过bindService的方式启动Service:这种启动方式启动的Service其生命周期为onCreate()——>onBind()——>onUnbind()——>onDestroy()。当通过Context.bindService()绑定服务成功后,Service会回调onCreate和onBind方法,此时重复调用Context.bindService()方法是无效的,当调用unBindService方法时Service回调onUnbind和onDestroy方法。此方式绑定启动的Service与绑定者相关联,绑定者启动Service后,若绑定者退出,则Service也会随着解绑、销毁,绑定者可以通过ServiceConnection的onServiceConnected()方法获取到Service的实例对象,从而调用Service内的方法。需要注意的是,通过绑定启动的Service绑定成功后,如果此时再调用startService()方法,Service的onStarCommand()方法会被调用,此时再执行unBindService()方法,Service的onUnbind()方法会被调用,但onDestroy()方法不会被调用,需要调用stopService()方法或者Service自身调用stopSelf()方法才能退出Service;而如果先执行stopService的操作,Service的onDestroy方法不会被调用,此时执行unBindService()方法,Service正常解绑退出。生命周期如下:
2018-12-06 17:02:40.037 27910-27910/com.anyidc.service_study E/tag: --------Service-------->>onCreate
2018-12-06 17:02:40.038 27910-27910/com.anyidc.service_study E/tag: --------Service-------->>onBind
2018-12-06 17:02:44.181 27910-27910/com.anyidc.service_study E/tag: --------Service-------->>onStartCommand
在这一步如果先stopService(),Service的onDestroy方法不执行,再进行解绑操作,则执行以下两个方法。
若先执行unBindService(),则Service方法的onUnbind方法执行,onDestroy方法不会执行,此时要通过stopService方法退出Service,onDestroy才会被执行。
2018-12-06 17:02:47.599 27910-27910/com.anyidc.service_study E/tag: --------Service-------->>onUnbind
2018-12-06 17:02:47.600 27910-27910/com.anyidc.service_study E/tag: --------Service-------->>onDestroy

IntentService

IntentService是Service的子类,可以使用它来做一些下载文件等耗时操作,其特征为:

  • 会创建独立的worker线程来处理所有的Intent请求;
  • 会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程的问题;
  • 所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;
  • 为Service的onBind()方法提供默认实现,返回null;
  • 为Service的onStartCommand方法提供默认实现,将请求Intent添加到队列中。

ContentProvider

ContentProvider主要用于在不同的应用之间实现数据共享的功能。ContentProvider的用法一般有两种,一种是使用现有的内容提供者来读取和操作相应程序的数据,另一种是创建自己的内容提供者给我们的数据提供外部访问接口。

使用现有的ContentProvider

对于每一个应用程序,如果想要访问内容提供者中的共享数据,就一定要借助ContentResolver类,可以通过Context.getContentResolver()方法获取到该类的实例。ContentResolver类提供了一系列方法对数据进行CRUD操作,ContentProvider通过Uri作为数据的唯一标识符,它主要由两部分组成:authority和path。authority一般采用程序的包名+provider进行命名,path则是用于区分同一程序中不同的表,通常跟在authority的后面。标准的Uri内容如下:

content://com.example.app.provider/table1

获取到内容的Uri后即可通过ContentResolver去获取和操作相应程序表中的数据了,以下为ContentResolver操作数据的相关方法:

  • 查数据
通过ContentResolver的query方法获取到游标对象,随后遍历游标的所有行即可获取数据
Cursor cursor=getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
  • 插入数据
通过ContentValues对象和ContentResolver的insert方法往表中插入数据
ContentValues values=new ContentValues();
values.put("列名",value);
getContentResolver().insert(uri,values);
  • 修改数据
通过ContentValues对象和ContentResolver的update方法修改表中数据
ContentValues values=new ContentValues();
values.put("列名",value);
getContentResolver().update(uri,values,"列名=?and列名2=?",new String[]{"text","1"});
  • 删除数据
通过ContentResolver的delete放放删除表中数据
getContentResolver().delete(uri,"列名=?",new String[]{"text"});

创建自己的内容提供者

创建自己的内容提供者需要创建一个类去继承ContentProvider并且实现其6个抽象方法。

public class ThisContentProvider extends ContentProvider {
	
    @Override
    public boolean onCreate() {
    //初始化内容提供者的时候调用。通常在这里完成对数据库的创建和升级。
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
    //从内容提供者中查询数据。
        return null;
    }

    @Override
    public String getType(Uri uri) {
    //根据传入的uri返回相应的MIME类型
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues contentValues) {
    //向内容提供者中插入数据
        return null;
    }

    @Override
    public int delete(Uri uri, String s, String[] strings) {
    //删除内容提供者中的数据
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
    //更新内容提供者中已有的数据。
        return 0;
    }
}

在自己的内容提供者类中,应该先获取本程序中的SQLiteDatabase对象,再通过此对象对uri中的相应表格进行CRUD操作,具体的逻辑应该根据项目的业务逻辑分别在六个方法中进行完善,特别需要注意的是,自定义的内容提供者编写完成后应该在清单文件中进行注册,并且需要指定authority属性,并将enabled和exported属性设置为true,表示允许此内容提供者被其他程序访问。

参考资料:

  • Android 四大组件 - 简书
  • 《Android开发艺术探索》
  • 《第一行代码》
  • 彻底弄懂Activity四大启动模式

你可能感兴趣的:(Android四大组件)