带你从理论到代码学习Activity

时光总嘲笑我的痴心妄想 提交于 2019-12-10 11:29:35

    从今天开始,将不定时给大家更新“带你从理论到代码学习XX”系列,顾名思义,就是对安卓开发的某块点进行讲解,从它主要的理论知识到最后使用它开发一个小实例来讲解,慢慢循序渐进,闯关下去。废话不多说,马上进入主题。

    

1. Activity的理论知识

作为经常打交到的组件之一,xml写完布局,然后就在Activity里进行逻辑处理,虽然简单,但或者这部分理论知识(生命周期、启动模式和显式/隐式启动Activity)能让你有“原来是这样”的豁然开朗之感,也可能对你面试有帮助。

 

生命周期

Activity的生命周期要说的其实就是三点:正常情况下的生命周期,异常情况下的生命周期,还有它的一个各个生命周期方法的流程图说明。

(1)正常的生命周期

    正常情况下的生命周期没什么好说的,就是:onCreate()--onStart()--onResume()--onPause()--onStop()--onDestroy()

 

(2)异常的生命周期

    首先明确,什么才是异常的情况,那就是当一些资源相关的系统配置发生改变(比如旋转屏幕、调出打字键盘等)或者系统内存不足导致优先级很低的Activity被关闭,这两种情况的发生都会让Activity处于异常的生命周期。

    异常情况下的生命周期如下图所示:

    相比正常的生命周期,出现了两个不一样的方法,onSaveInstanceState()和onRestoreInstance()。onSaveInstanceState()只在异常终止时执行,在onStop()前调用,保存当前Activity的状态(视图结构,比如TextView的名字内容和选中位置等)。而onRestoreInstance()则是在Activity重新创建时进行恢复这些状态数据,在onStart()后调用。onSaveInstanceState()和onRestoreInstance()用法如下:

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

        /**
         * 也可以在onCreate()方法里进行恢复数据,
         * 但要先判断bundle是否为空
         */
        if (savedInstanceState != null) {
            String data = savedInstanceState.getString("data");
            Log.d(TAG, "onCreate: " + data);
        }

    }

    /**
     * 保存数据
     * @param outState
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("data", "数据");

    }

    /**
     * 还是建议调用onRestoreInstanceState来恢复比较好
     * @param savedInstanceState
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String data = savedInstanceState.getString("data");
        Log.d(TAG, "onRestoreInstanceState: " + data);
    }

    那么,如果想要在异常情况下不让Activity重新创建的话,可以设置android:configChanges属性,它的值跟含义如图所示:

    至于说到的Activity的优先级则是:前台Activity(正在和用户进行交互的Activity),优先级是最高的;可见但非前台Activity(运行中的ActivityA弹出对话框形式的ActivityB,致使当前ActivityA可见但不能与用户交互),优先级次之;后台Activity则是优先级最低的。

    

(3)生命周期的各个方法之间的切换

    直接上图:

   

相信已经很熟悉了,不用我多说了,还有不明白的自行去搜索解决吧。

 

启动模式

Activity的启动模式也是老生常谈了,standard(标准模式)、singleTop(栈顶复用模式)、singleTask(栈内复用模式)和singleInstance(单实例模式)。

 

(1)这里说说singleTask,因为它还与taskAffinity属性密切相关。taskAffinity属性相当于一个栈名,singleTask模式下活动被启动时,最先开始有一个任务栈匹配的过程,就是先根据taskAffinity值来找到该活动需要的任务栈,如果有该栈,则直接使用,没有则创建一个新的栈。接着在该栈中再来寻找是否存在活动实例。如果不指定taskAffinity值,则默认为taskAffinity值为包名。

 

(2)指定启动模式有两种方式:

    intent.addFlags(intent, Flags标志位)或者android:lanuchMde="启动模式"。使用addFlags的方式优先级比xml的高,但常用的方式还是xml的设置方式。常用的标志位有:

     

启动Activity

(1)显示启动

    非常简单,需明确指定启动对象的组件信息:

    intent.setClass(this,XXX.class)

 

(2)隐式启动

    隐式启动稍微复杂一点,需要Intent匹配目标组件的IntentFilter中设置的过滤信息(action、category和data)。

    action:Intent中的action必须存在,且必须和IntentFilter中的其中一个action相同;

 

    category:Intent中可不设置category,但如果设置了,不管有几个,每个都要与IntentFilter中的相同;为什么Intent不设置category也可以成功启动,是因为系统在调用startActivity()或者startActivityForResult时默认为Intent加上“android.intent.category.DEFAULT”这个category,所以你的IntentFilter里当然也要设置好DEFAULT这个category。

 

    data:Intent的data必须和IntentFilter中的某一个data相同。data由mineType和URI两部分组成。mimeType表示媒体类型,比如:image/*、audio/*、video/*等等。URI包含多个数据,它的结构如下:

    <scheme>://<host>:<post>/[<path>|<pathPrefix>|<pathPattern>]

     

    scheme:URI模式,比如http、file和content,如果没有设置scheme,URI无效;

    host:URI主机名,如果host没指定,URI无效;

    port:URI端口号;

    path:完整路径信息;

    pathPrefix:路径的前缀信息;

    pathPattern:完整路径信息,但它里面可包含"*",表示0个或多个任意字符;

 

    IntentFilter的设置如下:

    

 注意:

    隐式启动可能会因匹配不到合适的Activity而报错,所以要提前判断是否存在相匹配的Activity,方法有:PackageManager.resolveActivity()与PackageManager.queryIntentActivity()以及Intent.resolveActivity()。

    设置data时,IntentFilter中如果没有定义URI,则默认为content或者file,所以Intent要设置conten或者file:intent.setDataAndType(Uri.parse("file://abc"),"image/png")。

这样Activity的理论知识就讲完了,理论懂了就得开始写代码了,这才是重中之重。

 

实现一个登录页面且跳转到主页面

登录页LoginActivity.java代码:

public class LoginActivity extends AppCompatActivity {

    private LinearLayout loginBtn;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        loginBtn = findViewById(R.id.login_btn);
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.activity1");
                intent.addCategory("com.example.activity1");
                intent.addCategory("android.intent.category.DEFAULT");//可加可不加

                startActivity(intent);
            }
        });
    }
}

 

可以看到在Intent中设置了action,category和data,其中DEFAULT的category不设置也是可以的。

登录页布局文件activity_login.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    android:background="#ffeeeeee"
    android:orientation="vertical"
    tools:context=".LoginActivity">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <ImageView
        android:id="@+id/logo_iv"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:src="@drawable/movie"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center_horizontal" />

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/login_circle_layout"
        android:layout_marginTop="20dp">

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/user_iv"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:src="@drawable/user"
                android:layout_margin="5dp"
                android:layout_gravity="center_vertical" />

            <EditText
                android:id="@+id/et_user"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@null"
                android:layout_gravity="center_vertical"
                android:hint="手机号/邮箱/用户名" />
        </LinearLayout>

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#ffeeeeee"/>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/pwd_iv"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:src="@drawable/pwd"
                android:layout_margin="5dp" />

            <EditText
                android:id="@+id/et_pwd"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:inputType="textPassword"
                android:ems="10"
                android:layout_weight="1"
                android:background="@null"
                android:layout_gravity="center_vertical"
                android:hint="登录密码" />
        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/login_btn"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/login_btn_press"
        android:layout_marginTop="20dp">

        <TextView
            android:id="@+id/tv_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="登录"
            android:layout_gravity="center_horizontal"
            android:layout_margin="10dp"
            android:textSize="18dp"
            />
    </LinearLayout>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="6">
    </LinearLayout>

</LinearLayout>

    这基本上是写一个登录页面的套路了,效果如下图:

    最后就是AndroidManifest.xml文件,因为我们是要LoginActivity跳转到MainActivity,所以MainActivity的IntentFilter要跟LoginActivity里设置的Intent相匹配:

	<activity android:name=".MainActivity">

           <intent-filter>
               <action android:name="com.example.activity1"/>
               <category android:name="com.example.activity1"/>
               <category android:name="android.intent.category.DEFAULT" />

           </intent-filter>
    </activity>

 代码很简单,很容易理解,最后运行代码,成功跳转。

 

 

                                                                                  扫一扫  关注我的公众号

                                                                           感兴趣获取源码就关注公众号吧!

                                                                           欢迎大家来关注,一起学习安卓!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!