精华内容
下载资源
问答
  • 手机怎么解决同ip账号_游戏工作室如何实现手游多开多窗口多IP
    千次阅读
    2020-10-22 22:00:45

    经常能看到的一个画面就是游戏工作室,一台电脑许多个手机游戏窗口同时进行,需求量1台程序运行好几个微端。或是相同应用程序开启好几个窗口。那样做能够节约成本,不用多个设备。

    但他们全是公用相同网络ip地扯得,那麼如何来防止由于网络ip共享资源而封禁呢?这就需求量保证,每一个窗口的lP是不一样的。

    这一需求量愈来愈多的状况下,我们的IP单窗口单网络ip-多窗口多IP的技术就应时而生了。

    6386986d009fdd89a0a7be0508525e28.png

    1.随着手机游戏的兴起,游戏工作室项目也不仅仅局限于网游了,但是手机的局限性在于一个手机一个游戏,操作不便,成本也比较高,这时候各种模拟器就随之产生了,用电脑登陆手机游戏就成为了可能。

    2.模拟器登录手机游戏方便易操作还能节约成本,多开也很方便,但是一台电脑也就是一个IP下登录多个账号,很容易被游戏公司检测到进行封号处理,将其视为外挂。

    3.随之而来的就是相关软件的兴起,因为同IP会进行封号,更改后就可以了 ,但是一台电脑的IP更改,多窗口下还是会被封号。所以现在我们的IP的软件,已经发展到可以更改多窗口多IP,就是把每个游戏窗口的IP都更改了。只需要在每个模拟器窗口里安装好我们的IP的安卓版软件就可以了。

    5dfa6f07c74f421acdef0d70d1d95dd4.png
    2a973da52d1dfde73f0a62ab5d9cf565.png

    4.至于怎么检测窗口IP呢?模拟器的窗口里面都会有一个浏览器,打开后百度IP就可以搜索到自己的IP地址,并以此判断是否更换成功,这就是游戏工作室实现手游多开多窗口多IP的方法。

    bed3ca1ccedf0c6714d2d63a16479882.png

    结尾:科技的进步让快捷支付变成人们生活中不可或缺的一部分,电竞行业也是发生了天翻地覆的变化,可无论什么职业都都离不开ip,我们应该好好利用如今的便捷,在合理的范围内使自己的事业更上一层楼,奔腾吧!后浪!

    更多相关内容
  • 这个一个有几个意思,一个程序不能多开,前台只能运行一个应用等。(微信官方功能,网页标签页多开)有兴趣的看我之前的一篇文章【实测:一个微信可以同时开多少个窗口】后面,谷歌给安卓搞了个MultiDroid。什么微信的...

    Windows本身就是窗户窗口的意思。而Windows的窗口化操作也是Windows的一个重要性标志之一。

    70377054_1

    (图片来源于网络)

    Android,如果拆分开来就是An和Droid,An就是一个。这个一个有几个意思,一个程序不能多开,前台只能运行一个应用等。

    (微信官方功能,网页标签页多开)

    有兴趣的看我之前的一篇文章【实测:一个微信可以同时开多少个窗口】

    后面,谷歌给安卓搞了个MultiDroid。什么微信的网页标签页多开,还有一些软件的多开工具,都是基于这个。

    今天我们讲的是,强行窗口化!

    70377054_3

    这款软件叫【Sky0lin】名字蛮怪的。

    70377054_4

    我们让程序窗口化运行用的就是它,它是一个xposed模块(又是xposed)

    70377054_5

    软件的界面还算可以,MaterialDesign化不够彻底,附带一个悬浮按钮。这个后面说。

    70377054_6

    程序列表就是用于窗口化运行程序的“白名单”。

    70377054_7

    右上角的问号按钮,点击附送一个小提示。。。

    70377054_8添加按钮点击后,也附送一个提示,额,不对,是使用说明。

    70377054_9

    点击任意一个复选框,都会同时勾选两个,但你可以手动反勾选第二个。可以只勾选第一个,但无法只勾选第二个。

    70377054_10

    添加了几个,测试下。

    70377054_11

    有个透明度,没啥用,屏幕旋转就比较有用了。

    注意,右下角有个三角形的按钮。

    70377054_12

    长按右下角的按钮可以隐藏标题栏,隐藏标题栏后,再长按右下角,可以弹出菜单。

    70377054_13

    开个微信吧。。。是不是感觉窗口小了,内容没有变小,显得拥挤放不下。。。

    70377054_14

    主界面更明显。。。

    70377054_15

    输入法无法窗口化。。。

    70377054_16

    QQ也是这样子。。。

    怎么办呢,因为DPI没变。。。软件有这个设置。

    70377054_17

    我们设置成280看下(1080P屏幕一般默认480)。。。

    70377054_18

    实力压缩。太小了。

    70377054_19

    这回我还成320了。这个效果差不多。

    70377054_20

    QQ也正常了。

    70377054_21

    70377054_22

    软件还有其它的设置。

    70377054_23

    这里吐个小槽,作者估计是用自适应格数的TableLayout写的布局?!但没有在子控件添加边距,所以导致了图标“无缝衔接”。

    好了,测试完了,退出窗口化,全屏运行。

    70377054_24

    额,这个什么鬼,DPI的后遗症。。。

    70377054_25

    好吧,我再改回来。。。

    其实,从Android 5.0开始,很多AOSP自带分屏功能,又或是窗口化运行的功能。

    70377054_26

    点击标签栏的方块键

    70377054_27

    70377054_28

    其实,分屏这功能用在手机上基本没太多意义,除非你真的很需要同屏多任务。

    而且,一些分屏功能无法适应输入法。

    其实,我前年就玩过一次分屏。但是不是这个软件,那个软件太坑,是根据Activity判定。跳转活动还会导致窗口出错。

    70377054_29

    而且,界面也很丑,也不支持DPI调节,这里就不讲它了。

    好了,就讲到这里,软件依旧是上传到QQ群99322260群文件里。

    本文结束,谢谢阅读!

    展开全文
  • android安卓手机分屏多窗口实现方法

    万次阅读 2016-04-05 10:48:06
    效果图 然后就是下载安装万能的xpoesd框架了 说一句需要root ...点击确定后重启手机。...在你刚安装的悬浮窗口前打勾,然后重启手机 重启后打开悬浮窗口,选择测试,如果是这样就OK了

    效果图

    安卓手机分屏多窗口图文教程 脚本之家
    frameborder="0" allowtransparency="true" scrolling="no" vspace="0" hspace="0" style="display: block; position: static; padding: 0px; margin: 0px; border-style: none; vertical-align: baseline; width: 338px; height: 112px;">

    安卓手机分屏多窗口图文教程 可以让你在玩游戏的时候做其他事[多图]图片2
    frameborder="0" allowtransparency="true" scrolling="no" vspace="0" hspace="0" style="display: block; position: static; padding: 0px; margin: 0px; border-style: none; vertical-align: baseline; width: 352px; height: 112px;">

    然后就是下载安装万能的xpoesd框架了

    说一句需要root

    安装后打开选择管理框架模块


    frameborder="0" allowtransparency="true" scrolling="no" vspace="0" hspace="0" style="display: block; position: static; padding: 0px; margin: 0px; border-style: none; vertical-align: baseline; width: 385px; height: 112px;">

    点击安装,更新


    frameborder="0" allowtransparency="true" scrolling="no" vspace="0" hspace="0" style="display: block; position: static; padding: 0px; margin: 0px; border-style: none; vertical-align: baseline; width: 386px; height: 112px;">

    然后就是这个样子。。点击确定后重启手机。

    点击激活应用模块

    在你刚安装的悬浮窗口前打勾,然后重启手机

    重启后打开悬浮窗口,选择测试,如果是这样就OK了

    -- 来自不吃饱哪有力气减肥客户端像这样设置

    -- 来自不吃饱哪有力气减肥客户端用这个添加程序

    展开全文
  • Android 多窗口实现

    千次阅读 2017-12-13 11:51:51
    禁用APK分屏-AndroidManifest.xml添加属性:android:resizeableActivity=... N多窗口在以往的Android系统上,所有Activity都是全屏的,如果不设置透明效果,一次只能看到一个Activity界面。 但是从Android N

    禁用APK分屏-AndroidManifest.xml添加属性:android:resizeableActivity="false"
      

    Android 7.0中的多窗口实现解析

    标签: Android N多窗口 在以往的Android系统上,所有Activity都是全屏的,如果不设置透明效果,一次只能看到一个Activity界面。

    但是从Android N(7.0)版本开始,系统支持了多窗口功能。在有了多窗口支持之后,用户可以同时打开和看到多个应用的界面。并且系统还支持在多个应用之间进行拖拽。在大屏幕设备上,这一功能非常实用。

    本文将详细讲解Android系统中多窗口功能的实现。

    多窗口功能介绍

    概述

    Android 从 Android N(7.0)版本开始引入了多窗口的功能。

    关于Android N的新特性,请参见这里:Android 7.0 for Developers
    关于多窗口的详细说明,请参见这里:Multi-Window Support


    Android N上的多窗口功能有三种模式

    1. 分屏模式
    这种模式可以在手机上使用。该模式将屏幕一分为二,同时显示两个应用的界面。如下图所示: splitscreen


    2. 画中画模式
    这种模式主要在TV上使用,在该模式下视频播放的窗口可以一直在最顶层显示。如下图所示: pip-active


    3. Freeform模式
    这种模式类似于我们常见的桌面操作系统,应用界面的窗口可以自由拖动和修改大小。如下图所示: freeform_mode


    生命周期

    多窗口不影响和改变原先Activity的生命周期。

    在多窗口模式,多个Activity可以同时可见,但只有一个Activity是最顶层的,即:获取焦点的Activity。

    所有其他Activity都会处于Paused状态(尽管它们是可见的)。
    在以下三种场景下,系统会通知应用有状态变化,应用可以进行处理:

    • 当用户以多窗口的模式启动的应用
    • 当用户改变了Activity的窗口大小
    • 当用户将应用窗口从多窗口模式改为全屏模式

    关于应用如何进行状态变化的处理,请参见这里:Handling Runtime Changes,这里不再赘述。 

    开发者相关

    Android从API Level 24开始,提供了以下一些机制来配合多窗口功能的使用。

    • Manifest新增属性
      • android:resizeableActivity=["true" | "false"]

      这个属性可以用在<activity>或者<application> 上。置为true,表示可以以分屏或者Freeform模式启动。false表示不支持多窗口模式。对于API目标Level为24的应用来说,这个值默认是true。


      android:supportsPictureInPicture=["true" | "false"]

      这个属性用在<activity>上,表示是否支持画中画模式。如果android:resizeableActivity为false,这个属性值将被忽略。

    • Layout新增属性
      • android:defaultWidth,android:defaultHeight Freeform模式下的默认宽度和高度
      • android:gravity Freeform模式下的初始Gravity
      • android:minWidth, android:minHeight 分屏和Freeform模式下的最小高度和宽度

    这里是一段代码示例:

    <activity android:name=".MyActivity">
        <layout android:defaultHeight="500dp"
              android:defaultWidth="600dp"
              android:gravity="top|end"
              android:minHeight="450dp"
              android:minWidth="300dp" />
    </activity>
    
    • 新增API
      • Activity.isInMultiWindowMode() 查询是否处于多窗口模式
      • Activity.isInPictureInPictureMode() 查询是否处于画中画模式
      • Activity.onMultiWindowModeChanged() 多窗口模式变化时进行通知(进入或退出多窗口)
      • Activity.onPictureInPictureModeChanged() 画中画模式变化时进行通知(进入或退出画中画模式)
      • Activity.enterPictureInPictureMode() 调用这个接口进入画中画模式,如果系统不支持,这个调用无效
      • ActivityOptions.setLaunchBounds() 在系统已经处于Freeform模式时,可以通过这个参数来控制新启动的Activity大小,如果系统不支持,这个调用无效
    • 拖拽相关 Android N之前,系统只允许在一个Activity内部进行拖拽。但从Android N开始,系统支持在多个Activity之间进行拖拽,下面是一些相关的API。具体说明请参见官方文档。

      • DragAndDropPermissions
      • View.startDragAndDrop()
      • View.cancelDragAndDrop()
      • View.updateDragShadow()
      • Activity.requestDragAndDropPermissions() 

    相关模块和主要类

    本文,我们主要关注多窗口的功能实现。这里列出了多窗口功能实现的主要类和模块。

    这里的代码路径是指AOSP的源码路径,关于如何获取AOSP源码请参见这里:Downloading the Source

    ActivityManager

    代码路径:/frameworks/base/services/core/Java/com/android/server/am

    • ActivityManagerService 负责运行时管理的系统服务,这个类掌管了Android系统的四大组件(Activity,Service,BroadcastReceiver,ContentProvider),应用进程的启动退出,进程优先级的控制。说它是Framework中最重要的系统服务都不为过。
    • TaskRecord,ActivityStack 管理Activity的容器,多窗口的实现强烈依赖于ActivityStack,下文会详细讲解。
    • ActivityStackSupervisor 顾名思义,专门负责管理ActivityStack。
    • ActivityStarter Android N新增类。掌控Activity的启动。 

    WindowManager

    代码路径:/frameworks/base/services/core/java/com/android/server/wm

    • WindowManagerService 负责窗口管理的系统服务。
    • Task,TaskStack 管理窗口对象的容器,与TaskRecord和ActivityStack对应。
    • WindowLayersController Android N新增类,专门负责Z-Order的计算。Z-Order决定了窗口的上下关系。 

    Framework API

    代码路径:frameworks/base/core/java/

    • ActivityManager 提供了管理Activity的接口和常量。
    • ActivityOptions 提供了启动Activity的参数选项,例如,在Freefrom模式下,设置窗口大小。 

    SystemUI

    代码路径:/frameworks/base/packages/SystemUI/

    顾名思义:系统UI,这里包括:NavigationBar,StatusBar,Keyguard等。

    • PhoneStatusBar SystemUI中非常重要的一个类,负责了很多组件的初始化和控制。 

    为了便于说明,下文将直接使用这里提到的类。如果你想查看这些类的源码,请参阅这里的路径。

    多窗口的功能实现

    多窗口功能的实现主要依赖于ActivityManagerService与WindowManagerService这两个系统服务,它们都位于system_server进程中。该进程是Android系统中一个非常重要的系统进程。Framework中的很多服务都位于这个进程中。

    整个Android的架构是CS的模型,应用程序是Client,而system_server进程就是对应的Server。

    应用程序调用的很多API都会发送到system_server进程中对应的系统服务上进行处理,例如startActivity这个API,最终就是由ActivityManagerService进行处理。

    而由于应用程序和system_server在各自独立的进程中运行,因此对于系统服务的请求需要通过Binder进行进程间通讯(IPC)来完成调用,以及调用结果的返回。

    两个系统服务简介

    ActivityManagerService负责Activity管理。

    对于应用中创建的每一个Activity,在ActivityManagerService中都会有一个与之对应的ActivityRecord,这个ActivityRecord记录了应用程序中的Activity的状态。ActivityManagerService会利用这个ActivityRecord作为标识,对应用程序中的Activity进程调度,例如生命周期的管理。

    实际上,ActivityManagerService的职责远超出的它的名称,ActivityManagerService负责了所有四大组件(Activity,Service,BroadcastReceiver,ContentProvider)的管理,以及应用程序的进程管理。

    WindowManagerService负责Window管理。包括:

    • 窗口的创建和销毁
    • 窗口的显示与隐藏
    • 窗口的布局
    • 窗口的Z-Order管理
    • 焦点的管理
    • 输入法和壁纸管理

    等等 每一个Activity都会有一个自己的窗口,在WindowManagerService中便会有一个与之对应的WindowState。WindowManagerService以此标示应用程序中的窗口,并用这个WindowState来存储,查询和控制窗口的状态。

    ActivityManagerService与WindowManagerService需要紧密配合在一起工作,因为无论是创建还是销毁Activity都牵涉到Actiivty对象和窗口对象的创建和销毁。这两者是既相互独立,又紧密关联在一起的。

    Activity启动过程

    Activity的启动过程主要包含以下几个步骤:

    • Intent的解析(Intent可能是隐式的:关于Intents and Intent Filters
    • Activity的匹配(符合Intent的Activity可能会有多个)
    • 应用进程的创建
    • Task,Stack的获取或者创建
    • Activity窗口的创建
    • Activity生命周期的调度(onCreate,onResume等)

    本文不打算讲解Activity启动的详细过程,对于这部分内容有兴趣的读者请参阅其他资料。

    Task和Stack

    Android系统中的每一个Activity都位于一个Task中。一个Task可以包含多个Activity,同一个Activity也可能有多个实例。 在AndroidManifest.xml中,我们可以通过android:launchMode来控制Activity在Task中的实例。

    另外,在startActivity的时候,我们也可以通过setFlag 来控制启动的Activity在Task中的实例。

    Task管理的意义还在于近期任务列表以及Back栈。 当你通过多任务键(有些设备上是长按Home键,有些设备上是专门提供的多任务键)调出多任务时,其实就是从ActivityManagerService获取了最近启动的Task列表。

    Back栈管理了当你在Activity上点击Back键,当前Activity销毁后应该跳转到哪一个Activity的逻辑。关于Task和Back栈,请参见这里:Tasks and Back Stack

    其实在ActivityManagerService与WindowManagerService内部管理中,在Task之外,还有一层容器,这个容器应用开发者和用户可能都不会感觉到或者用到,但它却非常重要,那就是Stack。 下文中,我们将看到,Android系统中的多窗口管理,就是建立在Stack的数据结构上的。 一个Stack中包含了多个Task,一个Task中包含了多个Activity(Window),下图描述了它们的关系:

    另外还有一点需要注意的是,ActivityManagerService和WindowManagerService中的Task和Stack结构是一一对应的,对应关系对于如下:

    • ActivityStack <–> TaskStack
    • TaskRecord <–> Task

    即,ActivityManagerService中的每一个ActivityStack或者TaskRecord在WindowManagerService中都有对应的TaskStack和Task,这两类对象都有唯一的id(id是int类型),它们通过id进行关联。

    多窗口与Stack

    用过macOS或者Ubuntu的人应该都会用过虚拟桌面的功能,如下图所示:

    这里创建了多个“虚拟桌面”,并在最上面一排列出了出来。 每个虚拟桌面里面都可以放置一个或多个应用窗口,虚拟桌面可以作为一个整体进行切换。

    Android为了支持多窗口,在运行时创建了多个Stack,Stack就是类似这里虚拟桌面的作用。

    每个Stack会有一个唯一的Id,在ActivityManager.java中定义了这些Stack的Id:

    /** First static stack ID. */
    public static final int FIRST_STATIC_STACK_ID = 0;
    
    /** Home activity stack ID. */
    public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
    
    /** ID of stack where fullscreen activities are normally launched into. */
    public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
    
    /** ID of stack where freeform/resized activities are normally launched into. */
    public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
    
    /** ID of stack that occupies a dedicated region of the screen. */
    public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
    
    /** ID of stack that always on top (always visible) when it exist. */
    public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
    

    由此我们可以知道,系统中可能会包含这么几个Stack:

    • 【Id:0】Home Stack,这个是Launcher所在的Stack。 其实还有一些系统界面也运行在这个Stack上,例如近期任务
    • 【Id:1】FullScren Stack,全屏的Activity所在的Stack。 但其实在分屏模式下,Id为1的Stack只占了半个屏幕。
    • 【Id:2】Freeform模式的Activity所在Stack
    • 【Id:3】Docked Stack 下文中我们将看到,在分屏模式下,屏幕有一半运行了一个固定的应用,这个就是这里的Docked Stack
    • 【Id:4】Pinned Stack 这个是画中画Activity所在的Stack

    需要注意的是,这些Stack并不是系统一启动就全部创建好的。而是在需要用到的时候才会创建。上文已经提到过,ActivityStackSupervisor负责ActivityStack的管理。

    有了以上这些背景知识之后,我们再来具体讲解一下Android系统中的三种多窗口模式。

    分屏模式

    在Nexus 6P手机上,分屏模式的启动和退出是长按多任务虚拟按键。 下图是在Nexus 6P上启动分屏模式的样子:

    在启动分屏模式的之后,系统会将屏幕一分为二。当前打开的应用移到屏幕上方(如果是横屏那就是左边),其他所有打开的应用,在下方(如果是横屏那就是右边)以多任务形式列出。

    之后用户在操作的时候,下方的半屏保持了原先的使用方式:可以启动或退出应用,可以启动多任务后进行切换,而上方的应用保持不变。 前面我们已经提到过,其实这里处于上半屏固定不变的应用就是处在Docked的Stack中(Id为3),下半屏是之前全屏的Stack进行了Resize(Id为1)。

    下面,我们就顺着长按多任务按钮为线索,来调查一下分屏模式是如何启动的: 其实无论是NavigationBar(屏幕最下方的三个虚拟按键)还是StatusBar(屏幕最上方的状态栏)都是在SystemUI中。我们可以以此为入口来调查。

    PhoneStatusBar#prepareNavigationBarView 为NavigationBar初始化了UI。同时也在这里为按钮设置了事件监听器。这里包括我们感兴趣的近期任务按钮的长按事件监听器:

    private void prepareNavigationBarView() {
       mNavigationBarView.reorient();
    
       ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
       recentsButton.setOnClickListener(mRecentsClickListener);
       recentsButton.setOnTouchListener(mRecentsPreloadOnTouchListener);
       recentsButton.setLongClickable(true);
       recentsButton.setOnLongClickListener(mRecentsLongClickListener);
       ...
    }
    

    在mRecentsLongClickListener中,主要的逻辑就是调用toggleSplitScreenMode。

    toggleSplitScreenMode这个方法的名称很明显的告诉我们,这里是在切换分屏模式

    private View.OnLongClickListener mRecentsLongClickListener = new View.OnLongClickListener() {
    
       @Override
       public boolean onLongClick(View v) {
           if (mRecents == null || !ActivityManager.supportsMultiWindow()
                   || !getComponent(Divider.class).getView().getSnapAlgorithm()
                           .isSplitScreenFeasible()) {
               return false;
           }
    
           toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
                   MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
           return true;
       }
    };
    

    再顺着往下看PhoneStatusBar#toggleSplitScreenMode的代码:

    这里我们看到,通过查询WindowManagerProxy.getInstance().getDockSide(); 来确定当前是否处于分屏模式,如果没有则将Top Task移到Docked的Stack上。这里的Top Task就是我们在长按多任务按键之前打开的当前应用。

    @Override
    protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
       if (mRecents == null) {
           return;
       }
       int dockSide = WindowManagerProxy.getInstance().getDockSide();
       if (dockSide == WindowManager.DOCKED_INVALID) {
           mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
                   ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
       } else {
           EventBus.getDefault().send(new UndockingTaskEvent());
           if (metricsUndockAction != -1) {
               MetricsLogger.action(mContext, metricsUndockAction);
           }
       }
    }
    

    之后便会调用到ActivityManagerService#moveTaskToDockedStack中。后面的大部分逻辑在ActivityStackSupervisor#moveTaskToStackLocked中,在这个方法中,会做如下几件事情:

    • 通过指定的taskId获取对应的TaskRecord
    • 为当前Activity替换窗口(因为要从FullScreen的Stack切换的Docked Stack上)
    • 调用mWindowManager.deferSurfaceLayout通知WindowManagerService暂停布局
    • 将当前TaskRecord移动到Docked Stack上
    • 为移动后的Task和Stack设置Bounds,并且进行resize。这里还会通知Activity onMultiWindowModeChanged
    • 调用mWindowManager.continueSurfaceLayout(); 通知WindowManagerService继续开始布局

    而Resize和布局就完全是WindowManagerService的事情,这里面需要计算两个Stack各自的大小,然后根据大小来对Stack中的Task和Activity窗口进行重新布局。

    由于篇幅关系,这里不再贴出更多的代码。如果有兴趣,请自行获取AOSP的代码然后查看。

    下图总结了启动分屏模式的执行逻辑: sequece_splitscreen_mode_start

    这里需要注意的是:

    黄色标记的模式是运行在SystemUI的进程中。

    蓝色标记的模式是运行在system_server进程中。

    moveTaskToDockedStack是一个Binder调用,通过IPC调用到了ActivityManagerService。

    画中画模式

    当应用程序调用Activity#enterPictureInPictureMode便进入了画中画模式。

    Activity#enterPictureInPictureMode会通过Binder调用到ActivityManagerService中对应的方法,该方法代码如下:

    public void enterPictureInPictureMode(IBinder token) {
       final long origId = Binder.clearCallingIdentity();
       try {
           synchronized(this) {
               if (!mSupportsPictureInPicture) {
                   throw new IllegalStateException("enterPictureInPictureMode: "
                           + "Device doesn't support picture-in-picture mode.");
               }
    
               final ActivityRecord r = ActivityRecord.forTokenLocked(token);
    
               if (r == null) {
                   throw new IllegalStateException("enterPictureInPictureMode: "
                           + "Can't find activity for token=" + token);
               }
    
               if (!r.supportsPictureInPicture()) {
                   throw new IllegalArgumentException("enterPictureInPictureMode: "
                           + "Picture-In-Picture not supported for r=" + r);
               }
    
               // Use the default launch bounds for pinned stack if it doesn't exist yet or use the
               // current bounds.
               final ActivityStack pinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID);
               final Rect bounds = (pinnedStack != null)
                       ? pinnedStack.mBounds : mDefaultPinnedStackBounds;
    
               mStackSupervisor.moveActivityToPinnedStackLocked(
                       r, "enterPictureInPictureMode", bounds);
           }
       } finally {
           Binder.restoreCallingIdentity(origId);
       }
    }
    

    这里的 mStackSupervisor.getStack(PINNED_STACK_ID); 是在获取Pinned Stack,当这个Stack不存在时,会将其创建。 IBinder token是调用enterPictureInPictureMode的Activity的Binder标示,通过这个标示可以获取到Activity对应的ActivityRecord对象,然后就是将这个ActivityRecord移动到Pinned Stack上。

    Pinned Stack的默认大小来自于mDefaultPinnedStackBounds,这个值是从Internal的Resource上获取的:

    mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
         com.android.internal.R.string.config_defaultPictureInPictureBounds));
    

    而com.android.internal.R.string.config_defaultPictureInPictureBounds的值是从配置文件:/frameworks/base/core/res/res/values/config.xml 中读取的。

    为什么一旦将Activity移动到Pinned Stack上,该窗口就能一直在最上层显示呢?这就是由Z-Order控制的,Z-Order决定了窗口的上下关系。 Android N中新增了一个类WindowLayersController来专门负责Z-Order的计算。在计算Z-Order的时候,有几类窗口会进行特殊处理,处于Pinned Stack上的窗口便是其中之一。

    下面这段代码是在统计哪些窗口是需要特殊处理的,这里可以看到,除了Pinned Stack上的窗口,还有分屏模式下的窗口以及输入法窗口都需要特殊处理:

    private void collectSpecialWindows(WindowState w) {
       if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
           mDockDivider = w;
           return;
       }
       if (w.mWillReplaceWindow) {
           mReplacingWindows.add(w);
       }
       if (w.mIsImWindow) {
           mInputMethodWindows.add(w);
           return;
       }
       final TaskStack stack = w.getStack();
       if (stack == null) {
           return;
       }
       if (stack.mStackId == PINNED_STACK_ID) {
           mPinnedWindows.add(w);
       } else if (stack.mStackId == DOCKED_STACK_ID) {
           mDockedWindows.add(w);
       }
    }
    

    在统计完这些特殊窗口之后,在计算Z-Order时会对它们进行特殊处理:

    private void adjustSpecialWindows() {
       int layer = mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER;
       // For pinned and docked stack window, we want to make them above other windows also when
       // these windows are animating.
       while (!mDockedWindows.isEmpty()) {
           layer = assignAndIncreaseLayerIfNeeded(mDockedWindows.remove(), layer);
       }
    
       layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
    
       if (mDockDivider != null && mDockDivider.isVisibleLw()) {
           while (!mInputMethodWindows.isEmpty()) {
               final WindowState w = mInputMethodWindows.remove();
               // Only ever move IME windows up, else we brake IME for windows above the divider.
               if (layer > w.mLayer) {
                   layer = assignAndIncreaseLayerIfNeeded(w, layer);
               }
           }
       }
    
       // We know that we will be animating a relaunching window in the near future, which will
       // receive a z-order increase. We want the replaced window to immediately receive the same
       // treatment, e.g. to be above the dock divider.
       while (!mReplacingWindows.isEmpty()) {
           layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
       }
    
       while (!mPinnedWindows.isEmpty()) {
           layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
       }
    }
    

    这段代码保证了处于Pinned Stack上的窗口(即处于画中画模式的窗口)的会在普通的应用窗口之上。

    Freeform模式

    在Andorid N设备上打开Freeform模式很简单,只需以下两个步骤:

    1. 执行以下命令:adb shell settings put global enable_freeform_support 1
    2. 然后重启手机:adb reboot

    重启之后,在近期任务界面会出现一个按钮,这个按钮可以将窗口切换到Freeform模式,如下图所示: 

    这个按钮的作用其实就是将当前应用移到Freeform Stack上,相关逻辑在:

    ActivityStackSupervisor中,代码如下:

    void findTaskToMoveToFrontLocked(TaskRecord task, int flags, ActivityOptions options, String reason, boolean forceNonResizeable) {
       ...
       if (task.isResizeable() && options != null) {
           int stackId = options.getLaunchStackId();
           if (canUseActivityOptionsLaunchBounds(options, stackId)) {
               final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
               task.updateOverrideConfiguration(bounds);
               if (stackId == INVALID_STACK_ID) {
                   stackId = task.getLaunchStackId();
               }
               if (stackId != task.stack.mStackId) {
                   final ActivityStack stack = moveTaskToStackUncheckedLocked(
                           task, stackId, ON_TOP, !FORCE_FOCUS, reason);
                   stackId = stack.mStackId;
                   ...
    }
    

    这段代码通过查询stackId,然后调用moveTaskToStackUncheckedLocked移动Task。 而这里通过task.getLaunchStackId() 获取到的stackId,其实就是FREEFORM_WORKSPACE_STACK_ID,相关代码如下:

    TaskRecord#getLaunchStackId代码如下:

    int getLaunchStackId() {
       if (!isApplicationTask()) {
           return HOME_STACK_ID;
       }
       if (mBounds != null) {
           return FREEFORM_WORKSPACE_STACK_ID;
       }
       return FULLSCREEN_WORKSPACE_STACK_ID;
    }
    

    即,如果设置了Bound,便表示该Task会在Freeform Stack上启动。

    PS:这里将应用切换到Freeform模式,必须先打开应用,然后在近期任务中切换。

    如果想要打开应用就直接进入Freeform模式,可以看一下这篇文章:

    Taskbar lets you enable Freeform mode on Android Nougat without root or adb

    如果没有Google Play,可以到这里下载上文中提到的Taskbar应用:Taskbar

    有兴趣的读者也可以去GitHub上获取TaskBar的源码:farmerbb/Taskbar

    其实TaskBar的原理就是在启动Activity的时候设置了Activity的Bounds,相关代码如下:

    public static void launchPhoneSize(Context context, Intent intent) {
       DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
       Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
    
       int width1 = display.getWidth() / 2;
       int width2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_width) / 2;
       int height1 = display.getHeight() / 2;
       int height2 = context.getResources().getDimensionPixelSize(R.dimen.phone_size_height) / 2;
    
       try {
           context.startActivity(intent, ActivityOptions.makeBasic().setLaunchBounds(new Rect(
                   width1 - width2,
                   height1 - height2,
                   width1 + width2,
                   height1 + height2
           )).toBundle());
       } catch (ActivityNotFoundException e) { /* Gracefully fail */ }
    }
    

    而在ActivityStarter#computeStackFocus中会判断如果新启动的Activity设置了Bounds,

    则在FULLSCREEN_WORKSPACE_STACK_ID这个Stack上启动Activity,相关代码如下:

    final int stackId = task != null ? task.getLaunchStackId() :
          bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
                  FULLSCREEN_WORKSPACE_STACK_ID;
    stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
          + r + " stackId=" + stack.mStackId);
    return stack;
    

    下图是启动Activity时创建Stack的调用过程: startActivity

    当你在全屏应用以及Freeform模式应用来回切换的时候,系统所做的其实就是在FullScreen Stack和Freeform Stack上来回切换而已,这和前面提到的虚拟桌面的几乎是一样的。

    至此,三种多窗口模式就都分析完了,回过头来再看一下,三种多窗口模式的实现其实都是依赖于Stack结构,明白其原理,发现也没有特别神秘的地方。


    展开全文
  • 手游玩家、工作室和网吧用户想在电脑上登手机游戏,其实在电脑上装一个手机模拟器就可以了,而且可以改不同的IP地址,实现游戏无限多开,今天小编就来为大家图解一下用模似器电脑玩手机游戏,实现游戏无限多开,...
  • Android7.0 多窗口你值得拥有

    万次阅读 2016-09-23 16:52:29
    多窗口分屏其实在国内并不陌生,已经有一些手机和平板搭载了“分屏任务”和”APP窗口化”功能,但这些都是手机厂商自主定制系统中添加的功能,并非安卓原生所有,因此他们的实现方式可能会不一样,但是现在google原始...
  • 下载Android sdk,并安装,设置环境变量查看adb版本运行窗口输入cmd,在弹出的窗口中输入指令:adb version 结束adb进程不管有没有adb服务启动,在不影响其他的情况下,结束adb服务:adb kill-server 启动adb服务...
  • uniapp 运行手机或模拟器

    千次阅读 2021-04-30 21:30:40
    在此目录下运行cmd窗口:输入命令adb devices 出现这种情况说明,你没有运行模拟器,或者你的模拟器还在加载中。 当你启动模拟器后: 记得重开一个cmd运行命令哦! 敲完命令之后,会得到一个端口号,打开你的...
  • Android多窗口模式(分屏模式)

    千次阅读 2020-12-19 02:20:54
    Android N 支持多窗口模式,或者叫分屏模式,即在屏幕上可以同时显示窗口。在手机模式下,两个应用可以并排或者上下同时显示,如图 1 所示,屏幕上半部分的窗口是系统的 CLOCK 应用,下半部分是系统设置功能。...
  • 如何关闭手机后台运行程序

    千次阅读 2021-06-04 02:52:05
    如何关闭手机后台运行程序如今手机应用五花八门,用户在手机世界中畅游的时候也会遇到一些问题,比如,手机了软件后会很卡,很用户想关闭那些后台运行的程序但是却不知道该怎么操作,其实要关闭后台程序可以有...
  • Android Studio 将程序在模拟器或手机运行

    千次阅读 多人点赞 2021-02-05 11:54:00
    用于在Android Studio写完代码后,在模拟器或者手机运行的内容 运行程序必须有一个运行的载体,可以是一部Android手机,也可以是模拟器。 1.启动模拟器 首先观察顶部工具栏的图标,点击创建和启动模拟器的...
  • 如何把Unity程序加载到安卓手机运行

    万次阅读 2018-03-17 22:14:07
    点击File->Build & Run,弹出对话框,给Apk取个名字,然后该Apk就会自动推送到手机上,时间可能会稍长,推送完后,在手机弹出的界面中允许安装即可。 总结:过程中有问题,可以参考以上三位博主的文章,本人亲测成功...
  • 因为现在做的是一个手机端项目 , 所以要在手机运行看适配, 上传文件, 这几天就上网学了下怎么弄在手机上看, 其实步骤很简单 , 但是想记录一下学习到新技能的开心 ^_^ 手机和电脑连同一个wifi , 或者电脑连你手机...
  • 最近开发一个应用, 发现Activity跳转之后, 最近任务里面有两个窗口, 类似这样 照的不是很清楚, 大体就这样, 两个窗口分别是两个Activity, 属于同一应用的. 出现这样的原因是因为两个Activity不属于同一个Task, ...
  • Android11系统真机运行,暂不支持Android Studio自带的Android11模拟器,请使用真实手机,如果碰到Android11手机无法真机运行,请单独发帖,并提供详细设备信息。 出现问题手机分析清楚问题在哪个环节。 从在...
  • Android N 多窗口模式

    千次阅读 2017-09-01 16:47:59
    前言 在智能手机更新换代越来越快,手机屏幕越来越大...终于,在Android 7.0(N)(API 24)引入了多窗口(Android N 的 Nexus Player还有画中画)的支持,也就是分屏显示,可以是上下排列,也可以是左右排列,可以在
  • vue 项目在手机运行效果

    千次阅读 2018-10-18 10:45:36
    按住WIN窗口+R弹窗后输入 ipconfig 就可以查看自己电脑的IP 就在自己的项目工程中修改IP地址: 在VUE项目中找到config文件下的dev:host及port host:是自己电脑的IP地址 // host:'192.168.XXX.XXX' port:是端口...
  • C语言运行窗口中的暂停与清屏

    千次阅读 2019-05-18 20:29:56
    运行完这条语句,立即清屏。 样例2: #include #include using namespace std; int main(){ int a; while(cin>>a){ cout; system("pause"); // system("cls"); } return 0; } 输入一个数,输出这个数的...
  • 一虚拟机1点击右上角 Edit Configurations-&gt;Open Select Deployment Target Dialog为虚拟机调试/2 USB Device为手机调试(其中手机要调成开发者模式,还要在开发者选项中打开允许USB调试)...
  • 这里写自定义目录标题Android Studio虚拟机显示比例不协调(运行手机框很大,屏幕很小) Android Studio虚拟机显示比例不协调(运行手机框很大,屏幕很小) 我按照别人的做法去修改了电脑的缩放比例,从原来的150改...
  • 让手机重现“一律允许使用这台计算机进行调试”确认窗口
  • 通过cmd窗口,输入命令ipconfig:我的是都用的wifi,因此我要的ip地址就是上图圈中的ip地址。然后把本地运行的连接,例如:localhost:8080/index 改成ip的来链接即:10.0.***.****:8080/index即可。问题:如果手...
  • 当前Python使用越来越广泛,并且越来越的人开始学习Python,不仅是从事IT行业的技术人员, 其他行业的人员也开始学习Python,以解决自己平时工作中的一些困境,同时最近少儿编程也开始流行,在电脑上进行编程学习...
  • monitor工具的位置具体在 Android SDK目录中tools下 双击之后自动运行。如果运行是白屏是因为javaJDK...运行成功之后 电脑连接手机 打开开发者模式, 连接成功之后 选择对应手机 就可以对所选的条目 进行抓取信息 ...
  • (1)安装插件 ...真机运行失败,失败原因:手机上没有信任本计算机的授权,请在手机上信任该授权 (4)如果报错提示: 手机上一般是没有这个目录的,你可能会认为这个 是默认的安装目录,想着是否...
  • Android Studio运行手机框很大 屏幕很小的解决办法

    千次阅读 多人点赞 2019-10-30 23:09:48
    关于Android Studio运行手机框很大 屏幕很小的解决办法 才开始接触移动软件开发,按照Android Studio安装教程相关博客安装过后,发生如图问题: 可以发现模拟器运行出来的结果是屏幕很小 (ps:想要知道解决方法可...
  • 三星s20系列多窗口分屏设置操作步骤来讲一讲吧,三星S20是一款屏幕体验超好的智能手机,它可以运行多窗口我们在分屏视图上同时运行两个应用程序。比如我们能一边聊天,一边看视频,是不是很对劲,下面我们就来说...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 94,102
精华内容 37,640
关键字:

如何让手机多窗口运行