yunshuipiao / Potato

Read the fucking source code for the Android interview
Apache License 2.0
80 stars 12 forks source link

Android:Intent and startActivity #64

Open yunshuipiao opened 5 years ago

yunshuipiao commented 5 years ago

Android:Intent and startActivity

[TOC]

本篇文章介绍在 Android 中打开另一个 Activity 的可行性验证

基础知识

App 的入口 Activity 和 Icon

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

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

任何 App 都有一个默认入口 Activity,一般在 AndroidManifest.xml 中如上配置。

如果将 category 修改为 :<category android:name="android.intent.category.DEFAULT"/>,那么该应用在桌面icon不可见,常见于一些设置 App。

使用 Intent 打开第三方应用或者指定 Activity 的方式

  1. 只知道包名-需要有默认的入口 Activity
  2. 启动指定第三方应用的 Activity,需要包名和 Activity 名,且该 Activity 的 Export="true";
  3. 隐式启动第三方应用

使用 PackageManager.getLaunchIntentForPackage()

String package_name="xx.xx.xx";
PackageManager packageManager = context.getPackageManager();
Intent it = packageManager.getLaunchIntentForPackage(package_name);
startActivity(it);

该方法针对只知道包名,想要启动该应用时使用,对该应用的唯一限制:有默认的入口 Activity

当没有默认的入口 Activity 时,会报 空指针 异常。

因此只需要如下判断即可:

val pkg = "com.android.vending"
val intent = packageManager.getLaunchIntentForPackage(pkg) ?: return
startActivity(intent)

使用 Intent.setComponent()

    fun startActivityWithComponent(){
        val pkg = "com.coolapk.market"
//        val cls = "com.coolapk.market.view.appmanager.AppManagerActivity"
        val cls = "com.coolapk.market.view.main.MainActivity"
        val intent = Intent()
        // 可选
//        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        val comp = ComponentName(pkg, cls)
        intent.component = comp
        startActivity(intent)
    }

此方法可以启动任意的 Activity,但限制比较多。

  1. 知道 App 的包名和 Activity 的全路径名称

  2. 需要目标 Activity 在 AndroidManifest.xml 中的属性为 :Export="true"

    这里需要注意,对于自己私有的 Activity,设置 intent-filter 之后,就会设置Export="true"

这种情况下应该如何判断该 Activity 是否可以打开:

使用:resolveActivityInfo 方法。

 fun startActivityWithComponent(){
        val pkg = "com.coolapk.market"
        val cls1 = "com.coolapk.market.view.main.MainActi"
        val cls2 = "com.coolapk.market.view.main.MainActivity"
        val arrayList = arrayListOf(cls2)
        // 可以针对 cls1, cls2 做测试
        val intent = Intent()
        // 可选
//        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        arrayList.forEach {
            val comp = ComponentName(pkg, it)
            intent.component = comp
            if (intent.resolveActivityInfo(packageManager, PackageManager.MATCH_DEFAULT_ONLY) != null) {
                startActivity(intent)
            }
        }
    }

隐式启动第三方应用

此方法用于启动系统中的功能性应用:比如电话,邮件,浏览器,图片预览等等。

 Intent intent = new Intent();
 intent.setAction(action);
 intent.addCategory(category);
 intent.setDataAndType("abc://www.dfg.com","image/gif");
 startActivity(intent);

条件:

  1. intent-filter 至少有一个action,一个 Category,可以没有 Data 和 Type。
  2. 如果有 Data,参数中 Data 比如符合规则。
  3. Action 或者 Category 必须同时匹配 Activity 中的一个 Action 和 一个 Category(默认:android.intent.category.DEFAULT)

这是,可以使用 Intent#resolveActivity方法即可判断。

Uri uri = Uri.parse("http://www.abc.xyz");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);

if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
} else {
    // 没有安装所需应用
}

总结

尽可能使用 intent.resolveActivityInfo() 方法