Activity 详解(下)

 

一.四种启动模式

 

 

1.知识补充

   

任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。Activity的管理是采用任务栈的形式。任务栈采用“后进先出”的栈结构。每按一次Back键,就有一个Activity出栈 。

 

Activity 详解(下)_第1张图片

 

 

 

 

 

2.启动模式的设置

 

启动模式有两种设置方式。

 

<1> 通过 AndroidMainifest 设置

 

 

android:launchMode可取值以及含义

 

 

   (1) standard:标准模式

         默认值。系统在启动该 Activity 的任务中创建 Activity 的新实例,即启动Activity和目标Activity在同一Activity栈中。Activity 可以多次实例化一个任务可以拥有多个实例

 

 

 

   (2) singleTop:栈顶复用模式

         如果当前任务的顶部已存在 目标Activity 的实例,则系统会通过调用其 onNewIntent() 方法来将 intent 转送给该实例,而不是创建 Activity 的新实例。Activity 可以多次实例化一个任务可以拥有多个实例(但前提是返回堆栈顶部的 Activity 不是该 Activity 的现有实例)。

         例如,假设任务的返回堆栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈为 A-B-C-D;D 位于顶部)。收到以 D 类型 Activity 为目标的 intent。如果 D 采用默认的 "standard" 启动模式,则会启动该类的新实例,并且堆栈将变为 A-B-C-D-D。但是,如果 D 的启动模式为 "singleTop",则 D 的现有实例会通过onNewIntent() 方法接收 intent,因为它位于堆栈顶部,堆栈仍为 A-B-C-D。但是,如果收到以 B 类型 Activity 为目标的 intent,则会在堆栈中添加 B 的新实例,并且堆栈将变为 A-B-C-D-B,即使其启动模式为 "singleTop" 也是如此。

 

 

   (3) singleTask:栈内复用模式

         系统会创建新任务,并实例化目标Activity。但是,如果任务中已存在该 Activity 的实例,则系统会通过调用其onNewIntent() 方法将 intent 转送到该现有实例,而不是创建新实例。Activity 一次只能有一个实例存在

 

 

   (4) singleInstance:单例模式。

         与 "singleTask" 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。

 

 

补充:在AndroidMainifest设置launchMode时,几个配套使用的属性

 

Activity 详解(下)_第2张图片

 

 

 

 

<2> 通过 Intent 设置标志位

