somethings basic for bapi badi and user exit

本文介绍了SAP系统中用于增强标准程序功能的三种主要技术:BAPI、BADI 和 USEREXIT 的工作原理及区别。重点阐述了 BADI 和 USEREXIT 在 SAP 标准程序更新时的不同表现,并对比了 BDC 和 BAPI 在处理 SAP 交易时的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 BAPI - These are published programs which is used to upload data i
nto SAP system.

BADI - This is a program enhancement technique. SAP pr
ovides BADI openings in the standard programs. You need to search for the s
uitable BADI as ur requirement and then do the coding and plug in the progr
am.

USEREXIT - It is also a program enhancement technique. here also u
need to find suitable userexit and code in ur program.

The main diff
bet BADI and USEREXIT is that in USEREXIT u code in the standard SAP progra
m, hence any updation in the version of the standard program will lead to t
he loss of ur coding. But same is not the case of BADI. Here the code remai
ns outside the standard program.

2. Both BDC and BAPI will work. For a
njoi SAP transaction, prefer a BAPI over a BDC

// frameworks/base/services/core/java/com/android/server/wm/ParallelSession.java package com.android.server.wm; import android.app.ActivityOptions; import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.XmlResourceParser; import android.graphics.Rect; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.Xml; import android.view.DisplayInfo; import android.view.WindowManager; import android.view.InputChannel; import com.android.server.input.InputManagerService; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.CopyOnWriteArrayList; import java.util.HashSet; import java.util.Set; import com.android.server.wm.ActivityRecord.State; /** * 管理平行视界会话,处理主副屏Activity的生命周期同步和布局 */ public class ParallelSession { private static final String TAG = "ParallelSession_wangw"; private static final boolean DEBUG = true; // 全局会话存储(按任务ID索引) private static final SparseArray<ParallelSession> sSessions = new SparseArray<>(); // 平行应用映射(包名 -> 副屏Activity类名) public static final ArrayMap<String, String> sParallelApps = new ArrayMap<>(); static { Log.i(TAG, "sParallelApps init !"); sParallelApps.put("com.taobao.taobao", "com.taobao.shop.activity.DetailActivity"); sParallelApps.put("com.jingdong.app.mall", "com.jd.lib.productdetail.ProductDetailActivity"); sParallelApps.put("com.alibaba.android.rimet", "com.alibaba.android.rimet.biz.web.WebActivity"); } private final int mTaskId; private ActivityRecord mPrimaryActivity; // 主屏活动 private ActivityRecord mSecondaryActivity; // 副屏活动 private float mSplitRatio = 0.4f; // 分屏比例(默认50%) private WindowState mPrimaryWindow; // 主屏窗口 private WindowState mSecondaryWindow; // 副屏窗口 // 使用原子操作标记防止递归 final AtomicBoolean isDestroying = new AtomicBoolean(false); // 生命周期监听器接口(用于状态同步) public interface LifecycleListener { void onStateChanged(ActivityRecord activity, State state); void onDestroyed(ActivityRecord activity); void onConfigurationChanged(ActivityRecord activity, Configuration newConfig); } // 线程安全的生命周期监听器列表 private final CopyOnWriteArrayList<LifecycleListener> mLifecycleListeners = new CopyOnWriteArrayList<>(); public ParallelSession(ActivityRecord primary) { //if(primary.getTask() == null) return; mTaskId = primary.getTask().mTaskId; //mPrimaryActivity = primary; sSessions.put(primary.getTask().mTaskId, this); setPrimaryActivity(primary); if (DEBUG) Log.d(TAG, "Created new ParallelSession for task: " + primary.getTask().mTaskId +", primary = "+primary.toString()); } public ParallelSession(int taskId, ActivityRecord primary) { //if(primary.getTask() == null) return; mTaskId = taskId; //mPrimaryActivity = primary; sSessions.put(taskId, this); setPrimaryActivity(primary); Log.i(TAG, "Created new ParallelSession for task:" + taskId); } // 设置主屏活动 public void setPrimaryActivity(ActivityRecord primary) { //if (mPrimaryActivity != null && sParallelApps.containsKey(primary.packageName)) { if (primary != null) { Log.w(TAG, "Primary activity != null"); if (!sParallelApps.containsKey(primary.packageName)) { Log.w(TAG, "Primary activity set failed!"); return; } } mPrimaryActivity = primary; //getPrimaryWindow().attach(); if(getWindowStateForRecord(mPrimaryActivity) != null) getWindowStateForRecord(mPrimaryActivity).attach(); linkPrimaryActivities(); if (DEBUG) Log.d(TAG, "Primary activity set: " + primary); } // 设置副屏活动 public void setSecondaryActivity(ActivityRecord secondary) { if (mSecondaryActivity != null) { Log.w(TAG, "Secondary activity already set!"); return; } mSecondaryActivity = secondary; linkSecondaryActivities(); if (DEBUG) Log.d(TAG, "Secondary activity set: " + secondary); } private void linkPrimaryActivities() { // 绑定生命周期事件 if (DEBUG) Log.d(TAG, "linkPrimaryActivities"); mPrimaryActivity.addLifecycleListener(new LifecycleListener() { @Override public void onStateChanged(ActivityRecord activity, State state) { if (DEBUG) Log.d(TAG, "linkPrimaryActivities mPrimaryActivity onStateChanged"); if (state == ActivityRecord.State.PAUSED && mSecondaryActivity != null) { if (DEBUG) Log.d(TAG, "Primary paused, pausing secondary"); // 使用任务调度暂停副屏活动 //mSecondaryActivity.pause(); //mSecondaryActivity.getTask().schedulePauseActivity(mSecondaryActivity); } else if (state == ActivityRecord.State.RESUMED && mSecondaryActivity != null) { if (DEBUG) Log.d(TAG, "Primary resumed, resuming secondary"); if (mSecondaryActivity.isState(ActivityRecord.State.RESUMED)) return; // 使用系统服务恢复副屏活动 //if (mSecondaryActivity.getTask() != null) mSecondaryActivity.getTask().resumeTopActivityUncheckedLocked(null,null); // 异步恢复副屏Activity handler.post(() -> { if (mSecondaryActivity.getTask() != null) { mSecondaryActivity.getTask().resumeTopActivityUncheckedLocked(null, null); } }); //mSecondaryActivity.resume(); } handleFocusChange(mPrimaryWindow); } @Override public void onConfigurationChanged(ActivityRecord activity, Configuration newConfig) { if (DEBUG) Log.d(TAG, "linkPrimaryActivities mPrimaryActivity onConfigurationChanged"); } @Override public void onDestroyed(ActivityRecord activity) { if (isDestroying.getAndSet(true)) { return; // 已经在销毁过程中 } /* if (DEBUG) Log.d(TAG, "linkPrimaryActivities mPrimaryActivity onDestroyed"); if (mSecondaryActivity != null && !mSecondaryActivity.finishing) { if (DEBUG) Log.d(TAG, "Primary destroyed, finishing secondary"); // 使用安全销毁方法 mSecondaryActivity.destroyIfPossible("Primary destroyed"); //mSecondaryActivity.finishIfPossible("Primary destroyed", true); } sSessions.remove(activity.getTask().mTaskId); */ try { if (DEBUG) Log.d(TAG, "linkPrimaryActivities mPrimaryActivity onDestroyed"); if (mSecondaryActivity != null && !mSecondaryActivity.finishing) { // 移除监听器防止回调 mSecondaryActivity.removeLifecycleListener(this); // 延迟销毁副屏Activity handler.postDelayed(() -> { if (!mSecondaryActivity.finishing) { mSecondaryActivity.destroyImmediately("Primary destroyed"); } }, 100); // 100ms延迟确保主Activity完全销毁 } } finally { if(sSessions != null && mSecondaryActivity != null && mSecondaryActivity.getTask() != null) sSessions.remove(activity.getTask().mTaskId); isDestroying.set(false); } } }); } private void linkSecondaryActivities() { // 绑定生命周期事件 if (DEBUG) Log.d(TAG, "linkSecondaryActivities"); // 副屏关闭时同步主屏 mSecondaryActivity.addLifecycleListener(new LifecycleListener() { @Override public void onStateChanged(ActivityRecord activity, State state) { if (DEBUG) Log.d(TAG, "linkSecondaryActivities mSecondaryActivity onStateChanged"); /* if (state == ActivityRecord.State.RESUMED && mPrimaryActivity != null) { if (DEBUG) Log.d(TAG, "Primary resumed, resuming secondary"); if (mPrimaryActivity.isState(ActivityRecord.State.RESUMED)) return; // 使用系统服务恢复副屏活动 if (mPrimaryActivity.getTask() != null) mPrimaryActivity.getTask().resumeTopActivityUncheckedLocked(null,null); //mSecondaryActivity.resume(); } */ handleFocusChange(mSecondaryWindow); } @Override public void onConfigurationChanged(ActivityRecord activity, Configuration newConfig) { if (DEBUG) Log.d(TAG, "linkSecondaryActivities mSecondaryActivity onConfigurationChanged"); } @Override public void onDestroyed(ActivityRecord activity) { if (isDestroying.getAndSet(true)) { return; } /* if (DEBUG) Log.d(TAG, "linkSecondaryActivities mSecondaryActivity onDestroyed"); if (mPrimaryActivity != null && !mPrimaryActivity.finishing) { if (DEBUG) Log.d(TAG, "Secondary destroyed, finishing primary"); // 使用安全销毁方法 mPrimaryActivity.destroyIfPossible("Secondary destroyed"); //mPrimaryActivity.finishIfPossible("Secondary destroyed", true); } sSessions.remove(mPrimaryActivity.getTask().mTaskId); */ try { if (DEBUG) Log.d(TAG, "Secondary destroyed"); if (mPrimaryActivity != null && !mPrimaryActivity.finishing) { // 移除监听器防止回调 mPrimaryActivity.removeLifecycleListener(this); // 延迟销毁主Activity handler.postDelayed(() -> { if (!mPrimaryActivity.finishing) { mPrimaryActivity.destroyImmediately("Secondary destroyed"); } }, 100); } } finally { if(sSessions != null && mPrimaryActivity != null && mPrimaryActivity.getTask() != null) sSessions.remove(mPrimaryActivity.getTask().mTaskId); isDestroying.set(false); } } }); } private Handler handler = new Handler(Looper.getMainLooper()){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); //todo somethings //if (msg.what == MSG_WHAT) { // 处理消息 //Log.e("CentralSurfacesImpl_wangw", "mHandler2 1 Message received"); //} } }; // 计算主屏显示区域 public Rect getPrimaryBounds(DisplayContent display) { if (DEBUG) Log.d(TAG, "getPrimaryBounds"); if (display == null) { Log.w(TAG, "Null display for primary bounds"); return new Rect(0, 0, 1080, 1920); // 默认值 } DisplayInfo info = display.getDisplayInfo(); Rect bounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight); bounds.right = (int) (bounds.width() * mSplitRatio); // 按比例分割 return bounds; } // 计算副屏显示区域 public Rect getSecondaryBounds(DisplayContent display) { if (DEBUG) Log.d(TAG, "getSecondaryBounds"); if (display == null) { Log.w(TAG, "Null display for secondary bounds"); return new Rect(0, 0, 1080, 1920); // 默认值 } DisplayInfo info = display.getDisplayInfo(); Rect bounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight); bounds.left = (int) (bounds.width() * mSplitRatio); // 按比例分割 return bounds; } public void setSplitRatio(float ratio) { if (DEBUG) Log.d(TAG, "setSplitRatio"); mSplitRatio = Math.max(0.2f, Math.min(0.8f, ratio)); if (mPrimaryActivity != null && mPrimaryActivity.getWindow() != null) { mPrimaryActivity.getTask().reparent(null, true); } if (mSecondaryActivity != null && mSecondaryActivity.getWindow() != null) { mSecondaryActivity.getTask().reparent(null, true); } } public static boolean shouldOpenInSecondary(ActivityRecord caller, ActivityRecord target) {//控制是否在第二界面打开 if (DEBUG) Log.d(TAG, "shouldOpenInSecondary, caller = "+caller+", target = "+target); if (caller == null || !(sParallelApps.containsKey(caller.packageName))) { return false; } return sParallelApps.containsKey(target.packageName); /* if (caller == null || !sParallelApps.containsKey(caller.packageName)) { return false; } String expectedActivity = sParallelApps.get(caller.packageName); return expectedActivity.equals(targetInfo.targetActivity) || expectedActivity.equals(targetInfo.name); */ } /* public static void loadConfigFromFile(File configFile) { if (!configFile.exists()) { Log.w(TAG, "Parallel config file not found: " + configFile); return; } try (FileInputStream fis = new FileInputStream(configFile)) { XmlResourceParser parser = Xml.newPullParser(); parser.setInput(fis, "UTF-8"); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { if (type != XmlPullParser.START_TAG) continue; final String tagName = parser.getName(); if ("parallel-apps".equals(tagName)) { parseParallelApps(parser); } } } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Error loading parallel config", e); } } private static void parseParallelApps(XmlPullParser parser) throws IOException, XmlPullParserException { sParallelApps.clear(); int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type != XmlPullParser.START_TAG) continue; if ("app".equals(parser.getName())) { String pkg = parser.getAttributeValue(null, "package"); String secondary = parser.getAttributeValue(null, "secondary"); if (pkg != null && secondary != null) { sParallelApps.put(pkg, secondary); if (DEBUG) Log.i(TAG, "Added parallel app: " + pkg + " -> " + secondary); } } } } */ // 在 ActivityRecord 类中添加获取 WindowState 的方法 /* public WindowState getWindowState(ActivityRecord record) { // 1. 获取 AppWindowToken final AppWindowToken appWindowToken = record.appWindowToken; if (appWindowToken == null) { return null; } // 2. 获取 WindowToken final WindowToken windowToken = appWindowToken.getWindowToken(); if (windowToken == null) { return null; } // 3. 获取 WindowState for (int i = windowToken.getChildCount() - 1; i >= 0; i--) { final WindowState win = (WindowState) windowToken.getChildAt(i); if (DEBUG) Log.d(TAG, "getWindowState window = " + win); if (win != null && win.mActivityRecord == record) { return win; } } return null; } */ // 正确获取与 ActivityRecord 关联的 WindowState public WindowState getWindowStateForRecord(ActivityRecord record) { WindowManagerService wms = record.mWmService; if (wms == null) { return null; } synchronized (wms.mGlobalLock) { // 方法1:直接通过 token 获取(推荐) WindowState win = wms.mWindowMap.get(record.token); if (win != null && win.mActivityRecord == record) { if (DEBUG) Log.d(TAG, "getWindowState win = " + win.mActivityRecord); return win; } // 方法2:遍历所有窗口(备选方案) for (WindowState ws : wms.mWindowMap.values()) { if (DEBUG) Log.d(TAG, "getWindowState ws = " + ws.mActivityRecord); if (ws.mActivityRecord == record) { return ws; } } return null; } } // 注册主屏窗口 public void registerPrimaryWindow(WindowState win) { mPrimaryWindow = win; if (DEBUG) Log.d(TAG, "Registered primary window: " + win); } public void registerSecondaryWindow(WindowState win) { mSecondaryWindow = win; if (DEBUG) Log.d(TAG, "Registered secondary window: " + win); } // 处理焦点变化(确保只有一个窗口获得焦点) public void handleFocusChange(WindowState focused) { if (DEBUG) Log.d(TAG, "handleFocusChange"); if (focused == null) return; if (focused == mPrimaryWindow && mSecondaryWindow != null) { //mSecondaryWindow.onSetFocused(false); // 取消副屏焦点 //focused.mWmService.mInputMonitor.setInputFocusLw(mSecondaryWindow, false); mSecondaryWindow.getDisplayContent().getInputMonitor().setInputFocusLw(mSecondaryWindow, false); } else if (focused == mSecondaryWindow && mPrimaryWindow != null) { //mPrimaryWindow.onSetFocused(false); // 取消主屏焦点 //focused.mWmService.mInputMonitor.setInputFocusLw(mPrimaryWindow, false); mPrimaryWindow.getDisplayContent().getInputMonitor().setInputFocusLw(mPrimaryWindow, false); } focused.mWmService.updateFocusedWindowLocked( WindowManagerService.UPDATE_FOCUS_NORMAL, true ); } // 添加窗口令牌映射 /* private final ArrayMap<IBinder, ActivityRecord> mTokenToRecord = new ArrayMap<>(); public void registerWindowToken(IBinder token, ActivityRecord ar) { mTokenToRecord.put(token, ar); } public ActivityRecord getRecordForToken(IBinder token) { return mTokenToRecord.get(token); } */ // 添加生命周期监听器 public void addLifecycleListener(LifecycleListener listener) { if (DEBUG) Log.d(TAG, "addLifecycleListener"); mLifecycleListeners.add(listener); } // 状态变化通知 public void onStateChanged(ActivityRecord activity, State state) { if (DEBUG) Log.d(TAG, "onStateChanged"); for (LifecycleListener listener : mLifecycleListeners) { listener.onStateChanged(activity, state); } } public void onDestroyed(ActivityRecord activity) { if (DEBUG) Log.d(TAG, "onDestroyed"); for (LifecycleListener listener : mLifecycleListeners) { listener.onDestroyed(activity); } if (activity == mPrimaryActivity || activity == mSecondaryActivity) { sSessions.remove(mTaskId); } } public void onConfigurationChanged(ActivityRecord activity, Configuration newConfig) { if (DEBUG) Log.d(TAG, "onConfigurationChanged"); for (LifecycleListener listener : mLifecycleListeners) { listener.onConfigurationChanged(activity, newConfig); } } // 添加此方法用于调试 public void dumpVisibilityState() { Log.d(TAG, "Primary Activity Visibility: " + (mPrimaryActivity != null ? mPrimaryActivity.isVisible() : "null")); Log.d(TAG, "Secondary Activity Visibility: " + (mSecondaryActivity != null ? mSecondaryActivity.isVisible() : "null")); Log.d(TAG, "Primary Window Visibility: " + (mPrimaryWindow != null ? mPrimaryWindow.isVisible() : "null")); Log.d(TAG, "Secondary Window Visibility: " + (mSecondaryWindow != null ? mSecondaryWindow.isVisible() : "null")); } // 销毁会话 public void destroy() { sSessions.remove(mTaskId); Log.i(TAG, "销毁平行会话,任务ID: " + mTaskId); } // 获取指定任务的会话 public static ParallelSession getSessionForTask(int taskId) { if (DEBUG) Log.d(TAG, "getSessionForTask = "+taskId); return sSessions.get(taskId); } public static void addSession(int taskId, ParallelSession session) { sSessions.put(taskId, session); } public static boolean isParallelSessionActive(int taskId) { if (DEBUG) Log.d(TAG, "isParallelSessionActive = "+taskId); return sSessions.get(taskId) != null; } public ActivityRecord getPrimaryActivity() { if (DEBUG) Log.d(TAG, "getPrimaryActivity = "+mPrimaryActivity); dumpVisibilityState(); return mPrimaryActivity; } public ActivityRecord getSecondaryActivity() { if (DEBUG) Log.d(TAG, "getSecondaryActivity = "+mSecondaryActivity); return mSecondaryActivity; } public WindowState getPrimaryWindow() { if (DEBUG) Log.d(TAG, "getPrimaryWindow = "+mPrimaryWindow); return mPrimaryWindow; } public WindowState getSecondaryWindow() { if (DEBUG) Log.d(TAG, "getSecondaryWindow = "+mSecondaryWindow); return mSecondaryWindow; } /* public interface ActivityLifecycleListener { void onStateChanged(ActivityRecord activity, State state); default void onDestroyed(ActivityRecord activity); void onConfigurationChanged(ActivityRecord activity, Configuration newConfig); } */ /** * 平行视界完整工作流程 入口触发:当主Activity启动副Activity时,ActivityStarter 检测到白名单匹配 会话创建:创建 ParallelSession 并关联主Activity 副屏创建:创建副屏任务栈,将目标Activity放入副屏 布局阶段:DisplayPolicy 为两个Activity计算分屏布局 输入路由:InputDispatcher 根据触摸位置分发到正确窗口 生命周期同步:通过监听器实现主副屏状态同步 新增 ParallelSession.java 新增类 会话管理核心 管理活动、窗口、布局和生命周期同步 DisplayPolicy.java 修改 layoutWindowLw() 方法 覆盖布局逻辑,应用平行视界布局 WindowManagerService.java 修改 添加 ParallelInputMonitor 处理触摸事件重定向 WindowState.java 修改 onSetFocused() 和 attach() 处理焦点管理和窗口注册 ActivityRecord.java 修改 生命周期方法覆盖 实现状态同步和配置变更通知 ActivityStarter.java 修改 execute() 方法 添加平行会话启动逻辑 新增 /system/etc/parallel_apps.xml 配置文件 应用配置列表 定义支持平行视界的应用及其分屏比例 */ }这是安卓14修改类似中兴平行视界的类,改完后副屏打开之后,主屏会消失不显示是什么原因,怎么解决;以及副屏幕返回之后,主屏幕一起退出了,如何修改
最新发布
08-01
<think>问题分析: 1. 副屏打开后主屏消失:可能的原因是在布局时主屏窗口被隐藏或布局区域计算错误,导致主屏窗口不可见。 2. 副屏返回后主屏一起退出:生命周期同步过于严格,当副屏销毁时,主屏也被销毁。 解决方案: 一、主屏消失问题 可能的原因: a) 布局区域计算错误,主屏窗口被放置在屏幕外或大小为零。 b) 窗口叠加顺序问题,副屏窗口覆盖了主屏窗口。 c) 窗口可见性设置错误。 检查点: 1. 在`getPrimaryBounds`和`getSecondaryBounds`中,确保计算出的区域正确,并且没有重叠。 2. 在窗口布局时,确保两个窗口都被正确添加到窗口管理器,并且可见性设置为true。 修改建议: 1. 在`DisplayPolicy`的`layoutWindowLw`方法中,为平行视界的窗口设置正确的显示区域。 2. 确保在`WindowState`的`computeFrame`中,使用正确的区域。 二、副屏返回导致主屏退出问题 原因:在`linkSecondaryActivities`中,当副屏Activity销毁时,会触发主屏Activity的销毁。 修改建议: 1. 调整生命周期监听器,当副屏销毁时,只销毁副屏,不销毁主屏。 2. 但是,平行视界场景中,我们希望副屏关闭时主屏也关闭(如淘宝商品详情页返回后,主页面也退出)。然而,如果这不是期望的行为,可以修改。 根据需求,可能有两种设计: a) 主屏和副屏是强关联的,一个退出另一个也退出(目前实现)。 b) 副屏退出后,主屏保留(例如,副屏是一个临时页面)。 根据问题描述,希望副屏返回后主屏不退出,因此我们需要修改生命周期同步逻辑。 具体修改: 1. 在`ParallelSession`中,移除副屏销毁时销毁主屏的代码。 2. 在副屏销毁时,只从会话中移除副屏,并解除监听,但保留主屏。 但是,注意主屏销毁时,副屏应该销毁(因为主屏是主导者)。 修改步骤: 1. 在`linkSecondaryActivities`中,修改副屏的`onDestroyed`监听器,不再销毁主屏。 2. 在副屏销毁时,只清理副屏相关的引用。 代码修改: 原代码(在`linkSecondaryActivities`中): ```java mSecondaryActivity.addLifecycleListener(new LifecycleListener() { // ... 其他方法 @Override public void onDestroyed(ActivityRecord activity) { if (isDestroying.getAndSet(true)) { return; } try { if (DEBUG) Log.d(TAG, "Secondary destroyed"); if (mPrimaryActivity != null && !mPrimaryActivity.finishing) { // 移除监听器防止回调 mPrimaryActivity.removeLifecycleListener(this); // 延迟销毁主Activity handler.postDelayed(() -> { if (!mPrimaryActivity.finishing) { mPrimaryActivity.destroyImmediately("Secondary destroyed"); } }, 100); } } finally { if(sSessions != null && mPrimaryActivity != null && mPrimaryActivity.getTask() != null) sSessions.remove(mPrimaryActivity.getTask().mTaskId); isDestroying.set(false); } } }); ``` 修改为: ```java mSecondaryActivity.addLifecycleListener(new LifecycleListener() { // ... 其他方法 @Override public void onDestroyed(ActivityRecord activity) { if (isDestroying.getAndSet(true)) { return; } try { if (DEBUG) Log.d(TAG, "Secondary destroyed, cleaning up"); // 移除副屏引用,不再销毁主屏 mSecondaryActivity = null; mSecondaryWindow = null; // 注意:这里不销毁主屏,所以主屏继续存在 } finally { // 从会话中移除?不,因为主屏还在,所以会话不应该移除,直到主屏销毁。 // 但是,当副屏销毁后,这个会话实际上已经结束,所以可以移除。 // 或者,我们保留会话,直到主屏销毁?根据设计决定。 // 这里我们选择移除会话,因为副屏已经销毁,平行视界结束。 if (sSessions != null) { sSessions.remove(mTaskId); } isDestroying.set(false); } } }); ``` 同时,在主屏的`onDestroyed`监听器中,我们仍然要销毁副屏(如果存在): ```java mPrimaryActivity.addLifecycleListener(new LifecycleListener() { // ... 其他方法 @Override public void onDestroyed(ActivityRecord activity) { if (isDestroying.getAndSet(true)) { return; // 已经在销毁过程中 } try { if (DEBUG) Log.d(TAG, "linkPrimaryActivities mPrimaryActivity onDestroyed"); if (mSecondaryActivity != null && !mSecondaryActivity.finishing) { // 移除监听器防止回调 mSecondaryActivity.removeLifecycleListener(this); // 延迟销毁副屏Activity handler.postDelayed(() -> { if (!mSecondaryActivity.finishing) { mSecondaryActivity.destroyImmediately("Primary destroyed"); } }, 100); // 100ms延迟确保主Activity完全销毁 } } finally { if(sSessions != null) { sSessions.remove(mTaskId); } isDestroying.set(false); } } }); ``` 这样,当主屏销毁时,副屏会被销毁;而副屏销毁时,只会清理自身,不会销毁主屏,但会移除会话(因为平行视界结束)。这样副屏返回后,主屏不会退出。 但是,这样修改后,副屏退出后主屏还在,但平行会话已经移除,后续再打开副屏可能需要重新创建会话?这取决于设计。 另外,主屏消失的问题,我们需要检查布局部分。由于代码中没有直接看到布局的调用,我猜测布局是在DisplayPolicy中进行的。因此,我们需要确保在计算窗口框架时,为两个窗口分配了正确的区域。 在`DisplayPolicy`的`layoutWindowLw`方法中,需要为平行视界的窗口设置框架。例如: ```java // 在DisplayPolicy.java的layoutWindowLw方法中 if (session != null) { if (session.getPrimaryActivity() != null && session.getPrimaryActivity() == activity) { // 主屏窗口 outFrame = session.getPrimaryBounds(displayContent); return; } else if (session.getSecondaryActivity() != null && session.getSecondaryActivity() == activity) { // 副屏窗口 outFrame = session.getSecondaryBounds(displayContent); return; } } ``` 如果布局区域计算正确,但窗口仍然不可见,可能是窗口的可见性被错误设置。检查窗口的`mViewVisibility`和`mAttached`等状态。 另外,在`WindowState`的`computeFrame`中,确保计算出的帧区域是有效的。 由于代码较长,无法直接看到所有相关部分,以上是可能的原因和修改方向。 总结修改步骤: 1. 修改副屏销毁的监听器,不再销毁主屏,而是清理副屏引用并移除会话。 2. 检查布局逻辑,确保两个窗口的区域正确且不重叠。 3. 检查窗口的可见性设置,确保没有被错误隐藏。 由于无法直接运行和调试,请根据以上分析检查代码。 注意:以上代码修改仅供参考,请根据实际框架代码调整。</think>### 问题分析与解决方案 #### 问题1:副屏打开后主屏消失 **原因分析**: 1. **窗口布局冲突**:主屏窗口被错误地放置在副屏区域或被覆盖 2. **可见性设置错误**:主屏窗口的可见性状态被错误修改 3. **任务栈管理问题**:副屏活动创建时主屏活动被意外暂停或隐藏 **解决方案**: 1. **修复窗口布局计算**: 在 `getPrimaryBounds()` 和 `getSecondaryBounds()` 中确保两个窗口区域不重叠 ```java // 修改后的窗口区域计算方法 public Rect getPrimaryBounds(DisplayContent display) { if (display == null) return new Rect(0, 0, 1080, 1920); DisplayInfo info = display.getDisplayInfo(); int width = info.logicalWidth; int height = info.logicalHeight; Rect bounds = new Rect(0, 0, (int)(width * mSplitRatio), height); return bounds; } public Rect getSecondaryBounds(DisplayContent display) { if (display == null) return new Rect(0, 0, 1080, 1920); DisplayInfo info = display.getDisplayInfo(); int width = info.logicalWidth; int height = info.logicalHeight; int left = (int)(width * mSplitRatio); Rect bounds = new Rect(left, 0, width, height); return bounds; } ``` 2. **确保主屏窗口可见性**: 在 `linkPrimaryActivities()` 中添加可见性控制 ```java private void linkPrimaryActivities() { mPrimaryActivity.addLifecycleListener(new LifecycleListener() { @Override public void onStateChanged(ActivityRecord activity, State state) { if (state == ActivityRecord.State.RESUMED) { // 确保主屏窗口可见 if (mPrimaryWindow != null && !mPrimaryWindow.isVisible()) { mPrimaryWindow.setVisibility(true); } // 确保副屏窗口可见 if (mSecondaryWindow != null && !mSecondaryWindow.isVisible()) { mSecondaryWindow.setVisibility(true); } } } }); } ``` 3. **修复任务栈管理**: 修改 `ActivityStarter` 中的任务栈处理逻辑(在系统代码中) ```java // 在 ActivityStarter.java 中 private int execute() { // ... 原有代码 ... // 如果是平行视界会话 if (ParallelSession.isParallelSessionActive(mStartActivity.getTask().mTaskId)) { // 不暂停主屏活动 mTargetStack.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } else { // 正常流程 mTargetStack.startActivityLocked(mStartActivity, ...); } } ``` #### 问题2:副屏返回后主屏一起退出 **原因分析**: 1. **生命周期同步过度**:副屏销毁时错误触发了主屏销毁 2. **任务栈关联过强**:副屏和主屏被绑定在同一个任务栈中 3. **销毁逻辑错误**:`onDestroyed()` 监听器中的错误处理 **解决方案**: 1. **修改生命周期同步逻辑**: 在 `linkSecondaryActivities()` 中移除销毁主屏的代码 ```java private void linkSecondaryActivities() { mSecondaryActivity.addLifecycleListener(new LifecycleListener() { @Override public void onDestroyed(ActivityRecord activity) { if (isDestroying.getAndSet(true)) return; try { if (DEBUG) Log.d(TAG, "Secondary destroyed, cleaning up"); // 仅解除关联,不销毁主屏 mSecondaryActivity = null; mSecondaryWindow = null; // 通知主屏副屏已关闭 if (mPrimaryActivity != null) { mPrimaryActivity.onSecondaryClosed(); } } finally { // 保留会话,仅标记副屏关闭 isDestroying.set(false); } } }); } ``` 2. **添加任务栈分离逻辑**: 在 `ActivityRecord` 中添加处理副屏关闭的方法 ```java // 在 ActivityRecord.java 中添加 public void onSecondaryClosed() { // 恢复主屏活动状态 if (isState(State.PAUSED)) { getTask().resumeTopActivityUncheckedLocked(null, null); } // 确保主屏窗口获得焦点 WindowState win = getWindowState(); if (win != null) { win.getDisplayContent().getInputMonitor().setInputFocusLw(win, true); } } ``` 3. **修复返回键处理**: 在 `ActivityTaskManagerService` 中修改返回逻辑 ```java // 在 ActivityTaskManagerService.java 中 boolean handleBackPressed(ActivityRecord r) { // 如果是平行视界的副屏活动 ParallelSession session = ParallelSession.getSessionForTask(r.getTask().mTaskId); if (session != null && session.getSecondaryActivity() == r) { // 仅销毁副屏,不触发任务栈回退 r.destroyImmediately("Back pressed"); return true; // 已处理 } return false; // 正常处理 } ``` ### 完整修改后的关键代码 #### 1. 修复窗口布局和可见性 ```java // 在 ParallelSession.java 中 private void linkPrimaryActivities() { mPrimaryActivity.addLifecycleListener(new LifecycleListener() { @Override public void onStateChanged(ActivityRecord activity, State state) { if (state == State.RESUMED && mSecondaryActivity != null) { // 确保窗口可见 ensureWindowsVisible(); // 延迟恢复副屏活动 handler.postDelayed(() -> { if (mSecondaryActivity.getTask() != null && !mSecondaryActivity.isState(State.RESUMED)) { mSecondaryActivity.getTask().resumeTopActivityUncheckedLocked(null, null); } }, 100); } } }); } private void ensureWindowsVisible() { if (mPrimaryWindow != null && !mPrimaryWindow.isVisible()) { mPrimaryWindow.setVisibility(true); } if (mSecondaryWindow != null && !mSecondaryWindow.isVisible()) { mSecondaryWindow.setVisibility(true); } } ``` #### 2. 修复返回键导致主屏退出 ```java // 在 ParallelSession.java 中 private void linkSecondaryActivities() { mSecondaryActivity.addLifecycleListener(new LifecycleListener() { @Override public void onDestroyed(ActivityRecord activity) { if (isDestroying.getAndSet(true)) return; try { Log.d(TAG, "Secondary activity destroyed"); // 解除关联但不销毁主屏 mSecondaryActivity = null; mSecondaryWindow = null; // 通知主屏副屏已关闭 if (mPrimaryActivity != null && !mPrimaryActivity.finishing) { mPrimaryActivity.onSecondaryClosed(); } } finally { isDestroying.set(false); } } }); } ``` #### 3. 添加 ActivityRecord 扩展方法 ```java // 在 ActivityRecord.java 中添加 public void onSecondaryClosed() { // 恢复主屏活动 if (isState(State.PAUSED) || isState(State.STOPPED)) { getTask().resumeTopActivityUncheckedLocked(null, null); } // 确保主屏获得焦点 WindowState win = getWindowState(); if (win != null) { win.getDisplayContent().getInputMonitor().setInputFocusLw(win, true); win.mWmService.updateFocusedWindowLocked( WindowManagerService.UPDATE_FOCUS_NORMAL, true); } } ``` ### 关键修改点说明 1. **窗口布局分离**: - 确保主屏和副屏窗口区域不重叠 - 使用精确的坐标计算防止相互覆盖 2. **可见性保证**: - 在活动恢复时显式设置窗口可见 - 添加双重检查防止窗口被错误隐藏 3. **生命周期解耦**: - 副屏销毁时只解除关联,不销毁主屏 - 添加专门的副屏关闭通知方法 - 主屏在副屏关闭后自动恢复活动状态 4. **返回键处理优化**: - 拦截副屏的返回键事件 - 只销毁副屏活动,不触发任务栈回退 - 保持主屏活动处于活跃状态 ### 测试建议 1. **窗口布局测试**: ```java // 添加调试日志 Rect primaryBounds = getPrimaryBounds(display); Rect secondaryBounds = getSecondaryBounds(display); Log.d(TAG, "Primary bounds: " + primaryBounds.toShortString()); Log.d(TAG, "Secondary bounds: " + secondaryBounds.toShortString()); ``` 2. **生命周期测试**: ```java // 添加状态跟踪 Log.d(TAG, "Primary state: " + mPrimaryActivity.getState()); Log.d(TAG, "Secondary state: " + (mSecondaryActivity != null ? mSecondaryActivity.getState() : "null")); ``` 3. **可见性测试**: ```java // 定期检查窗口状态 handler.postDelayed(() -> { Log.d(TAG, "Primary visible: " + (mPrimaryWindow != null && mPrimaryWindow.isVisible())); Log.d(TAG, "Secondary visible: " + (mSecondaryWindow != null && mSecondaryWindow.isVisible())); }, 1000); ``` ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值