Android-Service里面如何启动Activity?为什么要这么做?

1. 一个应用中有多少个Context,它们之间的区别是什么?

Context数量=Activity数量+Service数量+Application
在这里插入图片描述
从图中我们知道:

  • Context其实是一个抽象类,它有两个子类:ContextImplContextWrapper,ContextImplcontext的功能具体实现类,而ContextWrapper则是一个包装类,主要是调用ContextImpl里面的具体实现
  • ContextThemeWrapperServiceApplication都继承自ContextWrapper
  • Activity具有主题因此继承了ContextThemeWrapper

2. Service/广播中是否可以启动Activity?为什么?

在Activity中启动如下:

Intent intent=new Intent(this,TargetActivity);
startActivity(intent)

那么在Service中如何这么用会出现什么问题呢?

 java.lang.RuntimeException: Error receiving broadcast Intent { act=com....}...Caused by: android.util.AndroidRuntimeException:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

问题说是确少flags FLAG_ACTIVITY_NEW_TASK
那么加上

Intent intent=new Intent(this,TargetActivity);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)

正常启动…

分析
根据在Activity中startActivity的启动源码得知启动过程中并未对FALG进行检查

@Override
    public void startActivityForResult(
            String who, Intent intent, int requestCode, @Nullable Bundle options) {
        Uri referrer = onProvideReferrer();
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, who,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, who, requestCode,
                ar.getResultCode(), ar.getResultData());
        }
        cancelInputsAndStartExitTransition(options);
    }

ServiceBroadcastReciver中呢(ContextImpl)

@Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        final int targetSdkVersion = getApplicationInfo().targetSdkVersion;

        //[9.0系统源码]
        //[6.0的时候只有这一项条件]如果没有addFlags FALG_ACTIVITY_NEW_TACK (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 为true
        //[9.0新增](targetSdkVersion < Build.VERSION_CODES.N
        //                || targetSdkVersion >= Build.VERSION_CODES.P) 28 9.0=>版本<24 7.0
        //[7.0新增] options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) false 因为 options=null 有bug 导致有的版本可绕过次异常
        //[9.0修改为] options == null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) false
        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && (targetSdkVersion < Build.VERSION_CODES.N
                        || targetSdkVersion >= Build.VERSION_CODES.P)
                && (options == null
                        || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                            + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                            + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

有此我们可知:在Android7.0、8.0因为options判断的bug导致可以绕过flag的检查,因此我们最好还是加上flag标记。

为什么要如何设计?
之所以将Activity与其他组件以不同的方式启动Activity,是因为如果在其他组件启动Activity时,此时任务栈是其他程序的任务栈,导致要启动的Activity在其他的程序的任务栈中了,因此需要创建新的Task.

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴唐人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值