• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

Android-Studio-IDE-插件开发

android 搞代码 3年前 (2022-03-01) 43次浏览 已收录 0个评论
文章目录[隐藏]

作者:字节跳动终端技术——周宸韬

概述

这篇文章旨在向读者介绍IntelliJ IDE插件的开发流程以及罕用的一些通用性能,任何基于IntelliJ开发的IDE都能够通过该形式制作插件,例如Android Studio(AS),本篇也将基于Android Studio进行开展介绍,读者将从0到1学习到 Android Studio插件开发。

背景介绍

什么是IDE插件、IDE插件能做什么?

IDE插件是将一些性能集成到了IDE界面当中,当咱们应用IDE进行开发工作时能很不便的通过UI界面应用这些性能,例如大家相熟的project工程目录,Gradle工具栏,IDE底部的Run、Terminal、Build界面等,都是通过IDE插件来实现的,能够说大部分须要通过命令行执行、或用户手动的一些操作都能够通过插件实现,并以UI的模式出现。

如下图:左图为Android Studio IDE界面右侧Gradle工具栏,蕴含了很多Gradle工作,点击UI的成果等同于用户在命令行中输出Gradle命令。右图为IDE顶部菜单栏版本控制局部,其中对于版本的提交、拉取等按钮等价于命令行输出对应指令。

<center class=”half”>

 <img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin1.png" width="300"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin2.png" width="300"/> </center>

为什么要做Android Studio IDE插件?

笔者作为中台部门开发者,常常波及到一些通用的性能的开发,并以工具或组件等模式交由内部应用。所以如何升高用户学习老本、进步工作效率是我的指标,而这些优化方向都离不开奇妙的应用工具。例如本次要介绍的IDE插件的开发背景就是以此为指标:将本来须要应用命令行实现的工作、或者学习老本较高的操作通过UI进行包装,并且附丽在原生的AS界面中,通过UI的交互大幅升高用户学习老本,同时晋升应用体验。

举例比照一下,上面两幅图片是某个工程主动搭建性能的截图,左右两图别离为应用命令行和应用AS插件的体验比照,能够看到左侧在应用CLI命令行进行工程搭建时界面信息不够简洁明了,且用户交互体验较差,用户必须在应用前浏览文档,并且没有容错机制,输错就得从头开始。而雷同的性能应用右侧AS插件的体验则好很多,不仅各条信息分明明了,还能拓展更多细节性能,如动静测验用户输出,输出无误才可进行下一步等等,用户齐全能够在零常识背景的状况下应用该插件并轻松实现所有性能操作,而且靠近原生的界面更好看。

<center class=”half”>

 <img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin3.png" width="400"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin4.png" width="400"/> </center>

如何开发一个IDE插件?

筹备工作

在开发第一个插件前,咱们要下载正确的开发工具,在JetBrain官网中下载IntelliJ IDEA下载链接

这里应用的开发工具是IntelliJ IDEA而不是Android Studio,因为AS是基于IntelliJ为模版开发的,IDE插件必须通过IntelliJ开发、公布,再装置到Android Studio中能力应用。

咱们须要确认咱们应用的Android Studio是基于哪个IntelliJ版本。这很重要,和你以后应用的Android Studio版本雷同能让你在调试时很不便,而新版的IntelliJ蕴含的features在你应用的AS上可能并没有,导致插件无奈装置,或提醒兼容性报错。(图中的报错也会呈现在未开启向高版本兼容时产生,开发时按需开启) 。

<center class=”half”>

 <img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin5.png" width="400"/></center>

下载时请追随这个步骤:

  1. 关上你的Android Studio,查看版本号(The Build Number),这就是咱们须要的IntelliJ版本号。
  2. 在下载页面,点击Other versions,找到对应的IntelliJ版本,下载安装即可。

<center class=”half”>

 <img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin6.png" width="300"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin7.png" width="500"/> </center>

创立新的工程 + 配置