Intent intent=new Intent(SplashActivity.this, MyActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

 

addFlags可取值以及含义

 

   (1) FLAG_ACTIVITY_NEW_TASK

         在新任务中启动 Activity。如果您现在启动的 Activity 已经有任务在运行,则系统会将该任务转到前台并恢复其最后的状态,而 Activity 将在onNewIntent() 方法中收到新的 intent。

这与上一节中介绍的launchMode属性"singleTask" 产生的行为相同。

 

 

   (2) FLAG_ACTIVITY_CLEAR_TOP

         如果要启动的 Activity 已经在当前任务中运行,则不会启动该 Activity 的新实例,而是会销毁位于它之上的所有其他 Activity,并通过onNewIntent() 方法将此 intent 传送给它的已恢复实例(现在位于堆栈顶部)。

         

         launchMode属性没有可产生此行为的值。

   FLAG_ACTIVITY_CLEAR_TOP 最常与 FLAG_ACTIVITY_NEW_TASK 结合使用。将这两个标记结合使用,可以查找其他任务中的现有 Activity,并将其置于能够响应 intent 的位置。

 

 

   (3) FLAG_ACTIVITY_SINGLE_TOP

         如果要启动的 Activity 是当前 Activity(即位于返回堆栈顶部的 Activity),则现有实例会收到对onNewIntent() 方法的调用,而不会创建 Activity 的新实例。

         这与上一节中介绍的 "singleTop" launchMode值产生的行为相同。

 

 

<3> 二者设置的区别

有些启动模式可通过清单文件定义,但不能通过 intent 标记定义,同样,有些启动模式可通过 intent 标记定义,却不能在清单中定义。

                                  

 

 

 

3.亲和性

 

<1> 简介

 

亲和性 表示 Activity 倾向于属于哪个任务。默认情况下,同一应用中的所有 Activity 彼此具有亲和性。因此,在默认情况下,同一应用中的所有 Activity 都倾向于位于同一任务。

不过,也可以修改 Activity 的默认亲和性。在不同应用中定义的 Activity 可以具有相同的亲和性,或者在同一应用中定义的 Activity 也可以被指定不同的任务亲和性。

您可以使用  元素的 taskAffinity 属性修改任何给定 Activity 的亲和性。

taskAffinity 属性采用字符串值,该值必须不同于元素中声明的默认软件包名称,因为系统使用该名称来标识应用的默认任务亲和性。

 

 

 

<2> 作用

 

亲和性可在两种情况下发挥作用:

 

(1) 当启动 Activity 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标记时。

 

   默认情况下,新 Activity 会启动到调用 startActivity() 的 Activity 的任务中。它会被推送到调用方 Activity 所在的返回堆栈中。但是,如果传递给 startActivity() 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标记,则系统会寻找其他任务来容纳新 Activity。通常会是一个新任务,但也可能不是。如果已存在与新 Activity 具有相同亲和性的现有任务,则会将 Activity 启动到该任务中。如果不存在,则会启动一个新任务。

   如果此标记导致 Activity 启动一个新任务,而用户按下主屏幕按钮离开该任务,则必须为用户提供某种方式来返回到该任务。有些实体(例如通知管理器)总是在外部任务中启动 Activity,而不在它们自己的任务中启动,因此它们总是将 FLAG_ACTIVITY_NEW_TASK 添加到传递给 startActivity() 的 intent 中。如果您的 Activity 可由外部实体调用,而该实体可能使用此标记,请注意用户可以通过一种独立的方式返回到所启动的任务,例如使用启动器图标(任务的根 Activity 具有一个 CATEGORY_LAUNCHER intent 过滤器;请参阅下面的启动任务部分)。

 

 

(2) 当 Activity 的 allowTaskReparenting 属性设为 "true" 时。

 

   在这种情况下,一旦和 Activity 有亲和性的任务进入前台运行,Activity 就可从其启动的任务转移到该任务。

   举例来说,假设一款旅行应用中定义了一个报告特定城市天气状况的 Activity。该 Activity 与同一应用中的其他 Activity 具有相同的亲和性(默认应用亲和性),并通过此属性支持重新归属。当您的某个 Activity 启动该天气预报 Activity 时,该天气预报 Activity 最初会和您的 Activity 同属于一个任务。不过,当旅行应用的任务进入前台运行时,该天气预报 Activity 就会被重新分配给该任务并显示在其中。

 

 

 

 

举例说明

 

 

描述

 

MainActivity 跳转到AAAActivity,然后跳转到BBBActivity,再跳转到CCCActivity,最后跳转到DDDActivity。然后由DDDActivity跳转到BBBActivity。

 

 

<1> 所有Activity的启动模式都是默认模式。

 

完成所有跳转 查看Activity栈信息(Terminal 输入 adb shell dumpsys activity activities)

Activity 详解(下)_第3张图片

 

 

 

 

 

 

 

 

 

 

 

二.序列化

 

上一节降到Activity通过Intent或是Bundle传递数据(对象)需要序列化。那么什么是对象序列化,为什么要对象序列化,怎么对象序列化呢。

 

 

1.什么是序列化

 

简单来说,对象序列化,就是把运行时的对象信息,按照一定的规则,翻译成一串有迹可循的二进制流,然后将此二进制流,从一方传输到另一方。而在接收方,在接受到此二进制流之后,可以按照约定好的解析规则,进行反序列化,将对象信息解析出来,得到有用的数据信息。

 

再说的白一点就是将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。

 

对象信息,包括Class信息、继承关系、访问权限、变量类型以及数值信息等。因此,序列化后得到的二进制流,不仅仅包含了描述一个对象的Class的关键信息,也存储了具有实际意义的数值信息。因此,序列化可以描述为——转述对象信息,存储对象数据。

 

 

 

 

2.为什么要序列化

 

Android开发的时候,无法将对象的引用传给Activitie或者Fragment,我们需要将这些对象放到一个Intent或者Bundle里面,然后再传递。所以要序列化。

 

 

 

 

3.怎么序列化

 

Android中Intent如果要传递类对象,可以通过两种方式实现。

 

方式一:Serializable,要传递的类实现Serializable接口传递对象。Java自带

 

方式二:Parcelable,要传递的类实现Parcelable接口传递对象。android 专用

 

下面来一一讲解两种方式的实现

 

 

 

<1> Serializable

 

Java Bean

package com.wjn.lubandemo.bean;

import java.io.Serializable;

public class PictureBean implements Serializable {

    private static final long serialVersionUID = 73823513598685L;

    private String originArg;
    private String thumbArg;
    private String path;
    private int image;

    public String getOriginArg() {
        return originArg;
    }

    public void setOriginArg(String originArg) {
        this.originArg = originArg;
    }

    public String getThumbArg() {
        return thumbArg;
    }

    public void setThumbArg(String thumbArg) {
        this.thumbArg = thumbArg;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public int getImage() {
        return image;
    }

    public void setImage(int image) {
        this.image = image;
    }
}

 

 

传递

PictureBean pictureBean=new PictureBean();
pictureBean.setImage(123);
pictureBean.setOriginArg("wjncpy0");
pictureBean.setPath("path");
pictureBean.setThumbArg("cpywjn1");

Intent intent=new Intent(MainActivity.this,EventActivity.class);
intent.putExtra("bean",pictureBean);
startActivity(intent);

 

 

接收

Intent intent = getIntent();
PictureBean pictureBean = (PictureBean) intent.getSerializableExtra("bean");
if (null != pictureBean) {
    String s = pictureBean.getOriginArg();
    String s1 = pictureBean.getPath();
    String s2 = pictureBean.getThumbArg();
    int s3 = pictureBean.getImage();
    Log.d("TAG", "s----:" + s);
    Log.d("TAG", "s1----:" + s1);
    Log.d("TAG", "s2----:" + s2);
    Log.d("TAG", "s3----:" + s3);
}

 

 

结果

D/TAG: s----:wjncpy0

D/TAG: s1----:path

D/TAG: s2----:cpywjn1

D/TAG: s3----:123

 

 

 

 

<2> Parcelable

 

 

Java Bean

package com.wjn.lubandemo.bean;

import android.os.Parcel;
import android.os.Parcelable;

public class Student implements Parcelable {

    private int age;
    private String name;
    private long ids;
    private double money;

    public Student(Parcel in) {
        age = in.readInt();
        name = in.readString();
        ids = in.readLong();
        money = in.readDouble();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }

        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(age);
        dest.writeString(name);
        dest.writeLong(ids);
        dest.writeDouble(money);
    }

    public Student(){

    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setIds(long ids) {
        this.ids = ids;
    }

    public long getIds() {
        return ids;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public double getMoney() {
        return money;
    }
}

 

 

传递

Student student=new Student();
student.setAge(12);
student.setName("张三");
student.setIds(123456789);
student.setMoney(12.56);
Intent intent=new Intent(MainActivity.this,EventActivity.class);
intent.putExtra("bean",student);
startActivity(intent);

 

 

接收

Intent intent = getIntent();
Student student = intent.getParcelableExtra("bean");
if (null != student) {
    int age = student.getAge();
    String name = student.getName();
    long ids = student.getIds();
    double money = student.getMoney();
    Log.d("TAG", "age----:" + age);
    Log.d("TAG", "name----:" + name);
    Log.d("TAG", "ids----:" + ids);
    Log.d("TAG", "money----:" + money);
}

 

 

结果

D/TAG: age----:12
D/TAG: name----:张三
D/TAG: ids----:123456789
D/TAG: money----:12.56

 

 

 

 

Serializable 和Parcelable的对比

 

编码方面

 

Serializable代码量少,写起来方便。

Parcelable代码多一些。

 

 

效率上

 

Parcelable的速度比高十倍以上Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。

 

 

 

 

 

 

三.Activity,Window与View的关系

 

Activity,Window与View的关系

Activity 详解(下)_第4张图片

 

 

流程解析: Activity调用startActivity后最后会调用attach方法,然后在PolicyManager实现一个Ipolicy接口,接着实现一个Policy对象,接着调用makenewwindow(Context)方法,该方法会返回一个PhoneWindow对象,而PhoneWindow 是Window的子类,在这个PhoneWindow中有一个DecorView的内部类,是所有应用窗口的根View,即View的老大, 直接控制Activity是否显示(引用老司机原话..),好吧,接着里面有一个LinearLayout,里面又有两个FrameLayout他们分别拿来装ActionBar和CustomView,而我们setContentView()加载的布局就放到这个CustomView中!

总结下这三者的关系: 打个牵强的比喻: 我们可以把这三个类分别堪称:画家,画布,画笔画出的东西; 画家通过画笔( LayoutInflater.infalte)画出图案,再绘制在画布(addView)上! 最后显示出来(setContentView)

 

 

你可能感兴趣的:(综合)