Android基础四大组件之一Activity总结

Activity生命周期

onCreate、onStart、onResume、onPause、onStop、(onRestart())、onDestroy
在这里插入图片描述

Activity启动模式

Activity的管理方式 = 任务栈
任务栈 采用的结构 = “后进先出” 的栈结构
每按一次Back键,就有一个Activity出栈

4种启动模式,标准模式(Standard)、栈顶复用(SingleTop)、栈内复用(SingleTask)、单例(SingleInstance)

  • 4种启动模式特点

在这里插入图片描述

在这里插入图片描述

  • 4种启动模式区别

在这里插入图片描述

  • 启动模式有2种设置方式

AndroidMainifest设置、通过Intent设置标志位

  • 在AndroidMainifest设置

在AndroidMainifest的Activity配置进行设置

<activity=
android:launchMode="启动模式"
//属性
//standard:标准模式
//singleTop:栈顶复用模式
//singleTask:栈内复用模式
//singleInstance:单例模式
//如不设置,Activity的启动模式默认为**标准模式(standard)**
</activity>
  • 通过Intent设置标志位
Intent inten = new Intent (ActivityA.this,ActivityB.class);
intent,addFlags(Intent,FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

标记位属性

标记位属性 含义
FLAG_ACTIVITY_SINGLE_TOP 指定启动模式为栈顶复用模式(SingleTop)
FLAG_ACTIVITY_NEW_TASK 指定启动模式为栈内复用模式(SingleTask)
FLAG_ACTIVITY_CLEAR_TOP 所有位于其上层的Activity都要移除,SingleTask模式默认具有此标记效果
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 具有该标记的Activity不会出现在历史Activity的列表中,即无法通过历史列表回到该Activity上
二者设置的区别
优先级不同
Intent设置方式的优先级 > Manifest设置方式,即 以前者为准
限定范围不同
Manifest设置方式无法设定 FLAG_ACTIVITY_CLEAR_TOP;Intent设置方式 无法设置单例模式(SingleInstance)

  • 4种启动模式应用场景

singleTop适用点击通知栏通知启动的页面。
1)某个应用通知来了几十条,那么使用此模式能避免每次打开一个界面。
2)很多电商类的App,商品列表点击进去是商品详情页,商品详情页下面又有商品列表点进去还是商品详情页。

singleTask适用App的入口页面。
1)例子:浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。
2)例子:购物App的主界面,在陆续打开商品搜索界面、商品详细界面、订单界面、付款成功界面后,在付款成功界面一键返回主界面。

singleInstance适合需要保持活动独立性的场景,‌这种启动模式确保在整个手机操作系统中,‌只有一个实例存在,‌并且保证不再有其他Activity实例进入该Activity的任务栈中。‌它适用于以下具体场景:‌
如 经常调用的拨打电话、系统通讯录、系统Launcher、锁屏键、来电显示等系统应用。
缓存页面:‌对于播放视频或音频的页面,‌使用singleInstance模式可以确保在按返回键后继续播放,‌同时通过全局悬浮窗随时打开,‌且打开时不会总是新建页面,‌保持页面的独立性。‌
与程序分离的页面:‌例如闹铃提醒功能,‌将闹铃提醒与闹铃设置分离,‌使得功能更加独立和清晰。‌
独立的任务栈:‌需要在不同的应用程序或系统中启动该活动,‌并保持独立的任务栈,‌适用于需要在活动启动时始终显示在最顶层,‌不受其他活动影响的情况。‌
如 闹铃提醒,将闹铃提醒与闹铃设置分离。
singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。

Activity启动方式

启动Activity的方式主要是:显式Intent & 隐式Intent

  • 显式Intent(3种)
// 1. 使用构造函数 传入 Class对象
 Intent intent = new Intent(this, SecondActivity.class); 
 startActivity(intent);

// 2. 使用 setClassName()传入 包名+类名 / 包Context+类名
 Intent intent = new Intent(); 
 // 方式1:包名+类名
 // 参数1 = 包名称
 // 参数2 = 要启动的类的全限定名称 
 intent.setClassName("com.hc.hctest", "com.hc.hctest.SecondActivity"); 

 // 方式2:包Context+类名
 // 参数1 = 包Context,可直接传入Activity
 // 参数2 = 要启动的类的全限定名称 
 intent.setClassName(this, "com.hc.hctest.SecondActivity"); 
 startActivity(intent);

// 3. 通过ComponentName()传入 包名 & 类全名
 Intent intent = new Intent(); 
 // 参数1 = 包名称
 // 参数2 = 要启动的类的全限定名称 
 ComponentName cn = new ComponentName("com.hc.hctest", "com.hc.hctest.SecondActivity"); 
 intent.setComponent(cn); 
 startActivity(intent);
  • 隐式Intent
