前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >模仿Android微信小程序,实现小程序独立任务视图的效果

模仿Android微信小程序,实现小程序独立任务视图的效果

作者头像
用户1158055
发布于 2022-10-31 03:14:17
发布于 2022-10-31 03:14:17
1.3K00
代码可运行
举报
文章被收录于专栏:郭霖郭霖
运行总次数:0
代码可运行

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新。

大家好,久违的原创又来了。

今天跟大家分享一个非常有趣的技术,如何在我们的App中实现类似于微信小程序的功能。

哈哈开个玩笑,如果我能徒手实现一套微信小程序系统的话,早就被腾讯挖过去当架构师了。

小程序相信现在所有人都使用过的对吧,很多人甚至天天都在使用。小程序特别的方便,无需下载,无需安装,在微信当中打开就能立刻使用。随取随用,随用随走,也不占用任何手机的存储空间。

Android上的微信小程序做得格外的像一个真正的应用程序。为什么这么说呢?因为Android上的每个微信小程序甚至还能拥有自己的任务视图,就像是一个真正的独立应用程序一样。点击手机任务栏键可以看到如下界面:

上图中美团外卖、微博热搜、星巴克都是小程序。

拥有独立的任务视图的话,就可以更加方便地在多个小程序或微信本体之间进行快速切换,在这点上Android的体验要比iOS更好。

那么问题来了,这种依附于其他程序的小程序是如何做到拥有一个独立的任务视图的呢?

本篇文章我们就来一探究竟。

事实上,这是一个很基础的功能。有多基础呢?任何一位Android开发者在入门时都一定学过这个知识:Launch Mode。

因此,我就不在这里对Launch Mode进行展开讲解了。如果你真的从来没有听说过Launch Mode,建议参考《第一行代码 第3版》第3章的内容。

我们都知道,Android中Activity的启动模式一共有4种:standdard、singleTop、singleTask和singleInstance。

从字面意思上来看,singleTask表示的就是要启用一个单独的任务来存放当前Activity。但假如你把一个Activity声明成了singleTask,你会发现并不能得到我们想要的效果,所有的Activity仍然是放在同一个任务当中的。

这是因为,singleTask还会关联一个叫taskAffinity的属性,只有被声明成singleTask的Activity,且它的taskAffinity值也是独立的,那么这个Activity才会被放在一个单独的任务当中。

而默认情况下,每个Activity的taskAffinity属性值都是当前应用程序的包名,也就是说它们的值都是相同的,所以才不能得到我们想要的效果。

那么解决方法也很简单,给每一个要启用独立任务视图的Activity都赋值一个不同的taskAffinity值即可。

接下来我们就开始动手实践一下吧。

首先创建一个叫MiniProgramTest的项目。

接下来创建3个空的Activity,分别给它们起名为FirstActivity、SecondActivity和ThirdActivity。

然后编辑项目的activity_main.xml布局文件,在里面加入3个按钮,分别用于启动FirstActivity、SecondActivity和ThirdActivity:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/first_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动第一行代码"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintBottom_toTopOf="@+id/second_btn"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/second_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动第二行代码"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintBottom_toTopOf="@+id/third_btn"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/first_btn" />

    <Button
        android:id="@+id/third_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动第三行代码"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/second_btn" />

</androidx.constraintlayout.widget.ConstraintLayout>

布局文件定义好了之后,接下来修改MainActivity的代码,加入启动逻辑:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val firstBtn = findViewById<Button>(R.id.first_btn)
        val secondBtn = findViewById<Button>(R.id.second_btn)
        val thirdBtn = findViewById<Button>(R.id.third_btn)

        firstBtn.setOnClickListener {
            val intent = Intent(this, FirstActivity::class.java)
            startActivity(intent)
        }
        secondBtn.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)
        }
        thirdBtn.setOnClickListener {
            val intent = Intent(this, ThirdActivity::class.java)
            startActivity(intent)
        }
    }
}

代码非常简单,点击哪个按钮就去启动相应的Activity就可以了。

但如果仅仅是这样,FirstActivity、SecondActivity和ThirdActivity一定与MainActivity是存放在同一个任务当中的。