这部分也可参照官网:https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started.html

  1. 创立新工程蕴含两个向导页面,【抉择工程模版框架 + 填写插件工程信息】,按图中配置即可。

<center class=”half”>

 <img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin8.png" width="350"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin9.png" width="350"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin10.png" width="100" hight="300"/> </center>
  1. 实现两步向导程序后主动创立工程,咱们须要先理解两个外围文件:【build.gradle + plugin.xml】,并做一些前置的配置工作。

build.gradle

因为build.gradle和Android工程中的构建文件十分相似,这里只解释Android中没有的配置。

  • version:intellij闭包创立时只带一个属性 version,该属性代表用来构建这个插件的IntelliJ 平台IDE的版本,如果咱们在开发时调用【runIde】这个Gradle task,一个基于这个版本的IntelliJ IDE实例就会被创立。
  • localPath:因为咱们心愿在AS的环境下测试咱们的插件,所以咱们须要将AS作为咱们插件的一个依赖,减少一个属性叫localPath指定本机Android Studio应用程序Contents的装置目录,一个基于这个版本的Android Studio实例就会被创立(留神localPath不能和version属性同时应用,因为咱们本地的AS门路中曾经有了版本信息)。
  • plugins:增加开发须要的依赖插件。能够在这里增加很多咱们想用的插件,比方咱们想在插件中执行git命令,咱们能够增加 ’git4idea‘ plugin。
<code class="groovy">intellij {
    version '2020.1.4'
    localPath '/Applications/Android Studio.app/Contents'
    plugins = ['Kotlin','android','git4idea']
}

plugin.xml

在resource文件夹下能够找到plugin.xml文件,这个文件中能够配置咱们插件的各项属性,外围性能是注册咱们插件蕴含的components和service(性能类实现后还须要在这里进行注册能力应用,相似在AndroidManifest.xml中申明Activity和Service)。

  • 申明咱们的插件须要并且和AS相兼容:减少android和android studio modules作为依赖。
<code class="xml"><idea-plugin>
    ...
    <depends>com.intellij.modules.platform</depends>
    <depends>org.jetbrains.android</depends>
    <depends>com.intellij.modules.androidstudio</depends>

    <extensions defaultExtensionNs="com.intellij">
        <!-- Add your extensions here -->
    </extensions>

    <actions>
        <!-- Add your actions here -->
    </actions>

</idea-plugin>

运行插件

配置实现后咱们能够尝试运行插件工程,具体位置在Gradle工具栏 项目名称/Tasks/intelliJ/runIde门路。运行runIde工作,因为咱们配置了Android Studio为启动门路,所以一个Android Studio模仿IDE会关上,所有内容都和咱们本地的Android Studio没有差异。

<center class=”half”>

 <img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin11.png" width="270"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin12.png" width="670"/> </center>

IDE插件罕用性能介绍

创立一个Action

什么是Action?

Actions官网介绍: The system of actions allows plugins to add their own items to IDEA menus and toolbars. An action is a class, derived from the AnAction.

Actions是用户调用插件性能最常见的形式,如下图的工具目录是开发者常常用到的,外面所有的可选项都是一个Action,能够进一步开展的则是Action Group。

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin13.png" width="500"/> </center>

如何创立一个Action?

两个步骤:

  • 【code implementation – 实现Action的具体代码逻辑】:决定了这个action在哪个context下无效,并且在UI中被抉择后的性能(继承父类AnAction并重写actionPerformed()办法,用于Action被执行后的回调)。
  • 【registered – 在配置文件中注册】:决定了这个action在IDE界面的哪个地位呈现(创立新的group或寄存进现有的ActionGroup,以及在group中的地位)。

两个条件达成,action就能够从IntelliJ Platform中取得用户执行动作后的回调,例子: ‘HelloWorld’。

Code implementation

