IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android开发-Activity中“android:exported“属性的作用,以及“Permission Denial: starting Intent“错误解决 -> 正文阅读

[移动开发]Android开发-Activity中“android:exported“属性的作用,以及“Permission Denial: starting Intent“错误解决

如何在一个应用程序中,启动另外一个应用程序?最近正有这样的需求,也踩了一个小坑。本节介绍使用Activity中"android:exported"属性来实现这种访问。

Activity中"android:exported"属性说明:

在程序清单AndroidMenifest.xml文件中,可以设置这个属性。

Android中的Activity中"android:exported"属性设置为true,意味着允许让外部组件启动这个Activity;反之,则不允许让外部组件启动这个Activity;

如果设置了false,又在外部试图启动这个Activity,则会发生程序崩溃,报异常,例如:

java.lang.SecurityException: Permission Denial: starting Intent

入坑指南:

我要实现的功能是在App1中启动App2。在App1中,使用startActivity启动App2的activity,从而实现需求。只要将exported 设置为false,就入坑了。主要代码如下:

在app1中,

            //start other app
            Intent intent = mContext.getPackageManager().getLaunchIntentForPackage("com.test.app2");
            if (intent != null) {
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                mContext.startActivity(intent);
            }

?这就会启动App2中的入口Activity。

在App2中,AndroidManifest.xml文件,

<application android:icon="@drawable/icon"
        android:allowBackup="false"
        android:name=".MyApp2"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Light">
        <activity android:name="com.test.app2.activity.MainActivity"
            
            android:exported="true" //如果为false,就会发生异常
            android:finishOnTaskLaunch="false"
            android:launchMode="singleInstance"

            >

        </activity>
...
/>

?如果为exported 设置为false,就会发生异常,恭喜你,成功入坑。报错信息如下:

03-09 19:53:10.077 20340-20340/com.test.app1 D/AndroidRuntime: Shutting down VM
? ??
? ? --------- beginning of crash
03-09 19:53:10.078 20340-20340/com.test.app1 E/AndroidRuntime: FATAL EXCEPTION: main
? ? Process: com.test.app1, PID: 20340
? ? java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.test.app2 cmp=com.test.app2/com.test.app2.activity.MainActivity } from ProcessRecord{3d6aa2d 20340:com.test.app1/u0a83} (pid=20340, uid=10083) not exported from uid 10117
? ? ? ? at android.os.Parcel.readException(Parcel.java:1620)
? ? ? ? at android.os.Parcel.readException(Parcel.java:1573)
? ? ? ? at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:2658)
? ? ? ? at android.app.Instrumentation.execStartActivity(Instrumentation.java:1507)
? ? ? ? at android.app.Activity.startActivityForResult(Activity.java:3930)
? ? ? ? at android.app.Activity.startActivityForResult(Activity.java:3890)
? ? ? ? at android.app.Activity.startActivity(Activity.java:4213)
? ? ? ? at android.app.Activity.startActivity(Activity.java:4181)
? ? ? ? at com.test.app1.ui.DemoMainActivity.onClick(DemoMainActivity.java:70)
? ? ? ? at android.view.View.performClick(View.java:5204)
? ? ? ? at android.view.View$PerformClick.run(View.java:21153)
? ? ? ? at android.os.Handler.handleCallback(Handler.java:739)
? ? ? ? at android.os.Handler.dispatchMessage(Handler.java:95)
? ? ? ? at android.os.Looper.loop(Looper.java:148)
? ? ? ? at android.app.ActivityThread.main(ActivityThread.java:5417)
? ? ? ? at java.lang.reflect.Method.invoke(Native Method)
? ? ? ? at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
? ? ? ? at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

?当然,根据提示,也可以看出来,是“not exported”,所以,填坑也很容易:

android:exported="true"

源码阅读:

追根溯源,我们来看一下源码。搜索“not exported from uid” ,可以看到,有好几处都能搜到,而且代码文件看名字就能知道,是Android四大组件相关代码,如图:

?结合我们的错误信息提示,定位到错误提示的代码在ActivityStack.java的

startActivityLocked方法中:
    final int startAnyPerm = mService.checkPermission(
                START_ANY_ACTIVITY, callingPid, callingUid);
    final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
                callingUid, aInfo.applicationInfo.uid, aInfo.exported);
    if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {

        String msg;
            if (!aInfo.exported) {
                msg = "Permission Denial: starting " + intent.toString()
                        + " from " + callerApp + " (pid=" + callingPid
                        + ", uid=" + callingUid + ")"
                        + " not exported from uid " + aInfo.applicationInfo.uid;
            } else {
                msg = "Permission Denial: starting " + intent.toString()
                        + " from " + callerApp + " (pid=" + callingPid
                        + ", uid=" + callingUid + ")"
                        + " requires " + aInfo.permission;
            }
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }

哦,原来是在startActivityLocked中抛出的异常。还记得activity的启动流程么,一系列的startActivity相关函数的调用。

通过调用mService.checkPermission 和 mService.checkComponentPermission 进行获取权限。再根据返回值来判断是否有权限,如果没有权限,就会

throw new SecurityException(msg);

这个msg,就是上面我们提到的错误信息。

那么,mService是谁呢?答案就是大名鼎鼎的AMS(ActivityManagerService)的实例。

在check权限的时候,会进行进程id,用户id,是否是同一个应用,是否是系统用户以及exported是否为true......等等条件的判断。

我们直接来看到底是谁去check了exported的。答案是:ActivityManager。调用链:

ActivityStack.startActivityLocked ?-->
? ? ActivityManagerService.checkComponentPermission -->
? ? ? ? ActivityManager.checkComponentPermission ? ?-->

ActivityManager.checkComponentPermission的代码:

/** @hide */
    public static int checkComponentPermission(String permission, int uid,
            int owningUid, boolean exported) {
        // Root, system server get to do everything.
        if (uid == 0 || uid == Process.SYSTEM_UID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // Isolated processes don't get any permissions.
        if (UserId.isIsolated(uid)) {
            return PackageManager.PERMISSION_DENIED;
        }
        // If there is a uid that owns whatever is being accessed, it has
        // blanket access to it regardless of the permissions it requires.
        if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // If the target is not exported, then nobody else can get to it.
        if (!exported) {
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
            return PackageManager.PERMISSION_DENIED;
        }
        if (permission == null) {
            return PackageManager.PERMISSION_GRANTED;
        }
        try {
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            // Should never happen, but if it does... deny!
            Slog.e(TAG, "PackageManager is dead?!?", e);
        }
        return PackageManager.PERMISSION_DENIED;
    }

?可以看到,

// If the target is not exported, then nobody else can get to it.
? ? ? ? if (!exported) {
? ? ? ? ? ? Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
? ? ? ? ? ? return PackageManager.PERMISSION_DENIED;
? ? ? ? }

在这里进行了exported的判断,如果exported为false,就会

return PackageManager.PERMISSION_DENIED;

终于,知道了这个异常是怎么被throw出来的了。


android:exported="true" 和 android:exported="false",弄明白了么?
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-13 21:56:51  更:2022-03-13 21:57:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 17:33:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码