Android双屏异显的实现

概述

Android实现双屏异显的实现方式有2种。

方式一:在Android4.2及以上平台上,按照标准Android SDK提供的API,使用Presentation类,将一个APP的相关内容显示到指定的屏幕上,具体请参考https://developer.android.com/reference/android/app/Presentation.html。这种方式实现的是应用内的异显,也是View级别的异显。

方式二:通过修改Framework层的am,wm,display相关代码,从而在不同的显示设备上运行不同的应用,这种多任务的双屏异显方法实现了不同应用之间的异显,本文重点讲解这种双屏异显方法的实现方式。这种实现方式是基于某芯片厂商系列芯片,Android 6.0平台开发的。

这需要解决如下几个问题:

1.如何获取所有的TaskID?

在IActivityManager.java添加接口,public List<Integer> getAllTaskIds() throws RemoteException。

ActivityManagerNative.java种实现了IActivityManger接口,具体实现如下:

	@Override
	public List<Integer> getAllTaskIds() throws RemoteException{
            List<Integer> taskIds = new ArrayList<Integer>();
	    List<StackInfo> stackInfos = getAllStackInfos();
	    if(stackInfos != null && stackInfos.size() > 0){
	        for(StackInfo info : stackInfos){
		    int[] taskIdArray = info.taskIds;
		    reverseArray(taskIdArray);
		    if(taskIdArray != null && taskIdArray.length > 0){
		       for(int taskId : taskIdArray)
			    taskIds.add(taskId);
		    }
	        }
	    }
	    return taskIds;
	}

2.如何确保两个应用同时运行,同时保持Resume状态?如何确保副屏Activity不会随着主屏应用同时销毁?在ActivityStack.java下activityPausedLocked方法添加如下代码:
    if(r.task != null && r.task.taskId == mService.getSecondDisplayTaskId())
        return;

上述代码禁止副屏应用进入Pased状态,副屏应用将一直保持Resume状态


