荐 《第一行代码》阅读笔记(四)——探究活动
活动是什么?
——第一行代码
活动( Activity )是最容易吸引用户的地方,它是一种可以包含用户界面的组件,主要用于和用户进行交互。一个应用程序中可以包含零个或多个活动,但不包含任何活动的应用程序很少见,谁也不想让自己的应用永远无法被用户看到吧?其实在上一章中,你已经和活动打过交道了,并且对活动也有了初步的认识。不过上一章我们的重点是创建你的第一个Android项目,对活动的介绍并不多,在本章中我将对活动进行详细的介绍。
详细介绍活动
书中花费了一些篇幅来描述如何自己创建活动,其中包括AndroidManifest.xml的活动编辑、新建布局和将布局和活动联系起来,有兴趣可以看看。在开发中一般都会自动生成,但是理解这一过程可以有效的帮助我们理解Android开发。
setContentView()
——第一行代码
可以看到,这里调用了setContentView() 方法来给当前的活动加载一个布局,而在setContentView()方法中,我们一般都会传人一个布局文件的id。在第1章介绍项目资源的时候我曾提到过,项目中添加的任何资源都会在R文件中生成一个相应的资源id,因此我们刚才创建的first_ layout. xml布局的id现在应该是已经添加到R文件中了。在代码中去引用布局文件的方法你也已经学过了,只需要调用R. layout. first_ layout 就可以得到first_ layout.xml布局的id,然后将这个值传人setContentView( )方法即可。
声明主活动
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Toast
Toast.makeText(MainActivity.this, btnHome.getText().toString(), Toast.LENGTH_SHORT).show();
Toast类是一个小的提示框,他的方法makeText有三个参数,就像上面展示的一样。第一个参数是一个context,而活动本身就是一个上下文,所以直接传入MainActivity。第二个参数是一个string,就是你想要输出的语句。第三个参数是Toast类中的一个常量,short表示展示时间短,long表示时间长。
看到这里不知道其他小伙伴有没有和笔者一样的疑惑,因为在传入context的时候,有的时候直接传入MainActivity,有的时候传入this,有时候传如MainActivity.this,有的时候传入getContext(),这些到底有什么区别呢?
上网浏览后发现一篇文章,讲的很好,在这里分享给大家MainActivity.this 和this,至于其他的区别,笔者目前还没有办法解决。
Menu
第一步:新建main菜单
首先在res目录下新建一个menu 文件夹,右击res目录→New→Directory,输入文件夹名menu,点击OK。接着在这个文件夹下再新建一个名叫main的菜单文件,右击menu文件夹→New→Menu resource file,输入文件夹名main,点击OK。
第二步:编辑main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_item"
android:title="Add"/>
<item
android:id="@+id/remove_item"
android:title="Remove"/>
</menu>
第三步:修改MainActivity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
——第一行代码
通过getMenuInflater( )方法能够得到MenuInflater对象,再调用它的inflate()方法就可以给当前活动创建菜单了。inflate()方法接收两个参数,第一个参数用于指定我们通过哪一个资源文件来创建菜单,这里当然传人R. menu. main。第二个参数用于指定我们的菜单项将添加到哪一个Menu对象当中,这里直接使用onCreateOptionsMenu()方法中传人的menu参数。然后给这个方法返回true,表示允许创建的菜单显示出来,如果返回了false, 创建的菜单将无法显示。
第四步:添加响应事件
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_item:
Toast.makeText(this, "Add", Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this, "Remove", Toast.LENGTH_SHORT).show();
break;
default:
}
return true;
}
——第一行代码
在onOptionsItemSelected()方法中,通过调用item. getItemId()来判断我们点击的是哪一个菜单项,然后给每个菜单项加入自己的逻辑处理,这里我们就活学活用,弹出一个刚刚学会的Toast。
销毁一个活动
核心就是一个finsh(),功能就和返回键一样
Intent
有的知识就不重复记录了,笔者在这里着重说一下自己的感受。先上一段书上的描述
——第一行代码
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送广播等场景,由于服务、广播等概念你暂时还未涉及,那么本章我们的目光无疑就锁定在了启动活动上面。
而我的理解就是页面之间的跳转,因为安卓已经放弃了页面而是使用了活动。开发过网页项目的,笔者认为intent可以理解为网页之间的转发和重定向。
而Intent又分为两种——显式Intent和隐式Intent。
显式Intent
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
这是Intent的一个构造函数,第一个参数就是当前活动的上下文环境, 第二个参数就是需要跳转的类的class文件。然后再使用startActivity()用来启动活动,参数就是intent。
隐式Intent
不知道大家还记不记得之前讲过的AndroidManifest.xml文件中的Intent-filter标签,里面有两个属性,就是action和category,在实例化Intent的时候可以通过传入action和category来实现和页面的匹配。书上讲的很清楚了,照着做就行。而我感觉这个隐式的作用在于可以把相似的Activity标记同样的action,然后在实现调整的时候直接跳转这个action,然后再根据category来具体决定跳转到那个具体的活动。
其他用途
可以跳转其他应用,就比如第三方支付这类的。
书中讲解了一些例子,都不是很难,但是笔者在实验过程中出现了一些问题,无法正常运行,不知道怎么回事。但是感觉又不是很重要,所以跳过。
向下一个活动传递数据
第一步:传出
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data = "Hello SecondActivity" ;
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent. putExtra("extra_ data", data);
startActivity(intent);
}
});
主要函数是putExtra(),第一个参数是键,第二个才是需要传递的值。这个很好理解。
第二步:接收
Intent intent = getIntent();
String data = intent.getStringExtra("extra_ data");
Log.d("SecondActivity",data);
——第一行代码
首先可以通过getIntent()方法获取到用于启动SecondActivity的Intent,然后调用getStringExtra()方法, 传入相应的键值,就可以得到传递的数据了。这里由于我们传递的是字符串,所以使用getStringExtra()方 法来获取传递的数据。如果传递的是整型数据,则使用getIntExtra()方法; 如果传递的是布尔型数据,则使用getBooleanExtra()方法,以此类推。
其实也很简单明了,这里主要说一下这个"extra_ data"就像map中的key,而data就像value。
返回数据给上一个活动
第一步:修改传出方法
实现这个功能的关键在于使用startActivityForResult(intent,1);来替代startActivity(intent);
其中1表示请求码,只要唯一即可。
第二步:添加返回数据
在通过startActivityForResult调出的Activity中添加一按钮的点击事件,包括以下代码。
String data = "Hello FirstActivity";
Intent intent = new Intent();
intent. putExtra("return_ data", data);
setResult(RESULT_OK,intent);
finish();
——第一行代码
可以看到,我们还是构建了一个Intent,只不过这个Intent仅仅是用于传递数据而已,它没有指定任何的“意图”。紧接着把要传递的数据存放在Intent中,然后调用了setResult()方法。这个方法非常重要,是专门用于向上一个活动返回数据的。setResult() 方法接收两个参数,第一个参数用于向上一个活动返回处理结果,一般只使用RESULT_ 0K或RESULT_ CANCELED这两个值,第二个参数则把带有数据的Intent 传递回去,然后调用了finish()方法来销毁当前活动。
因为在事件的结尾结束了当前事件,这样会返回调用这个活动的活动,因为是startActivityForResult函数调用的,所有会自动执行onActivityResult函数,如果想要获取数据,就必须重写这个函数。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String returnedData = data.getStringExtra("data_ return");
Log.d("FirstActivity", returnedData);
}
break;
default:
}
}
其实也很好理解,requestCode就是我们startActivityForResult传递的1,用来辨识。resultCode就是setResult传入的值,在调用系统app时返回时RESULT_CANCELED如字面意思代表取消,RESULT_OK代表成功。没什么特别的意思。data就是一个携带值的Intent。
这是通过点击按钮,finish()函数实现的功能。通过back按钮依然可以实现是不过需要重写另一个函数,onBackPressed()
活动的生命周期
返回栈
其实就是一个栈,多个活动会进入一个栈,然后这个栈里面的活动就是一个任务。其实栈的特点就是先进先出,不了解的可以直接搜索栈。
活动状态
——第一行代码
1.运行状态
当一个活动位于返回栈的栈项时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。
2.暂停状态
当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。你可能会觉得既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域,你很快就会在后面看到这种活动。处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。
3.停止状态
当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
4.销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。
像这些专有名词,笔者一般都是保留书上的描述,因为书本中的语言一般都比较精准。然后再说一说笔者自己的理解。
- 运行状态,一般就是在栈顶的活动,其实就是咱们可以看见的最上面的活动。
- 暂停状态,这个其实很好理解,就是一个弹窗等待你确认,然后下面的那个活动就是暂停状态。不过随着安卓版本的更迭,我觉得这个状态的描述应该已经不准确了,因为很多程序都有小窗模式,而底下的那个活动仍然可以运动。
- 停止状态,应该就类似于后台的程序吧。
- 销毁状态,就是已经关闭了的活动。
活动的生存期
这里笔者选择和书籍相反的顺序,先制作demo,然后再讲解知识。所以大家请看
活动周期demo
第一步:新建DialogActivity和NomalActivity
第二步:修改布局
activity_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a dialog activity" />
</LinearLayout>
activity_nomal.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a normal activity" />
</LinearLayout>
第三步:修改主活动布局
增加两个按钮
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button_normal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="NormalActivity"
android:textAllCaps="false" />
<Button
android:id="@+id/button_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="DialogActivity"
android:textAllCaps="false" />
</LinearLayout>
第四步:修改主活动类
绑定事件,并重写上面提到的七个函数,然后观察生命周期
package com.firstcode.lifescopedemo;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonNormal = (Button) findViewById(R.id.button_normal);
Button buttonDialog = (Button) findViewById(R.id.button_dialog);
buttonNormal.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent dialogIntent = new Intent(MainActivity.this, NormalActivity.class);
startActivity(dialogIntent);
}
});
buttonDialog.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent normalIntent = new Intent(MainActivity.this, DialogActivity.class);
startActivity(normalIntent);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG,"onStart");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"onPause");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG,"onResume");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG,"onRestart");
}
}
第五步:修改AndroidManifest.xml
这里注意了,如果按书上的来写,会直接闪退。笔者查阅了一下资料,找到了答案。
Android主题设置为@android:style/Theme.Dialog报错解决办法
<activity
android:name=".DialogActivity"
android:theme="@style/Theme.AppCompat.Dialog">
</activity>
首先说下demo的结果
- 打开程序
按照顺序显示onCreate、onStart和onResume - 点击Normal按钮
按照顺序显示onPause和onStop - 返回
按照顺序显示onRestart、onStart和onResume - 点击Dialog按钮
按照顺序显示onPause - 返回
按照顺序显示onResume - 退出
按照顺序显示onPause、onStop和onDestroy
简单介绍一下笔者对他们的理解
-
onCreate()
:只会在活动第一次被创建的时候调用,应该完成一些初始化的功能。主要用于加载布局、绑定事件等。 -
onStart()
:这个方法主是在界面由非打开状态转变成打开状态时调用,即由停止状态转化成运行状态时,例如刚打开或者从别的页面返回时。相比于onResume()
更倾向于眼睛能看见。 -
onResume()
:准备启动或者恢复时这个活动的时候调用,即由暂停状态转化成运行状态时。特点就是此时的活动一定处于运行状态。 -
onPause()
:这个方法在系统准备去启动或者恢复另一个活动的时候调用。也就是正准备关闭,或者切换的时候。由运行状态转化成暂停状态时使用。 -
onStop()
:这个方法在活动完全不可见的时候调用。如果启动的新活动是一个对话框式的活动,那么onPause()
方法会得到执行,而onStop()
方法并不会执行。onPause()
和onStop()
的区别和onStart()
和onResume()
区别相似。由运行状态转化成停止状态时使用。 -
onDestroy()
:这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。简单理解就是退出。 -
onRestart()
:活动被重新启动时调用。笔者的理解就是除了第一次打开,以后没满足onStart()
和onResume()
的条件时,就满足onRestart()
。
以上7个方法中除了onRestart()
方法,其他都是两两相对的,从而又可以将活动分为3种生存期。
完整生存期:【 onCreate()
~ onDestroy()
】之间。从出生到死亡
可见生存期:【 onStart()
~ onStop()
】之间。活动对于用户总是可见的!一般使用onStart进行资源的加载,onStop对部分资源进行释放。
前台生存期:【 onResume()
~ onPause()
】之间。活动总是处于运行状态的。
Bundle
其实onCreate()一直带有一个Bundle参数,不知道是干啥的,现在终于知道了。是用于活动在被回收的情况下仍然可以实现数据的储存。实现这个功能的方法是savedInstanceState()和putString()和getString()。
方法十分简单,在活动中重写savedInstanceState()方法并用putString()亿key-value的方式存入数据。在需要的时候直接通过onCreate()带有的Bundle参数使用getString()获取。
Bundle还可以和Intent的联用
——第一行代码
不知道你有没有察觉,使用Bundle来保存和取出数据是不是有些似曾相识呢?没错!我们在使用Intent 传递数据时也是用的类似的方法。这里跟你提醒一点,Intent 还可以结合Bundle一起用于传递数据,首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标活动之后先从Intent中取出Bundle,再从Bundle中一一取出数据。
具体的代码我就不写了,要学会举一反三哦。
活动的启动模式
启动模式一共有4种,分别是standard、 singleTop、singleTask 和singleInstance,可以在AndroidManifest.xml中通过给标签指定android : launchMode属性来选择启动模式。
- standard
默认的启动方式,不会去判断栈里面是否有重复,所以一个活动可以重复打开。 - singleTop
对默认模式进行了一个优化,当活动正在栈顶的时候,如果再打开就不会重复实例化,打开多少个也都是栈顶的之前的活动。但是当这个活动不在栈顶,还是可以重复打开的。 - singleTask
之前不是解释过多个任务在一起的栈叫Task么,所有很好理解,就是整个任务只有一个。这种启动模式很多好的解决了之前的多个实例化的问题。但需要实例的活动已经存在在栈中的时候,就没必要新建了,而是原来已经存在的直接出栈。 - singleInstance
这个比较复杂,就是创新的使用了一个返回栈的概念。书上的例子笔者确实看明白了,但是作者说的共享活动和这个有什么关系笔者还是没想通。所以还是在以后用到的时候再详细了解吧。
本文地址:https://blog.csdn.net/SafeVidulInfo/article/details/107194778