欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

荐 《第一行代码》阅读笔记(四)——探究活动

程序员文章站 2022-06-23 22:30:03
活动是什么?——第一行代码活动( Activity )是最容易吸引用户的地方,它是一种可以包含用户界面的组件,主要用于和用户进行交互。一个应用程序中可以包含零个或多个活动,但不包含任何活动的应用程序很少见,谁也不想让自己的应用永远无法被用户看到吧?其实在上一章中,你已经和活动打过交道了,并且对活动也有了初步的认识。不过上一章我们的重点是创建你的第一个Android项目,对活动的介绍并不多,在本章中我将对活动进行详细的介绍。详细介绍活动书中花费了一些篇幅来描述如何自己创建活动,其中包括Androi...

活动是什么?

——第一行代码
活动( 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.销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。

像这些专有名词,笔者一般都是保留书上的描述,因为书本中的语言一般都比较精准。然后再说一说笔者自己的理解。

  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的结果

  1. 打开程序
    按照顺序显示onCreate、onStart和onResume
  2. 点击Normal按钮
    按照顺序显示onPause和onStop
  3. 返回
    按照顺序显示onRestart、onStart和onResume
  4. 点击Dialog按钮
    按照顺序显示onPause
  5. 返回
    按照顺序显示onResume
  6. 退出
    按照顺序显示onPause、onStop和onDestroy

简单介绍一下笔者对他们的理解

  1. onCreate():只会在活动第一次被创建的时候调用,应该完成一些初始化的功能。主要用于加载布局、绑定事件等。
  2. onStart():这个方法主是在界面由非打开状态转变成打开状态时调用,即由停止状态转化成运行状态时,例如刚打开或者从别的页面返回时。相比于onResume()更倾向于眼睛能看见。
  3. onResume():准备启动或者恢复时这个活动的时候调用,即由暂停状态转化成运行状态时。特点就是此时的活动一定处于运行状态。
  4. onPause():这个方法在系统准备去启动或者恢复另一个活动的时候调用。也就是正准备关闭,或者切换的时候。由运行状态转化成暂停状态时使用。
  5. onStop():这个方法在活动完全不可见的时候调用。如果启动的新活动是一个对话框式的活动,那么 onPause()方法会得到执行,而 onStop()方法并不会执行。onPause()onStop()的区别和onStart()onResume()区别相似。由运行状态转化成停止状态时使用。
  6. onDestroy():这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。简单理解就是退出。
  7. 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属性来选择启动模式。

  1. standard
    默认的启动方式,不会去判断栈里面是否有重复,所以一个活动可以重复打开。
  2. singleTop
    对默认模式进行了一个优化,当活动正在栈顶的时候,如果再打开就不会重复实例化,打开多少个也都是栈顶的之前的活动。但是当这个活动不在栈顶,还是可以重复打开的。
  3. singleTask
    之前不是解释过多个任务在一起的栈叫Task么,所有很好理解,就是整个任务只有一个。这种启动模式很多好的解决了之前的多个实例化的问题。但需要实例的活动已经存在在栈中的时候,就没必要新建了,而是原来已经存在的直接出栈。
  4. singleInstance
    这个比较复杂,就是创新的使用了一个返回栈的概念。书上的例子笔者确实看明白了,但是作者说的共享活动和这个有什么关系笔者还是没想通。所以还是在以后用到的时候再详细了解吧。

本文地址:https://blog.csdn.net/SafeVidulInfo/article/details/107194778