3.主屏与副屏的应用如何切换,同显与异显如何切换?这是通过操作主屏与副屏的窗口实现的,WindowManagerService.java添加如下代码:

	public void setOnlyShowInExtendDisplay(Session session,IWindow client,int transit){
		
		long origId = Binder.clearCallingIdentity();
		synchronized(mWindowMap){
			if(mDisplayContents == null || mDisplayContents.size() <= 1){
			return;
		}
		final int displayCount = mDisplayContents.size();
		DisplayContent defaultContent = getDefaultDisplayContentLocked();
		int displayId = 0;
		DisplayContent secondDisplayContent = null;
		for(int i = 0; i < displayCount;i++){
			final DisplayContent content = mDisplayContents.valueAt(i);
			if(content != defaultContent){
				secondDisplayContent = content;
				displayId = secondDisplayContent.getDisplayId();
				break;
			}
		}
		if(secondDisplayContent == null){
			return;
		}
		if(!okToDisplay()){
			return;
		}
		WindowState current = windowForClientLocked(session, client, false); 
		if(isHomeWindow(current)){
			return;
		}
		AppWindowToken wtoken = current.mAppToken;
		if(wtoken == null){
			return;
		}

		
        	
		int groupId = wtoken.mTask.mTaskId;
		mH.sendMessage(mH.obtainMessage(H.DO_TASK_DISPLAY_CHANGED, groupId, -1));
	    }
		Binder.restoreCallingIdentity(origId);
	}
	

	/**
	move window to second display
	*/
 	public void moveTransitionToSecondDisplay(int groupId,int transit){
		//if(!isShowDualScreen()){
		//	mSecondTaskIds.clear();
		//}
		//Settings.System.putInt(mContext.getContentResolver(), Settings.DUAL_SCREEN_ICON_USED, 0);
		List<Integer> allTaskIds = null;
		try{
			allTaskIds = mActivityManager.getAllTaskIds();
		}catch (Exception e){
			Log.i("DualScreen", "WindowManagerService->getAllTaskIds->e:" + e);
		}
		
		if(allTaskIds == null || allTaskIds.size() < 2)
			return;
		
		if(isShowDualScreen()){
			moveWindowToSecondDisplayWithDualShow();
			return;
		}
		
		
		long origId = Binder.clearCallingIdentity();
		int curMoveTaskId = -1;
		synchronized(mWindowMap){
			if(mDisplayContents == null || mDisplayContents.size() <= 1){
				return;
			}
			final int displayCount = mDisplayContents.size();
			DisplayContent defaultContent = getDefaultDisplayContentLocked();
			int displayId = 0;
			DisplayContent secondDisplayContent = null;
			for(int i = 0; i < displayCount;i++){
				final DisplayContent content = mDisplayContents.valueAt(i);
				if(content != defaultContent){
					secondDisplayContent = content;
					displayId = secondDisplayContent.getDisplayId();
					
					break;
				}
			}

			if(secondDisplayContent == null){
				return;
			}
			if(!okToDisplay()){
				return;
			}
			SurfaceControl.openTransaction();
			WindowState win = null;
			WindowList defaultWindows = defaultContent.getWindowList();
			
			try{
				WindowList secondDisplayAddList = new WindowList();
				WindowList secondDisplayWindows = secondDisplayContent.getWindowList();
				
				int topTaskId = -1;
				if(allTaskIds != null && allTaskIds.size() > 0){
					topTaskId = allTaskIds.get(0);
					//mSecondTaskIds.add(topTaskId);
				}
				
				for(int i= defaultWindows.size()-1;i>=0;i--){
					win = defaultWindows.get(i);
					if(win == null){
						continue;
					}
					if (win.mAppToken == null){
						continue;
					}
				
					if(win.mAppToken.mTask == null){
						continue;
					}
					int windowTaskId = win.mAppToken.mTask.mTaskId;
					if(windowTaskId == topTaskId){
						win.setPrimaryDisplay(true);
						defaultWindows.remove(win);
						mTempWindowList.add(win);
						win.mDisplayContent = secondDisplayContent;
						if(win.mWinAnimator != null){
							int layerStack = secondDisplayContent.getDisplay().getLayerStack();
							if(win.mWinAnimator.mSurfaceControl!= null){
								win.mWinAnimator.mSurfaceControl.setLayerStack(layerStack);
							}

						}
						secondDisplayAddList.add(0,win);
					}

				}
				secondDisplayWindows.clear();
				secondDisplayWindows.addAll(secondDisplayAddList);			
				
				for (int i = 0; i < displayCount; i++) {
					final DisplayContent content = mDisplayContents.valueAt(i);
					assignLayersLocked(content.getWindowList());
					content.layoutNeeded = true;
				}
				Log.i("DualScreen", "WindowManagerService->allTaskIds:" + allTaskIds);
				ArrayList<ActivityStack> allStacks = getAllStacks();
				Log.i("DualScreen", "moveTransitionToSecondDisplay->allStacks:" + allStacks);
				boolean isFind = false;
				for(int i = allStacks.size() - 1; i >= 0; --i){
					ActivityStack itemStack = allStacks.get(i);
					//ArrayList<TaskRecord> getAllTasks()
					List<TaskRecord> itemTasks = itemStack.getAllTasks();
					if(itemTasks != null && itemTasks.size() > 0){
						for(int k = itemTasks.size() - 1; k >= 0; --k){
							TaskRecord itemTask = itemTasks.get(k);
							List<ActivityRecord> itemActivities = itemTask.mActivities;
							for(int j = itemActivities.size() - 1; j >= 0; --j){
								ActivityRecord itemActivity = itemActivities.get(j);
								if(/*itemActivity.state == ActivityState.RESUMED && */itemTask.taskId != topTaskId){
									curMoveTaskId = itemTask.taskId;
									isFind = true;
									break;
								}
									
							}
							if(isFind)
								break;
						}
					}
					if(isFind)
						break;
				}
				mSecondDisplayTaskId = topTaskId;
				Log.i("DualScreen", "WindowManagerService->curMoveTaskId:" + curMoveTaskId);
				misMovingToSecond = true;
				Settings.System.putInt(mContext.getContentResolver(), Settings.DUAL_SCREEN_ICON_USED, 1);
				switchFocusWindow(curMoveTaskId);
				updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false);
				mAppTransition.setReady();
				performLayoutAndPlaceSurfacesLocked();
			}finally{
				SurfaceControl.closeTransaction();
			}
			
			//shouldAppMoveBack(-1);
		}
		
		Binder.restoreCallingIdentity(origId);
		
	}
	
	
	public void moveWindowToSecondDisplayWithDualShow(){
		long origId = Binder.clearCallingIdentity();
		int secondDisplayTaskId = getSecondDisplayTaskId();
		int currentFocusedTaskId = -1;
		int curMoveTaskId = secondDisplayTaskId;
		if(mFocusedApp != null && mFocusedApp.mTask != null)
			currentFocusedTaskId = mFocusedApp.mTask.mTaskId;
		Log.i("DualScreen", "WindowManagerService->moveWindowToSecondDisplayWithDualShow->currentFocusedTaskId:" + currentFocusedTaskId);
		//mSecondDisplayTaskId = currentFocusedTaskId;
		synchronized(mWindowMap){
			if(mDisplayContents == null || mDisplayContents.size() <= 1){
				return;
			}
			final int displayCount = mDisplayContents.size();
			DisplayContent defaultContent = getDefaultDisplayContentLocked();
			int displayId = 0;
			DisplayContent secondDisplayContent = null;
			for(int i = 0; i < displayCount;i++){
				final DisplayContent content = mDisplayContents.valueAt(i);
				if(content != defaultContent){
					secondDisplayContent = content;
					displayId = secondDisplayContent.getDisplayId();
					Log.d("DualScreen", "WindowManagerService->moveWindowToSecondDisplayWithDualShow->secondDisplayId:" + displayId);
					break;
				}
			}

			if(secondDisplayContent == null){
				return;
			}
			if(!okToDisplay()){
				return;
			}
			SurfaceControl.openTransaction();
			WindowState win = null;
			try{
				WindowList secondDisplayAddList = new WindowList();
				WindowList defaultDisplayAddList = new WindowList();
				WindowList secondDisplayWindows = secondDisplayContent.getWindowList();
				WindowList defaultWindows = defaultContent.getWindowList();
				
				if(mTempWindowList != null && mTempWindowList.size() > 0)
					defaultWindows.addAll(mTempWindowList);
				mTempWindowList.clear();
				
				for(int i= defaultWindows.size()-1;i>=0;i--){
					win = defaultWindows.get(i);
					if(win == null){
						continue;
					}
					if (win.mAppToken == null){
						continue;
					}
				
					if(win.mAppToken.mTask == null){
						continue;
					}
					int windowTaskId = win.mAppToken.mTask.mTaskId;
					if(windowTaskId == currentFocusedTaskId){
						defaultWindows.remove(win);
						win.mDisplayContent = secondDisplayContent;
						if(win.mWinAnimator != null){
							int layerStack = secondDisplayContent.getDisplay().getLayerStack();
							if(win.mWinAnimator.mSurfaceControl!= null){
								win.mWinAnimator.mSurfaceControl.setLayerStack(layerStack);
							}
							
						}
						secondDisplayAddList.add(0,win);
					}

				}
				
				
				for(int i= secondDisplayWindows.size()-1;i>=0;i--){
					win = secondDisplayWindows.get(i);
					if(win == null){
						continue;
					}
					if (win.mAppToken == null){
						continue;
					}
				
					if(win.mAppToken.mTask == null){
						continue;
					}
					int windowTaskId = win.mAppToken.mTask.mTaskId;
					if(windowTaskId == secondDisplayTaskId){
						secondDisplayWindows.remove(win);
						win.mDisplayContent = defaultContent;
						if(win.mWinAnimator != null){
							int layerStack = defaultContent.getDisplay().getLayerStack();
							if(win.mWinAnimator.mSurfaceControl!= null){
								win.mWinAnimator.mSurfaceControl.setLayerStack(layerStack);
							}
							
						}
						defaultDisplayAddList.add(0,win);
					}

				}
				
				//secondDisplayWindows.clear();
				secondDisplayWindows.addAll(0,secondDisplayAddList);
				//defaultWindows.clear();
				defaultWindows.addAll(0, defaultDisplayAddList);
				for (int i = 0; i < displayCount; i++) {
					final DisplayContent content = mDisplayContents.valueAt(i);
					assignLayersLocked(content.getWindowList());
					content.layoutNeeded = true;
				}
				//Log.i("DualScreen", "WindowManagerService->moveWindowToSecondDisplayWithDualShow->allTaskIds:" + allTaskIds);
				ArrayList<ActivityStack> allStacks = getAllStacks();
				Log.i("DualScreen", "WindowManagerService->moveWindowToSecondDisplayWithDualShow->allStacks:" + allStacks);
				mSecondDisplayTaskId = currentFocusedTaskId;
				Log.i("DualScreen", "WindowManagerService->moveWindowToSecondDisplayWithDualShow->curMoveTaskId:" + curMoveTaskId);
				misMovingToSecond = true;
				Settings.System.putInt(mContext.getContentResolver(), Settings.DUAL_SCREEN_ICON_USED, 1);
				switchFocusWindow(curMoveTaskId);
				updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false);
				mAppTransition.setReady();
				performLayoutAndPlaceSurfacesLocked();
			}finally{
				SurfaceControl.closeTransaction();
			}
			
			//shouldAppMoveBack(-1);
		}
		
		Binder.restoreCallingIdentity(origId);
	}


