Android自学笔记

关于Activity你必须要知道的一切

2018-09-08  本文已影响122人  Android_Jieyao

前言:Activity是Android四大组件之一,为用户提供与系统交互的界面,每一个应用都有一个或者多个Acticity,这样会有各种各样的细节问题需要查找,我将本人接触到的知识点汇总到此篇文章。

1.Activity简单介绍

一个Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务,例如拨号、拍照、发送email、看地图。每一个activity被给予一个窗口,在上面可以绘制用户交互的画面。窗口通常充满屏幕,但也可以小于屏幕而浮于其它窗口之上。一个用户交互界面对应一个activity,相当于是界面的容器setContentView(),activity 是Context的子类,同时实现了window.callback接口(里面方法如dispatchtouchevent可以分发事件)和keyevent.callback等, 可以处理与窗体用户交互的事件。

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {}

2.Activity的创建

1.创建步骤:

2.注意点:

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"

3.Activity的的跳转

Activity的跳转需要创建Intent对象,通过设置intent对象的参数指定要跳转的activity。通过设置Activity的包名和类名实现跳转,称为显式意图。
通过指定动作实现跳转,称为隐式意图。

1. 显式意图

Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
startActivity(intent);
Intent intent = new Intent();
//启动系统自带的拨号器应用
intent.setClassName("com.android.dialer","com.android.dialer.DialtactsActivity");
startActivity(intent);

2. 隐式意图

Intent intent = new Intent();
//启动系统自带的拨号器应用
intent.setAction(Intent.ACTION_DIAL);
startActivity(intent);
<intent-filter >
     <action android:name="com.chenqiao.second"/>
     <data android:scheme="chenqiao" android:mimeType="aa/bb"/>
     <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
//[1]创建意图对象 意图就是我要完成一件事
Intent intent = new Intent();
//[2] 设置跳转的动作
intent.setAction("com.chenqiao.testactivity");
//[3] 设置category
intent.addCategory("android.intent.category.DEFAULT");
//[4]设置数据
// intent.setData(Uri.parse("chenqiao:"+110));
//[5]设置数据类型
//intent.setType("aa/bb");
//[6]注意 如果setdata 方法和 settype 方法一起使用的时候 应该使用下
//面这个方法
intent.setDataAndType(Uri.parse("chenqiao:"+110), "aa/bb");
//[4]开启Activity
startActivity(intent);
<intent-filter>
<action android:name="com.itheima.testactivity" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="aa/bb" android:scheme="chenqiao" />
</intent-filter>

3.意图匹配原则

<intent-filter . . . >
<action android:name="com.example.project.SHOW_CURRENT" />
<action android:name="com.example.project.SHOW_RECENT" />
<action android:name="com.example.project.SHOW_PENDING" />
. . .
</intent-filter>

一个Intent对象只命名一个单独的行为,而一个过滤器可以列出多个行为。列表不能为空;一个过滤器至少要包含一个元素,否则它将屏蔽所有的意图。要通过这个检测,在Intent对象中指定的行为必须与过滤器所列出的行为之一相匹配。如果该对象或是过滤器没有指定一个行为,就会有下面的结果:如果过滤器没有列出任何行为,就没有行为可以与意图相匹配,所有的意图都无法通过检测。没有意图可以通过过滤器。另一方面,如果Intent对象没有指定任何的行为,它将自动通过检测——只要过滤器含有一个或以上的行为。

<intent-filter . . . >
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
. . .
</intent-filter>

注意之前描述过的表示行为和类型的常量不在mainfest文件中被使用。而是使用完整的字符串值。例如,在范例中的“android.intent.category.BROWSABLE”字符串和文档之前提到的CATEGORY_BROWSABLE常量相对应。类似地,字符串“android.intent.action.EDIT”和ACTION_EDIT常量相对应。
一个意图要通过类别检测,Intent对象中的每一个类别都必须与过滤器中的某个类别相匹配。过滤器可以列出其他更多的类别,但不能省略任何一个意图中含有的类别。原则上,所以说,一个不含有类别的Intent对象,无论过滤器中有哪些类别,总是能够通过这项检测。这通常是正确的。不过,有一个例外,Android把所有传递给startActivity()的隐式意图视为它们包含了至少这样一个类别:“android.intent.category.DEFAULT”(CATEGORY_DEFAULT常量)。因此,要接受隐式意图的活动必须在其意图过滤器中包含有“android.intent.category.DEFAULT”。(设有“android.intent.action.MAIN”和“android.intent.category.LAUNCHER”的过滤器不在此范围之中。它们将活动标记为了新任务的开始,并显示在应用启动器(launcher)屏幕上。它们可以在类别列表中包含“android.intent.category.DEFAULT”,不过这并不是必须的)