<code class="kotlin">class HelloWorldAction : AnAction() {

    override fun actionPerformed(event: AnActionEvent) {
        //这里创立了一个音讯提醒弹窗,在IDE中展现“Hello World”
        val notificationGroup = NotificationGroup(
            displayId = "myActionId",
            displayType = NotificationDisplayType.BALLOON
        )

        val notification = notificationGroup.createNotification(
            title = "chentao Demo",
            content = "Hello World",
            type = NotificationType.INFORMATION
        ).notify(event.project) //从办法的Event对象中获取到以后IDE正在展现的project,在该project中展现弹窗
    }
}

Registering a Custom Action

<code class="xml"><actions>
    <!-- Add your actions here -->
    <!-- 创立了一个ActionGroup -->
    <group id = "ChentaoDemo.TopMenu"
           text="ChentaoDemo Plugin"
           description="Demo Plugin in top menu">
           <!-- 注册HelloWorld Action -->
        <action class="com.chentao.demo.actions.HelloWorldAction"
                id="DemoAction"
                text="Hello World Action"
                description="This is a test action">
                <!-- 设置 HelloWorld Action 的键盘快捷键-->
            <keyboard-shortcut first-keystroke="control alt p" keymap="$default"/>
            <!-- 将HelloWorld Action增加到剪切拷贝组中 -->
            <add-to-group group-id="CutCopyPasteGroup" anchor="last"/>          
        </action>
        <!-- 将这个Group增加到主菜单 -->
        <add-to-group group-id="MainMenu" anchor="last"/>
    </group>
</actions>

运行插件 – 后果展现

实现了以上两步后,运行runIde Task,顶部的主菜单栏开端呈现了咱们增加的ActionGroup,开展可看见HelloWorldAction,点击Action,右下角弹出“Hello World”提示信息。咱们不仅能够创立Group来搁置Action,还能够将Action增加进IDE已有的Group当中,如下左图中,咱们将HelloWorld Action增加进了IDE的CutCopyPasteGroup,和复制粘贴等Action放在了一起。

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin14.png" width="800"/> </center>

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin15.png" width="400"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin16.png" width="400"/> </center>

plugin.xml文件中actions的group能够更为简单,group能够互相蕴含,并造成工具栏或菜单(如下图),有趣味的同学能够拉取Demo(文章开端)体验一下。

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin17.png" width="800"/> </center>

向导程序Wizard

Wizard意为向导程序,就是指引使用者实现某个性能的程序,通常为单个或多个指引界面组成。例如上面两幅图为Android Studio中经典的创立新工程窗口,就蕴含两个页面的向导程序。上面将介绍如何制作出和图中主题完全相同的向导程序。

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin18.png" width="480"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin19.png" width="480"/> </center>

向导程序的根底类属于android.jar,由以下几个外围类形成:

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin20.png" width="300"/> </center>

1. ModelWizard

向导程序的“主类”,一个ModelWizard蕴含了一个ModelWizardStep的队列(Step队列是一个有序的队列,并且每个Step都蕴含了它的下一个Step的援用),当一个ModelWizard完结时,它会遍历所有steps,拜访step对应的WizardModel,并调用WizardModel#handleFinished()办法。

2. ModelWizardStep

一个Step就是Wizard向导程序中的一个独自页面,它负责创立一个UI界面出现给用户,确定页面上的信息是否无效,并且将用户数据保留在对应的WizardModel对象中。

3. SkippableWizardStep

能够设置可见性的Step,能够通过前一个Step来管制跟在其后的Step可见性,例如一个Step提供了一些选项给用户,并依据用户的抉择来决定之后哪些Steps能够被展现。

4. WizardModel

Model就是数据的汇合,这些数据由wizard中的每个step进行填充。多个step能够共享同一个model,外围的办法是handleFinished(),当用户在向导程序中点击了“Finish”按钮时,wizard完结,这个办法将会被调用进行最终的逻辑解决。

Wizard向导程序工作流程图

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin21.jpeg" width="900"/> </center>

创立一个Android Studio款式的向导程序