因此下面我们就要去编写最核心的代码了,修改AndroidManifest.xml文件,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.miniprogramtest">

    <application
        ...>

        <activity
            android:name=".FirstActivity"
            android:exported="false"
            android:label="第一行代码"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.miniprogramtest.first"
            />

        <activity
            android:name=".SecondActivity"
            android:exported="false"
            android:label="第二行代码"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.miniprogramtest.second" />

        <activity
            android:name=".ThirdActivity"
            android:exported="false"
            android:label="第三行代码"
            android:launchMode="singleTask"
            android:taskAffinity="com.example.miniprogramtest.third"
            />
        ...
    </application>

</manifest>

可以看到,这里我们将FirstActivity、SecondActivity和ThirdActivity的launchMode都设置成了singleTask,并且给它们都指定了一个不同的taskAffinity。

现在运行一下程序,并分别点击界面上的3个按钮,然后按下手机任务栏键,我们就能看到如下效果了:

有没有觉得很神奇?明明都是同一个App中的3个Activity,现在我们竟然可以让它们在3个独立的任务视图中显示,是不是感觉就好像是微信小程序一样?

不过,虽然FirstActivity、SecondActivity和ThirdActivity都拥有独立的任务视图了,它们和微信小程序还有一个非常明显的差距。

因为每个程序都有自己专属的应用Logo,小程序也不例外。就像我们在最开始的图片中看到的一样,美团小程序有美团的Logo,微博小程序有微博的Logo,星巴克小程序有星巴克的Logo。

而目前,FirstActivity、SecondActivity和ThirdActivity显示的都是MiniProgramTest这个项目的Logo,这使得它们看上去仍然不像是一个独立的应用程序。

下面我们就开始着手优化这部分问题。

首先,这里我准备了3张图片first_line.png、second_line.png、third_line.png,分别用于作为FirstActivity、SecondActivity和ThirdActivity的Logo:

接下来,编辑FirstActivity、SecondActivity和ThirdActivity的代码,在里面加入如下逻辑:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class FirstActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        setCustomTaskDescription()
    }

    private fun setCustomTaskDescription() {
        val taskDescription = ActivityManager.TaskDescription(
            "FirstActivity",
            BitmapFactory.decodeResource(resources, R.drawable.first_line)
        )
        setTaskDescription(taskDescription)
    }
}

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        setCustomTaskDescription()
    }

    private fun setCustomTaskDescription() {
        val taskDescription = ActivityManager.TaskDescription(
            "SecondActivity",
            BitmapFactory.decodeResource(resources, R.drawable.second_line)
        )
        setTaskDescription(taskDescription)
    }
}

class ThirdActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_third)
        setCustomTaskDescription()
    }

    private fun setCustomTaskDescription() {
        val taskDescription = ActivityManager.TaskDescription(
            "ThirdActivity",
            BitmapFactory.decodeResource(resources, R.drawable.third_line)
        )
        setTaskDescription(taskDescription)
    }
}

这3段代码的逻辑基本都是相同的。

核心部分就是调用了setCustomTaskDescription()方法来给当前Activity设置一个自定义的TaskDescription。

所谓TaskDescription就是给当前的任务设置一个描述,描述中可以包含任务的名称和图标。

那么这里我们给FirstActivity、SecondActivity和ThirdActivity分别设置了不同的TaskDescription,这样在任务视图当中,就可以看到各不相同的应用Logo了,如下图所示:

其实到这里为止,我们就把微信小程序的外壳搭建得差不多了。剩下的部分,当然也是最难的部分,就是在这个壳子里面添加小程序的内容了。这部分的技术以前端为主,并不是我擅长的领域,我也讲不了,因此就不再继续向下延伸了。

不过或许还有些朋友会存在这样的疑惑:目前我们的技术实现方案是给每个小程序定义一个单独的Activity(FirstActivity、SecondActivity和ThirdActivity),而微信小程序却可以有无限多个,我们显然不可能在AndroidManifest.xml文件中注册无限个Activity,那么微信又是如何实现的呢?

其实这只是一个美丽的误会,因为微信小程序并不是可以有无限多个,只是你平时没有注意这个小细节而已。

我们通过做个实验来验证一下吧,观察下图中的效果:

可以看到,这里我事先依次按照顺序打开了哔哩哔哩、QQ音乐、微博热搜、京东购物、星巴克,这5个小程序。

这个时候回到微信当中,再打开一个顺丰速运小程序。

再次回到任务视图列表界面,你会发现现在多了一个顺丰速运的小程序,而最早打开的哔哩哔哩小程序却从任务视图列表中消失不见了。

由此可以看出,微信其实在AndroidManifest.xml文件中也只是放置了5个占位的Activity。当你尝试打开第6个小程序时,最先打开的那个小程序就会被回收,将它的容器提供给第6个小程序使用。

