一个应用中总会存在多个活动,通过这些活动的相互跳转,数据传递,逻辑处理,便可完成一个应用的功能。在各个活动的跳转时,会通过Intent(意图),Intent使Activity跳转大致可分为显示意图和隐式意图.
1.使用显式 Intent
Intent有多个构造函数的重载,其中一个是Intent(Context packageContext ,Class<?>cls)。这个构造函数接收两个参数,第一个参数Context是要求提供一个启动活动的上下文,第二个参数需要指定一个目标活动。如下代码:
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(Intent);
活动本就是一个Context对象,所以第一个参数传入this,第二个参数传入SecondActivity为目标活动,然后通过startActivity()方法执行这个Intent。这种传入启动活动和目标活动的方式称为显示Intent,不要忘记在AndroidManifest.xml文件中添加Activity声明。
2.使用隐式 Intent
相比较与显示,隐式Intent则含蓄了很多,它并没有明确指出我们想要启动哪个活动,而是通过指定一系列更为抽象的action和category等信息,然后由系统去分析这个Intent,并帮我们找到合适的活动去启动。
什么叫做合适的活动呢,简单来说就是可以响应我们这个隐式Intent的活动,就目前来说,SecondActivity可以响应什么样的隐式Intent呢,其实它现在还什么都响应不了。需要我们手动的去设置。之前提到过要在配置文件中声明我们的活动,在活动的标签下配置<intent-filter>的内容,从字面理解标签的意思是<意图-筛选>。
<activity android:name=".SecondActivity">
<intent-filter>
<action name="com.hm.intenttestdemo.ACTION.START"/>
<category name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
只有某个活动能够同时匹配<action>和<category>时,才可以通过Intent来启动它。
Intent intent = new Intent();
intent.setAction("com.hm.intenttestdemo.ACTION.START");
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
初始化Intent,为intent设置Action,添加Category,此时系统会自动帮助我们找到合适的需要启动的活动,系统如何找到呢,这就回到了刚才我们在配置文件中设置的<intent-filter>标签的中的action和category了,如果同时满足,则该活动就是系统为我们找到的将要启动的最合适的活动了。其实每个活动在配置文件中声明时,都会默认设置category参数,默认的值都为Intent.Category_Default,所以该属性的设置通常使用时都省略设置了。
3.更多隐式Intent的用法
其实Intent的用法还有很多,设想一个应用需要访问一个网址,就拿百毒来说吧,我们可以在应用内做一个WebView活动专门负责上网,但是我们Android系统有自带强大的浏览器应用,那如何在使用其他应用时启动浏览器应用呢。通过下面的方法:
Intent intent =newIntent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("www.baidu.com"));
startActivity(intent);
这里我们首先设置Intent的Action是Intent.Action_View,这个是Android内置的动作,其常量值为android.intent.action.VIEW.然后通过parse()方法,讲一个字符串转为Uri对象。再调用setData()方法将Uri传入。这样就可以访问系统的浏览器应用了。
回顾之前Intent的隐式意图的使用,在配置文件中<intent-filter>配置中也可以添加<data>标签,用于指定当前活动能够响应什么样的数据类型。
android:scheme 用于指定数据的协议部分,如上例中的http部分
android:host 用于指定数据的主机名部分,如上列中的www.baidu.com
android:port 用于指定数据的端口部分,一般紧随在主机名之后
android:path 用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容
android:mimeType 用于指定可以处理的数据类型,允许指定通配符的方式进行指定。
只有指定了<data>标签中的内容和Intent中的携带的Data完全一致时,当前活动才能够响应Intent.但是通常在<intent-filter>的<data>中,都不会指定过多的内容,比如上面的例子,只需要指定android:scheme为http,就可以响应所有的http协议的Intent了。同样的如果你同时配置了android:host为www.baidu.con时,那Intent携带的data也只能为该Url地址才能响应活动。
当然,除了http协议外,我们还可以指定其他更多的协议,来调用系统的应用为我们服务,比如相机(Camera),电话(Tel),地理位置(Geo)等。
4.Intent传递数据(向下)
当我们通过Intent在各个活动跳转时,避免不了需要传递数据,在向目标活动传递数据时,不管是通过显示还是隐式的方式传递时,系统提供了很多方便的Api供我们使用。
Intent intent = new Intent(Context context,XXActivity.class);
intent.putExtra("IngeterNum",12);
intent.putExtra("DoubleNum",10.123);
intent.putExtra("BooleanFlag",true);
intent.putExtra("StringStr","haha");
startActivity(intent);
当然在目标活动页,就是需要接受参数的活动页我们通过:
Intent intent = getIntent();
int intNum = intent.getIntExtra("IngeterNum",10);
String str = intent.getStringExtra("StringStr");
我们可以发现在putExtra()方法中传入的Key要与目标活动页使用getXXExtra()方法接收参数时传入的key要完全相同,同时有的该方法需要你传入一个默认值,通常基本的参数类型只需要设置就可以使用了。
基本类型使用很简单,如果我们需要传递对象怎么办?其实也只是比基本类型稍微复杂一点,当我们创建一个对象时,会接触到一个对象可序列化的概念,通过类实现Serializable接口或者Parcelable。
4.1通过实现Serializable接口
传递数据
Person p =newPerson();
p.setAge(25);
p.setName("张三");
intent.putExtra("Person_Key",p);
startActivity(intent);
接收数据
Person person = (Person)getIntent().getSerializableExtra("Person_Key");
同样传递数据和接收数据时的Key要统一。
4.2通过转为Json字符串传递
将对象类型转为字符串类型,也是数据传递的一种方式
传递数据
Person p =newPerson();
p.setAge(25);
p.setName("张三");
intent.putExtra("Person_Json_key",new Gson().toJson(p));
startActivity(intent);
接收数据
String personJson = getIntent().getStringExtra("Person_Json_key");
Person p = newGson().fromJson(personJson,Person.class);
该方法主要使用Gson对象的方法,先将对象转为String,再将String在目标活动界面接收后转为对象。
4.3通过实现Parcelable接口
实现Parcelable接口需要实现两个方法
describeContents方法。 内容接口描述,默认返回0就可以。
writeToParcel方法。 将传递的数据打包到Parcelable容器中。
除了要实现这两个方法还必须要创建一个Parcelable.Creator接口的实例,用于读取Parcel容器中的数据
传递数据
Person p =newPerson();
p.setAge(25);
p.setName("张三");
intent.putExtra("Person_Json_key",new Gson().toJson(p));
startActivity(intent);
接收数据
Person person = (Person)getIntent().getSerializableExtra("Person_Key");
同样传递数据和接收数据时的Key要统一。
简化Parcel操作
如果你使用android Studio 可以通过安装android-parce-intellij-plugin插件,或者自己配置模板进行操作。
5.Intent传递数据(向上)
Intent既然可以向下一个活动传递数据,自然也可以向上一个活动传递数据,当然返回上一个活动只需要按一下设备的返回按钮便会返回到上一个活动。但是如何向上一个活动传递数据呢?其实startActivity()方法是开启一个新的活动,startActivityForResult()方法也是开启一个新活动的方法,当然该方法有两个参数,第一个参数毫无疑问是intent,第二参数是一个整形参数的请求码。
Intent intent = new Intent(context,OtherActivity.class);
startActivityForResult(intent,1);
我们来到刚才启动的OtherActivity活动页面,通过出发一个Button的点击事件来返回到上一个活动页。
((Button)findViewById(R.id.id_button)).setOnClickListener(new View.OnClickListener(){
public void OnClick(View view){
Intent intent = new Intent();
intent.putExra("data_return","Hello FirstActivity");
setResult(RESULT.OK,intent);
finish();
}
});
在button的点击事件中,我们先创建了Intent对象,通过putExtra()方法为Intent添加需要传递的参数,调用活动的setResult()方法,然后finish()掉本活动。注意setResult()方法,传递了两个参数,一个是RESULT.OK,这个是系统的整形常量,与它相似的还有一个是RESULT.CANCELED,这两个值代表的是向上返回的处理结果,intent中携带者需要向上传递的数据.由于我们启动下一个活动使用的是startActivityForResult()方法,活动就会调用onActivityResult()方法,因此我们需要在FirstActivity中重写这个方法:
@Override
protected void onActivityResult(int requestCode, int resultCode,Intent data) {
switch(requestCode) {
case1:
if(resultCode ==RESULT_OK) {
String returnData = data.getStringExtra("data_return");
Log.d("FirstActivity",returnData);
}
break;
}
onActivityResult()方法有三个参数,第一个参数requestCode,即我们请求活动跳转时传递的请求码,第二个参数时resultCode,即我们在返回数据时传入的处理结果,第三个参数时Intent(data),即返回携带者的Intent。因为可能会有多个地方在跳转到其他活动时调用startActivityForResult方法传入不同的requestCode,在返回上一个页面时调用setResult方法时,传入不同的处理结果码,所以我们在回调onActivityResult方法时需要通过盘点请求码(requestCode)和返回码(resultCode),来处理不同的逻辑。