[Android]【安卓】Activity详解

[Android]【安卓】Activity详解

本篇博客已收录到我的安卓开发小结中——点击【安卓开发小结】

参考资料:《第一行代码》、《Android开发艺术探索》

一、生命周期

1、典型情况下生命周期

[Android]【安卓】Activity详解_第1张图片

  (1)onCreate:表示Activity正在被创建,做一些初始化动作,只在创建时调用一次
  (2)onStart:表示Activity正在被启动,这时Activity已经可见,但不在前台,用户看不到也无法交互。
  (3)onResume:表示Activity已经可见,并出现在前台开始活动。
  (4)onPause:表示Activity正在停止,做一些快速的轻量级回收任务。因为新Activity的onResume方法要等当前Activity的onPause方法调用之后才会调用。此时用户回到原Activity的话,onResume方法会被调用,但在onPause方法停留时间极短,用户很难重现这一场景。当前Activity被部分遮挡时,如弹出一个弹窗,onPause方法被调用,onStop方法不被调用(这一情况经验证,没有看到onPause被调用)。
  (5)onStop:表示Activity即将停止,做一些快速的轻量级回收任务。Activity采用透明主题时,onStop方法不会被调用。
  (6)onDestroy:表示Activity即将被销毁,做一些回收工作和最终的资源释放,只调用一次
  (7)onRestart:表示Activity正在重新启动,一般是在Activity从不可见变为可见时调用。区别于onStart方法,否则不能知道是正常流程还是从onPause方法恢复的。一般是用户行为导致的,如按Home键返回桌面,或者打开了一个新的Activity,然后又切换回原来的Activity。


  (1)运行状态
  当一个活动处于返回栈栈顶时,它就处于运行状态。极端情况下系统才会回收这种状态的活动,因为回收运行状态的活动,将会带来极差的用户体验。
  (2)暂停状态
  当一个活动不处于栈顶,且部分可见时(如在它之上有一个对话框显示),它处于暂停状态。一般情况下系统也是不愿意回收暂停状态的活动的,因为它仍旧部分可见,回收它,也会造成极差的用户体验。
  (3)停止状态
  当一个活动不处于栈顶,且完全不可见时,进入停止状态。这时系统仍然会为这种活动保存相应的状态和成员变量,但这是不安全的,因为当需要内存时,暂停状态的活动随时可能被回收。
  (4)销毁状态
  当一个活动被移除返回栈后,它就进入了销毁状态。系统最倾向于回收这种状态的活动,以保证内存充足。


  (1)完整生存期
  活动在onCreate()到onDestroy()方法之间所经历的,就是完整生存期。一般情况下,一个活动会在onCreate()方法中完成各种初始化操作,在onDestroy()方法中完成释放内存的操作。
  (2)可见生存期
  活动在onStart()方法和onStop()方法之间所经历的,就是可见生存期。在可见生存期内,活动对于用户总是可见的,即使有可能无法和用户交互。我们可以通过这两个方法,合理地管理那些对用户可见的资源,如在onStart()方法中对资源进行加载,在onStop()方法中,对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
  (3)前台生存期
  活动在onResume()到onPause()之间的所经历的,就是前台生存期。在前台生存期内,活动总是处于运行状态,是可以与用户交互的。

standard模式正常回调过程:

onCreate –> onStart –> onResume –> Activity运行 –> onPause –> onStop –> onDestroy

  • onCreat-onDestroy对:
    在Activity创建和销毁时调用,只调用一次。

  • onStart-onStop对:
    可见不在前台时调用onStart方法,不可见且不在前台时调用onStop方法。随着用户操作或者屏幕亮灭,这两个方法可能会被多次调用。在onStop状态,让Activity重新回到前台,如果应用没有因为内存不足被杀死,那么调用
    onRestart-> onStart->onResume->Activity运行。如果应用进程被系统杀死了,那么要从onCreate方法重新开始。

  • onResume-onPause对:
    可见且在前台时调onResume方法,可见不在前台时调onPause方法。随着用户操作或者屏幕亮灭,这两个方法可能会被多次调用。在onPause状态,让Activity重新回到前台,那么只调用onResume方法,即onResume->Activity运行。

  • 惯性:
    onCreate->onStart->onResume->Activity运行,会按顺序执行到Activity运行。停止时会执行onPause->onStop,onDestroy方法用户操作时调用。(questionNo:onDestroy方法在结束进程时会调用吗?是惯性到onDestroy吗?answer:手动结束应用进程时,会onPause->onStop,但不会调用onDestroy方法。 )

2、异常情况下的生命周期分析

(1)资源相关的系统配置发生改变导致Activity被杀死并重新创建。
  资源相关的配置,如图片资源,竖屏和横屏拿到资源是不一样的(设定了landscape或portrait状态下的图片),突然旋转屏幕,默认情况下,Activity就会被销毁并重建,这个过程我们可以手动终止。
[Android]【安卓】Activity详解_第2张图片
  异常发生时,onPause方法和onSaveInstanceState方法的执行先后是随机的:
[Android]【安卓】Activity详解_第3张图片
  正常情况下,onSaveInstanceState方法不会被调用,因此可以根据这个方法判断Activity是否被重建。onSaveInstanceState和onRestoreInstanceState方法都是对Bundle对象进行操作,从而存储和取出数据,onCreate方法里也有Bundle对象这个参数,可以在onCreate方法里操作,二者的区别是,onRestoreInstanceState方法里的Bundle一定是有值的,但是onCreate方法里的Bundle对象正常启动时是没有值的,所以要进行非空判断,官方文档推荐使用onRestoreInstanceState方法来恢复数据。(onRestoreInstanceState方法在onStart方法之后,onResume方法之前调用)
  此外,在这两个方法里,系统为我们自动做了一些保存恢复工作,如Activity的视图结构,如文本框里用户输入的数据,如ListView滚动的位置,但具体是哪些数据,需要查看对应View里的相关源码。
  onSaveInstanceState方法,只有在Activity即将被销毁并且有机会重新显示的情况下才会被调用。即Activity异常终止时才会调用,正常销毁不会调用(手动杀死应用进程也不会)。
(2)、资源内存不足导致低优先级的Activity被杀死。
  这里的数据存储和恢复过程与1是一致的。
  Activity的优先级情况:
  a、前台Activity—正在和用户交互的Activity,优先级最高。
  b、可见但非前台的Activity—如Activity中弹出了一个对话框,Activity可见,但是位于后台无法与用户直接交互。
  c、后台Activity—已经被暂停的Activity,比如执行了onStop方法的Activity,优先级最低。
  当系统内存不足时,优先杀死低优先级的Activity所在的进程,此外,脱离四大组件的后台工作也会很快被系统杀死,所以后台工作应该放入Service中,从而保证一定的优先级。
(3)、用onSaveInstanceState和Bundle来恢复数据。  

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String stringTmp ;
        setContentView(R.layout.first_layout);
        if(savedInstanceState != null){
            stringTmp = savedInstanceState.getString("bundleKey");
            TextView textView = (TextView)findViewById(R.id.text);
            textView.setText(stringTmp);
        }
        Button button1 = (Button) findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivityForResult(intent, 1);
                System.gc();//建议系统回收对象,否则一般不会回收的(好吧,还是不回收)
            }
        });
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("bundleKey","this is the data from bundle");
    }

  Bundle类型,它提供一系列的put方法和get方法来传入、取出数据。此外,我们还可以把bundle对象放到Intent里,将数据传递给目标对象。

(4)、系统配置发生改变时,阻止Activity的回收和重建
  在AndroidManifest.xml中给Activity指定configChanges属性。比如不想在旋转屏幕时起作用:

android:configChanges=“orientation”

  可以使用|号相连,来阻止多种情况起作用,需要注意的是locale、orientation、smallestScreenSize、screenSize,条件如下图:
[Android]【安卓】Activity详解_第4张图片

二、启动模式

在说启动模式前,先看一下任务栈(返回栈)。

  • Android使用任务(Task)来管理活动。一个任务就是一组存放在栈(也称返回栈 Back Stack)里的活动的集合。栈是一种先进后出的数据结构。

  • 在默认情况下,每当我们启动一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。每当我们销毁一个活动(按Back键或调用finish()方法),处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。

  • 系统总是会显示处于栈顶的活动给用户。

  • 默认情况下所有Activity所需的任务栈的名字为应用的包名。任务栈有前台和后台之分,后台栈中的Activity处于暂停状态,用户可以通过切换,将后台任务栈再次调到前台。

  • 可以通过指定TaskAffinity(任务相关性,是一个字符串,默认为应用包名)属性来指定任务栈的名称(不能与包名相同,否则没有意义)。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,其他情况没有意义。TaskAffinity用法,androidManifest.xml中:

        <activity 
            android:name=".MainActivity"
            android:taskAffinity="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            intent-filter>
        activity>
  • 当TaskAffinity和singleTask启动模式配对使用的时候,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。

  • 当一个应用A启动了应用B的某个Activity C,C的allowTaskReparenting属性为true, 那么当应用B被启动后,C会直接从应用A的任务栈转移到应用B的任务栈,如应用A启动了应用B的Activity c,按home键回到桌面,点击应用B的图标,没有启动B的主Activity,而是重新显示c。
    因为A启动了C,C只能运行在A的任务栈中,但是正常情况C的TaskAffinity的值肯定不和A的任务栈一样(包名不同),而当B启动后,B会创建自己的任务栈,这时系统发现C想要的任务栈已经创建,所以会把C从A的任务栈移到B的任务栈中。allowTaskReparenting允许任务栈重父,意思应该就是,允许Activity运行在与TaskAffinity值不同的任务栈中。

  • 查看当前任务栈的方法:

adb shell dumpsys activity>dir\1.txt

  在txt使用查找命令查找

Running activities (most recent first)

  活动一共有四种启动模式:standard、singleTop、singleTask和singleInstance。
  在AndroidManifest.xml中给< activity>标签指定android:launchMode属性来选择启动模式,如下:

        <activity 
            android:name=".MainActivity"
            android:launchMode="singleTop"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            intent-filter>
        activity>

(1)standard:标准模式
  每启动一个Activity就会创建一个新的实例,不管这个实例是否已经存在,并具有典型情况下的生命周期。谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。
  使用ApplicationContext去启动standard模式的Activity会报错,非Activity类型的Context没有所谓的任务栈,要给它设立一个FLAG_ACTIVITY_NEW_TASK标记位,这样启动时就会为它建立一个新的任务栈,而不是去找原Context的任务栈。
2)singleTop:栈顶复用模式
  可以说是standard模式的“子类”(就Activity复用的条件和入的栈而言,是一个一般到特殊的过程)。
  新的Activity已经位于任务栈的栈顶,那么此Activity不会重新创建实例,因此它的onCreate、onStart方法不会被系统调用,而是会调用一个onNewIntent方法,通过这个方法,我们可以取得当前请求的信息。
  如果新的Activity不在栈顶,即使已经存在,仍然是会重新创建Activity实例。
(3)singleTask:栈内复用模式
  可以说是singleTop模式的“子类”。single in task,taskAffinity应该指定。
  A:新的Activity要求的任务栈S存在。
  检查栈S里是否存在新的Activity A,存在,因为clearTop效果,将A之上的Activity全部出栈,使其到达栈顶。
  B:新的Activity要求的任务栈S不存在。
  创建新的A的实例,并入栈S。
  此外,这种模式也是和singeTop模式类似,会复用Activity实例,所以当重复创建时,不会调用onCreate、onStart方法,而是会调用onNewIntent方法。