好了,本篇文章到这里就结束了。内容其实非常的简单,但是已经把在Android上如何实现小程序外层的架子讲明白了。至于如何实现小程序最核心的内容部分,那就要看各位架构师的水准了。

我们下期再见。

如果想要学习Kotlin和最新的Android知识,可以参考我的新书 《第一行代码 第3版》点击此处查看详情

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
字节Android工程师都在学习的Activity与Activity调用栈,你都学习了?
以下几种情况下,Activity的生命周期会发生异常 1,资源相关的系统配置发生改变 比如,旋转屏幕,在默认状态下,Activity就会被销毁并且重新创建。
te大大
2021/11/16
4910
安卓入门-第二章-探究活动
 通过上一章的学习,你已经成功创建了你的第一个Android项目。不过仅仅满足于此显然是不够的,是时候学点新的东西了。作为你的导师,我有义务帮你制定好后面的学习路线,那么今天我们应该从哪儿入手呢?现在你可以想象一下,假如你已经写出了一个非常优秀的应用程序,然后推荐给你的第一个用户,你会从哪里开始介绍呢?毫无疑问,当然是从界面开始介绍了!因为即使你的程序算法再高效,架构再出色,用户根本不会在乎这些,他们一开始只会对看得到的东西感兴趣,那么我们今天的主题自然也要从看得到的入手了。