4.副屏页面如何全屏显示?修改PhoneWindowManager.java下layoutWindowLw方法添加代码如下:

     pf.top = df.top = of.top = cf.top = vf.top = 0;
     pf.right = df.right = of.right = cf.right = vf.right = width;
     pf.bottom = df.bottom = of.bottom = cf.bottom = vf.bottom = height;




  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
### 回答1: Android双屏异显是指在使用Android系统的设备上,将屏幕内容同时显示在两个不同的显示屏上。 Android双屏异显提供了更多的屏幕空间,可以同时显示更多的内容。在处理多任务的情况下,用户可以在一个屏幕上工作,另一个屏幕上显示其他相关信息。例如,在编辑文档时,可以将工具栏和菜单显示在一个屏幕上,同时在另一个屏幕上显示实时预览。 实现Android双屏异显的方法有多种。最常见的方法是使用HDMI或其他类型的视频输出接口连接外部显示设备,如电视或投影仪。通过将设备与外部显示设备连接,用户可以将内容镜像或扩展到外部屏幕上。 另一种方法是使用Android设备自带的无线投屏功能,如Miracast或Chromecast。用户可以将内容通过无线连接投射到另一个屏幕上。这种方法可以提供更大的灵活性和便利性,因为不需要使用任何物理连接线。 尽管Android双屏异显提供了更多的功能和便利性,但也有一些限制和挑战。首先,设备本身的处理能力和内存大小可能会对双屏显示的性能产生一定的影响。此外,应用程序的适配问题也是一个挑战。并非所有的应用程序都能适应双屏异显,并且在两个屏幕上显示内容可能会带来用户界面和交互设计上的问题。 总的来说,Android双屏异显为用户提供了更大的屏幕空间和更多的显示选项,提升了多任务处理的效率和便利性。随着技术的不断发展,我们相信Android双屏异显在未来还会有更多的创新和改进。 ### 回答2: Android双屏异显指的是在Android设备上同时使用两个屏幕进行不同的显示操作。这种功能可以通过一些特定的设备或者应用程序来实现。 在一些特定的Android设备上,如折叠式手机或者可插拔模块化手机,可以使用双屏异显功能。用户可以将设备的屏幕折叠或连接到外部显示器,然后在两个屏幕上同时显示不同的内容。这为用户提供了更大的显示区域,使得多任务处理更加方便。例如,在一边浏览网页或观看视频,同时在另一屏幕上进行聊天或写作业。 此外,也有一些应用程序可以在普通的Android设备上实现双屏异显的功能。例如,一些视频播放器可以将视频内容显示在外部屏幕上,而控制界面则显示在主屏幕上。这样,用户可以同时观看视频并控制播放器的功能。还有一些多任务管理应用程序可以帮助用户在不同的屏幕上同时打开和操作多个应用程序。 使用Android双屏异显功能可以提高用户的工作效率和娱乐体验。无论是通过特定的设备还是应用程序,双屏异显都为用户提供了更大的显示空间和更灵活的操作方式。未来,随着技术的发展和Android系统的升级,这种功能可能会得到进一步的改进和拓展,为用户带来更便捷的使用体验。 ### 回答3: Android双屏异显是指在一台Android设备上同时连接两个显示屏并显示不同的内容。这种功能通常可以通过HDMI接口或无线投射技术实现。 首先,连接第一个显示屏。用户可以使用HDMI线缆将设备连接到外部显示器,或者使用无线交流协议,如Miracast,将内容投影到另一个显示设备上。 然后,用户可以选择在设置中配置不同的显示模式。例如,可以选择克隆模式,使两个屏幕显示相同的内容,或者扩展模式,使两个屏幕显示不同的内容。 在这种情况下,用户可以同时在主屏幕上进行一项任务,如浏览网页,而在外部显示屏上播放视频或展示演示文稿。这使得Android设备成为一个多任务处理的工具,并提供更大的屏幕空间和潜在的生产力提升。 但是,要注意的是,并非所有的Android设备都支持双屏异显功能。这取决于设备的硬件和操作系统的版本。用户应该查看设备的规格说明和操作系统设置,以确定是否支持此功能。 总而言之,Android双屏异显是一种方便和强大的功能,允许用户同时在两个屏幕上处理不同的任务。它为用户提供了更大的屏幕空间和更高的生产力,使Android设备成为更加多功能的工具。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值