(4)singleInstance:单实例模式
  可以说是singleTask模式的“子类”。
  它具有singleTask模式的所有特性,它的特殊性在于,singleInstance模式下的Activity实例在一个单独任务栈S中。
  


  假设现在有两个任务栈,前台任务栈里的Activity为AB,后台任务栈里的Activity为CD,且CD的启动模式为singleTask,然后启动C和启动D,将有不同的结果,见下图:
[Android]【安卓】Activity详解_第5张图片
  这里既然有两个任务栈,说明SingleTask模式的Activity指定了TaskAffinity属性的值,并且这个值与包名不一致,所以B启动D的时候,应该是让SingleTask任务栈调到前台,前台任务栈推到后台,而不是把CD移动到AB的任务栈里,除非是CD指定的任务栈不存在,且CD设置了allowTaskReparenting属性为true,才出现B启动D,把D压入B的任务栈中。
  启动C,D会弹出SingleTask的任务栈是因为SingleTask模式的CLEAR_TOP效果。点两次返回键,B又出现在前台,是因为前台任务栈SingleTask模式的任务栈里的Activity都出栈后,会将后台任务栈恢复到前台。
  
给Activity指定启动模式的两种方法:
(1)、在AndroidManifest.xml中指定

android:launchMode=“singleTask”

(2)、在Intent中设置标志位,

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

区别:
(1)、优先级代码里比xml高,要以代码里为准。
(2)、限定范围不同,xml里无法为Activity直接指定FLAG_ACTIVITY_CLEAR_TOP标识,代码里无法指定singleInstance模式。

Activity的Flags
  这里的标记位就是上文提到的使用Intent在代码里设置Activity启动方式的标记位。
  常用标记位如下:

  • FLAG_ACTIVITY_NEW_TASK为Activity指定启动模式为“singleTask”。

  • FLAG_ACTIVITY_SINGLE_TOP为Activity指定启动模式为“singleTop”

  • FLAG_ACTIVITY_CLEAR_TOP在“standard”模式下,使用这个标记位,如果默认任务栈里,已经有了目标ActivityA,那么A和A之上的Activity都要出栈,然后新建一个A压入栈顶。与“singleTask”配合使用时,使用这个标记位,目标A之上的Activity都要出栈,并且onNewIntent方法会被调用。

  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS具有这个标志的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。等同在xml中指定Activity的属性为:

android:excludeFromRecents=”true

三、启动方式

  Activity的启动分为显式和隐式启动,原则上二者不该同时存在,同时存在以显式为主。显式需要指定被启动对象的组件信息(包名、类名),隐式不需要指定目标组件信息,只要Intent可以匹配上目标Activity的IntentFilter设置的过滤信息。
  
