Android传递信息-Intent


Intent详细解释

Intent是一种意图,用于启动Activity、Service、BroadcastReceiver

类型 方法
Activity startActivity(Intent intent)
startActivityForResult(Intent intent, int requestCode)
Service ComponentName startService(Intent service)
boolean bindService(Intent service, ServiceConnection conn, int flags)
BroadcastReceiver sendBroadcast(Intent intent)
sendOrderedBroadcast(Intent intent, String receiverPermission)

右侧的方法都是在Context中(除了startActivityForResult),而且Activity和Service都是Context的子类,就是在Activity和Service中都能启动三大组件

sp161110_091100

Intent有7个属性:Action、Category、Data、Type、Extra、Flag、Compontent

Intent有两种启动方式:显示启动(指定找XXX)、隐式启动(指定条件,比如是一个大美女)

七大属性

Compontent

适用于显示启动,表示来源(就是上下文参数)和启动的目的地(要启动的组件)

三个构造函数:

ComponentName(String pkg, String cls)

ComponentName(Context pkg, String cls)

ComponentName(Context pkg, Class<?> cls)

启动另外一个App中的Activity

第一个可以用来启动第二个APP中的Activity:

        //指定另外一个APP的包名和类名
        ComponentName name = new ComponentName("win.haotinayi.animator", "win.haotinayi.animator.MainActivity");
        Intent intent = new Intent();
        intent.setComponent(name);
        startActivity(intent);

注意第二个参数要写全名,这样可以启动包名是win.haotinayi.animator中的MianActivity

启动本App中的Activity

指定上下文和对应的组件名:

        ComponentName name = new ComponentName(MainActivity.this, "win.haotinayi.intent.Main2Activity");
//两种方法一个是直接写出全名,一个是写出class
//        ComponentName name = new ComponentName(MainActivity.this, Main2Activity.class);
        Intent intent = new Intent();
        intent.setComponent(name);
        startActivity(intent);

简化写法

可以在构造Intent的时候直接写出上下文和类名(只适用显示启动)

        Intent intent = new Intent(MainActivity.this, Main2Activity.class);
        startActivity(intent);

Category和Action

Action表示一个具体的动作,Categoey是Action增加的额外信息。在隐式启动中要给三个组件添加子标签而intent-filter子标签可以有action、category、data。Data和Type属性都在data标签内。

即使没有指定特定的Category系统会默认添加一个的标签。

在intent-filter中可以有多个action和category,但是最多只能有一个data标签,action和category的本质就是一个字符串,用name属性表示,比如主启动Activity:

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

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

隐式启动

启动Mian3Activity的代码:

        Intent intent = new Intent();
        intent.setAction("haotianyi.win");
        startActivity(intent);
        <activity android:name=".Main3Activity">
            <intent-filter>
                <action android:name="haotianyi.win"/>
            </intent-filter>
        </activity>

这时会报错:

sp161110_101257

这是因为系统默认会给intent添加值是android.intent.category.DEFAULT的category,更改注册文件,可以启动

        <activity android:name=".Main3Activity">
            <intent-filter>
                <action android:name="haotianyi.win"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

当两个Activity的注册文件当Action相同,但是category不同,那么只要action和intent匹配就可以启动,AndroidManifest文件:

        <activity android:name=".Main3Activity">
            <intent-filter>
                <action android:name="haotianyi.win"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <!--只是多了一个值是“你妹的”category属性-->
        <activity android:name=".Main2Activity">
            <intent-filter>
                <action android:name="haotianyi.win"/>
                <category android:name="nimeide"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

Activity的启动代码:

        Intent intent = new Intent();
        intent.setAction("haotianyi.win");
        startActivity(intent);

这时候系统会弹出让你选择那一个Activity:

sp161110_101818

从Intent中获得信息

在Mian2Activity中添加代码,获得当前的ComponentName、Action和Category

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

        ComponentName component = getIntent().getComponent();
        System.out.println(component.getClassName());
        System.out.println(component.getPackageName());

        System.out.println(getIntent().getAction());

        Set<String> categories = getIntent().getCategories();
//      假如用for循环的话会报错,因为值是null
//      这里的Categeory是Intent携带的而不是Activity注册的Intent
        System.out.println(categories);
    }

sp161110_102836

从借助Intent从目标组件返回信息

可以借助Intent从一个组件发送信息到另一个组件(处理信息),另外一个组件返回处理后的值,这样的情景可以使用onActivityForResult方法

启动Activity,第二个参数起到一个标识的作用(从哪里来)

        Intent intent = new Intent();
        intent.setAction("haotianyi.win");
        startActivityForResult(intent,0);

在第二个Activity中,设置结果,setResult参数也起到表示作用(处理结果怎么样)

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

