Activity

1.Activity的创建

Activity 是Android 四大组件之一,用于展示界面
* Activity 中所有操作都与用户密切相关,是一个负责与用户交互的组件,它上面可以显示一些控件也可以监听并处理用户的事件。
* 一个Activity 通常就是一个单独的屏幕,Activity 之间通过Intent 进行通信。

1.自定义类继承Activity

新建一个Android 工程,在src 目录下新建一个类,不妨起名MyActivity,让该类继承Android SDK提供的Activity。

2.MyActivity 中覆写onCreate()方法

onCreate() 方法在当前Activity 被系统创建的时候调用

  • 在该方法中一般要通过setContentView(R.layout.myactivity)方法给当前Activity 绑定布局文件或视图。
  • 因此为了让我们的MyActivity能够展示界面,需要在工程目录的res/layout/目录下创建一个布局文件

<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="wrap_content"
        android:layout_height="wrap_content"
        android:text="这是MyActivity的界面"/>
LinearLayout>

3.AndroidManifest.xml 中进行Activity 的注册

Android 中共有四大组件,除了BroadCastReceiver 之外,其他三个组件如果要使用那么必须在AndroidManifest.xml 中进行注册(也叫声明)

activity/android:name:要注册的Activity 的全限定类名
activity/android:label:Activity 左上角显示的名称

intent-filter:叫意图过滤器,一般用于隐式启动该Activity
action/android:name:意图过滤器的Action 名称,可以自定义也可以使用系统提供的。
category/android:name:意图过滤器的category 名称,只能使用系统提供的常量值。
  • 对于MainActivity来说正是配置了特定(action为android:name=”android.intent.action.MAIN”,category为android:name=”android.intent.category.LAUNCHER”)的intent-filter,那么当应用安装好以后,才会在桌面创建一个图标,点击该图标打开MainActivity。

  • 自定义的MyActivity如果也配置了一模一样的intent-filter,那么系统也会为我的MyActivity 创建一个图标,如果用户点击了该图标,那么也可以直接打开我的MyActivity 界面。

  • 也就是说一个Android 工程可以创建多个图标,不过通常情况下一个Android 应用只要给一个入口的Activity 配置为入口Activity 即可。

2.Activity的跳转

显式跳转:在可以引用到另外一个Activity 的字节码,或者包名和类名的时候,通过字节码,或者包名+类名的方法实现的跳转叫做显示跳转。显示跳转多用于自己工程内部多个Activity 之间的跳转,因为在自己工程内部可以很方便地获取到另外一个Activity 的字节码。

隐式跳转:隐式跳转不需要引用到另外一个Activity 的字节码,或者包名+类名,只需要知道另外一个Activity 在AndroidManifest.xml 中配置的intent-filter 中的action 和category 即可。言外之意,如果你想让你的Activity 可以被隐式意图的形式启动起来,那么就必须为该Activity 配置intent-filter。

  • Activity 之间的跳转都是通过Intent 进行的。
  • Intent 即意图,不仅用于描述一个Activity的信息,同时也是一个数据的载体。

  • 显示意图

//通过Intent 的带参构造函数
Intent intent = new Intent();
//通过Intent 的setClass 方法
intent.setClass(this, SecondActivity.class);
//Intent 可以携带的数据类型
1.八种基本数据类型booleanbytecharshortintfloatdoublelong 和String 以及这9 种数据类型的数组形式
2.实现了Serializable 接口的对象
3.实现了Android 的Parcelable 接口的对象以及其数组对象
  • 隐式意图
//要跳转的activity在清单文件里增在intent-filter
<intent-filter >
        <action android:name="自己定义,习惯用包名后加功能名"/>
        <category android:name="android.intent.category.DEFAULT"/> //默认
</intent-filter>

//谁要跳转到这个activity,谁的方法里面调用
Intent intent = new Intent();
intent.setAction("要跳转的activity在清单文件里配置的action");
intent.addCategory("android.intent.category.DEFAULT");-->默认
startActivity(intent);
  • 隐示意图需要注意的地方
在清单文件的 intent-filter 里面还可以配置 data标签,data标签可以配置多个不同种类型的
例如:
<data android:scheme="自己定义"/> -->设置前缀,与电话播放器调用很像
<data android:mimeType="text/plain"/> -->定义类型,这里不能随意定义

在java代码里,如果同时配置了scheme和mineType:
intent.setDataAndType(scheme,mimeType);

如果只配置scheme:
intent.setData();

如果只配置了mimeType:
intent.setType();

3.示例代码

  • 闪屏页面
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(){
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, CalcActivity.class);
                startActivity(intent);
                finish();//把mainActivity关闭
            }
        }.start();
    }
}
  • 主页面
public class ResultActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_result);
        // 获取传递过来的Intent
        Intent intent = getIntent();
        // 取出对应的数据
        String name = intent.getStringExtra("name");
        Bitmap bitmap = intent.getParcelableExtra("bitmap");
    }
}

