1、Activity

2023-09-01  本文已影响0人  Leo_JiangZhiHao

1.1 Activity的基本用法

创建布局

<?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/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 1" />

</LinearLayout>

创建Activity并加载布局

public class FirstActivity extends AppCompatActivity {

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

在AndroidManifest.xml中注册

Activity的注册声明要放在<application>标签内,这里是通过<activity>标签来对Activity进行注册的。
在<activity>标签中,我们使用了android::name来指定具体注册哪一个Activity,这里填入的.FirstActivity是com.example.myapplication.FirstActivity的缩写。因为在最外层的<manifest>标签中已经通过package属性制定了程序的包名是com.example.myapplication。
我们程序需要配置主Activity,即程序运行起来的时候首先启动的Activity。配置主Activity的方法是在<activity>标签的内部加入<intent-filter>标签,并在这个标签里添加<action android:name="android.intent.action.MAIN" />、<category android:name="android.intent.category.LAUNCHER" />这两句声明即可。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <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/Theme.MyApplication">
        <activity
            android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

如果你的程序没有声明任何一个Activity作为主Activity,这个程序仍然是可以正常安装的,只是你无法在启动器中看到或者打开这个程序。这种程序一般是作为第三方服务供其他应用在内部进行调用的。

在Activity中使用Toast

public class FirstActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);
        Button button1 = findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(FirstActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

销毁Activity

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        finish();
    }
});

1.2 使用Intent在Activity之间穿梭

使用显式Intent

显式Intent明确指出了想要启动哪一个Activity。
创建SecondActivity,并修改FirstActivity中按钮的点击事件,点击button1即在FirstActivity的基础上打开SecondActivity:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
        startActivity(intent);
    }
});

使用隐式Intent

隐式Intent制定了一系列更为抽象的action和category等信息,系统帮我们找出可以响应这个隐式Intent的Activity去启动。
通过在<activity>标签下配置<intent-filter>的内容,可以指定当前Activity能够响应的action和category。只有action和category中的内容同时匹配Intent中指定的action和category时,这个Activity才能响应该Intent。
修改AndroidManifest.xml中SecondActivity的<activity>标签:

<activity
    android:name=".SecondActivity">
    <intent-filter>
        <action android:name="com.example.myapplication.ACTION_START" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

修改FirstActivity中按钮的点击事件:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent("com.example.myapplication.ACTION_START");
        startActivity(intent);
    }
});

"android.intent.category.DEFAULT"是一种默认的category,在调用startActivity()方法的时候会自动将这个category添加到Intent中。所以点击button1在FirstActivity的基础上成功打开了SecondActivity。
每个Intent中只能指定一个action,但能通过addCategory()方法指定多个category。
修改AndroidManifest.xml中SecondActivity的<activity>标签:

<activity
    android:name=".SecondActivity">
    <intent-filter>
        <action android:name="com.example.myapplication.ACTION_START" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="com.example.myapplication.MY_CATEGORY" />
    </intent-filter>
</activity>

修改FirstActivity中按钮的点击事件:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent("com.example.myapplication.ACTION_START");
        intent.addCategory("com.example.myapplication.MY_CATEGORY");
        startActivity(intent);
    }
});

再次运行,点击button1在FirstActivity的基础上同样成功打开了SecondActivity。

使用隐式Intent启动其他程序的Activity

使用隐式Intent不仅可以启动自己程序内的Activity,还可以启动其他程序的Activity。
修改FirstActivity中按钮的点击事件,点击button1即调用系统的浏览器来打开这个网页:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(Uri.parse("https://www.baidu.com"));
        startActivity(intent);
    }
});