但第二个Activity处理完成并且被finish掉,第一个Activity会回调onActivityForResult方法

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode,resultCode,data);
        System.out.println(resultCode);
    }

sp161110_104515

返回桌面

设置Action和Category标签即可

        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        startActivity(intent);

Extra

Intent额外携带的数据,用putXXX来携带各种类型的数据,用getXXXX来获得数据,更改上面的案例,Activty1的发送代码:

        Intent intent = new Intent();
        intent.putExtra("hello","你好啊");
        intent.setAction("haotianyi.win");
        startActivityForResult(intent,0);

Activity2的处理代码,就是给原有数据添加一句话

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        String hello = getIntent().getStringExtra("hello");
        hello += "-----这时来自Activity2的问候";
        Intent result = getIntent().putExtra("hello", hello);
//      打印Activity2正在处理
        System.out.println(getLocalClassName()+"正在处理数据-------"+hello);
        setResult(1,result);
    }

Activity1的回调代码

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode,resultCode,data);
//      打印结果码
        System.out.println(resultCode);
//      打印处理之后的结果
        System.out.println(getLocalClassName()+"处理之后的数据是------"+data.getStringExtra("hello"));
    }

sp161110_105503

Data和Type

Intent的Data属性是执行动作的URI和MIME类型,不同的Action有不同的Data数据指定。比如:ACTION_EDIT Action应该和要编辑的文档URI Data匹配,ACTION_VIEW应用应该和要显示的URI匹配。

Intent的Type属性显式指定Intent的数据类型(MIME)。一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行判定。只要Type负责abc/xyz格式的字符串都可以。

Data和Type在单独指定的时候会相互覆盖,同时指定来两个值使用:setDataAndType方法

Data的匹配规则

Data的参数类型是Uri,例如content://win.hao.ty/data/1

Uri对应的名称是scheme://host/path 三个部分,在host后面可能有port相当于端口号

scheme、host、path是一个范围递减的三个属性(从大到小),就是某一个注册Activity只是指定了scheme属性,那么所有的Intent的Data属性只要以对应字段开头都会匹配

具体匹配实例

有这样三个Activity

       <activity android:name=".Main2Activity">
            <intent-filter>
                <action android:name="haotianyi.win"/>
                <data android:scheme="hao"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

        <activity android:name=".Main3Activity">
            <intent-filter>
                <action android:name="haotianyi.win"/>
                <data android:scheme="hao" android:host="tianyi"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

        <activity android:name=".Main4Activity">
            <intent-filter>
                <action android:name="haotianyi.win"/>
                <data android:scheme="hao" android:host="tianyi" android:path="/datas/1"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

对应的Intent如下,分别注释掉不同的赋值语句在启动的时候就会打开几个Activity

        Intent intent = new Intent("haotianyi.win");
//       只是启动一个Activity
        intent.setData(Uri.parse("hao://"));
//      启动了两个Activity
        intent.setData(Uri.parse("hao://tianyi"));
//       启动了三个Activity
        intent.setData(Uri.parse("hao://tianyi/datas/1"));
        startActivity(intent);

Flag

Flag经常和Activity的启动模式一起来玩,Activity的四种启动模式:

默认模式

standard:不管这个Activity是否在栈中,都执行new操作,假如:栈中顺序是A B C D ,此时D通过Intent跳转到A,那么栈中结构就变成 A B C D A ,点击返回按钮的 显示顺序是 D C B A,依次摧毁。

对应的flag:Intent.FLAG_ACTIVITY_NEW_TASK

当目标Activity的affinity的属性和这个Activity相同时,就完全是默认模式,否则就是在一个新栈的默认模式

SingleTop

singleTop:永远在栈顶,当前Activity D位于栈顶的时候,如果通过Intent跳转到它本身的Activity (即D),那么不会重新创建一个新的D实例,所以栈中的结构依旧为A B C D,如果跳转到B,那么由于B不处于栈顶,所以会新建一个B实例并压入到栈中,结构就变成了A B C D B。

对应flag:FLAG_ACTIVITY_SINGLE_TOP

SingleTask

singleTask:也是永远在栈顶,不过当要启动的D不再栈顶时,会把上面的Activity全部干掉

对应flag:FLAG_ACTIVITY_CLEAR_TOP

SingleInstance

singleInstance:所有栈内只有一个实例,当启动D时,会先新建一个栈,然后再把D入栈

其他flag

FLAG_ACTIVITY_NO_HISTORY 一旦退出,它不会在栈内,比如:原来是A,B,C这个时候再C中以这个FLAG启动D的,D再启动E,这个时候栈中情况为A,B,C,E。


We're here to put a dent in the universe. Otherwise why else even be here?