短信助手
* 主界面代码实现

public class MainActivity extends Activity {
    /**收件人号码*/
    private EditText et_number;
    /**短信内容*/
    private EditText et_message;
    /**选择短信模板的请求码*/
    public static final int SELECT_SMS = 1;
    /**选择联系人的请求码*/
    public static final int SELECT_CONTACT = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_message = (EditText) findViewById(R.id.et_message);
        et_number = (EditText) findViewById(R.id.et_number);
    }

    public void selectSmsContent(View view) {
        Intent intent = new Intent(this, SmsListActivity.class);
        // startActivity(intent);
        // 开启新的Activity并且获取返回值
        startActivityForResult(intent, SELECT_SMS);
    }

    // 开启新的界面 选择联系人
    public void selectContact(View view) {
        Intent intent = new Intent(this, ContactListActivity.class);
        // startActivity(intent);
        // 开启新的Activity并且获取返回值
        startActivityForResult(intent, SELECT_CONTACT);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == SELECT_SMS) {
            //System.out.println("我们开启的新的选择短信的界面被关闭了,结果数据返回到这里");
            if (data != null) {
                String message = data.getStringExtra("message");
                et_message.setText(message);
            }
            }else if(requestCode == SELECT_CONTACT) {
            //System.out.println("我们开启的新的选择联系人界面被关闭了,结果数据返回到这里");
            if(data!=null){
                String number = data.getStringExtra("number");
                et_number.setText(number);
            }
        }
    }
}
  • 联系人界面代码
/**
 * 联系人选择界面
 */
public class ContactListActivity extends Activity {
    private ListView lv_contact;
    private String[] numbers = {
        "13512340000",
        "13512340001",
        "13512340002",
        "13512340003",
        "13512340004", 
        "13512340005",
        "13512340006",
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contactlist);
        //找到控件
        lv_contact = (ListView) findViewById(R.id.lv_contact);
        //设置适配器
        lv_contact.setAdapter(new ArrayAdapter(this, R.layout.item, numbers));
        //listview条目点击事件
        lv_contact.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view,int position, long id) {
                String number = numbers[position];
                //把当前界面的数据,返回给开启我的界面.
                Intent intent = new Intent();
                intent.putExtra("number", number);
                setResult(0,intent);
                //关闭当前界面
                finish();
            }
        });
    }
}
  • 选择短信模板界面代码
/**
 * 选择短信模板界面
 */
public class SmsListActivity extends Activity {
    private ListView lv;
    private String[] smsMessages={
        "心情预报:今夜到明早想你,预计下午转为很想你,受此情绪影响,傍晚将转为暴想,此类天气将持续到见你为止。",
        "世界如此忙碌,用心的人就会幸福;想你的脸,心里就温暖;想你的嘴,笑容跟着灿烂!随着新年的到来,关心你的人要跟你说声:新年快乐!",
        "新年到了,衷心祝福你。祝你年年圆满如意,月月事事顺心,日日喜悦无忧,时时高兴欢喜,刻刻充满朝气!"
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_smslist);
        lv = (ListView) findViewById(R.id.lv);
        lv.setAdapter(new ArrayAdapter(this,R.layout.item, smsMessages));
        lv.setOnItemClickListener(new OnItemClickListener() {
            //当listview的条目被点击的时候调用的方法
            @Override
            public void onItemClick(AdapterView parent, View view,int position, long id) {
                System.out.println("position:"+position);
                String message = smsMessages[position];
                //把当前界面的数据,返回给开启我的界面.
                Intent data = new Intent();
                data.putExtra("message", message);
                setResult(0, data);
                //把当前界面关闭
                finish();
            }
        });
    }
}

3.Activity 的生命周期

Activity 从创建到销毁,整个生命周期是一个非常复杂的过程,该过程由Android 系统负责维护

1.Activity 的3种状态

Resumed、Paused、Stopped可以理解为:激活或运行状态、暂停状态、停止状态

  • Resumed 状态
    • Activity 位于前端位置,并且获取到了用户的焦点。也就是当前Activity 完全可见也可用。
      注意:在Android 中目前只允许同时只能有一个Activity 位于前端位置。
  • 2.Paused 状态
    • 如果另外一个Activity 位于前端位置并且获取了焦点,但是该Activity 还依然可见,那么该Activity 就处于了Paused 状态。比如如果另外一个Activity 虽然位于前端,但是是透明的或者没有占满整个屏幕,那么就会出现上面的这种情况。位于Paused 状态的Activity 依然是“存活”着的,但是如果系统内存极端的不足,那么就有可能被系统“杀死”以便释放内存。
  • 3.Stopped 状态
    • 当另外一个Activity 完全将该Activity 遮盖住的情况下,那么该Activity 就处于停止状态了。位于停止状态的Activity 依然“活着”,但是它已经对用户完全不可见了,因此只要系统需要释放内存就会将该Activity“杀死”。

