从今天开始,将不定时给大家更新“带你从理论到代码学习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>
代码很简单,很容易理解,最后运行代码,成功跳转。
扫一扫 关注我的公众号
感兴趣获取源码就关注公众号吧!
欢迎大家来关注,一起学习安卓!
来源:CSDN
作者:Pingred_hjh
链接:https://blog.csdn.net/qq_39867049/article/details/103469554