Android Activity的启动和跳转

来源:互联网 发布:淘宝网官方网站登入 编辑:程序博客网 时间:2024/05/17 04:59

启动当前app中的Activity

在Android中要跳转到另一个Activity,可以通过在当前Activity中调用startActivity()或startActivityForResult()来实现。
以startActivity()为例,从MainActivity跳转到SecondActivity的示例代码如下。

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

或者

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

或者

Intent intent = new Intent();ComponentName componentName = new ComponentName(MainActivity.this, SecondActivity.class);intent.setComponent(componentName);startActivity(intent);

查看Intent类的源码可以发现这三种方式实际上是完全等价的,都是创建了一个ComponentName对象,然后赋值给Intent类的mComponent成员变量。

启动其他app中的Activity

通常我们都是跳转到当前app自身的其他Activity中,但事实上startActivity()或startActivityForResult()还可以启动手机上其他已经安装的app中的Activity。

假设要启动的Activity所在app的package name为com.ccpat.otherapp,类名为com.ccpat.otherapp.MainActivity。启动该Activity可以使用如下方法。

setComponent方法

示例代码如下

ComponentName componetName = new ComponentName("com.ccpat.otherapp", "com.ccpat.otherapp.MainActivity");Intent intent= new Intent();intent.setComponent(componetName);startActivity(intent);

再举个例子,如果我们想要在自己的app中开启微信,可以使用如下代码。

ComponentName componetName = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI");Intent intent= new Intent();// 在LauncherUI的onCreate中会对当前Activity堆栈的baseactivity的包名做校验,// 如果baseactivity的包名不是"com.tencent.mm",则直接finish。// 所以这里启动的时候需要将其放到一个新的Activity堆栈中 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setComponent(componetName);startActivity(intent);

setClassName方法

代码示例如下:

Intent intent = new Intent();String packageName = "com.ccpat.otherapp";String className = "com.ccpat.otherapp.MainActivity";intent.setClassName(packageName, className);startActivity(intent);

如果要启动的Activity所在的app不存在,或者Activity不存在,则会抛出ActivityNotFoundException异常,如图所示。
这里写图片描述

setComponent和setClassName方法的统一性

查看Intent类的源码可以发现这种方式和setComponent方式是完全等价的,同样都是创建了一个ComponentName对象,然后赋值给Intent类的mComponent成员变量。附上Activity类中的setClassName()方法的实现及注释。

这里写图片描述

由于setComponent和setClassName方法本质上是相同的,所以后面会将它们作为一种方法来看待,称为setComponent/setClassName方法。

setComponent/setClassName方法启动Activity的要求

通过这种方法启动的其他app中的Activity,必须是exported。
在AndroidManifest.xml中声明一个Activity时可以为其添加android:exported属性,它的值可以是true或false。如果一个Activity为exported,它才可以被其他应用启动,否则它就只能在当前app中启动。
如果没有显式的指定这个值,则它的默认值取决于该Activity是否有设置intent-filter,如果有设置intent-filter,则默认为true,否则为false。

如下Activity的exported属性为true

<!-- 显式的声明exported为true --> <activity    android:name=".MainActivity"    android:exported="true" />
<!-- 指定了intent-filter,exported默认为true --> <activity    android:name=".MainActivity">    <intent-filter>        <action android:name="android.net.VpnService" />    </intent-filter></activity>

如下Activity的exported属性为false

<!-- 没有intent-filter,exported默认为false --> <activity    android:name=".MainActivity" />
<!-- 显式的声明exported为false,则无论有无intent-filter,exported属性都是false --> <activity    android:name=".MainActivity"    android:exported="false">    <intent-filter>        <action android:name="android.net.VpnService" />    </intent-filter></activity>

如果通过上述方式启动了其他app中一个非exported的Activity,则会抛出SecurityException异常,如图所示。
这里写图片描述

异常处理

由于无法控制用户手机上是否会安装某个app,也无法决定该app中Activity的类名及exported属性是否会随着版本升级发生改变,所以使用上述方式启动Activity时,通常都需要用try catch来捕获异常。

例如:

try {    ComponentName componetName = new ComponentName("com.ccpat.otherapp", "com.ccpat.otherapp.MainActivity");    Intent intent= new Intent();    intent.setComponent(componetName);    startActivity(intent);} catch (ActivityNotFoundException e) {} catch (SecurityException e) {}

此外,部分机型的ROM将这种启动其他app中Activity的行为纳入到权限管理中,如果一个app要其启动其他app中的Activity,会弹出一个权限请求的界面,只有用户选择同意后才可以启动。

setComponent/setClassName方法启动当前app中的Activity

setComponent/setClassName方法既可以用来启动其他app中的Activity,也可以用来启动当前app中的Activity,只需要将对应的包名和类名替换为当前app的包名和要启动的Activity类名即可。和启动其他app中Activity唯一的不同是,setComponent/setClassName方法启动当前app中的Activity不需要该Activity的exported属性为true。

显式(explicit)intent启动Activity