这里我们首先指定了Intent的action是Intent.ACTION_VIEW,其常量值为"android.intent.action.VIEW"。然后通过Uri.parse()方法将一个网址字符串解析成一个Uri对象,再调用Intent的setData()方法将这个Uri对象传递进去。
我们可以在<intent-filter>标签中再配置一个<data>标签,用于更精确地指定当前Activity能够响应的数据。只有<data>标签中指定的内容和Intent中携带的Data完全一致时,当前Activity才能够响应该Intent。
创建ThirdActivity,修改AndroidManifest.xml中ThirdActivity的<activity>标签:

<activity
    android:name=".ThirdActivity">
    <intent-filter tools:ignore="AppLinkUrlError">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="https" />
    </intent-filter>
</activity>

在<data>标签中,通过android:scheme指定了数据的协议必须是https协议。这样子ThirdActivity就和浏览器一样,能够响应一个打开网页的Intent了。
由于Android Studio认为所有能够响应ACTION_VIEW的Activity都应该加上BROWSABLE的category,否则会警告,这里直接在<intent-filter>标签上使用tools:ignore忽略警告。
可以看到,系统自动弹出了一个列表,显示了目前能够响应这个Intent的所有程序。

1.3.1.png

我们还可以指定很多其他协议,比如geo表示显示地理位置、tel表示拨打电话。修改FirstActivity中按钮的点击事件,点击button1即调用系统拨号界面:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(Intent.ACTION_DIAL);
        intent.setData(Uri.parse("tel:10000"));
        startActivity(intent);
    }
});

向下一个Activity传递数据

Intent中提供了一系列的putExtra()方法的重载,可以把我们想要传递的数据暂存在Intent中,在启动另一个Activity后,只需要把这些数据从Intent中取出就可以了。
修改FirstActivity中按钮的点击事件,把字符串传递到SecondActivity中:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        String data = "Hello SecondActivity";
        Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
        intent.putExtra("extra_data", data);
        startActivity(intent);
    }
});

修改SecondActivity,将传递的数据取出:

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        String data = getIntent().getStringExtra("extra_data");
        Log.d("SecondActivity", "extra data is " + data);
    }
}

返回数据给上一个Activity

Activity类中还有一个用于启动Activity的startActivityForResult()方法,它期望在Activity销毁的时候能够返回一个结果给上一个Activity。
修改FirstActivity中按钮的点击事件:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
        startActivityForResult(intent, 1);
    }
});

修改SecondActivity,添加按钮button2,给按钮注册点击事件,并在点击事件中添加返回数据的逻辑:

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Button button2 = findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.putExtra("data_return", "Hello FirstActivity");
                setResult(RESULT_OK, intent);
                finish();
            }
        });
    }
}

在SecondActivity被销毁之后会回调上一个Activity的onActivityResult()方法,因此我们需要再FirstActivity中重写这个方法得到返回的数据:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case 1:
            if (resultCode == RESULT_OK) {
                String dataReturn = "null";
                if (data != null) {
                    dataReturn = data.getStringExtra("data_return");
                }
                Log.d("FirstActivity", "return data is " + dataReturn);
            }
    }
}

onActivityResult()方法带有3个参数:第一个参数requestCode,即在我们启动Activity是传入的请求码;第二个参数resultCode,即我们在返回数据时传入的处理结果;第三个参数data,即携带着返回数据的Intent。

1.3 Activity的生命周期

返回栈

Android是使用任务(Task)来管理Activity的,一个任务就是一组存放在栈里的Activity的集合,这个栈也被称作返回栈(Back Stack)。每当启动一个新的Activity,它会入栈,并处于栈顶的位置。而每当按下Back键或调用finish()方法销毁一个Activity时,处于栈顶的Activity就会出栈,这时前一个入栈的Activity就会重新处于栈顶的位置。系统总是会显示处于栈顶的Activity。

1.3.2.png

Activity的生命周期

1.3.3.png

系统回收Activity