同样在android.jar库中,和wizard同级的名叫ui的包中提供了一个很不便的类,帮忙使用者创立AS款式的ModelWizard,只需将ModelWizard对象作为参数放入StudioWizardDialogBuilder的结构器中即可。应用AS款式包装咱们的插件UI能让用户应用时更有原生的感觉,体验更好。

<code class="kotlin">class CreateNewProjectAction : AnAction() {

    override fun actionPerformed(e: AnActionEvent) {
        StudioWizardDialogBuilder(
            ModelWizard.Builder().addStep(NewProjectStep()).build(),
            "Create New MARS Project"
        ).build().show()
    }
}

class ProjectWizardModel : WizardModel() {
    //记录一些心愿保留的字段
      //...
    override fun handleFinished() {
        //解决最初的逻辑
    }
}

class NewProjectStep : ModelWizardStep<ProjectWizardModel?>(ProjectWizardModel(), "Create MARS Project") {

    init {
      //创立Step页面的UI
    }
    //链接下一个Step
    override fun createDependentSteps(): MutableCollection<out ModelWizardStep<*>> {
        return arrayListOf(SelectBaselineStep(model))
    }
}

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin22.png" width="480"/><img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin23.png" width="480"/> </center>

Tool Windows

Tool Windows是IDE的子窗口。这些窗口通常都在IDE主窗口的“边框”上领有属于本人的一个tool window button,点击后将在IDE主窗口的左、右、下侧激活panel来展现信息。如下图左一Gradle工具栏。创立Tool Window须要提供一个JPanel,并通过ToolWindowFactory来实现。

ToolWindowFactory

Performs lazy initialization of a tool window registered in {@code plugin.xml}.

使用者必须创立ToolWindowFactory的实现类,并实现createToolWindowContent()办法,在该办法中初始化tool Window的UI,并增加到Android Studio中。ToolWindowFactory提供了懒加载机制,这样实现的益处是未应用的工具窗口不会减少启动工夫或导致内存应用方面的任何开销:如果用户没有与该工具窗口交互,相干代码就不会被加载和执行。

<code class="java">public class MyToolWindowFactory implements ToolWindowFactory {

    @Override
    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
        // 初始化自定义组件对象
        MyToolWindow myToolWindow = new MyToolWindow(project, toolWindow);

        // 组件增加到AS中
        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
        Content content = contentFactory.createContent(myToolWindow.getContent(), "", false);
        toolWindow.getContentManager().addContent(content);
    }
}

在插件中应用Tool Windows有两种模式:

  • declarative setup:能够了解为动态,在plugin.xml文件中注册,始终可见用户随时都能够应用。
  • Programmatic Setup:通过API接口动静注入,能够在一些操作前后呈现和暗藏。

Declarative Setup

<code class="xml"><extensions defaultExtensionNs="com.intellij">
    <!-- Add your extensions here -->
    <toolWindow id="MyToolWindow" secondary="true" anchor="right" factoryClass="com.volcengine.plugin.toolwindow.MyToolWindowFactory"/>
</extensions>

Programmatic Setup

<code class="java">updateBaselineBtn.addActionListener(e -> {
    BaselineWindow baselineWindow = new BaselineWindow(versionsJson, project, toolWindow);
    ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
    Content content = contentFactory.createContent(baselineWindow.getContent(), "", false);
    toolWindow.getContentManager().addContent(content);
    toolWindow.getContentManager().setSelectedContent(content);
});

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin24.png" width="320"/>

<img src=”http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin25.png&#8221; width=”320″/>

<img src=”http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin26.png&#8221; width=”320″/> </center>

UI创立工具

Wizard向导程序和Tool Window工具栏都须要UI作为面板内容的填充,基本上来说,须要的只是一个内容丰盛的JPanel作为Content。AS 插件中的UI大量的应用了Java Swing组件,所以对Swing比拟相熟的同学上手会很快,这里介绍几种在AS插件中生成UI的技巧。