查看Intent类的源码可以发现,无论是“启动当前app中的Activity”中介绍的三种方式,还是“启动其他app中的Activity”中介绍的setComponent/setClassName方法,本质上都是相同的。它们都是创建了一个ComponentName对象,然后将其赋值给Intent类的mComponent成员变量。这也是为什么setComponent/setClassName方法也可以用来启动当前app中的Activity。

上述这些启动Activity的方式都称为通过显式(explicit)Intent来启动Activity。这种启动方式的特点是它们都需要明确的知道要启动的Activity所在的app的包名及Activity自身的类名。所以如果同时使用了多种方式,则显然只有最后设置的才会生效。

在下面的例子中通过不同的方式设置了4个要启动的Activity,但最后实际启动的只有”com.ccpat.app2”中的”com.ccpat.app2.MainActivity”。

Intent intent= new Intent(MainActivity.this, SecondActivity.class); // 方式1intent.setClass(MainActivity.this, ThirdActivity.class);            // 方式2ComponentName componetName = new ComponentName("com.ccpat.app1", "com.ccpat.app1.MainActivity");intent.setComponent(componetName);                                  // 方式3String packageName = "com.ccpat.app2";String className = "com.ccpat.app2.MainActivity";intent.setClassName(packageName, className);                        // 方式4startActivity(intent);

通过显式Intent来启动其他app中Activity的方式需要已知目标app的包名和Activity类名。这种方式有一定的局限性,一方面我们想要启动某项功能,但是可能并不知道该功能对应的app包名和Activity的类名,另一方面,应用的包名可能会随着Android系统版本或应用市场的不同而不同,Activity的类名也可能随着版本的更新而改变。在Android中提供了另外一种启动其他应用中Activity的方式,即隐式intent的方式。通过隐式intent方式启动Activity不需要知道Activity的类名及对应的包名。

隐式intent启动Activity

通过隐式intent启动其他app中Activity的示例如下:

Intent intent = new Intent("com.ccpat.ACTION_TEST");startActivity(intent);

或者

Intent intent = new Intent();intent.setAction("com.ccpat.ACTION_TEST");startActivity(intent);

这两种方式同样是完全等价的。

隐式intent方法启动Activity的要求

和显式intent方法启动Activity一样,如果目标Activity不在当前app中,而是在其他app中,则必须是exported,否则会抛出SecurityException。此外,要启动的Activity必须要在intent-filter中声明指定的action。

例如,上述方式启动的action为com.ccpat.ACTION_TEST,则需要将com.ccpat.ACTION_TEST声明在要启动的Activity所在app的AndroidManifest.xml中。

<activity    android:name=".MainActivity" >    <intent-filter>        <action android:name="com.ccpat.ACTION_TEST" />        <category android:name="android.intent.category.DEFAULT"     </intent-filter></activity>

由于隐式intent方法启动的Activity必须指定intent-filter,其exported属性默认为true,所以可以不用在AndroidManifest.xml中手动添加这项属性。但是如果只希望在当前app中通过隐式intent启动该Activity,则可以加上android:exported=”false”。

异常处理

和显式Intent一样,通过隐式intent方法启动的Activity时,如果设备中没有找到任何Activity处理该action,则会抛出ActivityNotFoundException的异常。因此,一般来说,通过隐式Intent来启动Activity也应当加上try catch来捕获异常。

隐式intent启动Activity示例

通过隐式intent方式启动Activity只需要知道该Activity所声明的action即可,而action对应的是某项功能,因此只需要知道某项功能对应的action字符串,将其填入intent中。

在Android系统中已经有很多已经定义好action的Activity,它们都可以通过隐式Intent来启动。这里列举了一些常见的ACTION。

// 拨打电话Intent intent = new Intent(Intent.ACTION_CALL);// 打开系统时间设置(在Settings类中还有很多其他定义好的ACTION,对应于其他的设置界面)Intent intent = new Intent(Settings.ACTION_DATE_SETTINGS);// 相机Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// 文件选择Intent intent = new Intent(Intent.ACTION_PICK);// 系统分享Intent intent = new Intent(Intent.ACTION_SEND);

如果有多个Activity声明了同一个Action,系统会按照一定的规则进行匹配。在隐式intent中可以增加一些额外信息用于匹配过程,额外的信息包括PackageName,Category,data,type,这里不再详述。

intent中显式和隐式一起使用

如果在一个intent中同时使用了显式和隐式两种方式来指定要启动的Activity,则实际启动的Activity以显式Intent指定的为准,和他们使用的先后顺序无关。

例如:

Intent intent = new Intent();String packageName = "com.ccpat.otherapp";String className = "com.ccpat.otherapp.MainActivity";intent.setClassName(packageName, className);intent.setAction(Intent.ACTION_VIEW);intent.addCategory("android.intent.category.BROWSABLE");intent.setData(Uri.parse("http://www.csdn.net"));startActivity(intent);

这里先是通过显式intent指定要启动的activity为com.ccpat.otherapp中的com.ccpat.otherapp.MainActivity,然后又通过隐式intent指定要启动的activity为action为Intent.ACTION_VIEW,category为”android.intent.category.BROWSABLE”。 这时会显式intent指定的activity,也就是启动com.ccpat.otherapp中的com.ccpat.otherapp.MainActivity,而不是打开浏览器。

0 0
原创粉丝点击