当一个Activity进入停止状态,是有可能被系统回收的。假设应用中有一个Activity A,用户在Activity A的基础上启动了Activity B,Activity A就进入停止状态,这个时候由于系统内存不足,将Activity A回收,然后用户按下 Back 键返回Activity A,这时还是会显示Activity A ,只是不会执行onRestart()方法,而是会执行Activity A的onCreate()方法,因为Activity A 被重新创建了一次。
但是,Activity A是可能存在临时数据和状态的。比如文本输入框中输入的文字。
Activity中提供了onSaveInstanceState()回调方法,它会在Activity被回收之前被调用,因此我们可以通过这个方法来解决活动被回收时需要保存临时数据的场景。
onSaveInstanceState()方法会携带一个Bundle类型参数,Bundle提供了一系列的方法用于保存数据,比如可以使用putString()保存字符串。
在Activity中添加如下代码,保存临时数据:

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    String data = "something you just typed";
    outState.putString("key", data);
}

修改Activity的onCreate()方法,将之前保存的数据取出:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null) {
        String data = savedInstanceState.getString("key");
        Log.d("MainActivity", data);
    }
}

1.4 Activity的启动模式

Activity的启动模式一共有4种,分别是standard、singleTop、singleTask、singleInstance,可以在AndroidManifest.xml中通过给<activity>标签指定android:launchMode属性来选择启动模式。

standard

standard是Activity默认的启动模式,在不进行显式指定的情况下,所有Activity都会自动使用这种启动模式。
在standard模式下,每当启动一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。
对于使用standard模式的Activity,系统不会在乎这个Activity是否已经在返回栈中存在,每次启动都会创建一个该Activity的新实例。
修改FirstActivity中按钮的点击事件,每次点击都会打开FirstActivity:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
        startActivity(intent);
    }
});

FirstActivity的启动模式为默认的standard,返回栈如图:

1.4.1.png

singleTop

当Activity的启动模式指定为singleTop,在启动Activity时如果发现返回栈的栈顶已经是该Activity,则认为可以直接使用它,不会再创建新的Activity实例。
修改AndroidManifest.xm中FirstActivity的启动模式为singleTop,再次运行:每当想要再启动FirstActivity时都会直接使用栈顶的FirstActivity,因此这时的FirstActivity也只会有一个实例,也仅需按一次Back键就可以退出程序。
修改FirstActivity中按钮的点击事件,点击会打开SecondActivity,返回栈如图:

1.4.2.png

singleTask

当Activity的启动模式指定为singleTask,每次启动该Activity时,系统首先会在返回栈中检查是否存在该Activity的实例,如果发现已经存在则直接使用该实例,并把在这个Activity之上的所有其他Activity统统出栈,如果没有发现就会创建一个新的Activity实例。
修改AndroidManifest.xm中FirstActivity的启动模式为singleTask,再次运行,返回栈如图:

1.4.3.png

singleInstance

当Activity的启动模式指定为singleInstance,会启用一个新的返回栈来管理这个Activity。
假设我们的程序中有一个Activity是允许其他程序调用的,如果想实现其他程序和我们的程序可以共享这个Activity的实例,就可以使用singleInstance模式。
在这种模式下,会有一个单独的返回栈来管理这个Activity,不管是哪个应用程序来访问这个Activity,都共用的同一个返回栈,也就解决了共享Activity实例的问题。
举个例子,FirstActivity(默认standard模式)点击按钮打开SecondActivity(singleInstance模式),SecondActivity点击按钮打开ThirdActivity(默认standard模式),返回栈如图:

1.4.4.png

因为FirstActivity和ThirdActivity是存放在同一个栈里的,所以在ThirdActivity的界面按下Back键,ThirdActivity才会出栈,这时FirstActivity就到了栈顶,因此也就出现了从ThirdActivity直接返回到FirstActivity的现象。在FirstActivity界面再次按下Back键,这时当前的栈已经空了,于是就显示了另一个栈的栈顶活动,即SecondActivity。最后再次按下Back键,这时所有的栈都已经空了,也就很自然退出了程序。

上一篇 下一篇

猜你喜欢

热点阅读