android 7.1分屏(应用可能无法在分屏模式下正常运行)

平台

rk3288 + android 7.12

现象

在这里插入图片描述

在7.1上, 进入分屏有两种方法,

  1. 点击recent 键, 然后长按应用标题栏, 再拖放到分屏区
  2. 长按recent键, 系统分自动进入分屏, 并让用户选择第二个需要显示的应用.

在进入分屏时, UC浏览器提示: 应用可能无法在分屏模式下正常运行, 点击屏幕或等几秒钟后会自动消失
并非所有应用都会提示.

分析

若想让应用不提示很简单, 给定支持分屏的属性即可:

        android:resizeableActivity="true"
        android:supportsPictureInPicture="true"

这里不详细说明这两个属性.

查查提示的字符串从何而来:
|-- frameworks/base/packages/SystemUI/res/values-zh-rCN/strings.xml

	<string name="dock_forced_resizable" msgid="5914261505436217520">"应用可能无法在分屏模式下正常运行。"</string>

|-- frameworks/base/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java

	//自动隐藏时间.
    private static final long DISMISS_DELAY = 2500;

    private final Runnable mFinishRunnable = new Runnable() {
        @Override
        public void run() {
            finish();
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.forced_resizable_activity);
        TextView tv = (TextView) findViewById(com.android.internal.R.id.message);
        tv.setText(R.string.dock_forced_resizable);//引用文本
        getWindow().setTitle(getString(R.string.dock_forced_resizable));
        getWindow().getDecorView().setOnTouchListener(this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        getWindow().getDecorView().postDelayed(mFinishRunnable, DISMISS_DELAY);
    }

|-- frameworks/base/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java

	    public ForcedResizableInfoActivityController(Context context) {
        mContext = context;
        EventBus.getDefault().register(this);
        //注册TASK 监听, 并由 AM中回调
        SystemServicesProxy.getInstance(context).registerTaskStackListener(
                new TaskStackListener() {
                    @Override
                    public void onActivityForcedResizable(String packageName, int taskId) {
                        activityForcedResizable(packageName, taskId);
                    }

                    @Override
                    public void onActivityDismissingDockedStack() {
                        activityDismissingDockedStack();
                    }
                });
    }

	private void activityForcedResizable(String packageName, int taskId) {
        if (debounce(packageName)) {
            return;
        }
        mPendingTaskIds.add(taskId);
        postTimeout();
    }

    private void showPending() {
        mHandler.removeCallbacks(mTimeoutRunnable);
        for (int i = mPendingTaskIds.size() - 1; i >= 0; i--) {
            Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
            ActivityOptions options = ActivityOptions.makeBasic();
            options.setLaunchTaskId(mPendingTaskIds.valueAt(i));//设置Activity所属的TASK
            options.setTaskOverlay(true);
            mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);//启动并显示提示
        }
        mPendingTaskIds.clear();
    }

|-- frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java

    /**
     * Registers a task stack listener with the system.
     * This should be called on the main thread.
     */
    public void registerTaskStackListener(TaskStackListener listener) {
        if (mIam == null) return;

        mTaskStackListeners.add(listener);
        if (mTaskStackListeners.size() == 1) {
            // Register mTaskStackListener to IActivityManager only once if needed.
            try {
                mIam.registerTaskStackListener(mTaskStackListener);
            } catch (Exception e) {
                Log.w(TAG, "Failed to call registerTaskStackListener", e);
            }
        }
    }

AM 中调用onActivityForcedResizable()
|-- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

			//收到消息并回调
            case NOTIFY_FORCED_RESIZABLE_MSG: {
                synchronized (ActivityManagerService.this) {
                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
                        try {
                            // Make a one-way callback to the listener
                            mTaskStackListeners.getBroadcastItem(i).onActivityForcedResizable(
                                    (String) msg.obj, msg.arg1);
                        } catch (RemoteException e){
                            // Handled by the RemoteCallbackList
                        }
                    }
                    mTaskStackListeners.finishBroadcast();
                }
                break;
            }

	

|-- frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

	void handleNonResizableTaskIfNeeded(
            TaskRecord task, int preferredStackId, int actualStackId, boolean forceNonResizable) {
        if ((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
                || task.isHomeTask()) {
            return;
        }

        final ActivityRecord topActivity = task.getTopActivity();
        if (!task.canGoInDockedStack() || forceNonResizable) {
            // Display a warning toast that we tried to put a non-dockable task in the docked stack.
            mService.mHandler.sendEmptyMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);

            // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
            // we need to move it to top of fullscreen stack, otherwise it will be covered.
            moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
        } else if (topActivity != null && topActivity.isNonResizableOrForced()
                && !topActivity.noDisplay) {//判断是否需要发送NOTIFY_FORCED_RESIZABLE_MSG
            String packageName = topActivity.appInfo.packageName;
            mService.mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, task.taskId, 0,
                    packageName).sendToTarget();
        }
    }

关键函数判断是否发送FORCED_RESIZABLE 消息, 如果ActivityInfo 的 resizeMode是支持分屏的就不发送,
反之则发送提示消息去启动显示信息:
|-- frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java

    boolean isNonResizableOrForced() {
        return !isHomeActivity() && info.resizeMode != RESIZE_MODE_RESIZEABLE
                && info.resizeMode != RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
    }

两个常量分别对应分屏的模式:

import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;

ActivityInfo 的由来:
|-- frameworks/base/core/java/android/content/pm/ActivityInfo.java

	    public int resizeMode = RESIZE_MODE_RESIZEABLE;

|-- frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java

    static ActivityRecord restoreFromXml(XmlPullParser in,
            ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
        ...
        final ActivityManagerService service = stackSupervisor.mService;
        final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
                userId);
        return r;
    }

|-- frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

	    final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
            ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
            TaskRecord inTask) {
		...     
                aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
                        null /*profilerInfo*/);
		...
            aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
    }

调用的顺序一般是: 通过 PackageManagerService 获取ResolveInfo 再获取 ResolveInfo.ai
|-- frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

    @Override
    public ResolveInfo resolveIntent(Intent intent, String resolvedType,
            int flags, int userId) {..}

	private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
            int flags, List<ResolveInfo> query, int userId){}

AndroidManifest.xml的解析工作是由PackageParser完成的, 这个类完成了Package, Service, Activity等组件的解析工作:
|-- frameworks/base/core/java/android/content/pm/PackageParser.java

	    private Activity parseActivity(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError,
            boolean receiver, boolean hardwareAccelerated)
            throws XmlPullParserException, IOException {
			...
            a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
            final boolean appDefault = (owner.applicationInfo.privateFlags
                    & PRIVATE_FLAG_RESIZEABLE_ACTIVITIES) != 0;
            // This flag is used to workaround the issue with ignored resizeableActivity param when
            // either targetSdkVersion is not set at all or <uses-sdk> tag is below <application>
            // tag in AndroidManifest. If this param was explicitly set to 'false' we need to set
            // corresponding resizeMode regardless of targetSdkVersion value at this point in time.
            final boolean resizeableSetExplicitly
                    = sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity);
            final boolean resizeable = sa.getBoolean(
                    R.styleable.AndroidManifestActivity_resizeableActivity, appDefault);

            if (resizeable) {
                if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
                        false)) {
                    a.info.resizeMode = RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
                } else {
                    a.info.resizeMode = RESIZE_MODE_RESIZEABLE;
                }
            } else if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N
                    || resizeableSetExplicitly) {
                a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
            } else if (!a.info.isFixedOrientation() && (a.info.flags & FLAG_IMMERSIVE) == 0) {
                a.info.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
            }
			...
			}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值