Fisherman渔夫
2020/02/19
3.2K0
探究活动Activity(2)界面跳转及生命周期
1.使用Intent在活动中跳转 首先我们新建一个名为MyActivityTest的项目,其中我们将活动命名为FirstActivity,布局命名为first_layout.xml,记得勾选Generate Layout File ,如下图所示
晨曦_LLW
2020/09/25
1.1K0
Android 的singleTask和singleInstance的一点思考[通俗易懂]
Activity的四种启动模式主要有standard、singleTop、singleTask、singleInstance四种。不同的启动模式对该Activity有着不同的启动方式,对应AndroidManifest中的android:launchMode属性。
全栈程序员站长
2022/09/13
6.3K0
Activity 的启动方式和 flag 详解
活动的:Activity 在栈顶,它是可视、有焦点、可接受用户输入的。Android 试图尽最大可能保持它活动状态,杀死其它 Activity 来确保当前活动 Activity 有足够的资源可使用。 当另外一个 Activity 被激活,这个将会被暂停。
李林LiLin
2020/12/05
2.1K0
解开Android应用程序组件Activity的”singleTask”之谜
在Android应用程序中,可以配置Activity以四种方式来启动,其中最令人迷惑的就是”singleTask”这种方式了,官方文档称以这种方式启动的Activity总是属于一个任务的根Activity。果真如此吗?本文将为你解开Activity的”singleTask”之谜。
全栈程序员站长
2022/09/13
8780
探究Android活动的小技巧
当我们在查看别人的代码的时候可能不能第一眼就发现首次的活动是哪一个,我们可以通过以下的方法来找出当前的主活动是哪一个:
Dream城堡
2018/12/13
6360
Activity的启动模式
在Android程序中,应用程序通过活动栈来管理Activity,活动栈中有多少个Activity对象,我们在退出程序的时候就要按多少下返回键(即要将活动栈中的所有Activity出栈),但是这样的话难免会有活动栈中存在相同的Activity对象,那么我们该如何解决这个问题呢。
指点
2019/01/18
6590
Activity的启动模式
Android开发学习——显式intent和隐式intent(1)
区别 显式Intent:通过指定Intent组件名称来实现的,它一般用在知道目标组件名称的前提下,一般是在相同的应用程序内部实现的。 隐式Intent:通过Intent Filter来实现的,它一般用在没有明确指出目标组件名称的前提下,一般是用于在不同应用程序之间。
Max超
2019/01/21
7370
Activity启动模式SingleTask和Intent.FLAG_ACTIVITY_CLEAR_TOP区别
介绍 Android 启动模式之前,先介绍两个概念task和taskAffinity
全栈程序员站长
2022/09/09
1.3K0
Activity启动模式singleTask的理解
Task是一些Activity的集合,以Activity栈的形式存放。因此,Task是概念上的,Activity栈是实体上的。
全栈程序员站长
2022/09/13
4380
深入探讨Android启动优化策略
在当今激烈竞争的移动应用市场,应用的启动速度直接影响着用户的第一印象和满意度。作为主流的移动操作系统之一,Android的启动优化是开发者必须关注的关键领域。本文将详细介绍一些强大有效的Android启动优化策略,帮助你优化应用的启动过程,为用户创造更出色的体验。
Rouse
2023/08/31
3610
深入探讨Android启动优化策略
Android 四大组件学习之Activity七
本节学习Activity的加载模式。可能就会问,什么是加载模式? 加载方式有什么用途? 这是因为Android系统对Activity是采用Task栈来管理的。Task栈是采用先进后出的方式,先启动的Activity放在栈底,后启动的Activity是放在栈顶。
DragonKingZhu
2022/05/08
2710
Android 四大组件学习之Activity七
酷炫的外部开启Activity新姿势
在H5页面疯狂的今天,H5和Native的交互就至关重要,而且交互的方式有很多,google提供了一个公共的方式:js与native互调,即js可以调用Native方法,Native同样也可以调用js方法。不过今天要讲的并不是Url拦截的方式和JavaScript注入方式,因为这种交互方式存在着不少问题:
先知先觉
2019/01/21
6770
Android开发笔记(三十九)Activity的生命周期
下面是Activity类与生命周期有关的方法: onCreate : 创建页面 onStart : 开始页面 onStop : 停止页面 onResume : 恢复页面 onPause : 暂停页面 onDestroy : 销毁页面 onRestart : 重启页面 onNewIntent : 重用栈中已存在的实例 onSaveInstanceState : 保存实例状态。使用场景:1、从A视图跳转到B视图,需要保存A视图的状态(不考虑特殊情况);2、屏幕从竖屏变为横屏,需要保存竖屏时的视图状态,从横屏变为竖屏亦然;3、当前Activity处于后台,系统因资源紧张将其杀死。 onRestoreInstanceState : 恢复实例状态。使用场景:1、屏幕从竖屏变为横屏,横屏显示时需要恢复之前保存的竖屏状态;2、activity后台运行被系统杀死。此处注意,从B视图返回A视图时并不调用该方法。 最简单的页面启动顺序:onCreate->onStart->onResume 最简单的页面退出顺序:onPause->onStop->onDestroy
aqi00
2019/01/18
1.1K0
7.Activity
创建新的activity(活动) 新创建的activity,必须在清单文件中做配置,否则系统找不到,在显示时会直接报错 <activity android:name="com.itheima.createactivity.SecondActivity"></activity> 只要有以下代码,那么就是入口activity,就会生成快捷图标,写几个就会出现几个程序快捷方式 一个应用程序可以在桌面创建多个快捷图标。 activity的名称、图标可以和应用程序的名称、图标不相同 <intent-filt
六月的雨
2018/05/14
1.4K0
关于Activity生命周期的小结
开头先说一下写这篇博客的初衷,由于博主在找实习的过程中面试经常被问到Activity生命周期有关的问题,所以特此写一篇博客来记一下。
俞其荣
2022/07/28
2980
关于Activity生命周期的小结
Android开发笔记(十九)底部标签栏TabBar
现在的APP,大多在页面底部显示标签栏Tabbar,用于切换不同栏目的页面。Tabbar起源于iOS,iOS的Tabbar自动位于页面下方,可是Android搬过来的时候做了改动,自带的Tabbar位于页面上方,很不适合用户的使用习惯。为此我们在Android实现底部标签栏,得额外进行底部适配处理,适配思路基本都是在底部罗列一排的按钮,然后根据点击不同的按钮,跳到不同的Activity页面。具体的实现方式,博主目前发现了三个:
aqi00
2019/01/18
5.4K0
Activity中启动和关闭其他Activity
上一期我们学习了Activity的创建和配置,当时留了一个悬念,如何才能在默认启动的Activity中打开其他新建的Activity呢?那么本期一起来学习如何启动和关闭Activity。 一、概述 经过前面内容的学习,知道一个Android应用通常都会包含多个Activity,但只有一个Activity 会作为程序的入口——当该Android应用运行时将会自动启动并执行该Activity。至于应用中的其他Activity,通常都由入口 Activity启动,或由入口 Activity启动的
分享达人秀
2018/02/05
4.6K0
Activity中启动和关闭其他Activity
使用Intent在活动之间穿梭
Intent是Android中各组件进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据.
Dream城堡
2018/12/12
7510
相关推荐
字节Android工程师都在学习的Activity与Activity调用栈,你都学习了?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档