我们编写的代码要根据Activity 的不同状态让其做不同的工作,比如如果我们的Activity 是用于播放视频的,那么当其位于Resumed 状态时我们可以让视频正常的播放,当Activity 位于Paused 状态的时候,我们也应该让我们的视频暂停播放,当Activity 位于Stopped 状态时我们应该停止播放视频并释放资源。

2.Activity 生命周期的回调方法

OnCreate    
当Activity 第一次创建时被调用,在该方法中我们应该执行创建视图、初始化数据等工作。
该方法被调用之后紧接着就是调用onStart 方法。

onStart 
当Activity 对用户可见前被系统调用。

onResume    
当Activity 可以跟用户交互前被调用,该方法被调用后Activity 就处于Resumed 状态。

onPause 
当另外一个Activity 即将成为Resumed 状态前调用,在该方法中应该保存临时数据、停止动画、暂停视频播放器等。
必须要注意的就是该方法执行必须要快,因为只有当该方法执行过之后,另外一个Activity 才会成为Resumed 状态,不然会造成Activity 直接切换的“卡顿”现象。

onStop  
当Activity 对用户已经完全不可见的时候就会调用该方法。
当另外一个Activity 已经成为Resumed 状态或者当前Activity 被销毁的情况下会导致当前Activity 不可见。

onDestory   
当Activity 被销毁前调用。
当前Activity 调用finish()方法或者系统为了释放内存而将其销毁都会调用该方法。

onRestart   
当Activity 处于onStop 状态之后,如果重新启动则会调用该方法。
比如如果当前Activity位于Resumed 状态,此时我们按了Home 键,那么Activity 就完全不可见了,onStop 方法就会被执行,然后再长按Home 键再将该Activity 重新启动起来就会调用onRestart 方法。

3.横竖屏切换时Activity 的生命周期

Android 手机在横竖屏切换时,默认情况下会把Activity 先销毁再创建。

在类似手机游戏、手机影音这一类的应用中,这个体验是非常差的。不让Activity 在横竖屏切换时销毁,只需要在清单文件声明Activity 时配置节点的几个属性即可,其方式如下:

//参数配置到Activity,Activity 就不会再销毁和重新创建了
android:configChanges="orientation|keyboardHidden|screenSize"

//固定Activity 的方向
想固定Activity 的方向其实比较简单,有两种方法:
    //通过配置文件
    在AndroidManifest.xml 中的activity 节点中添加如下属性。
    android:screenOrientation="portrait"
    该属性通常有两个常量值,portrait:垂直方向,landscape:水平方向。

    //通过代码
    在Activity 的onCreate 方法中执行如下方法。
    //垂直方向
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    //水平方向
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

4.Activity 的启动模式

1.Activity 的任务栈

Task Stack(任务栈)是一个具有栈结构的容器,可以放置多个Activity 实例

  • 数据结构:先进后出

2.优缺点

  • 优点:提高了用户体验
  • 缺点:
    • 每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity 全部清除出栈时,任务栈被销毁,程序才会退出,这样的设计在某种程度上可能造成了用户体验差,需要点击多次返回才可以把程序退出了。
    • 每开启一次页面都会在任务栈中添加一个Activity 还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。

3.启动模式

Activity 的启动模式
启动模式(launchMode)在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity 实例,是否重用已存在的Activity 实例,是否和其他Activity 实例共用一个task。

Activity 一共有以下四种launchMode:standard、singleTop、singleTask、singleInstance。

我们可以在AndroidManifest.xml配置的android:launchMode属性为以上四种之一即可。

  • standard: 标准启动模式
    特点:默认启动模式, 每次激活Activity时(startActivity),都创建Activity实例,并放入任务栈.

  • singleTop:单一顶部模式
    特点:如果activity已经被开启,而且是在栈顶,就不会在创建当前这个activity的实例,而是复用这个已经开启的activity,但是如果不是在栈顶,就会初始化一个新的实例,在整个栈里允许有多个实例

  • singleTask:单一任务栈
    特点:当前栈里只允许有一个当前activity的实例,如果要开启的activity在栈里存在,并且在底部,就会移除这个activity上面所有的activity
    应用场景:如果这个activity非常消耗cpu和内存,建议把这个activity的启动模式设置为singleTask,浏览器的browserActivity 设置的就是

  • singleinstance:单一实例
    特点:整个手机操作系统只有一个实例,并且是单独运行在自己的任务栈里
    应用场景:通话界面的activity

Activity的启动模式是面试几乎都会问到的一个知识点,也是很重要的一个知识点,在项目开发中经常会用到启动模式来帮助我们解决一些比较难解决的bug,也可以帮助我们去优化一个项目的任务栈,防止界面太多导致卡顿和OOM。

你可能感兴趣的:(学习笔记)