GUI Form

New –> Swing UI Designer –> GUI Form 填写信息后就会生成对可视化的.form文件以及绑定的java类,在对应的java文件中减少一个getRootPanel办法获取root panel就能够将构建好的Panel给到向导程序或工具栏中应用。

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin27.png" /> </center>

Eclipse – WindowBuilder

下面提到的GUI Form有一个毛病,只能应用Java,并且.fome文件和.java文件强绑定,咱们也无奈独自应用这个生成的java文件,并且当咱们想编写纯Kotlin代码时,GUI Form就显得不好用了。

Eclipse是少数同学刚接触Java时应用的经典IDE,其中有一个WindowBuilder插件同样能够可视化创立GUI界面,然而相比于GUI Form,WindowBuilder生成的是独自的.java文件,用户在GUI可视化界面操作的每个步骤都会生成对应的源码,咱们能够间接copy这些代码到AS插件当中,并应用“convert java code to Kotlin”性能,将这些代码一键转为Kotlin代码,十分不便(更重要的是,WindowBuilder的应用体验集体感觉更好)。

<center class=”half”>

<img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin28.png"/>
    <img src="http://lf3-client-infra.bytetos.com/obj/client-infra-images/ASPlugin29.png"/>

</center>

Kotlin UI DSL

IntelliJ 插件官网提供的一些基于Kotlin的畛域特定语言,能够在Kotlin代码中写UI,长处是代码柔美,毛病是累,具体可参考官网的指引https://plugins.jetbrains.com…

数据长久化

有时咱们心愿能保留用户在插件中的操作或一些配置,防止反复的工作以及必要数据的读取,或防止用户反复屡次输出。IntelliJ Platform提供了一些不便的API来做数据长久化。

Plugin Service

这是IntelliJ插件开发中的根底能力,分为三种不同的类型,当咱们想要在IDE插件的不同生命周期进行一些状态和逻辑上的解决,就能够应用这三种服务,例如:长久化状态、订阅事件、Application启动/敞开时、Project被关上/敞开时。

Service 接口类型 作用形容
Application Level IDEA启动时会初始化,IDEA生命周期中仅存在一个实例
Project Level IDEA 会为每一个 Project 实例创立一个 Project 级别的实例
Module Level IDEA 会为每一个 Project 的加载过的Module实例Module级别的实例,在多模块我的项目中容易导致内存泄露

这块代码是模仿在IDE启动时,主动测验以后是否存在新版本的性能,若有新版本则进行更新操作,就是应用了长久化存储来实现的。

<code class="kotlin">@State(name = "DemoConfiguration", storages = [
    Storage(value = "demoConfiguration.xml")
])

class DemoComoponent:ApplicationComponent, PersistentStateComponent<DemoComoponent>, Serializable {
    var version = 1
    var localVersion = 0;

    private fun isANerVersion() = localVersion < version

    private fun updateVersion(){
        localVersion = version
    }

    override fun initComponent() {
        if(isANerVersion()){
            updateVersion()
        }
    }

    override fun getState(): DemoComoponent? = this
    override fun loadState(state: DemoComoponent) {
        XmlSerializerUtil.copyBean(state, this)
    }
}

长久化存储的两种形式

1. PropertiesComponent

这是一个简略的Key-Value数据结构,能够当作Map应用,用于保留application 和 project 级别的数据。

<code class="java">//获取 application 级别的 PropertiesComponent
PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();

//获取 project 级别的 PropertiesComponent,指定相应的 project
PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(Project);

// set & get
propertiesComponent.setValue(name, value)
propertiesComponent.getValue(name)

2. PersistentStateComponent

简单类型的数据结构应用PersistentStateComponent,能够指定长久化的存储地位。