// 通过Category、Action设置
Intent intent = new Intent(); 
intent.addCategory(Intent.CATEGORY_DEFAULT); 
intent.addCategory("com.hc.second"); 
intent.setAction("com.hc.action"); 
startActivity(intent);

启动App过程(从系统角度分析)

https://blog.51cto.com/u_16213595/9838739

普通Activity启动过程(从系统角度分析)

Launcher启动根Activity启动过程(从系统角度分析)

在这里插入图片描述

具体流程:
当请求启动Activity时:
Launcher进程通过Binder驱动向ActivityManagerService类发起startActivity请求;
ActivityManagerService类接收到请求后,向ActivityStack类发送启动Activity的请求;
ActivityStack类记录需启动的Activity的信息 & 调整Activity栈 将其置于栈顶、通过 Binder 驱动 将 Activity 的启动信息传递到ApplicationThread线程中(即Binder线程)
ApplicationThread线程通过Handler将Activity的启动信息发送到主线程ActivityThread
主线程ActivityThread类接收到该信息 & 请求后,通过ClassLoader机制加载相应的Activity类,最终调用Activity的onCreate(),最后 启动完毕

Activity卡顿原因

  • 总结有以下几点:

1、内存泄漏
原因:内存泄漏导致内存占用高,导致JVM频繁触发GC

2、加载大数据
原因:内存泄漏导致内存占用高,导致JVM频繁触发GC

3、UI线程做了耗时逻辑
原因:主线程被阻塞
解决方案:另起工作线程,执行耗时操作

4、UI视图过度绘制
原因:绘制时间过长
解决方案:缩短UI视图绘制时间

  • 加速启动Activity方式总结
    在这里插入图片描述

Activity中提供3种工作线程访问UI线程方法

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

Fragment生命周期

  • 生命流程图

在这里插入图片描述

  • 详细解析
    在这里插入图片描述

  • 常见场景的生命周期流程
    在这里插入图片描述

  • 与Activity生命周期对比
    在这里插入图片描述

保存 & 恢复Activity 状态缓存

Activity保存临时数据 & 状态onSaveInstanceState()

  • 核心方法
    onSaveInstanceState()

  • 调用时机
    当系统 未经你许可 时,可能 销毁了你的Activity,则会被系统调用 。

特别说明:

“可能“ 仅表达一种可能性,而不是确实销毁,下面会继续讲解
若是 被用户主动销毁(如 用户按Back键),则不会调用
肯定在 调用onStop()前被调用,但不保证在onPause()前 / 后
3. 具体调用场景
假定为Activity A显示在当前Activity栈的最上层时,以下情况会执行onSaveInstanceState()

注:系统不知道你切换到其他地方后要运行多少其他的程序,自然也不知Activity A是否会被销毁,故系统会调用onSaveInstanceState(),下面所说的所有情况该遵循这原则

在这里插入图片描述
4. 使用说明

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {

// 通过Bundle参数以键值对的方式进行数据的存储
// 数据恢复:onRestoreInstanceState() & onCreate()
// 上述二者都有一个Bundle类型的参数用于恢复数据
        savedInstanceState.putBoolean("MyBoolean", true);
        savedInstanceState.putDouble("myDouble", 1.9);
        savedInstanceState.putInt("MyInt", 1);
        savedInstanceState.putString("MyString", "Welcome back to Android");
        // ...
        super.onSaveInstanceState(savedInstanceState);
}

补充说明:

布局每1个View默认实现:onSaveInstanceState(),即UI的任何改变都会自动的存储和在activity重新创建的时候自动的恢复(只有在为该UI提供了唯一ID后才起作用)

若需复写该方法从而存储额外的状态信息时,应先调用父类的onSaveInstanceState()(因为默认的onSaveInstanceState()帮助UI存储它的状态)

只使用该方法记录Activity的瞬间状态(UI的状态),而不是去存储持久化数据,因为onSaveInstanceState()调用时机不确定性;可使用 onPause()存储 持久化数据

Activity恢复临时数据 & 状态onRestoreInstanceState

  1. 核心方法
    onRestoreInstanceState()

  2. 调用时机
    当系统“未经你许可”时,确实销毁了你的Activity,则重新启动时会被系统调用

特别说明:

与onSaveInstanceState()区别:此处是 “确实销毁”后才调用
若是 被用户主动销毁(如 用户按Back键),则不会调用
肯定在调用 onStop()前被调用,但不保证在onPause()前 / 后
3. 具体调用场景
若 异常关闭了Activity,即调用了onSaveInstanceState() & 下次启动时会调用onRestoreInstanceState()