1、使用显示Intent
  新建一个secondActivity,在FirstActivity的onCreate()方法中添加如下代码:

       Button button1 = (Button)findViewById(R.id.firstButton);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });

  在AndroidManifest中声明Activity:

    <activity android:name=".SecondActivity">activity>

  向下一个Activity传递数据
  在第一个活动中添加如下代码:

        Button button1 = (Button)findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                intent.putExtra("key","hello");
                startActivity(intent);
            }
        });

  在第二个活动添加如下代码:

        final TextView textView = (TextView)findViewById(R.id.secondTextId);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = getIntent();
                String strings = intent.getStringExtra("key");
                textView.setText(strings);
            }
        });

  putExtra()方法有多种重载的方法,第一个参数是键值,第二个参数可以是String、boolean、int等多种类型。通过上述方式,就可以实现上一个活动向下一个活动传递数据。


  返回数据给上一个Activity
  上一个activity的启动方式不能是startActivity,要用startActivityFoResult方法。

    private final int ACTIVITY_BACK = 1;

    //启动bActivity。
    @OnClick(R.id.frame)
    void atmosphereSettingClick() {
        Intent intent = new Intent(aActivity.this,bActivity.class);
        startActivityForResult(intent,ACTIVITY_BACK);
    }

    //接收来自bActivity的数据
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case ACTIVITY_BACK://一个activity可能启动不同activity,用这个来区分
                if(resultCode == RESULT_OK){//这个字段区分栈顶activity是单纯的出栈,还是要向下传递消息
                    redBack = data.getIntExtra(RED_KEK,0);
                    greenBack = data.getIntExtra(GREEN_KEY,0);
                    blueBack = data.getIntExtra(BLUE_KEY,0);

                }
                break;
            default:
                break;
        }
    }

  栈顶的activity的处理,这里我重写系统返回按键的onBackPressed方法,因为你把setResult放在finish方法或者onDestroy方法里的话,退出得太快,消息没办法传递下去。

    @Override
    public void onBackPressed() {
        getColorSendBack(selected);
        super.onBackPressed();
    }

    /**
     * 将选择的颜色返回给上一个activity
     */
    private void getColorSendBack(int mode){
        if(redBack == NO_COLOR){
            redBack = 0;
            blueBack = 0;
            greenBack = 0;
        }
        Intent intent = new Intent();
        intent.putExtra(RED_KEK,redBack);
        intent.putExtra(GREEN_KEY,greenBack);
        intent.putExtra(BLUE_KEY,blueBack);
        setResult(RESULT_OK,intent);//此处告诉次顶activity,不是单纯结束,有消息下来了
      }

