导语
Activity&页面跳转&数据传递 介绍
一、创建第二个Activity
- 1.自定义类继承Activity (一个新的页面) 给其创建布局文件
- 2.需要在清单文件中为其配置一个activity标签 - <activity android:name="包名.类名"> </activity>- 注意:
- 1.在清单中进行如下配置,会在桌面出现两个图标,名字一样的快捷方式,
- 因为主页面,已经在activity标签配置了Activite入口,新页面又配置了Activity入口 - <activity android:name="com.itheima.secondactivity.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SecondActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
- 2.在清单中进行如下配置后,会在桌面出现两个不同图标,不同名字的启动快捷方式 
- 分别对应打开主页面和新建页面 - <activity android:icon="@drawable/photo1" android:label="启动第一个" android:name="com.itheima.secondactivity.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:icon="@drawable/photo2" android:label="启动第二个" android:name=".SecondActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
 
- activity标签中如果带有以下的 - 子节点,则会在系统中多创建一个快捷图标,就是新页面activity的启动图标 - <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
- 一个应用程序可以在桌面创建多个快捷图标。
- activity的名称、图标可以和应用程序的名称、图标不相同 - android:icon="@drawable/ic_launcher" android:label="@string/app_name"
###一个应用程序可以创建多个入口的activity,快捷图标 ,对应打开不同的页面
###只要配置了以下
<intent-filter>
     <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>`
###注意一个新页面,可以配置多个,用于其他页面进行匹配,使能够让其他页面跳转过来 
#二、Activity的跳转
##2.1如何实现页面跳转
- Activity的跳转需要创建Intent对象,通过设置intent对象的参数指定要跳转Activity - public void click2(View v){ //显式意图 Intent intent = new Intent(); //指定目标Activity的字节码 intent.setClass(this, SecondActivity.class); //显式跳转 startActivity(intent); }
##Activity跳转分类
- 通过对Intent对象的参数设置不同,跳转方式也不同- 显式意图:通过设置Activity的包名和类名实现跳转,称为显式意图
- 隐式意图:通过指定动作实现跳转,称为隐式意图
 
###显式意图:直接指定要激活的activity。效率高。直接指定。
- 跳转至同一项目下的另一个Activity,直接指定该Activity的字节码即可 - public void click2(View v){ //显式意图 Intent intent = new Intent(); //指定目标Activity的字节码 intent.setClass(this, SecondActivity.class); //显式跳转 startActivity(intent); }
- 跳转至其他应用中的Activity,需要指定该应用的包名和该Activity的类名 - public void click3(View v){ //显式意图 Intent intent = new Intent(); //启动系统自带的拨号器应用 //arg0:目标Activity所在的项目的应用包名 //arg1:目标Activity的包名和类名 intent.setClassName("com.android.dialer", "com.android.dialer.DialtactsActivity"); //显式跳转 startActivity(intent); }
###隐式意图:(动作,数据,category)开启新的activity。
- 1.隐式意图跳转至指定Activity - 实际上系统的拨号器已经在自己的清单文件中进行了配置,使得此拨号器页面能够实现隐式启动,那么如何配置呢? - public void click4(View v){ Intent intent = new Intent(); //启动系统自带的拨号器应用 intent.setAction(Intent.ACTION_DIAL); startActivity(intent); }
 
- 2.要让一个Activity可以被隐式启动,需要在清单文件的activity节点中设置intent-filter子节点- action 指定动作(可以自定义,可以使用系统自带的)name可以写任意自定义,一般比照谷歌的写法
- data 指定数据(操作什么内容)
- category 类别 (默认类别,机顶盒,车载电脑)
 
<intent-filter >
     <action android:name="com.itheima.second"/>
     <data 
         android:scheme="asd" 
         android:mimeType="aa/bb"/>
     <category android:name="android.intent.category.DEFAULT"/>
 </intent-filter>
##activity隐式启动在清单文件中的配置
- action activity对应的动作。 可以用任意一个字符串描述(最好,顾名思义)
- category 额外的类别参数。  指定工作的设备(车载电脑,机顶盒,默认的手机) 通常指定的参数android.intent.category.DEFAULT
- data  指定数据和数据的类型  - scheme 数据前缀
- host 主机名
- port 端口号
- path 路径 记得在前面加上 /
- mimeType 数据类型 从tomcat里面查找常见的数据类型
 
- 隐式启动页面,该页面的配置示例
<activity 
        android:name="com.itheima.activityjump.SecondActivity">
        <intent-filter >
            <action android:name="com.itheima.a1"/>
            <data android:scheme="youyiyi1"/>
            <action android:name="com.itheima.a3"/>
            <data android:scheme="youyiyi3"/>
            <data android:mimeType="text/name"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
        <intent-filter >
            <action android:name="com.itheima.a2"/>
            <data android:scheme="youyiyi2"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>
- 3.隐式意图启动Activity,需要为intent设置以上三个属性,且值必须与该Activity在清单文件中对三个属性的定义匹配
- 4.隐式意图启动Activity时要注意:- 设置参数时setType()会清除setData(),而setData()会清除setType(), 两者不能共存,
- 所以通过setDataAndType(Uri.parse(“youyiyi3:xiaoming”), “text/name”);
- 一并将两者全部设置了
 
- 页面一跳转到页面二示例(如:隐式跳转上面配置的页面) - public void click5(View v){ - Intent intent = new Intent(); intent.setAction("com.itheima.a1"); //匹配mimetype- // intent.setType(“text/name”); - //匹配scheme,注意setData()会将上面的setType清除- // intent.setData(Uri.parse(“youyiyi3:xiaoming”)); - //匹配scheme和mimetype intent.setDataAndType(Uri.parse("youyiyi3:xiaoming"), "text/name"); //如果没有指定category,则自动添加以下代码- // intent.addCategory(Intent.CATEGORY_DEFAULT); - startActivity(intent); }
- 页面一跳转到页面二示例二(如:隐式跳转上面配置的页面) 
 
public void click5(View v){
     Intent intent = new Intent();
     //匹配动作
     intent.setAction("com.itheima.a2");
     //匹配scheme
     intent.setData(Uri.parse("youyiyi2:xiaoming"));
     startActivity(intent);
 }
- 5.intent-filter节点及其子节点都可以同时定义多个,隐式启动时只需与任意一个匹配即可
#####在新页面获取通过setData传递的数据
- 获取启动此Activity的intent对象 ,通过intent得到数据
 Intent intent = getIntent();
 Uri uri = intent.getData();
 //第二个Activity获取传进过来的数据
 public class SecondActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          //指定Activity显示哪一个布局文件
          setContentView(R.layout.activity_second);
          //获取传递过来的数据
          //获取启动此Activity的intent对象
          Intent intent = getIntent();
          Uri uri = intent.getData();
          System.out.println(uri);
     }
}
###显式意图和隐式意图的应用场景
- 显式意图用于启动同一应用中的Activity,直接指定包名类名。效率高。
- 隐式意图用于启动不同应用中的Activity,通过action和data指定。
- 另外对于隐式意图:- 如果系统中存在多个Activity的intent-filter同时与你的intent匹配,那么系统会显示一个对话框,列出所有匹配的Activity,由用户选择启动哪一个
 
#Activity跳转时的数据传递
- Activity通过Intent启动时,可以通过Intent对象携带数据到目标Activity
- 8大基本类型数据 以及数据的数组都是可以通过intnet传递- intent.putExtra(name,values)
 
- 对象也可以通过intent传递。- 要求对象一定是实现Serializable接口的 可序列化的对象。
- 要求对象一定是实现Parcelable 接口
 
- 方式一:直接将数据封装到intent中 - public void click(View v){ EditText et_malename = (EditText) findViewById(R.id.et_malename); EditText et_femalename = (EditText) findViewById(R.id.et_femalename); String maleName = et_malename.getText().toString(); String femaleName = et_femalename.getText().toString(); Intent intent = new Intent(this, SecondActivity.class); //把数据封装至intent中 intent.putExtra("maleName", maleName); intent.putExtra("femaleName", femaleName); startActivity(intent); }
- 方式二:先把数据封装至bundle中,再把bundle封装至intent中 - public void click(View v){ EditText et_malename = (EditText) findViewById(R.id.et_malename); EditText et_femalename = (EditText) findViewById(R.id.et_femalename); String maleName = et_malename.getText().toString(); String femaleName = et_femalename.getText().toString(); Intent intent = new Intent(this, SecondActivity.class); //把数据封装至bundle中 Bundle bundle = new Bundle(); bundle.putString("maleName", maleName); bundle.putString("femaleName", femaleName); //把bundle封装至intent中 intent.putExtras(bundle); startActivity(intent); } }
- 在目标Activity中取出数据(理解:在新页面,获取传递过来的数据) - Intent intent = getIntent(); String maleName = intent.getStringExtra("maleName"); String femaleName = intent.getStringExtra("femaleName"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //指定Activity显示哪一个布局文件 setContentView(R.layout.activity_second); Intent intent = getIntent(); //直接获取封装在intent中的数据 //String maleName = intent.getStringExtra("maleName"); //String femaleName = intent.getStringExtra("femaleName"); //先通过intent获取Bundle对象 Bundle bundle = intent.getExtras(); //再通过Bundle对象,获取数据 String maleName = bundle.getString("maleName"); String femaleName = (String) bundle.get("femaleName"); Random rd = new Random(); int yinyuan = rd.nextInt(11) + 90; ((TextView)findViewById(R.id.tv)).setText(maleName + "与" + femaleName + "的缘分为" + yinyuan + ",实乃天作之合"); }
#Activity生命周期
- void onCreate()- Activity已经被创建完毕,
- activity被创建的时候调用。 适合做界面的初始化操作。
 
- void onStart()- Activity已经显示在屏幕,但没有得到焦点,
- activity界面用户可见。 适合更新界面。 继续播放视频
 
- void onResume()- Activity得到焦点,可以与用户交互
- 获取焦点 按钮可以被点击
 
- void onPause()- Activity失去焦点,无法再与用户交互,但依然可见
- 失去焦点 按钮可以看到,但是不可以被点击。游戏使用
 
- void onStop()- Activity不可见,进入后台
- activity界面用户不可见。 界面不可见适合清理操作。暂停视频。视频播放器
 
- void onDestroy()- Activity被销毁,
- activity被销毁的时候调用。 适合做扫尾的操作。数据保存的操作。短信内容的保存
 
- void onRestart()- Activity从不可见变成可见时会执行此方法
 
- Activity生命周期使用场景- Activity创建时需要初始化资源,销毁时需要释放资源;或者播放器应用,在界面进入后台时需要自动暂停
 
###完整生命周期(entire lifetime)
onCreate–>onStart–>onResume–>onPause–>onStop–>onDestory
###可视生命周期(visible lifetime)
onStart–>onResume–>onPause–>onStop
###前台生命周期(foreground lifetime)
onResume–>onPause
- opRestart()
- 在onPause(),onStop()时进程被杀死了,下面不会执行
#Activity的四种启动模式
##任务栈
- 什么是任务栈?- 记录当前用户操作的行为的一种数据结构.(后进先出的数据结构)
- 最近打开的界面,先被关闭。
- 参考发送邮件的步骤 理解任务栈的概念。
- 每个应用会有一个Activity任务栈,存放已启动的Activity。一个应用程序默认是只有一个任务栈,特殊情况下singleinstance会有多个任务栈
 
##进程
android系统 ,应用程序退出和进程退出是两个不同的概念。
android系统为了让应用程序可以被快速的开启。所有的应用程序退出后,进程是不会退出的。’
只有系统的内存空间严重不足的时候,才会把进程给回收。
点击图标–>linux创建进程–>dalvik虚拟机–>读取清单文件,加载activity。
android理解应用程序退出: 任务栈清空了。
##线程
进程是操作系统分配内存空间的单位,每个进程的内存空间都是独立的。
线程是运行在进程里面。线程cpu执行的最小单位。如果进程挂了,线程也挂了。
##应用程序 application
android应用程序每个应用程序都是运行在自己的sandbox(沙箱)。
理解成一组activity,service,content priovder broadcastreceiver的组合。
##Activity的四种启动模式,修改任务栈的排列情况
- 1、standard 标准启动模式。一个activity默认就是标准的启动模式。 - 开启新的activity(可以开启自身),activity就会被创建出来,加入到任务栈的栈顶。
 适用于绝大多数的应用场景。
- 不用在清单中进行配置
 
- 开启新的activity(可以开启自身),activity就会被创建出来,加入到任务栈的栈顶。
- 2、singleTop 单一顶部启动模式。 - 如果任务栈的栈顶存在这个要开启的activity,不会重新的创建activity,而是复用已经存在的activity。保证栈顶如果存在,不会重复创建。
- 在同一个任务栈里面可以有多个实例存在。
- 应用场景:浏览器的书签(浏览器添加到书签)singletop
- 清单配置 - <activity android:launchMode="singleTop" android:name=".SecondActivity"> </activity>
 
- 3、singeTask 单一任务栈启动模式。,在当前任务栈里面只能有一个实例存在。 - 当开启activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的activity,并且把这个activity上面的所有的别的activity都清空,复用这个已经存在的activity。
- 保证整个任务栈里面只有一个实例存在
- 应用场景:浏览器的activity
- 清单配置 - <activity android:launchMode="singeTask" android:name=".SecondActivity"> </activity>
- 如果一个activity的创建需要占用大量的系统资源(cpu,内存)一般配置这个activity为singletask的启动模式。webkit内核 c代码 - 在什么时候使用singletask模式。 
 BrowserActivity 浏览器 开销(内存占用,cpu占用)非常大,singletask。保证在一个任务栈里面只有一个实例存在。
 webkit 内核 页面。
 很多东西需要初始化
 html解析器
 html渲染器
 css 渲染器
 javascript 执行引擎- 如果一个activity的资源开销非常大,建议使用singletask的启动模式。 
 浏览器的activity使用的就是singletask的启动模式。
 
- 4、singleInstance 单一实例启动模式。非常特殊, activity会运行在自己的任务栈里面,并且这个任务栈里面只有一个实例存在 - 类似于java中的单例模式,单态模式。在整个android手机操作系统里面只有一个activity的实例存在。
- singleinstance启动模式的activity会运行在自己单独的任务栈里面
- 如果你要保证一个activity在整个手机操作系统里面只有一个实例存在,使用singleInstance
- 应用场景: 电话拨打界面
- InCallScreen 通话的activity配置模式是singleinstance的。在整个手机操作系统里面只有他一个实例存在。
- 清单配置 - <activity android:launchMode="singleInstance" android:name=".SecondActivity"> </activity>
 
##任务栈的细节
- 每个应用程序运行,系统都会分配一个新的任务栈。
- 问题:问系统里面有多少个任务栈存在?
- 有多少个应用程序正在运行(activity没有都被销y毁,回桌面),就有几个任务栈。
 应该是就至少有几个
- 如果一个应用里面有一个singleinstance呢??? 
- 任务栈的id是一个int类型的整数,自增长的id。不关机的话由getTaskId()得到的进程id会不断增加 
- 一启动,会给launcher,systemui两个任务栈
- 当只启动后台服务,没有启动画面,是不会分配任务栈的
##横竖屏切换的生命周期
- 默认情况下 ,横竖屏切换, 销毁当前的activity,重新创建一个新的activity
- 快捷键ctrl+F11 
- 在一些特殊的应用程序常见下,比如游戏,不希望横竖屏切换activity被销毁重新创建 
- 需求:禁用掉横竖屏切换的生命周期
- 横竖屏写死 - android:screenOrientation="landscape" 横屏 android:screenOrientation="portrait" 竖屏
- 让系统的环境 不再去敏感横竖屏的切换。 - android:configChanges="orientation|screenSize|keyboardHidden"
- 写死横竖屏示例: - <activity android:screenOrientation="portrait" android:configChanges="orientation|screenSize|keyboardHidden" android:name="com.itheima.lifecycle.MainActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
#掌握开启activity获取返回值
###从A界面打开B界面, B界面关闭的时候,返回一个数据给A界面
步骤:
- 1, 用新方法startActivityForResult(intent, 0)替代startActivity(intent)去 
 启动目标activity并且获取返回值,- 在主页面用这个方法启动的目标Activity,当目标Activity销毁时,会触发主页面onActivityResult方法
- startActivityForResult()方法: - 参数1:意图(启动新界面)
- 参数2:请求码(用来在触发启动onActivityResult方法时,做匹配用)
 - startActivityForResult(intent, 0); - public void click1(View v){ 
 Intent intent = new Intent(this, ContactActivity.class);
 //startActivity(intent);- //用这个方法启动的Activity,当销毁时,会触发onActivityResult方法 
 startActivityForResult(intent, 10);
 }
 
- 2.在新开启的界面里面实现设置数据的逻辑 - setResult()方法 - 参数1:结果码 返回给父页面,做匹配用
- 参数2:要返回的数据,放在意图中 - Intent data = new Intent(); 
 data.putExtra(“phone”, phone);
 //设置一个结果数据,数据会返回给调用者
 setResult(0, data);
 finish();//关闭掉当前的activity,才会返回数据
 
 
-示例–当点击目标Activity中的ListView中任何一条目,触发设置数据的逻辑,销毁目标Activity
    public class ContactActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_contact);
            final String[] names = new String[]{
                "白九日",
                "莎九日",
                "包九日",
            };
            ListView lv = (ListView) findViewById(R.id.lv);
            lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item_listview, 
                    R.id.tv_name, names));
            //给listview的条目设置点击侦听
            lv.setOnItemClickListener(new OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                        int position, long id) {
                    Intent data = new Intent();
                    data.putExtra("name", names[position]);
                    //当此Activity销毁时,data就会被传递给上一个Activity
                    setResult(100, data);
                    //销毁当前Activity
                    finish();
                }
            });
        }
}
-示例2–
public class MyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        //指定Activity显示哪一个布局文件
        setContentView(R.layout.activity_my);
        //
        final TextView tv_wife_name = (TextView) findViewById(R.id.tv_wife_name);
        final TextView tv_wife_action = (TextView) findViewById(R.id.tv_wife_action);
        tv_wife_name.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent data = new Intent();
                data.putExtra("name", tv_wife_name.getText());
                setResult(300, data);
                finish();
            }
        });
        tv_wife_action.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent data = new Intent();
                data.putExtra("body", tv_wife_action.getText());
                setResult(400, data);
                finish();
            }
        });
    }
}
- 3.在开启者activity里面实现方法(在主界面实现onActivityResult)
- onActivityResult()方法: - 参数1:requestCode 请求码 区分请求来自于哪个按钮。
- 参数2:resultCode 结果码 区分请求来自于哪个结果。
- 参数3:data参数就是返回的结果数据。从里面把数据取出来。 - onActivityResult(int requestCode, int resultCode, Intent data)- 通过data获取返回的数据 
 
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    //通过请求码来判断数据来自哪个Activity
    if(requestCode == 10){
        String name = data.getStringExtra("name");
        EditText et = (EditText) findViewById(R.id.et_name);
        et.setText(name);
    }
    else if(requestCode == 20){
        String name = data.getStringExtra("sms");
        EditText et = (EditText) findViewById(R.id.et_body);
        et.setText(name);
    }
    else if(requestCode == 30){
        //通过结果码判断数据属于什么类型
        if(resultCode == 300){
            String name = data.getStringExtra("name");
            EditText et = (EditText) findViewById(R.id.et_name);
            et.setText(name);
        }
        else if(resultCode == 400){
            String name = data.getStringExtra("body");
            EditText et = (EditText) findViewById(R.id.et_body);
            et.setText(name);
        }
    }
}
- 4.根据请求码和结果码确定业务逻辑