zoukankan      html  css  js  c++  java
  • 【Bugly安卓开发干货】Android APP 高速 Pad 化实现

    Bugly 技术干货系列内容主要涉及移动开发方向。是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创。转载请标明出处。

    怎样能在最快的时间内,实现一个最新版本号 android app 的 pad 化呢?从拿到一个大型手机 app 代码開始开发到第一个其全新 pad 版本号的公布,我们用了不到3个月时间给出了一份惬意的答案。

    项目背景

    採用最新版本号手机 APP(之后称为 MyApp)代码。实现其 Pad 化,为平板和大屏手机用户提供更好的体验。

    为实现 MyApp 的 Pad 化工作。须要我们首先来了解一下 MyApp 项目经典页面的构成以及 Pad 化后的页面结构的变化。

    1.MyApp 页面经典构成

    如今主流手机 APP 主页通常採用标签栏加标签内容方式显示。而通过主页进入的二级页面全部採用全屏方式展示。

    比方手机 QQ,微信,支付宝等等都是採用 Tab 栏方式为主,进入一个详细功能后,全屏打开。我们项目也是如此。

    以下看一下 MyApp 项目手机端的页面构成图。

    腾讯Bugly
    Bugly安卓开发

    左側是一个 Tab 栏(区域1)加 Tab Content(区域2)构成的页面。右側是在 TabContent 中点击详细功能后进入的一个功能详情页面(全屏区域3)。
    查看代码,发现除 TabContent 区域2,从主页開始到其它全屏显示的页面全部採用 Android Activity 组件实现。经统计得出大概有几百个 Activity。

    这些 Activity还包括比方Web进程。peak 进程(图片选择查看)等其它非主进程 Activity。

    1.MyApp pad 化的设计图

    了解了手机 MyApp 页面构成后,还要来看 Pad 化后 UI 结构的变化,通过对照来探索 Pad 化最佳的实现方案。

    以下是我们的 PAD 版本号页面结构图。
    这里写图片描写叙述

    因为 Pad 平板的空间要远大于手机空间。所以。在主页中 Pad 所展示的内容要比手机很多其它。通过观察设计图发现。整个页面分为了3块区域,与手机端页面的1,2,3区域一一对应。Tab 栏被移到了左側1区,Tab Content 被移到了中间2区。而在2区打开的 Details 页面则要求在3区展示,而不再是像手机 APP 一样全屏展示。

    手机 APP Pad 化道路的探索过程

    通过了解 MyApp 项目经典页面构成和 pad 版页面结构的变化,以及高速Pad 化的原则,我们開始了对手机 APP pad 化实现方案的漫漫探索。


    首先想到的是,既然手机APP页面主要是由Activity构成。那么我们能不能把 Activity 缩小。让多个 Activity 在同一屏幕显示呢。非常快我们的方案1出来了。

    方案1。假设把设计图的整个页面称为主 Activity,主 Activity 全屏显示不变,在主 Activiy 中打开的新 Activity (称为A)缩小显示在设计图3区,我们就能够实现 Pad 设计的要求。那么我们详细实现步骤为:
    1。A类 Activity 继承 Base Activity
    2。改动 Base Activity 的 window 的起始坐标x和宽度 width,让其刚好位于3区。
    3,A类 Activity 背景改为透明
    4,让在A类 Activity 继续打开的 Activity。反复1,2,3,4步骤。

    可是非常快就发现了问题。
    1. 在当前 Tab 打开 Activity A,切换 Tab 后 A Activity 仍然显示。
    2. 每一个 Tab 打开的 Activity,都处于同一个 Activity 栈中,按打开先后顺序加入,点击返回键也是顺序退出的。这样每一个 Tab 中打开的Activity 都混在一起了,而不是彼此独立。导致 back 键出现故障。

    既然直接显示 Actvity 有问题,想想反正都是显示UI布局,能不能把 A 类 Activity 的根布局拉出来挂载在主 Activity 右側?从而我们推导出了方案2。

    方案2:在主 Activity 启动 A 类 Activity 时,获取 A 的根布局。加入到主 Activity 在右側3区预留的一个空布局中。

    详细实现步骤为:
    1,重写主 Activity 的 startActivity 方法。
    2。使用 LocalActivityManager 启动 A 类 Aactivity 返回 window 对象。
    3,通过 Window 对象 window.getDecorView()返回打开 Activity 的布局并 Add 到主 Actvity 上。


    4,重写主 Activity 的 Back 逻辑,在点击返回键时 remove 掉挂载的 decorView。

    可是在 Demo 上一測试,就发现了非常多问题。
    1. 用 mat 查看到 A 类 Activity 是怎么也释放不掉的,因为 LocalActivityManager 已经抓住了 A 类 Activity 的 parent。


    2. 直接拿出 A 类 Activity 的 decorView,已经让A类 Activity 丧失了 Activity 的一切特性,包括生命周期,返回逻辑,ActivityResult,以及启动其它 Activity 等功能。

    会导致之前正常执行的A类 Activity 出现大量问题。

    既然直接拿到根视图没实用。那该怎么做才好呢?怎么做才干使 A 类 Activity 的页面挂载在主 Activity 右側。又能保证 A 的生命周期和 Activity 行为呢?经过大家一番思考讨论后。能不能利用插件的思想,把 A 类 Activity 中的生命周期方法以及继承自 Activity 类的方法都拿出来了,在适当时候自己调用呢?这样就保证了原来 A 中的代码不会出现故障,不须要改动 A 中的不论什么代码。可是用什么做容器(代理)比較好了?之后我们想到了用 Fragment,。因为 Fragment 能够作为所属 Activity 的一个块存在于不论什么位置,并且 Fragment 有自己的生命周期,并受所属 Activity 的生命周期影响。它就像一个子 Activity 一样。简直是绝佳容器。并且 Fragment 比較轻量。本身由 Activity 来管理(而不像 Activity 由 Android 系统服务管理),在不同的布局结构中重用 Fragment 能够优化屏幕空间和用户体验。


    注意。以下所说的把 Activity 转换为 Fragment 并非直接把 Activity 变为 Fragment,这会付出巨大代价。而是以一个空的 Fragment 为容器来承载 Activity。

    最终方案3顺利出炉。

    方案3,把 Activity 转换为 Fragment。使用 Fragment 模拟 Activity 的方法。

    然后把 Fragment 直接加入到主 Activity 的右側布局中。

    实现的详细步骤为:
    1,新增 BasePadActivity,让全部 Activity 继承 BasePadActivity,重写 StartActivity 方法。在该方法中手动 New 出A类 Activity,并把主 Activity 的上下文对象 Context 传递给它。


    2,创建 MyFragment,持有A类 Activity 实例引用,在 MyFragment 生命周期中直接调用A类 Activity 生命周期方法,并把A类 Activity 的视图传递给 Fragment 使用。


    3,A类 Activity 中继承自 Activity 的方法全部重写,详细实现由步骤1中的得到的主 Activity 上下文 context 处理。这样A类 Activity 已经成为一个普通实例化对象,不再由 Android 系统管理。

    该方案有较多长处,因为继承 base。不仅能迅速将大量 Activity 快换为 Fragment。并且转换后,使原来A类 Activity 的功能逻辑维持正常。

    并且因为A类 Activity 的上下文事实上使用了主 Activity 的上下文对象,须要在A类 Activity 获取 Resouce。Window,Asset 对象等都能通过主 Activity 的 context 进行获取。


    该方案实现后,最初測试好像一切正常,可是不就后也发现了若干问题:
    1. 原 Activity 自己定义 TitleBar 出现故障。
    2. 每一个 Tab 标签中打开的 Fragment,因为都属于一个主 Activity,导致它们仅仅有一个 Fragment 栈,Back 返回时会出现与方案1相似的问题。


    3. 虽然 Activity 转换为 Fragment 后,大部分行为都进行了模拟,可是另一些重要行为没有做处理,比方说 Activity 的启动模式。Back 键,onActivityResult 等等,这些还要进行完好。
    4. 对于声明为多进程的 Activity。转换为 Fragment 后失去了多进程的特性。因为这些 Fragment 属于主 Activity,主 Activity 是属于手Q进程的。

    该方案虽然也有诸多问题,可是经过调研和測试,发现基本都是能解决的。出于该方案的长处,以及对其出现的问题的解决难易评估,最终决定在该方案基础上进行优化和完好。那么对上述出现的若干问题该怎样解决呢?

    问题1。Activity 替换成 Fragment 之后。怎样实现自己定义 TitleBar?

    设置自己定义 TitleBar,是 Activity 所提供的接口,查看手机APP代码,大部分 Activity 都继承了 TitleBarActivity。通过

    getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
                R.layout.custom_commen_title);  
    

    来定义 TitleBar 的样式。所以改为 Fragment 之后导致大量 Activity titlebar 显示不出。甚至 crash。

    那么能不能实现一个自己定义的 window 对象继承 android.view.Window,通过 getWindow()得到的是我们自己定义的 Window 对象,它能够处理自己定义 Titlebar 的使用。

    问题2。怎样确保每一个标签页中 Fragment 的操作互不干扰?

    Pad 版本号主页也是分为多个 Tab 标签栏的,每一个标签栏中对 Fragment 的操作应该是相互独立的。

    Android中Fragment都是由 FragmentManager 来管理的。

    Fragment 的加入,替换,移除等操作都是由 FragmentManager 中对象 FragmentTransaction 来记录和执行。每一个 Activity 中仅仅有一个 FragmentManager 实例。通过代码
    Activity.java

    final FragmentManagerImpl mFragments = new FragmentManagerImpl();
    public FragmentManager getFragmentManager() {
        return mFragments;
    }  
    

    获取。假设把设计图中的整个页面称为主 Activity,用主 Activity 中一个 FragmentManager 来管理全部标签栏的 Fragment 显然会引起混乱,那么能否实现每一个标签页中都有一个 FragmentManager 的实例来管理当前标签中全部 Activity 转换的 Fragment?

    问题3:Activity 替换成 Fragment 之后,完好 Fragment 模拟 Activity 的行为。

    完好 Fragment 的 Activity 行为。比方还须要模拟 Activity 的启动模式、Activity result、startActivity、finish、onBackPressed 等等。

    问题4。怎样处理多进程 Activity 的显示?

    在回答这个问题之前。要先问一个问题,为什么不都转换为 Fragment 呢?
    之前研究手机 APP 项目代码发现,很多Activity都是设计成属于其它进程,比方 Web 进程。这样设计的原因:

    其一是这类 Activity 功能都是属于同一模块。出现 crash 也不会让整个QQ崩溃。
    另外一个重要原因是,Android 平台对每一个进程都有内存限制。使用多进程就能够使APP所使用的内存加大几倍。其它进程能够分担主进程的内存压力。大大减少内存溢出导致的 crash。

    所以不能轻易把这些 Activity 转换为 Fragment。因为转换为 Fragment,就失去了 Activity 多进程的特性,违背了之前设计初衷,大大添加了APP的内存压力。

    那么这种情况下能否让多个 Activity 在同一屏幕显示,能不能让从主 Activity 打开的新 Activity 变为透明,并且让其大小和位置刚好覆盖设计图的区域3。同一时候让属于主 Activity 的区域1,和区域2接收事件。这样既让 Acitvity 拥有多进程的特性,又让他们看起来就像是在同一个 Activity 中操作。咦,这不是我们的方案1吗? 对的,因为以上种种原因,对于多进程的 Activity,我们还是要依照方案1来处理。

    那么怎样解决解决方式1中的问题。

    问题5。多进程的 Activity 在切换标签后怎样处理?Back 键怎样处理?

    在每一个标签页打开的多进程 Activity,应该仅仅与本标签页有关联,在切换到其它标签后。这些 Activity 应该隐藏起来,又一次再切换 Tab 回到该标签时,之前在该标签打开的这些 Activity 应该又一次显示。

    并且每一个 Tab 的 Activitys 都应该有一个 Activity 栈来管理。
    这该怎样实现呢?通过阅读http://developer.android.com/guide/components/tasks-and-back-stack.html了解到
    一个 app 中通常包括若干个 Activitys,我们能够把这些 Activity 分为若干类。让每一类都属于同一个 Task,以多任务的方式把这些 Activity 分为若干组。

    比方把在 Tab1栏内打开的多进程 Activity 放入一个Task中。把Tab2中打开的多进程 Activity 放入另外一个 Task 中。切换 tab 时,仅仅须要让两个 task 交替移到前台显示或后台隐藏就可以,并且每一个Task中都维护着一个 Activity 栈。该想法似乎能解决问题。

    那么看到这里大家又会有另外一个疑问了?既然能解决方式1中的问题,为什么不直接全部使用方案1呢?还要把Activity转为Fragment干嘛?
    1。实现的问题,使用多Task的实现方式,在Android中须要声明Activity的TaskAffinity,而 TaskAffinity 不能在代码中动态声明。而仅仅能写在配置文件里,导致不同Tab打开的同一个Activity可能须要在配置文件里声明两次,因为它们的 TaskAffinity 要不一样,而同一个Activity是不能声明两次的。所以仅仅有写一个空的 Activity 继承它。导致大量空Activity产生,并且在代码中启动 Activity 前还要重定向到继承的Activity。比較麻烦。多进程 Activity 毕竟还是少数,所以能够这么做。

    但全部这样实现明显不太可取。

    2。体验的问题,当切换 Tab,把 Task 移入前台,会有一个延时。并且这个延时并不确定,导致切回 tab,会先显示底部的页面,然后 task 中Activity 才覆盖上来。

    3。机型的问题,极少数机型可能是因为厂家定制的原因,在多个 Activity 显示在同一屏幕时会有一个问题,在接收左側主 Activity 的事件时,A类 Activity 会消失。经过原因查找,发现A类 Activity 的 task 自己主动回到了后台。应该是系统源代码被改动了。这种话基本没法用了。

    经过大家若干分析讨论,我们基本理清楚了方案3所遇到问题的大致解决版本号,经过均衡考虑,使用以下解决方式是眼下 Pad 化最好的解决方式。

    APP 高速 Pad 化实现架构方案

    对 MyApp pad 化的整个流程以及会遇到的问题理清楚之后。经过思考和大家的讨论确定了我们手机端 APP Pad 化的架构方案:

    1,转换 APP 主进程的 Activity 为 Fragment,转换过程尽量不改动原来 Activity 的不论什么代码。
    2,让转换后的 Fragment 模拟 Activity 的行为。保持 Fragment 和原来 Activity 的行为一致。


    3,使用 LocalActivityManager 实现每一个 Tab 标签 Fragments 操作的独立性。
    4,多进程 Activity(包括插件 Activity)不转换为 Fragment,实现多任务分屏显示。

    1,巧妙转化 Activity 为 Fragment

    实现 BasePadActivity 为全部 Activity 基类,对 Activity 转换为 Fragment 的操作在 BasePadActivity 中实现,利用相似插件的想法。以Fragment为壳,把真正详细的实现从 Activity 中搬入 Fragment,而不须要改动原 Activity 的不论什么代码。通过重写 StartActivity 方法。让原来去 startActivity 的动作变为加入一个新的 Fragment 动作,并调用 addToBackStatck()方法加入到 Fragment 栈中。


    以下给出部分伪代码的实现 。注意以下代码都是伪代码,非常多仅仅有方法而无实现, 这里主要是讲思路

    BasePadActivity.java  
    
    public class BasePadActivity extends Activity{
        /**
         * 通过重写startActivityForResult来将Fragment转换为Activity
         */
        @Override
        public void startActivityForResult(Intent intent) {
            if (isfragment && !isNewProcessActivty()){ //多进程Activity不转换为Fragment
                    //初始化activity,注意该Activity是我们自己实例化出来的
                    BasePadActivity activity =(BasePadActivity)newInstance(intent);
                    activity.attachBaseContext(getBaseContext()) 
                    activity.onCreate(intent.getExtras());  //模拟Activity的onCreate
                    activity.addMyFragment();
                }
            }
        }
    
        public class MyFragment extends Fragment {
            @Override
            public View onCreateView() {
                View contentView = getWindow().getDecorView();   //Fragment的View为Activity的decorView
                return contentView;
            }
    
            @Override
            public void onResume() {
                super.onResume();
                BasePadActivity.this.onResume();  //模拟Activity 
            }
    
            @Override
            public void onPause() {
                super.onPause();
                BasePadActivity.this.onPause(); //模拟Activity onPause
            }
            ........
            ........
        }
    }  
    

    代码巧妙实现了全部继承 BasePadActivity 的 Activity 转换为Fragment 的过程,当然这里仅仅展示了转换一小部分。其它细节问题并没有在代码中列出来。当打开 Activity 的过程转换为打开 Fragment 的过程后。我们须要让 Fragment 模拟 Activity 的行为。

    2,Fragment 模拟 Activity 的行为

    模拟 Activity 的 finish 方法

     BasePadActivity.java    
    
     public void finish() {  
        if (bActivityToFragment) {
            removeTopFragment(); //移除Fragment栈中最顶层Fragment
        } 
    }  
    

    模拟 Activity 的 onActivityResult,在当前 Fragment 被 finish 去触发

    private void removeTopFragment() {
        if(popBackStackImmediate()){     //成功把顶层Fragment,从Fragment栈中移出。

    handleSetResult(requestCode,false); } } private void handleSetResult(int requestCode){ //触发上层Fragment的onActivityResult topFragment.onActivityResult(requestCode, resultCode, data); }

    模拟 Activity 的返回事件,当把 Activity 转换为 Fragment 时。其返回事件已经由该 Fragment 所属的 Activity 接收。因此须要处理其所属真正 Activity 的返回事件。通过 FragmentManager 能够管理该 Activity 中全部 Fragment。

    BasePadActivity.java     
    
    public void onBackPressed() {
        if(isActivityForFragment()){  //,为Fragment所属Activity
            if(!handleFragmentBackEvent()){  //先处理Fragment本身的返回事件。比方想先关闭当前Fragment菜单。
                finishTopFragment();  //最后finish该Fragment
            }
        }
    }  
    

    模拟 Activity 的启动方式,通过获取 intent.getFlags()值。来推断 Activity 的启动模式,通过

    public boolean hasFlagClearTop(int flags){
         return  (flags & Intent.FLAG_ACTIVITY_CLEAR_TOP )!=0  ;
    }
    
    public boolean hasFlagNewTask(int flags){
        return  (flags & Intent.FLAG_ACTIVITY_NEW_TASK )!=0  ;
    }  
    

    来推断 Flag 中是否包括对应启动方式值。来对 Fragment 的打开做对应处理。这里会略微麻烦一点。故不作代码说明了。

    3。Fragment 实现自己定义的 TitleBar

    在不改变原来 Activity 代码的情况下。通过改变 Window 对象,自己实现对 Fragment 布局的控制。实现自己定义 Titlebar。
    BasePadActivity.java

    public Window getWindow() {
        if(customWindow==null){   //自己定义的window,主要为了解决非常多Activity继承IphoneTitleBar的问题
                customWindow = new Window4FragmentTitle(mContext); 
            }
            return customWindow;
    }  
    
    Window4FragmentTitle.java    
    
    public class Window4FragmentTitle extends Window{
        @Override
        public void setContentView(View view, LayoutParams params) {
            if (mContentParent == null) {
                installDecor();
            } 
            mContentParent.addView(view, params);
        }
    
        @Override
        public void setFeatureInt(int featureId, int value) {
             if(featureId != FEATURE_CUSTOM_TITLE){
                 return;
             }
             FrameLayout titleContainer = (FrameLayout) findViewById(android.R.id.title);
             if (titleContainer != null) {
                 mLayoutInflater.inflate(value, titleContainer);
             }
        }
    }  
    

    通过 Window4FragmentTitle 调用 setFeatureInt(int)方法,会把自己定义的 Title 布局嵌入到我们创建的布局 mDecor 中,然后把 mDecor 放入 Fragment,实现自己定义 Titlebar 在 Fragment 中的显示。

    4。使用 LocalActivityManager,实现对每一个 Tab 中 Fragments 的独立管理。

    使用 LocalActivityManager 实现标签布局,使每一个 Tab 中都有一个 Acitvity 对象。而每一个 Activity 中都会有一个 FragmentManager 对该 Tab 的 Frament 栈进行管理。这样每一个 Tab 的 Fragments 相互独立,互不影响。
    主Activity

        addFrame(Tab1Content.class, mTabs[1]);   //Tab1
        addFrame(Tab2Content.class, mTabs[2]);    //Tab2
        .....
        public void addFrame(){
            mTabHost.setup(getLocalActivityManager());
            TabSpec tabSpec = mTabHost.newTabSpec("").setIndicator(tab).setContent(new Intent(this, clz));
            mTabHost.addTab(tabSpec);
    
        }
    

    通过 setContent(new Intent(this, clz) ,把每一个标签栏内容以子 Activity 的方式加入进来。

    5,多任务分屏显示。

    前面说过。对于多进程的 Activity。为了保持其模块化以及分担主进程内存压力的特点,经过大家讨论,不把他们转换为 Fragment,那么就须要解决多个 Activity 一起展示的问题。经过研究,得出的有效实现方式是:让在每一个标签栏内打开的 Activity 透明化,并且让其大小和位置刚好居于设计图3区,同一时候能让处于该 Activity 下方的左側区域的主Activity 接收点击事件

    1,Activity 透明化实现,在配置文件里声明 Activity 的 theme 为透明
    2,定义 Activity 的大小和位置。


    在 Activity 的 onCreate 方法中实现

    WindowManager.LayoutParams layoutParams = window.getAttributes();
    layoutParams.gravity = Gravity.RIGHT | Gravity.TOP;  //位置居于右側
    layoutParams.width =  mActivity.getRightPanelWidth(); //宽度为右側区域宽度    
    

    * 3,让左側主 Activity 接收事件*
    通过设置 window 的 WindowManager.LayoutParams 的 flag 为 FLAG_NOT_TOUCH_MODAL 能够让显示在透明 Activity 左側下方的主 Activity 接收事件。
    新打开的 Activity 位于右区,左区为主 Activity 显示区域。左区和右区能同一时候接收用户点击事件,看起来就好像同一个 Activity 一样。

    可是因为在当前 Tab 打开的位于右区的 Activity,是尾随当前Tab的,在切换 Tab 后,应该消失。比方 Tab1中打开的 Actvity。切换到Tab2时应该隐藏掉。又一次再切换回 Tab1时让其又一次显示。保留现场。该功能要怎样实现呢?经过对 Android 特性的理解以及思考。发现能够是用多任务分屏显示方式实现不同 Tab 多进程 Activitys 的显示和隐藏。让不同Tab打开的 Activitys 分属于不同 Task,每一个 task 拥有一个 Activity 栈来管理当中 Activity,切换 tab 要做的就是不同 Task 的切换。

    这样逻辑非常清楚,也符合高速 Pad 化的原则。那么详细该怎么实现呢?
    为每一个 Tab 打开的第一个 Activity 提供一个不同的 TaskAffinity
    首先我们来了解。什么是 TaskAffinity
    在某些情况下。Android 须要知道一个 Activity 属于哪个 Task,这是通过任务共用性(TaskAffinity)完毕的。TaskAffinity 为执行一个或多个Activity 的 task 提供一个独特的名称,当使用 Intent.FLAG_ACTIVITY_NEW_TASK 标志的 Activity,并为该 Activity 声明一个独特的 TaskAffinity 时,该 Activity 不再执行在启动它的 Task 里。而是会又一次启动一个新的 Task,新的 task 管理一个新的 Activity 栈。而打开的这个 Activity 则位于栈底。
    了解了 TaskAffinity,我们在配置文件里为打开的多进程 Activity 设置相关 tab 的 TaskAffinity 值
    以下展示对 web 进程 Activity 的处理

    <activity
      android:name=" BrowserActivity1"
      android:process=":web"
      android:taskAffinity="com.tab1. BrowserActivity1" 
      android:theme="@style/Default.Transparent" />
    
    <activity
      android:name=" BrowserActivity2"
      android:process=":web"
      android:taskAffinity="com.tab2.BrowserActivity2" 
      android:theme="@style/Default.Transparent"/>
    
    ........  
    

    在不同 Tab 打开的 BrowserActivity,都为它们设置了不同的 TaskAffinity,在代码中当发现打开的页面是 Web 页面时,则在哪个Tab打开。页面重定向到设置了对应 TaskAffinity 的 Activity上。

    public void startActivityForResult(Intent intent) {
      if(isBrowserActivity(intent)){   //纯打开QQBrowserActivity
        int curTab = getTabIndex();  //获取当前Tab索引
        Class<?> c = getBrowserMap().get(curTab);  //取出对应打开的Activity
        redirectAndOpenInNewTask()    //重定向    
      }
      ......
      ......
    }  
    

    这样就为在不同 Tab 打开的 Activity 创建了不同的 Task。然后在切换Tab时通过发送广播动态的显示和隐藏 Task。

    public void onTabSelected(int curTabIndex) {
        Intent i = new Intent("action");
        i.putExtra("cur_Tab_Id",curTabIndex);    //切换到当前Tab的索引
        sendBroadcast(i);
    }
    

    在 Task 的根 Activity 中接收广播,处理 Task 显示和隐藏逻辑

    public void onReceive(Context context, Intent intent) {
            int tab = intent.getIntExtra(CUR_TAB_ID,-1);
                if(tabIndex>=0 && tabIndex == tab){
                     moveTaskToFront(mActivity.getTaskId());   //移动当前Task移入后台
                }else{
                     moveTaskToBack();  //把该Activity所属Task移动到前台
    
                }
        }  
    

    到这里基本上攻克了多进程 Activity 与主 Activity 同屏显示所带来的问题。

    总结

    通过上述方案。以及一些问题的巧妙解决。最终实现了 MyApp Pad 化的高速开发,并且 MyApp for Pad 第一个版本号的公布上线。到如今6到7个版本号的迭代,一直都是稳定执行的。该方案的长处是,仅仅要维护好架构,其它开发人员在对单个页面改造时。不须要管它是真正 Activity 还是 Fragment,仅仅要知道这些页面表现的都是 Activity 的行为,就和在手机 APP 上开发是一样的。

    扩展

    通过 MyApp Pad 化开发方案的实现,我们想我们这个方法能否够写成一个通用的手机 App Pad 化组件。为公司的其它Android产品Pad化提供技术支持。就算你的 APP 布局并非 Tab 方式,我们开放出一个 Base Activity。其它 App 的 Activity 通过继承 Base,就能自己主动转换为Fragment,并且能为多进程 Activity 提供处理方案。

    但这肯定须要考虑很多其它的情况。和解决很多其它问题。路漫漫其修远兮。吾将上下而求索。

    期待完好的 Android App PAD 组件能与大家见面。

    假设你认为内容意犹未尽。假设你想了解很多其它相关信息。请扫描以下二维码。关注我们的微信公众账号,能够获取很多其它技术类干货,还有精彩活动与你分享~
    这里写图片描写叙述

    腾讯 Bugly是一款专为移动开发人员打造的质量监控工具。帮助开发人员高速,便捷的定位线上应用崩溃的情况以及解决方式。智能合并功能帮助开发同学把每天上报的数千条 Crash 依据根因合并分类。每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报能够在公布后高速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统。鹅厂的project师都在使用,快来加入我们吧。

  • 相关阅读:
    wpf之依赖属性
    wpf之布局控件
    WPF之绑定
    wpf之触发器
    wpf之样式
    wpf之TreeView
    wpf(五)
    【Javaweb】poi实现通过上传excel表格批量导入数据到数据库
    Java读取批量Excel文件
    Centos上通过yum命令删除有关MySQL
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/8419415.html
Copyright © 2011-2022 走看看