2、使用隐式Intent
  IntentFilter的过滤信息有action、category、data三种。一个Activity里可以有多组IntentFilter,每组IntentFilter里可以包含多个action、category、data,每组IntentFilter的任意三个及以上action、category、data形成一个匹配约束,只要Java代码里能够匹配一个匹配约束,即可启动目标Activity,如下:

    <activity android:name=".Activity2">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="com.weihy"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http"/>
            intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/*"/>
            intent-filter>
        activity>
        Button button = (Button)findViewById(R.id.openButton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent1 = new Intent(Intent.ACTION_VIEW);
                intent1.setData(Uri.parse("http://www.baidu.com"));
                startActivity(intent1);
            }
        });

  a、action匹配规则
  action是一个字符串,系统预定义了一些action,我们也可以在应用定义自己的action。Java代码里的Intent必须要有action,且这个字符串必须和xml中的action一致,区分大小写。
  b、category匹配规则
  category是一个字符串,系统预定义了一些category,我们也可以在应用定义自己的category。Java代码里category可以缺省,缺省时系统匹配xml中的android.intent.category.DEFAULT字符串,不缺省,匹配规则与action一致。
  c、data匹配规则
  如果xml里的IntentFilter定义了data,那么Java代码里的Intent必须要有data。data由两部分组成,mimeType和URI,mimeType是指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式,URI表示统一资源标识符,可以定位本地和网络的资源,结构如下:

://:/[||]

  如:

    content://com.example.project:200/folder/subfolder/etc
    http://www.baidu.com:80/search/info

  Scheme:URI的模式,如http、file、content等,如果URI没有指定scheme,那么那么其他参数无效,即整个URI无效。
  Host:URI的主机名,如www.baidu.com,如果URI没有指定Host,那么那么其他参数无效,即整个URI无效。
  Port:URI中的端口号,比如80。
  Path:URI中的完整路径信息。
  PathPattern:URI中的完整路径信息,但是可以包含通配符“*”,要表示字符,*要写成\\*,\要写成\\\\。
  PathPrefix:URI中表示路径的前缀信息。

(1)如下过滤规则

            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/*"/>
            intent-filter>

  这种规则指定了媒体类型为所有类型的图片,Intent中mimeType必须为“image/*”才能匹配,xml中的过滤规则虽然没有指定URI但是scheme有默认值为content或file,所以intent中URI的scheme必须为content或file

intent1.setDataAndType(Uri.parse("file://asd"),"image/*");

(2)如下过滤规则

            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/*" android:scheme="file" android:host="asd"/>
            intent-filter>

  像这种指定了完整的data的xml,Java代码不可以先用setData方法,再用setType方法指定URI和mimeType,因为这两个方法会将对方的值互相清空,应该用setDataAndType方法。
  此外,以下两种data过滤规则写法等价

<data android:scheme="file" android:host="asd"/>
    <data android:scheme="file"/>
    <data android:host="asd"/>

  判断是否有目标Activity可以响应我们的Intent,从而 避免crash的两种方法。
  a、Intent的resolveActivity方法。
  b、PackageManager的resolveActivity方法或queryIntentActivities方法,后者不是返回最佳的匹配Activity信息,而是返回所有的匹配Activity信息。MATCH_DEFAULT_ONLY参数是为了将不含DEFAULT的category的Activity排除,因为没有这个的category的Activity无法接收隐式Intent启动的。

        Button button = (Button)findViewById(R.id.openButton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent1 = new Intent(Intent.ACTION_SEND);
                intent1.setDataAndType(Uri.parse("file://asd"),"image/jpg");
                PackageManager packageManager = getApplicationContext().getPackageManager();
                ResolveInfo info = packageManager.resolveActivity(intent1,PackageManager.MATCH_DEFAULT_ONLY);
                if(info != null){
                    startActivity(intent1);
                }else{
                    Toast.makeText(getApplicationContext(),info.toString()+" == info",Toast.LENGTH_LONG);
                }
            }
        });
        <activity android:name=".Activity2">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="com.weihy"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http"/>
            intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/jpg" android:scheme="file" android:host="asd"/>
            intent-filter>
        activity>

  值得注意的是,Android7.0以下我们这么写可以让Activity2匹配到Intent里的信息,但是Android7.0的权限问题,这么写会报错android.os.FileUriExposedException: file://asd exposed beyond app through Intent.getData()。
  最后,还有一种特殊的过滤规则:

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            intent-filter>

  这样的action和category是用来标识这个Activity是应用的入口Activity,并且应用可以出现在应用列表中,二者缺一不可

3、更多隐式Intent用法
  使用隐式Intent不仅可以自己程序内的活动,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享称为可能。
  使用隐式Intent打开系统自带的浏览器:

    textView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this,"this is a toast!",Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setData(Uri.parse("https://www.baidu.com"));
            startActivity(intent);
        }
    });

  Intent.ACTION_VIEW是安卓系统的一个内置动作,它是一个常量字符串——”android.intent.action.VIEW”。
  Uri.parse()将网址解析成一个Uri对象,然后通过setData()方法,将这个Uri对象传递进去
  
  我们可以在标签中再配置一个标签,用于更精确的指定当前活动可以响应什么类型的数据。

        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:scheme="https"/>
            intent-filter>
        activity>
        Button button1 = (Button)findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("android.intent.action.VIEW");
                intent.setData(Uri.parse("https://www.baidu.com"));
                startActivity(intent);
            }
        });

 这时候,除了系统自带的浏览器会响应外,我们的SecondActivity也会响应,就像第二点的截图一样。
  标签主要可以配置一下内容:
  android:scheme。用于指定数据的协议部分,如http部分。
  android:host。用于指定数据的主机名部分,如www.baidu.com部分。
  android:port。用于指定数据的端口部分,一般紧随在主机名后。
  android:path。用于指定主机和端口后的部分,如一端网址中跟在域名之后的内容。
  android:mimeType。用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
  只有Intent中携带的Data和标签中的内容一致时,当前活动才能够响应该Intent。

  除了http协议以外,我们还可以指定其他协议,如geo表示地理位置、tel表示拨打电话。

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

你可能感兴趣的:(Android_Develop)