Android初级01四大组件

这part主要探讨的是Android的四大组件。

1.Activity

标准生命周期

说到Activity,想必第一时间想到生命周期这玩意。
一般来说,Activity启动经过以下几个步骤:

onCreate() ----> onStart() ----> onResume() ----> Activity is running.
 
Activity正常结束掉会经过以下几个步骤:
onPause() ----> onStop() ----> onDestroy() ----> Activity is sht down.
 
也就是说,假如正常标准的走完整个流程,那么就是以下这种情况:

onCreate() ----> onStart() ----> onResume() ----> onPause() ----> onStop() ----> onDestroy()

那么这几个方法调用的实际时机又是怎么样呢?

  • onCreate() ":Activity创建的时候,以及当Activity被onPause()或者onStop()的时候,假如memory不足,被kill掉(kill掉的是那个在后台的Activity),用户重新回到这个Activity,那么就会再次调用onCreate()
     
  • onStart():在onCreate()之后被调用,以及当被onStop()之后,再次回到这个Activity时,会先调用生命周期之外的onRestart(),随后被调用。onStart()可以理解为是当前的Activity “被看见了!”
     
  • onResume():情况较多。
    1. onStart()之后,这里有两种情况,一种是从onCreate()之后的onStart(),一种是onRestart()之后的onStart()
    2. Activity被覆盖,然后回到前台。(包括Dialog风格的Activity
    3. 解锁屏幕的时候。
    4. 总的来说,就是假如Activity回到前台的时候onResume()是一定会被调用的。
       
  • onPause():实际上是跟onResume()成对来活动的。
    1. Activity被覆盖的时候。
    2. Activity退居后台的时候。(退居后台的操作比如有跳转到新的Activity按Home键回到桌面等
    3. 锁屏的时候。
    4. onPause()实际代表的就是当前的Activity "暂停了" ,也就意味着Activity被短暂的遮挡住了。而且需要注意的是,只有当上一个Activity的onPause()执行完毕之后,下一个新的Activity的onResume()才会被调用,也就是上一个Activity的onPause()是影响到下一个Activity的刷新时间的!
       
  • onStop():有三种情况。
    1. 第一种就是当当前的Activity跳转到新Activity的时候。
    2. Home键等操作让Activity退居到后台的时候。
    3. onPause()之后。
    4. 其实onStop()代表的是当前Activity "看不着了!" 注意一下 “被覆盖” 是不会调用这个方法的。
       
  • onDestroy():有以下几种情况。
    1. finish掉的时候。
    2. 正常生命周期的onStop()之后。
    3. 出现意外情况当前的Activity被Kill掉的时候。(内存不足啊,横竖屏啊之类的)。
       
  • onRestart():只会在被onStop()之后才会被调用,上面也说过了,onStop()之后代表的是这个Activity “看不见了!” ,也就是说从后台被推到前台的时候才会被调用。值得注意的是假如这个Activity被 “Kill掉了!” 然后再回到前台的时候,这个方法是不会被调用的, 只有被Stop而没被Kill掉的情况下才会走到这个方法!
     

看完上面这段,现在看这个图应该很清楚了:


Android初级01四大组件_第1张图片
生命周期图.png

特殊情况

一旦Activity因为某种原因被kill掉,而这时候用户又需要到这个Activity,再次被推回前台的时候,这个时候会产生特殊情况。
 
一般来说,引起特殊情况的原因有以下两种:
 

  • 系统配置发生了变化,比如横竖屏的切换。
     
  • 退居到后台的Activity因内存不足而被kill掉。
     

这两种都会走onStop()onDestroy(),等于完全被销毁掉了。

然后这个时候,假如这个Activity又再次被推到前台使用的话,就会从onCreate()开始重新走一遍生命流程。

这涉及到两个特殊的方法:

  • onSaveInstanceState
     
  • onRestoreInstanceState

被销毁时,会调用onSaveInstanceState,目的是希望能够用Bundle来进行当前页面状态的储存。

销毁之后,再次恢复的话,就会走onCreate然后就是onRestoreInstanceState用来恢复其状态。

像EditextView这种,官方已经实现了上面两个方法,也就是说假如遇到了这种意外被销毁的情况,将会被自动恢复。如果你是自定义的View,也想对被销毁的视图进行恢复的话,也需要实现这两个方法。

关于调用时机的情况,onSaveInstanceState的源码是如此描述的:

this method will occur before {@link #onStop} and there are no guarantees about whether it will occur before or after {@link #onPause}.

也就是说,会在onStop之前调用,但不确定是否会在onPause之前或之后调用。

一段简单的数据恢复演示:

    @Override
    protected void onCreate(Bundle savedInstanceState) {    
        super.onCreate(savedInstanceState);    
        //值得注意的是,在onCreate()中进行数据恢复的话,需要判断是否为空,因为当onCreate时正常启动而非意外销毁的情况下,为null    
        if (savedInstanceState != null) {       
            String save_str = savedInstanceState.getString("save_str");        
            Log.d(TAG, "onCreate,restore data:" + save_str);    
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {    
        super.onSaveInstanceState(outState, outPersistentState);    
        outState.putString("save_str", "test_data");
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {    
        super.onRestoreInstanceState(savedInstanceState);    
        //在这里不需要判空,因为只要走了这个方法的,说明savedInstanceState肯定有值。    
        String save_str = savedInstanceState.getString("save_str");    
        Log.d(TAG, "onRestoreInstanceState,restore date:" + save_str);
    }

 
当然我们也可以让阻止它销毁重建,比如帮activity设置个属性,android:configChanges="orientation"

这个配置属性除了这个以外,还有很多个,常用的是localeorientationkeyboardHidden这三个,可以适当记一记。

启动模式

在说启动模式之前,先要简单明白任务栈的概念。

任务栈

跟内存一样,“后进先出” 是栈的基本理念。

这是我基于自己理解的灵魂手绘图:


Android初级01四大组件_第2张图片
栈的灵魂手绘.jpg

在Android中,Activity所处的栈是跟包名挂钩的,而且可以通过修改AndroidMenifest标签中的android:taskAffinity=xxx.xx.xx来修改启动的栈名。(注意这个taskAffinity属性的值为字符串,且中间必须含有包名分隔符“.”),默认情况下是本应用的包名,所以必须要是非本包包名这个值才会起作用。

栈分为 前台任务栈后台任务栈 ,后台任务栈处于暂停状态,二者可以通过操作互相切换。

四种启动模式

启动模式有四种,分别为:

standardsingleTopsingleTasksingleInstance

  • standard:标准模式,也是默认的启动模式。
  1. 不管是否存在,一旦启动就重新创建一个实例。
  2. 会创建在启动的栈中,也就意味着多个栈中可以存在多个实例。
     
  • singleTop:栈顶复用模式。
  1. 如果需要启动的新Activity已经位于栈顶,那么就不会重新创建。
  2. 如果需要启动的新Activity已存在但是不位于栈顶,那么还是会进行重新创建。

这里做个小测试,假如当前有三个Activity在栈中,ThirdActivity在栈顶:

Running activities (most recent first):
      TaskRecord{1300817 #6 A=com.jormun.myandroidstudy U=0 StackId=1 sz=3}
        Run #2: ActivityRecord{ebe0b66 u0 com.jormun.myandroidstudy/.activity.ThirdActivity t6}
        Run #1: ActivityRecord{7b723d7 u0 com.jormun.myandroidstudy/.activity.SecondActivity t6}
        Run #0: ActivityRecord{87d7a27 u0 com.jormun.myandroidstudy/.MainActivity t6}

随后再跳转到SecondActivity,singTop模式,再来看看adb的日志怎么说:

Running activities (most recent first):
      TaskRecord{1300817 #6 A=com.jormun.myandroidstudy U=0 StackId=1 sz=4}
        Run #3: ActivityRecord{d104a67 u0 com.jormun.myandroidstudy/.activity.SecondActivity t6}
        Run #2: ActivityRecord{ebe0b66 u0 com.jormun.myandroidstudy/.activity.ThirdActivity t6}
        Run #1: ActivityRecord{7b723d7 u0 com.jormun.myandroidstudy/.activity.SecondActivity t6}
        Run #0: ActivityRecord{87d7a27 u0 com.jormun.myandroidstudy/.MainActivity t6}

可以看到,即便是SecondActivity存在了,一旦不在栈顶,那么还是会重新创建的。

  • singleTask:栈内复用模式
  1. 假如栈中存在,那么就会调到栈顶进行复用。会执行onNewIntent,不存在的话就会新建一个实例放到栈中。
  2. 这种模式是跟需要启动的栈直接相关的,假如需要启动的栈中不存在该实例,那么会新建一个栈来放进去。上面也说了,栈是跟你的设置有关。
  3. 该启动模式,假如栈中已存在但不在栈顶,那么会把上方所有实例全部清栈,也就是clearTop效果。

针对第三点,我们来做个小测试看看,当前栈中有四个实例:

Running activities (most recent first):
      TaskRecord{77265d2 #8 A=com.jormun.myandroidstudy U=0 StackId=1 sz=4}
        Run #3: ActivityRecord{529f01f u0 com.jormun.myandroidstudy/.activity.FourthActivity t8}
        Run #2: ActivityRecord{2f09d4b u0 com.jormun.myandroidstudy/.activity.ThirdActivity t8}
        Run #1: ActivityRecord{59ade37 u0 com.jormun.myandroidstudy/.activity.SecondActivity t8}
        Run #0: ActivityRecord{a4ced8b u0 com.jormun.myandroidstudy/.MainActivity t8}

这时我们返回到SecondActivity,看看adb又怎么说:

 Running activities (most recent first):
      TaskRecord{77265d2 #8 A=com.jormun.myandroidstudy U=0 StackId=1 sz=2}
        Run #1: ActivityRecord{59ade37 u0 com.jormun.myandroidstudy/.activity.SecondActivity t8}
        Run #0: ActivityRecord{a4ced8b u0 com.jormun.myandroidstudy/.MainActivity t8}

很明显的看到,直接把上面的实例全部干掉了,而不是简单的把SecondActivity放到最上面去使用。

  • singleInstance: 单实例模式。
  1. 这种模式继承了singleTask的所有特性,属于加强版的singleTask
  2. 一个Activity存在于一个栈中,启动一个创建一个,因此可以复用。

说完启动模式之后,我们来说说怎么指定启动模式。

  • AndroidMenifest标签。最基本的方法,例子:


  • 代码中指定,相信经常想通过Fragment来跳转Activity的人都不会陌生,例子:
    Intent intent = new Intent(MainActivity.this,SecondActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);

需要注意的一些地方:

  • 假如两种的存在,以第二种为准。
  • singleInstance不适用于第二种。
  • 第一种无法指定FLAG_ACTIVITY_CLEAR_TOP标识。
隐式调用

隐式调用需要用intentFilter来进行过滤,对应AndroidMenifest标签中的

一个可以有多个,一个Intent只需要对应到一组就可以启动。

通过隐式调用的时候,可以用PackageManager或者IntentresolveActivity方法,也可以用PackageManagerqueryIntentActivities方法进行判断,不返回null就可以成功启动Activity

一个含有以下三种信息:

actioncategorydata

下面来逐一看看这3个东西。

  • action:字符串类型。
  1. 必须存在一个actionIntent中。
  2. 一个中可以有多个actionIntent中设置的action匹配到任何一个即可。
  3. 严格区分大小写。
  • category:字符串类型。
  1. Intent中有默认的category值:android.intent.category.DEFAULT
  2. 因为有默认值的存在,因此category可有可无,但是一旦有,无论多少都要与中定义的相同。
  • data:其实就是个URI。
  1. Scheme:URI的模式,比如http、file、content等等,这个不定义,整个URI都不生效。
  2. Host:URI的主机名,同上,这个不定义则整个都不生效。
  3. Port:URI中的端口号。
  4. Path:完整的路径信息。
  5. PathPattern:同上。
  6. pathPrefix:路径的前缀信息。
  7. mimeType:媒体类型,如图片的image/jpeg,视频的audio/mpeg4-generic、vedio/* 这些

除去mimeType,其实就是以下这样的格式:

://:/[||]

写成这样就一目了然了:

http://www.baidu.com:80/search/info

关于data有以下一些需要注意:

  • URI部分,默认值为contentfile
  • 如需要为Intent指定完整的data,必须要调用setDataAndType

2.Service

简单介绍一下Service吧。

个人理解:
Service是一个运行在后台的组件,不需要和用户交互,但可以跟其它组件进行交互。
Service适用于一些不需要依赖于UI而一直运行的任务,比如后台下载,检测链接状态(心跳)等等。
因为是依赖于进程的,所以只要进程还存在,那么就可以一直跑下去。

实现Service的方式

Service:

最简单的方式是创建一个继承自Service的类,然后重写里面的方法,再在有需要的地方启动一下即可。
示例:

public class MyTestService extends Service {    
    
    private static final String KEY_SERVICE_TAG = "SERVICE_TAG";    
    
    @Override    
    public void onCreate() {        
        super.onCreate();
        //打印create的状态
        Log.e(KEY_SERVICE_TAG, "service_create");   
    }
    
    @Override   
    public int onStartCommand(Intent intent, int flags, int startId) {        
        //打印startCommand状态
        Log.e(KEY_SERVICE_TAG, "service_start");                
        return super.onStartCommand(intent, flags, startId);    
    }
    
    @Override    
    public void onDestroy() {        
        super.onDestroy();        
        //打印Destroy的状态
        Log.e(KEY_SERVICE_TAG, "service_stop");    
    }
    
    @Nullable    
    @Override    
    public IBinder onBind(Intent intent) {
        return null;    
    }
 }

 
千万注意要在清单文件注册一下AndroidMenifest一下就好。

启动也很简单,还是用Intent:

Intent serviceStartIntent = new Intent(this, MyTestService.class);
//注意一下这里是startService,别跟StartActivity给混了
startService(serviceStartIntent);

停止就这样:

Intent serviceStopIntent = new Intent(this, MyTestService.class);
stopService(serviceStopIntent);

或者在Service中自己停掉自己:

stopSelf();

 
这种方式的优缺点:

  • 优点:经典做法,可以简单的实现到Service的启停。
  • 缺点:一旦忘记stop就会一直运行,无法拿到IBinder进行交互。
IntentService:

用以上的方式当然可以简单的实现,不过假如你在某处start了一个Service,但是你忘记关了那就蛋疼了,所以官方也考虑到这种情况,提供了一个执行完任务就自动关闭的Service给我们用,叫 IntentService
用法也很简单,继承IntentService来重写方法即可,示例:

未完待续......

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