<intent-filter . . . >
<data android:mimeType="video/mpeg" android:scheme="http" . . . />
<data android:mimeType="audio/mpeg" android:scheme="http" . . . />
. . .
</intent-filter>

每一个元素可以指定一个URI和数据类型(MINE媒体类型)。URI的每一个部分由不同的属性——模式(scheme)、主机(host)、接口(port)和路径(path)。当一个Intent对象中的URI和过滤器中的URI相比较时,仅比较过滤器中所包含的URI部分。例如,如果一个过滤器仅指定了一个模式,那所有具有这个模式的URI就与过滤器相匹配。如果过滤器指定了一个模式及一个授权但是没有指定路径,那么无论是什么路径,具有相同模式和授权的URI将得到匹配。如果过滤器指定了模式、授权和路径,那就只有具有相同模式、授权和路径的URI得到匹配。不过,过滤器中指定的路径可以包含通配符以仅仅限定部分路径。一个data元素的类型(type)属性指定了数据的MINE类型。在过滤器中这比URI更为常见。Intent对象和过滤器都可以用””通配符作为子类别域来标识子类别匹配,例如“text/”或“audio/*”。

数据检测同时将Intent对象中的URI和数据类型与过滤器中的URI和数据类型相比较。该过程遵循以下规则:

1.不包含URI和数据类型的Intent对象只有在过滤器也不指定任何URI及数据类型时才能通过检测。
2.包含URI但不包含数据类型(且数据类型不能通过URI推断出来)的Intent对象只有在其URI与过滤器中的URI相匹配且过滤器同样没有指定类型时才能通过检测。这是仅仅在类似mailto:和tel:这样没有指向实际数据的URI时才会出现的情况。
3.包含数据类型但不包含URI的Intent对象只有在过滤器列出了相同的数据类型 而没有指定URI时才能通过检测。
4.同时包含有URI和数据类型(或URI可以推导出数据类型)的Intent对象在其数据类型与过滤器中列出的类型相匹配时通过检测的数据类型部分。当其URI与过滤器中的URI相匹配,或,其具有一个content:或file:URI并且过滤器没有指定URI时,通过测试的URI部分。换言之,如果过滤器仅列出了数据类型,一个组件将被假定支持content:和file:数据。

4.Activity跳转时的数据传递

Activity之间的数据传递,常见的有4中,Intent传递简单数据,Bundle传递数据包,传递值对象,获取Activity的返回参数。

1. Intent传递简单数据

Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("maleName", maleName);
intent.putExtra("femaleName", femaleName);
startActivity(intent);
Intent intent = getIntent();
String maleName = intent.getStringExtra("maleName");
String femaleName = intent.getStringExtra("femaleName");

2. Bundle传递数据包
MainActivity:

Intent intent = new Intent(MainActivity.this,OtherActivity.class);

 Bundle bundle = new Bundle();

 bundle.putString("name","MirGao");
 bundle.putString("age","24");

 intent.putExtras(bundle);
 startActivity(intent);

OtherActivity:

Intent intent = getIntent();
Bundle b = intent.getExtras();
tv.setText(b.getString("name") + "  " + b.getString("age"));

3.传递值对象

所谓的值对象,就是我们通常在项目中自定义的,有数据类型的javabean。
那我们就先自定义一个数据类型。

public class UserBean implements Serializable{
        
            private String name;
            private String age;
        
            public UserBean(String name, String age) {
                this.name = name;
                this.age = age;
            }
        
        
            public void setName(String name) {
                this.name = name;
            }
        
            public void setAge(String age) {
                this.age = age;
            }
        
            public String getName() {
                return name;
            }
        
            public String getAge() {
                return age;
            }
        }

在这里我们定义了两个都是String类型的参数,并且实现了Serializable接口。使我们的自定义数据类型进行数据序列化。

在MainActivity中直接进行自定义对象的传递,并赋予两个参数:

Intent intent = new Intent(MainActivity.this,OtherActivity.class);
intent.putExtra("UserBean",new UserBean("MirGao","24"));
startActivity(intent);

OtherActivity中获取值对象数据:

UserBean userBean ;
userBean = (UserBean) getIntent().getSerializableExtra("UserBean");
tv.setText(userBean.getName() + "  " + userBean.getAge());

使用UserBean对象获取序列化后的对象并进行强制转换,并通过获取的对象,进行操作。使用序列化很简单,方便的可以进行复杂,大量数据的传递。但是,Serializable与Parcelable相比而言 效率比较低 ,所以Android平台又给我们提供了Parcelable,他是一个专门针对移动工具上数据序列化的接口,那么,下面我们就来进行学习。

public class UserBean implements Parcelable{

    private String name;
    private String age;

    public UserBean(String name, String age) {
        this.name = name;
        this.age = age;
    }

    protected UserBean(Parcel in) {
        name = in.readString();
        age = in.readString();
    }

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

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

    public String getName() {
        return name;
    }

    public String getAge() {
        return age;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(getName());
        dest.writeString(getAge());
    }

    public static final Creator<UserBean> CREATOR = new Creator<UserBean>() {
        @Override
        public UserBean createFromParcel(Parcel in) {
            return new UserBean(in.readString(),in.readString());
        }

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

我们实现了Parcelable接口,并且实现了3个方法,writeToParcel这个方法中是需要我们去手动读取的,我们把我们要传递的变量保存,以供其他的成员变量或者组件使用。

Other中获取数据:

UserBean userBean = getIntent().getParcelableExtra("UserBean");
tv.setText(userBean.getName() + "  " + userBean.getAge());

总结:Serializable和Parcelable都是序列化接口,因为Serializable简单,方便,Android系统对他有自动完成序列化的操作,所以速度是比较慢的。而Parcelable中都是手动去添加数据类型,读取数据,Android系统并没有给我们提供序列化机制,所以Parcelable的数据是相对复杂的,但是速度是比较快的。所以在使用时,注意优缺点选择。

5.启动Activity并获取返回值

从A界面打开B界面, B界面关闭的时候,返回一个数据给A界面:

//第一个参数为请求码,即调用startActivityForResult()传递过去的值
//第二个参数为结果码,可以根据业务需求自己编号,结果码用于标识返回数据来自哪个新Activity
startActivityForResult(intent, 0);
Intent data = new Intent();
data.putExtra("phone", phone);
//设置一个结果数据,数据会返回给调用者
setResult(0, data);
finish();//关闭掉当前的activity,才会返回数据
//通过data获取返回的数据
onActivityResult(int requestCode, int resultCode, Intent data) 

5. Activity的生命周期

一张图看清Activity的生命周期图:

Activity的生命周期

6.Activity数据的保存

场景:当Activity由于系统配置等发生改变,会导致Activity被杀死而重新创建。即会调用onDestroy销毁Activity,再重新onCreate开启新Activity,系统通过调用onSaveInstanceState和onRestoreInstanceState分别保存和恢复视图(View)状态。

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//这里,当Acivity第一次被创建的时候为空
//所以我们需要判断一下
if( savedInstanceState != null ){
savedInstanceState.getString("anAnt");
}
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);

outState.putString("anAnt","Android");

}

onSaveInstanceState

onSaveInstanceState在onStop之前调用,但不一定在onPause之前或者之后执行。onRestoreInstanceState在onStart之后调用。需要注意的是,onSaveInstanceState方法只会在Activity被异常终止,在Activity即将被销毁且有机会重新显示的情况下才会调用。具体有以下几种情形:

1、当用户按下HOME键时。
这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则。

2、长按HOME键,选择运行其他的程序时。

3、按下电源按键(关闭屏幕显示)时。

4、从activity A中启动一个新的activity时。

5、屏幕方向切换时,例如从竖屏切换到横屏时。(如果不指定configchange属性) 在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState一定会被执行。

总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。此外,由于默认的onSaveInstanceState()方法的实现帮助UI存储它的状态,所以如果你需要覆盖这个方法去存储额外的状态信息时,你应该在执行任何代码之前都调用父类的onSaveInstanceState()方法(super.onSaveInstanceState())。 既然有现成的可用,那么我们到底还要不要自己实现onSaveInstanceState()?这得看情况了,如果你自己的派生类中有变量影响到UI,或你程序的行为,当然就要把这个变量也保存了,那么就需要自己实现,否则就不需要。

onRestoreInstanceState

onRestoreInstanceState方法在activity确定被销毁以后,重建activity时调用,注意是确定activity被销毁,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行。

另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原。 还有onRestoreInstanceState在onstart之后执行。

至于这两个函数的使用,给出示范代码(留意自定义代码在调用super的前或后):

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);

boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}

7. Activity的启动模式

一个应用一般包含很多Activity,它们按照各自打开的顺序排列在返回栈(Back Stack)中,这些Activity统称为Task。返回栈中的Activity永远不会重新排列,遵循先进后出的原则。

通过在AndroidManifest.xml配置Activity的启动模式。

<activity
    android:name=".aidldemo.BindingActivity"
    android:launchMode="standard"
    ... />

在代码中向Intent添加相应标志。

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

8.结束语

以上就是我在学习的过程当中遇到的关于Activity的有关问题总结, 个人眼界有限, 可能还不是很全面, 我会在以后的学习过程当中不断补充。纯粹为了自己温故而知新,不断的学习。┭┮﹏┭┮

上一篇下一篇

猜你喜欢

热点阅读