<code class="java">public interface PersistentStateComponent<T> {
  @Nullable
  T getState();
  void loadState(T state);
}
  1. 创立一个PersistentStateComponent的实现类,T示意须要长久化的数据结构类型,能够是任意类,甚至是实现类自身,而后重写getState和loadState办法。
  2. 若要指定存储的地位,须要在浮现类上减少@State注解。
  3. 若不心愿其中的某个字段被长久化,能够在该字段上减少@Transient 注解。
<code class="kotlin">@State(
    name = "ChentaoPlugin" ,
    storages = [Storage("chentao-plugin.xml")]
)

class AarCheckBoxSettings :PersistentStateComponent<HashMap<String, AarCheckBoxState>> {
    var checkBoxStateList = HashMap<String, AarCheckBoxState>()

    override fun getState(): HashMap<String, AarCheckBoxState>? {
        return checkBoxStateList
    }
    override fun loadState(stateList: HashMap<String, AarCheckBoxState>) {
        checkBoxStateList = stateList
    }

    //将长久化组件申明为Serveice的获取形式是通过ServiceManager
    companion object{
        @JvmStatic
        fun getInstance(): PersistentStateComponent<HashMap<String, AarCheckBoxState>>{
            return ServiceManager.getService(AarCheckBoxSettings::class.java)
        }
    }
}
data class AarCheckBoxState(val componentId:String, val isSelected:Boolean)
注册长久化组件

PersistentStateComponent的实现类须要在plugin.xml中注册为 Service后应用。

<code class="xml"><extensions defaultExtensionNs="com.intellij">
    <applicationService serviceImplementation="com.volcengine.plugin.actions.AarCheckBoxSettings"/>
</extensions>

插件打包与装置

  • 打包:在Gradle工具栏中运行assemble工作,即可在/build/distribution/{插件名称}-{插件版本}.zip门路下找到打包好的插件zip包。
  • 本地装置:还没将插件公布到插件市场前咱们能够抉择装置本地插件,关上AS菜单栏/Android Studio/Preference/Plugins/Install Plugin from Disk… 装置后即可应用。
  • 公布插件市场:

总结

回顾开发过程:IDE插件的外围步骤:装置正确版本IntelliJ –> 配置工程 –> 创立Action –> 将简单流程注入Wizard向导程序或ToolWindow工具栏(同时创立UI) –> 应用数据长久化保留必要数据 –> 打包&装置&公布。

笔者感觉IDE插件开发的难点次要是摸索的过程,IDE插件较冷门,网上介绍的文章很少,官网介绍了一些性能和组件后也没有具体的API指引,令人有点无从下手。最终,通过反编译查看一些官网插件(Firebase、Flutter等)的源码,以及收集Google、Youtube和各大博客的信息,终于将AS插件的一期雏形打造结束,也将学到的一些罕用的通用能力在本文中进行整顿,心愿能帮到之后想要接触AS插件开发的同学。

Demo

https://github.com/ChentaoZho&#8230;

对于字节终端技术团队

字节跳动终端技术团队(Client Infrastructure)是大前端根底技术的全球化研发团队(别离在北京、上海、杭州、深圳、广州、新加坡和美国山景城设有研发团队),负责整个字节跳动的大前端基础设施建设,晋升公司全产品线的性能、稳定性和工程效率;反对的产品包含但不限于抖音、今日头条、西瓜视频、飞书、懂车帝等,在挪动端、Web、Desktop等各终端都有深入研究。

就是当初!客户端/前端/服务端/端智能算法/测试开发 面向寰球范畴招聘!一起来用技术扭转世界,感兴趣请分割 [[email protected]](mailto:chenxuwei.cxw@bytedanc…),邮件主题 简历-姓名-求职意向-冀望城市-电话

字节跳动利用开发套件MARS是字节跳动终端技术团队过来九年在抖音、今日头条、西瓜视频、飞书、懂车帝等 App 的研发实际成绩,面向挪动研发、前端开发、QA、 运维、产品经理、项目经理以及经营角色,提供一站式整体研发解决方案,助力企业研发模式降级,升高企业研发综合老本。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Android-Studio-IDE-插件开发

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址