注:此时结合Activity的生命周期的调用顺序是:

onCreate()
onStart()
onRestoreInstanceState()
onResume()

  1. 使用示例
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
        double myDouble = savedInstanceState.getDouble("myDouble");
        int myInt = savedInstanceState.getInt("MyInt");
        String myString = savedInstanceState.getString("MyString");
}

onSaveInstanceState()、onRestoreInstanceState()不一定 成对被调用
如:当正在显示Activity A时,用户按下HOME键回到主界面,然后用户紧接着又返回到Activity A,此时Activity A一般不会因为内存的原因被系统销毁,故Activity A的onRestoreInstanceState()不会被执行

onSaveInstanceState的bundle参数会传递到onCreate方法中,可选择在onCreate()中做数据还原

Activityg&Fragment关系与通信

Activity传递数据到 Fragment,通过Bundle方式

具体Demo步骤如下:

步骤1:Activity的布局文件
activcity_2_fragment.xml

步骤2:设置 Fragment的布局文件
fragment.xml

步骤3:设置Activity的类文件
Activity2Fragment

public class Activity2Fragment extends AppCompatActivity {

    TextView text;

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

        text = (TextView) findViewById(R.id.text);

        // 步骤1:获取FragmentManager
        FragmentManager fragmentManager = getFragmentManager();

        // 步骤2:获取FragmentTransaction
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        // 步骤3:创建需要添加的Fragment 
        final mFragment fragment = new mFragment();

        // 步骤4:创建Bundle对象
        // 作用:存储数据,并传递到Fragment中
        Bundle bundle = new Bundle();

        // 步骤5:往bundle中添加数据
        bundle.putString("message", "I love Google");

        // 步骤6:把数据设置到Fragment中
        fragment.setArguments(bundle);

        // 步骤7:动态添加fragment
        // 即将创建的fragment添加到Activity布局文件中定义的占位符中(FrameLayout)
        fragmentTransaction.add(R.id.fragment_container, fragment);
        fragmentTransaction.commit();


    }
}

步骤4:设置Fragment的类文件
mFragment.java

public class mFragment extends Fragment {
    Button button;
    TextView text;
    Bundle bundle;
    String message;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View contentView = inflater.inflate(R.layout.fragment, container, false);
        // 设置布局文件

        button = (Button) contentView.findViewById(R.id.button);
        text = (TextView) contentView.findViewById(R.id.text);

        // 步骤1:通过getArgments()获取从Activity传过来的全部值
        bundle = this.getArguments();

        // 步骤2:获取某一值
        message = bundle.getString("message");

        // 步骤3:设置按钮,将设置的值显示出来
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 显示传递过来的值
                text.setText(message);

            }
        });

        return contentView;
    }
}

至此,Activity 传递数据到 Fragment 讲解完毕。

Fragment 传递数据到 Activity,通过接口回调

具体Demo
步骤1:在Activity的布局文件定义1占位符(FrameLayout)
activity_main.xml

步骤2:设置Fragment的布局文件
fragment.xml

步骤3:设置回调接口
该接口用于用于Activity与Fragment通信
ICallBack.java

public interface ICallBack {
    void get_message_from_Fragment(String string);

}

步骤4:设置Fragment的类文件
mFragment.java

public class mFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View contentView = inflater.inflate(R.layout.fragment, container, false);
        // 设置布局文件
        return contentView;
    }

    // 设置 接口回调 方法
    public void sendMessage(ICallBack callBack){

        callBack.get_message_from_Fragment("消息:我来自Fragment");

    }
}

步骤5:设置Acticvity的类文件
Main_Activity.java

public class MainActivity extends AppCompatActivity {

    Button button;
    TextView text;

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

        button = (Button)findViewById(R.id.button);
        text = (TextView)findViewById(R.id.text);

        // 步骤1:获取FragmentManager
        FragmentManager fragmentManager = getFragmentManager();

        // 步骤2:获取FragmentTransaction
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        // 步骤3:创建需要添加的Fragment 
        final mFragment fragment = new mFragment();

        // 步骤4:动态添加fragment
        // 即将创建的fragment添加到Activity布局文件中定义的占位符中(FrameLayout)
        fragmentTransaction.add(R.id.fragment_container, fragment);
        fragmentTransaction.commit();


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 通过接口回调将消息从fragment发送到Activity
                fragment.sendMessage(new ICallBack() {
                    @Override
                    public void get_message_from_Fragment(String string) {
                            text.setText(string);
                    }
                });

            }
        });
    }


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值