MetaData Programme

本文围绕元数据编程展开,重点介绍了Annotation技术。阐述了元数据概念,展示了Annotation的意义、实现示例及class文件格式。对比了Annotation与XML,指出其优缺点。还探讨了Annotation在AOP等方面的应用前景,并给出银行卡业务的AOP示例代码。

MetaData Programme

作者:Anders小明

1.1.        什么是元数据编程

什么是元数据,元数据就是描述数据的数据(data about data)。最明显的例子是XML Schemaxml schema就是描述xml的数据,所以它是元数据。另一个例子是数据库,比如我们可以查询数据库中有几个表,每个表都有什么字段,这些数据就是元数据。

在开发的世界里,元数据就是能够绑定到一个类的附加信息,在静态或者运行时间。JCR175给我们提供annotation就是一种元数据。

不过在这之前一个我们已经广泛使用的元数据是XML,如就是EJBXML发布描述符中,你需要定义基于每一个方法的事务属性。应用服务器指导什么时候,什么地方开始,挂起或者提交一个事务,因为你在BEANXML的配置文件中的元数据内已经定义如方法:RequiredRequiresNewSupport等等,它们绑定在你的EJB类和事务管理之间。XDoclet是另一个元数据的例子。

1.2.        Annotation的意义和简单例子

       JDK1.5提供的annotation与我们所常见的classesfieldssmethods间是什么关系。如下:如果说类和数据成员是名词,方法是动词,那么annotation就是形容词或者副词,分别描述它们的所具有属性。

好,现在就来实现一个annotation

import java.lang.annotation.Retention;

package sample.annotation

@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)

    public @interface Broker {

    String name();

    String address();

    }

}

使用这个annotation

Import sample.annotation.broker

@Broker(name="anders", address="xiamen")

public class Agent {

    public String getTelPhone (){

         return  "010-0592-2519280";

    }

}

运行期得到这个annotation

public class Main {

    public static void main(String[] args){

Agent agent = new Agent();

        try{

            Annotation[] a = agent.getClass().getMethod("getBrokerName").getAnnotations();

            for (int i=0; i<a.length ; i++)  {

                 if( a[i] instanceof Broker){

                      Broker broker = (Broker)a[i];

                     System.out.println(broker.name());

                 }

             }

        }

        catch(Exception e){

            e.printStackTrace(System.out);

        }

   }

}

1.3.        Annotationclass文件格式

       利用sun公司的提供的javap,我们可以看到annotation的在class文件中的表示。以下为对比结果:

源码:

@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)

public @interface Broker {

    String name();

    String address();

}

Javap结果:

Compiled from "Broker.java"

interface Broker extends java.lang.annotation.Annotation

  SourceFile: "Broker.java"

  minor version: 0

  major version: 0

  Constant pool:

const #1 = class    #9;  // Broker

const #2 = class    #10; // Object

const #3 = class    #11; //Annotation

const #4 = Asciz    name;

const #5 = Asciz   ()Ljava/lang/String;;

const #6 = Asciz    address;

const #7 = Asciz    SourceFile;

const #8 = Asciz    Broker.java;

const #9 = Asciz    Broker;

const #10 = Asciz   java/lang/Object;

const #11 = Asciz   java/lang/annotation/Annotation;

 

{

public abstract java.lang.String name();

 

public abstract java.lang.String address();

}  

 

源码:

public interface Broker {

    String name();

    String address();

}

Javap结果:

Compiled from "Broker.java"

interface Broker

  SourceFile: "Broker.java"

  minor version: 0

  major version: 0

  Constant pool:

const #1 = class    #8; //  Broker

const #2 = class    #9; //  Object

const #3 = Asciz    name;

const #4 = Asciz    ()Ljava/lang/String;;

const #5 = Asciz    address;

const #6 = Asciz    SourceFile;

const #7 = Asciz    Broker.java;

const #8 = Asciz    Broker;

const #9 = Asciz    java/lang/Object;

 

{

public abstract java.lang.String name();

 

public abstract java.lang.String address();

 

}

 

源码:

@Broker(name="anders", address="xiamen")

public class Agent {

    public String getTelPhone(){

        return  "0592-2519580";

    }

}

Javap结果:

Compiled from "Agent.java"

public class Agent extends java.lang.Object

  SourceFile: "Agent.java"

  RuntimeVisibleAnnotations: length = 0x10

   00 01 00 11 00 02 00 12 73 00 13 00 14 73 00 15

  

  minor version: 0

  major version: 0

  Constant pool:

const #1 = Method   #4.#22; //  java/lang/Object."<init>":()V

const #2 = String   #23;    //  0592-2519580

const #3 = class    #24;    //  Agent

const #4 = class    #25;    //  Object

const #5 = Asciz    <init>;

const #6 = Asciz    ()V;

const #7 = Asciz    Code;

const #8 = Asciz    LineNumberTable;

const #9 = Asciz    LocalVariableTable;

const #10 = Asciz   this;

const #11 = Asciz   LAgent;;

const #12 = Asciz   getTelPhone;

const #13 = Asciz   ()Ljava/lang/String;;

const #14 = Asciz   SourceFile;

const #15 = Asciz   Agent.java;

const #16 = Asciz   RuntimeVisibleAnnotations;

const #17 = Asciz   LBroker;;

const #18 = Asciz   name;

const #19 = Asciz   anders;

const #20 = Asciz   address;

const #21 = Asciz   xiamen;

const #22 = NameAndType #5:#6;//  "<init>":()V

const #23 = Asciz   0592-2519580;

const #24 = Asciz   Agent;

const #25 = Asciz   java/lang/Object;

// 以下为方法域,略  

 

源码:

public class Agent {

    public String getTelPhone(){

        return  "0592-2519580";

    }

}

Javap结果:

Compiled from "Agent.java"

public class Agent extends java.lang.Object

  SourceFile: "Agent.java"

  minor version: 0

  major version: 0

  Constant pool:

const #1 = Method   #4.#16; //  java/lang/Object."<init>":()V

const #2 = String   #17;    //  0592-2519580

const #3 = class    #18;    //  Agent

const #4 = class    #19;    //  Object

const #5 = Asciz    <init>;

const #6 = Asciz    ()V;

const #7 = Asciz    Code;

const #8 = Asciz    LineNumberTable;

const #9 = Asciz    LocalVariableTable;

const #10 = Asciz   this;

const #11 = Asciz   LAgent;;

const #12 = Asciz   getTelPhone;

const #13 = Asciz   ()Ljava/lang/String;;

const #14 = Asciz   SourceFile;

const #15 = Asciz   Agent.java;

const #16 = NameAndType #5:#6;//  "<init>":()V

const #17 = Asciz   0592-2519580;

const #18 = Asciz   Agent;

const #19 = Asciz   java/lang/Object;

// 以下为方法域,略

 

补充说明:我们都知道在java 1.0发布时,java class file的格式就已经定下来,要说明的是为了应未来的需要java class file设计了属性说的机制。一直到j2se1.4都没有怎么改变。但这次为了更好的支持metadata技术,一共增加了8个属性。分別是:

EnclosingMethodAttribute Anonymous Class Local Inner Class 使用此 Attribute 描述该Class Scope

Signature AttributeGenerics ClassMethod、或 Filed使用此 Attribute 来记录所要的类型,因为java的范型采用了擦拭法。

LocalVariableTypeTable Attribute:主要是給 debugger 使用,目的和「LocalVariableTable Attribute类似,只是「LocalVariableTable Attribute 记录所要的参数表,而「LocalVariableTypeTable Attribute 记录参数的类型。

RuntimeVisibleAnnotations Attribute:确定该annotation可以被reflectionAPI返回,适用对象:ClassMethodField

RuntimeInvisibleAnnotations Attribute:确定该annotation无法被reflectionAPI返回,适用对象: ClassMethodField 

RuntimeVisibleParameterAnnotations Attribute:同「RuntimeVisibleAnnotations Attribute,适用对象:Method,(该Method 的参数

RuntimeInvisibleParameterAnnotations Attribute:同「RuntimeInvisibleAnnotations Attribute,适用对象:Method,(该Method 的参数。

AnnotationDefault Attribute:适用对象:Method,记录默认值。

1.4.        为什么需要Annotation

       annotation之前我们已经广泛使用另外一种元数据xml,为什么需要annotationAnnotationxml的作为元数据的区别是什么——位置。Annotation写在源代码中,而xml写在外部。

       为什么要这样?如果你开发过EJB,你一定为你的EJB写过xml描述文件。当大量的EJB需要描述时,就出现了所谓的"descriptor hell"。这个也导致了著名的XDoclet的出现。而annotation出现可以避免这种descriptor hell。另外你更改了某个方法为其增加或者减少一个参数,你就对应的修改xml文件,而使用annotation则不必。使用annotation将开发和部署更方便,提供开发效率。

       另外:使用xml的另一个问题是:很多Xml配置太过verbose。相比较EJBHibernate 或者Webwork可以明显的发现不同。

 

1.5.        再议Annotation

EJB3中,Annotation把开发和部署的工作合在一起。但是在一些企业环境中,开发人员并不控制诸如数据源名等(这些是部署部门和管理部门的工作),这样把数据源名写在xml中将比较好。

       Annotation是本身静态的,一旦添加或者修改annotation都需要重新编译,在运行时读取,这样就丧失了运行时配置的能力。因此Annotations 不会取代xml,它只是提供了另一种途径。并且我相信sun公司将在未来提供一个方式可以在运行期更改metadata

关于这点TSS上有着很激烈的讨论,很多开发人员提出:利用xml来更改annotation,并希望类似的方式能被采纳为标准规范。比如使用如下格式:

<class name="org.hibernate.Item">

@Entity

    @Table(name="AUCTION_ITEM")

    <method sig="getBar()">@Transient</method>

    <method sig="doSomething(int, String)">

        @Tx(REQUIRES_NEW)

    </method>

</class>

 

  当然也有不同意见:But then, I think of "overriding" as a different problem to "xml deployment descriptors", and so I think we need two solutions. I think Cedric and Bill are trying to kill two birds with one stone, so maybe their proposals are better....

关于为annotation提供动态配置能力的问题,其中一个网友认为:Sun make it real pain to do the deployment XML so that they can introduce annotation to fix it. The annotation can make code/deployment coupling so strong that Sun can come out with a new way (annotation interceptor in jdk 1.6? :)) for fixing it. and the cycles goes on...这让我想起了类似的现象:JSPTagLib。希望Annotation不会和TagLib有同样的命运。

       Annotation本身引入另一种类型的接口。在EJB3中确实使程序更加POJO,也消除了一些接口。并且编译后的代码也可以直接移植到另一个并不处理这些annotations的环境中(感谢VM在加载类时并不检查那些annotationsclasses,甚至这些类不在classpath中)。然而代码也确实增加了另一些接口。这个表现在编译期,如果没有这些annotation classes,是编译不过的。

       另一个问题(还好不算很重要),关于annotationnamespace。在多层应用的系统中,可能会出现同样的全局annotationnamespace冲突。比如一些公共的annotation,如@Transaction,将会被应用在很多个层次中。尽量让namespace长些,可以避免这个问题。

1.6.        元数据编程的应用:

Annotation已经被集成到很多的java规范和标准中,很重要的是它已经被J2EE标准,如EJB3所采用,当然也被许多开源的组件体系如:AspectJ

       Annotation最重要的应用将是AOP:由于annotation可以天然的表示系统中的另一个横切面,同时Annotation的识别是通过反射得到的,所以Annotation很自然的应用到基于动态代理的AOP实现。AOPAlliance也支持metadata handlingAspectJ也发布了基于annotation的新版本。

       在实现AOP上,使用annotation也比使用XML有一个优势:如前所述,annotation更像是形容词和副词,这样比较不容易verbose。当然这个是相对的,在实际的实现中更依赖开发人员的努力。

       这里,笔者将展示一个不完整也不成熟的基于annotationAOP例子代码——关于银行卡的例子。

       功能需求:银行卡业务分为转帐,查询余额,查询明细,POS消费等。这其中转帐和POS消费是要收费的(转帐收取的是用户的手续费,而POS消费收取的是商家的手续费),另外POS消费还可能有积分的(比如笔者的牡丹贷记卡)。消费转帐都要记录明细。但查询余额就不需要记录在明细中。

       代码如下(在这个例子没有用动态代理也没有用已有的AOP框架,使代码看起来简单些)

// 银行卡对象

public class Card {

    private String account;

//some field method

}

Annotation:

// 手续费

// type= "user", 表示收取用户手续费; type= "Biz", 表示收取商家手续费

public @interface Fee{

    String type();

}

// 积分

public @interface Index {

}

// 记录明细

public @interface BizLog {

}

// 事务处理

public @interface Transaction {

}

// 业务接口

public interface BizAction {

    void execute(Card card, RunData rundata);

}

// 转帐业务

@Fee(type="user")

@Transaction

@BizLog

public class TransferAction implements BizAction  {

    public void execute(Card card, RunData rundata) {

        //To change body of implemented methods use File | Settings | File Templates.

    }

}

// POS消费

@Fee(type="Biz")

@Transaction

@BizLog

@Index

public class POSAction implements BizAction {

    public void execute(Card card, RunData rundata) {

        //To change body of implemented methods use File | Settings | File Templates.

    }

}

// 查询明细

public class QueryDetail implements BizAction {   

    public void execute(Card card, RunData rundata) {

        //To change body of implemented methods use File | Settings | File Templates.

    }

}

// 业务操作监视器接口

public interface BizActionMonitor {

    void execute(BizAction action, RunData rundata);

}

// 业务操作监视器实现

public class BizActionMonitorImpl implements BizActionMonitor{

    public void execute(BizAction action, RunData rundata) {

        Annotation[] annotations = action.getClass().getAnnotations();

        for(Annotation annotation : annotations){

            if (annotation instanceof Fee){  // 计算手续费   }

            if (annotation instanceof Index){ //计算积分    }

            if (annotation instanceof Transaction){ // 准备事务  }

            if (annotation instanceof BizLog){ // 记录明细   }

        }

    }

}

// 控制器对象

public class controller{

    private BizActionMonitor monitor;  

    public void execute(BizActionUI, rundata){

        BizAction action = getAction(BizActionUI);     

        monitor.execute(action, rundata);

    }

}

// 运行时数据

public interface RunData {

   // some method

}

// 用户设备(POS机, ATM或者柜台终端)接口

public class BizActionUI {

      private RunData rundata;

      private Controller controller;

      public BizActionUI(RunData rundata, Controller controller){

          this.rundata = rundata;

          this.controller = controller;

      }

      public void execute(){ // 某个子类实现  }

      public void commit(){

          controller.execute(this, rundata);

      }

}

public class Main{

    private Rundata rundata;

    private Controller controller; 

    public populateUI(command){

        BizActionUI ui = getUI(command);

        ui.execute();

    }  

    public BizActionUI getUI(command){

        //...

        BizActionUI ui

        if( //....){

            ui = new SomeBizActionUI(rundata, controller);

        }

        return ui;     

    }   

    public static main(String[] args){

        //...

        Main main = new Main();

        main.populateUI(command)

        //...

    }

}

 

1.7.        结束语:

本文讨论了annotation技术,展示了annotationclass文件格式,并讨论了annotation技术本身的优势和不足,并于现有的xml技术加以比较,展望了annotation技术的应用前景AOP

限于笔者自身的水平(包括技术水平和写作水平),技术上对annotation的学习比较有限,写作上也感觉好多话无法用文字来表达,因而本文的代码会比较多(大概有一半以上)。

package top.yogiczy.mytv.tv.ui.screensold.main.components import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.key import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import kotlinx.coroutines.launch import android.widget.Toast import android.content.Context import android.content.Intent import androidx.compose.runtime.remember import top.yogiczy.mytv.core.data.utils.Constants import top.yogiczy.mytv.core.data.utils.Logger import top.yogiczy.mytv.core.data.entities.actions.KeyDownAction import top.yogiczy.mytv.core.data.entities.channel.Channel import top.yogiczy.mytv.core.data.entities.channel.ChannelGroupList import top.yogiczy.mytv.core.data.entities.channel.ChannelGroupList.Companion.channelList import top.yogiczy.mytv.core.data.entities.channel.ChannelLine import top.yogiczy.mytv.core.data.entities.channel.ChannelList import top.yogiczy.mytv.core.data.entities.epg.Epg import top.yogiczy.mytv.core.data.entities.epg.EpgList import top.yogiczy.mytv.core.data.entities.epg.EpgList.Companion.match import top.yogiczy.mytv.core.data.entities.epg.EpgList.Companion.recentProgramme import top.yogiczy.mytv.core.data.entities.epg.EpgProgrammeReserveList import top.yogiczy.mytv.core.data.entities.iptvsource.IptvSourceList import top.yogiczy.mytv.core.data.repositories.epg.EpgRepository import top.yogiczy.mytv.core.data.repositories.iptv.IptvRepository import top.yogiczy.mytv.tv.ui.material.PopupContent import top.yogiczy.mytv.tv.ui.material.Snackbar import top.yogiczy.mytv.tv.ui.material.Visibility import top.yogiczy.mytv.tv.ui.material.popupable import top.yogiczy.mytv.tv.ui.screen.settings.SettingsSubCategories import top.yogiczy.mytv.tv.ui.screen.main.MainViewModel import top.yogiczy.mytv.tv.ui.screen.main.mainVM import top.yogiczy.mytv.tv.ui.screen.settings.SettingsViewModel import top.yogiczy.mytv.tv.ui.screen.settings.settingsVM import top.yogiczy.mytv.tv.ui.screensold.audiotracks.AudioTracksScreen import top.yogiczy.mytv.tv.ui.screensold.channel.ChannelNumberSelectScreen import top.yogiczy.mytv.tv.ui.screensold.channel.ChannelScreen import top.yogiczy.mytv.tv.ui.screensold.channel.ChannelTempScreen import top.yogiczy.mytv.tv.ui.screensold.channel.rememberChannelNumberSelectState import top.yogiczy.mytv.tv.ui.screensold.channelline.ChannelLineScreen import top.yogiczy.mytv.tv.ui.screensold.classicchannel.ClassicChannelScreen import top.yogiczy.mytv.tv.ui.screensold.datetime.DatetimeScreen import top.yogiczy.mytv.tv.ui.screensold.epg.EpgProgrammeProgressScreen import top.yogiczy.mytv.tv.ui.screensold.epg.EpgScreen import top.yogiczy.mytv.tv.ui.screensold.epgreverse.EpgReverseScreen import top.yogiczy.mytv.tv.ui.screensold.quickop.QuickOpScreen import top.yogiczy.mytv.tv.ui.screensold.subtitletracks.SubtitleTracksScreen import top.yogiczy.mytv.tv.ui.screensold.iptvsource.IptvSourceScreen import top.yogiczy.mytv.tv.ui.screensold.videoplayer.VideoPlayerScreen import top.yogiczy.mytv.tv.ui.screensold.videoplayer.player.VideoPlayer import top.yogiczy.mytv.tv.ui.screensold.videoplayer.rememberVideoPlayerState import top.yogiczy.mytv.tv.ui.screensold.videoplayercontroller.VideoPlayerControllerScreen import top.yogiczy.mytv.tv.ui.screensold.videoplayerdiaplaymode.VideoPlayerDisplayModeScreen import top.yogiczy.mytv.tv.ui.screensold.videotracks.VideoTracksScreen import top.yogiczy.mytv.tv.ui.screensold.webview.WebViewScreen import top.yogiczy.mytv.tv.ui.screensold.webview.WebViewScreen_X5 import top.yogiczy.mytv.tv.ui.utils.backHandler import top.yogiczy.mytv.tv.ui.utils.handleDragGestures import top.yogiczy.mytv.tv.ui.utils.handleKeyEvents import top.yogiczy.mytv.tv.ui.utils.Configs import top.yogiczy.mytv.tv.X5CorePreLoadService @Composable fun MainContent( modifier: Modifier = Modifier, isLoadingProvider: () -> Boolean = { false }, filteredChannelGroupListProvider: () -> ChannelGroupList = { ChannelGroupList() }, favoriteChannelListProvider: () -> ChannelList = { ChannelList() }, epgListProvider: () -> EpgList = { EpgList() }, settingsViewModel: SettingsViewModel = settingsVM, onChannelFavoriteToggle: (Channel) -> Unit = {}, toSettingsScreen: (SettingsSubCategories?) -> Unit = {}, toDashboardScreen: () -> Unit = {}, onReload: () -> Unit = {}, onBackPressed: () -> Unit = {}, ) { val isLoading = isLoadingProvider() val coroutineScope = rememberCoroutineScope() val log = remember { Logger.create("MainContent")} val videoPlayerState = rememberVideoPlayerState(defaultDisplayModeProvider = { settingsViewModel.videoPlayerDisplayMode }) val mainContentState = rememberMainContentState( videoPlayerState = videoPlayerState, channelGroupListProvider = filteredChannelGroupListProvider, favoriteChannelListProvider = favoriteChannelListProvider, ) val channelNumberSelectState = rememberChannelNumberSelectState { val idx = it.toInt() - 1 filteredChannelGroupListProvider().channelList.getOrNull(idx)?.let { channel -> mainContentState.changeCurrentChannel(channel) } } Box( modifier = modifier .popupable() .backHandler { onBackPressed() } .handleKeyEvents( onUp = { getKeyDownEvent(settingsViewModel.keyDownEventUp, settingsViewModel, mainContentState, isLoading) }, onDown = { getKeyDownEvent(settingsViewModel.keyDownEventDown, settingsViewModel, mainContentState, isLoading) }, onLeft = { getKeyDownEvent(settingsViewModel.keyDownEventLeft, settingsViewModel, mainContentState, isLoading) }, onRight = { getKeyDownEvent(settingsViewModel.keyDownEventRight, settingsViewModel, mainContentState, isLoading) }, onLongUp = { getKeyDownEvent(settingsViewModel.keyDownEventLongUp, settingsViewModel, mainContentState, isLoading) }, onSelect = { getKeyDownEvent(settingsViewModel.keyDownEventSelect, settingsViewModel, mainContentState, isLoading) }, onLongSelect = { getKeyDownEvent(settingsViewModel.keyDownEventLongSelect, settingsViewModel, mainContentState, isLoading) }, onSettings = { mainContentState.isQuickOpScreenVisible = true }, onLongLeft = { getKeyDownEvent(settingsViewModel.keyDownEventLongLeft, settingsViewModel, mainContentState, isLoading) }, onLongRight = { getKeyDownEvent(settingsViewModel.keyDownEventLongRight, settingsViewModel, mainContentState, isLoading) }, onLongDown = { getKeyDownEvent(settingsViewModel.keyDownEventLongDown, settingsViewModel, mainContentState, isLoading) }, onNumber = { channelNumberSelectState.input(it) }, ) .handleDragGestures( onSwipeDown = { getKeyDownEvent(KeyDownAction.ChangeCurrentChannelToPrev, settingsViewModel, mainContentState, isLoading) }, onSwipeUp = { getKeyDownEvent(KeyDownAction.ChangeCurrentChannelToNext, settingsViewModel, mainContentState, isLoading) }, onSwipeRight = { getKeyDownEvent(KeyDownAction.ChangeCurrentChannelLineIdxToPrev, settingsViewModel, mainContentState, isLoading) }, onSwipeLeft = { getKeyDownEvent(KeyDownAction.ChangeCurrentChannelLineIdxToNext, settingsViewModel, mainContentState, isLoading) }, ), ) { Visibility({ mainContentState.currentChannelLine?.hybridType != ChannelLine.HybridType.WebView }) { VideoPlayerScreen( state = videoPlayerState, showMetadataProvider = { settingsViewModel.debugShowVideoPlayerMetadata }, forceTextureView = false, ) } key(mainContentState.isInPlaybackMode) { Visibility({ mainContentState.currentChannelLine?.hybridType == ChannelLine.HybridType.WebView }) { mainContentState.currentChannelLine.let { log.i("当前频道$it, 播放链接: ${it.playableUrl}") val isX5Available = com.tencent.smtt.sdk.QbSdk.canLoadX5(LocalContext.current) if (settingsViewModel.webViewCore == Configs.WebViewCore.X5 && !isX5Available){ settingsViewModel.webViewCore = Configs.WebViewCore.SYSTEM Toast.makeText( LocalContext.current, "X5内核不可用,将进行初始化。已切换为系统内核", Toast.LENGTH_LONG ).show() preInitX5Core(LocalContext.current) } when (settingsViewModel.webViewCore) { Configs.WebViewCore.SYSTEM -> { WebViewScreen( urlProvider = { Pair( it.playbackUrl ?: it.url, it.httpUserAgent ?: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" ) }, onVideoResolutionChanged = { width, height -> videoPlayerState.metadata = videoPlayerState.metadata.copy( video = (videoPlayerState.metadata.video ?: VideoPlayer.Metadata.Video()).copy( width = width, height = height, ), ) mainContentState.isTempChannelScreenVisible = false }, ) } Configs.WebViewCore.X5 -> { WebViewScreen_X5( urlProvider = { Pair( it.playbackUrl ?: it.url, it.httpUserAgent ?: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" ) }, onVideoResolutionChanged = { width, height -> videoPlayerState.metadata = videoPlayerState.metadata.copy( video = (videoPlayerState.metadata.video ?: VideoPlayer.Metadata.Video()).copy( width = width, height = height, ), ) mainContentState.isTempChannelScreenVisible = false }, onSelect = { mainContentState.isChannelScreenVisible = true }, onLongSelect = { mainContentState.isQuickOpScreenVisible = true }, ) } } } } } } Visibility({ settingsViewModel.uiShowEpgProgrammePermanentProgress }) { EpgProgrammeProgressScreen( currentEpgProgrammeProvider = { mainContentState.currentPlaybackEpgProgramme ?: epgListProvider().recentProgramme( mainContentState.currentChannel )?.now }, videoPlayerCurrentPositionProvider = { videoPlayerState.currentPosition }, ) } Visibility({ !mainContentState.isTempChannelScreenVisible && !mainContentState.isChannelScreenVisible && !mainContentState.isIptvSourceScreenVisible && !mainContentState.isQuickOpScreenVisible && !mainContentState.isEpgScreenVisible && !mainContentState.isChannelLineScreenVisible && channelNumberSelectState.channelNumber.isEmpty() }) { DatetimeScreen(showModeProvider = { settingsViewModel.uiTimeShowMode }) } ChannelNumberSelectScreen(channelNumberProvider = { channelNumberSelectState.channelNumber }) Visibility({ mainContentState.isTempChannelScreenVisible && !mainContentState.isChannelScreenVisible && !mainContentState.isIptvSourceScreenVisible && !mainContentState.isQuickOpScreenVisible && !mainContentState.isEpgScreenVisible && !mainContentState.isChannelLineScreenVisible && !mainContentState.isVideoPlayerControllerScreenVisible && channelNumberSelectState.channelNumber.isEmpty() }) { ChannelTempScreen( channelProvider = { mainContentState.currentChannel }, channelLineIdxProvider = { mainContentState.currentChannelLineIdx }, recentEpgProgrammeProvider = { epgListProvider().recentProgramme(mainContentState.currentChannel) }, currentPlaybackEpgProgrammeProvider = { mainContentState.currentPlaybackEpgProgramme }, playerMetadataProvider = { videoPlayerState.metadata }, ) } PopupContent( visibleProvider = { mainContentState.isEpgScreenVisible }, onDismissRequest = { mainContentState.isEpgScreenVisible = false }, ) { EpgScreen( epgProvider = { epgListProvider().match(mainContentState.currentChannel) ?: Epg.empty( mainContentState.currentChannel ) }, epgProgrammeReserveListProvider = { EpgProgrammeReserveList(settingsViewModel.epgChannelReserveList.filter { it.channel == mainContentState.currentChannel.name }) }, supportPlaybackProvider = { mainContentState.supportPlayback() }, currentPlaybackEpgProgrammeProvider = { mainContentState.currentPlaybackEpgProgramme }, onEpgProgrammePlayback = { mainContentState.isEpgScreenVisible = false mainContentState.changeCurrentChannel( mainContentState.currentChannel, mainContentState.currentChannelLineIdx, it, ) }, onEpgProgrammeReserve = { programme -> mainContentState.reverseEpgProgrammeOrNot( mainContentState.currentChannel, programme ) }, onClose = { mainContentState.isEpgScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isIptvSourceScreenVisible }, onDismissRequest = { mainContentState.isIptvSourceScreenVisible = false }, ) { IptvSourceScreen( currentIptvSourceProvider = { settingsViewModel.iptvSourceCurrent }, iptvSourceListProvider = {settingsViewModel.iptvSourceList}, onIptvSourceChanged = { mainContentState.isIptvSourceScreenVisible = false settingsViewModel.iptvSourceCurrent = it settingsViewModel.iptvChannelGroupHiddenList = emptySet() settingsViewModel.iptvChannelLastPlay = Channel.EMPTY onReload() }, onClose = { mainContentState.isIptvSourceScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isChannelLineScreenVisible }, onDismissRequest = { mainContentState.isChannelLineScreenVisible = false }, ) { ChannelLineScreen( channelProvider = { mainContentState.currentChannel }, currentLineProvider = { mainContentState.currentChannelLine }, onLineSelected = { mainContentState.isChannelLineScreenVisible = false mainContentState.changeCurrentChannel( mainContentState.currentChannel, mainContentState.currentChannel.lineList.indexOf(it), ) }, onClose = { mainContentState.isChannelLineScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isVideoPlayerControllerScreenVisible }, onDismissRequest = { mainContentState.isVideoPlayerControllerScreenVisible = false }, ) { val threshold = 1000L * 60 * 60 * 24 * 365 val hour0 = -28800000L VideoPlayerControllerScreen( isVideoPlayerPlayingProvider = { videoPlayerState.isPlaying }, isVideoPlayerBufferingProvider = { videoPlayerState.isBuffering }, videoPlayerCurrentPositionProvider = { if (videoPlayerState.currentPosition >= threshold) videoPlayerState.currentPosition else hour0 + videoPlayerState.currentPosition }, videoPlayerDurationProvider = { if (videoPlayerState.currentPosition >= threshold) { val playback = mainContentState.currentPlaybackEpgProgramme if (playback != null) { playback.startAt to playback.endAt } else { val programme = epgListProvider().recentProgramme(mainContentState.currentChannel)?.now (programme?.startAt ?: hour0) to (programme?.endAt ?: hour0) } } else { hour0 to (hour0 + videoPlayerState.duration) } }, onVideoPlayerPlay = { videoPlayerState.play() }, onVideoPlayerPause = { videoPlayerState.pause() }, onVideoPlayerSeekTo = { videoPlayerState.seekTo(it) }, onClose = { mainContentState.isVideoPlayerControllerScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isVideoPlayerDisplayModeScreenVisible }, onDismissRequest = { mainContentState.isVideoPlayerDisplayModeScreenVisible = false }, ) { VideoPlayerDisplayModeScreen( currentDisplayModeProvider = { videoPlayerState.displayMode }, onDisplayModeChanged = { videoPlayerState.displayMode = it }, onApplyToGlobal = { mainContentState.isVideoPlayerDisplayModeScreenVisible = false settingsViewModel.videoPlayerDisplayMode = videoPlayerState.displayMode Snackbar.show("已应用到全局") }, onClose = { mainContentState.isVideoPlayerDisplayModeScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isVideoTracksScreenVisible }, onDismissRequest = { mainContentState.isVideoTracksScreenVisible = false }, ) { VideoTracksScreen( trackListProvider = { videoPlayerState.metadata.videoTracks }, onTrackChanged = { videoPlayerState.selectVideoTrack(it) mainContentState.isVideoTracksScreenVisible = false }, onClose = { mainContentState.isVideoTracksScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isAudioTracksScreenVisible }, onDismissRequest = { mainContentState.isAudioTracksScreenVisible = false }, ) { AudioTracksScreen( trackListProvider = { videoPlayerState.metadata.audioTracks }, onTrackChanged = { videoPlayerState.selectAudioTrack(it) mainContentState.isAudioTracksScreenVisible = false }, onClose = { mainContentState.isAudioTracksScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isSubtitleTracksScreenVisible }, onDismissRequest = { mainContentState.isSubtitleTracksScreenVisible = false }, ) { SubtitleTracksScreen( trackListProvider = { videoPlayerState.metadata.subtitleTracks }, onTrackChanged = { videoPlayerState.selectSubtitleTrack(it) mainContentState.isSubtitleTracksScreenVisible = false }, onClose = { mainContentState.isSubtitleTracksScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isQuickOpScreenVisible }, onDismissRequest = { mainContentState.isQuickOpScreenVisible = false }, ) { QuickOpScreen( currentChannelProvider = { mainContentState.currentChannel }, currentChannelLineIdxProvider = { mainContentState.currentChannelLineIdx }, currentChannelNumberProvider = { (filteredChannelGroupListProvider().channelList.indexOf(mainContentState.currentChannel) + 1).toString() }, epgListProvider = epgListProvider, currentPlaybackEpgProgrammeProvider = { mainContentState.currentPlaybackEpgProgramme }, videoPlayerMetadataProvider = { videoPlayerState.metadata }, videoPlayerIndicatorProvider = { mainContentState.currentChannelLine?.hybridType != ChannelLine.HybridType.WebView }, onShowIptvSource ={ mainContentState.isQuickOpScreenVisible = false mainContentState.isIptvSourceScreenVisible = true }, onShowEpg = { mainContentState.isQuickOpScreenVisible = false mainContentState.isEpgScreenVisible = true }, onShowChannelLine = { mainContentState.isQuickOpScreenVisible = false mainContentState.isChannelLineScreenVisible = true }, onShowVideoPlayerController = { mainContentState.isQuickOpScreenVisible = false mainContentState.isVideoPlayerControllerScreenVisible = true }, onShowVideoPlayerDisplayMode = { mainContentState.isQuickOpScreenVisible = false mainContentState.isVideoPlayerDisplayModeScreenVisible = true }, onShowVideoTracks = { mainContentState.isQuickOpScreenVisible = false mainContentState.isVideoTracksScreenVisible = true }, onShowAudioTracks = { mainContentState.isQuickOpScreenVisible = false mainContentState.isAudioTracksScreenVisible = true }, onShowSubtitleTracks = { mainContentState.isQuickOpScreenVisible = false mainContentState.isSubtitleTracksScreenVisible = true }, toSettingsScreen = { mainContentState.isQuickOpScreenVisible = false toSettingsScreen(it) }, toDashboardScreen = { mainContentState.isQuickOpScreenVisible = false toDashboardScreen() }, onClearCache = { settingsViewModel.iptvChannelLinePlayableHostList = emptySet() settingsViewModel.iptvChannelLinePlayableUrlList = emptySet() coroutineScope.launch { IptvRepository(settingsViewModel.iptvSourceCurrent).clearCache() EpgRepository(settingsViewModel.epgSourceCurrent).clearCache() Snackbar.show("缓存已清除,请重启应用") } }, onClose = { mainContentState.isQuickOpScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isChannelScreenVisible && !settingsViewModel.uiUseClassicPanelScreen }, onDismissRequest = { mainContentState.isChannelScreenVisible = false }, ) { ChannelScreen( channelGroupListProvider = filteredChannelGroupListProvider, favoriteChannelListProvider = favoriteChannelListProvider, currentChannelProvider = { mainContentState.currentChannel }, currentChannelLineIdxProvider = { mainContentState.currentChannelLineIdx }, showChannelLogoProvider = { settingsViewModel.uiShowChannelLogo }, onChannelSelected = { mainContentState.isChannelScreenVisible = false mainContentState.changeCurrentChannel(it) }, onChannelFavoriteToggle = onChannelFavoriteToggle, epgListProvider = epgListProvider, showEpgProgrammeProgressProvider = { settingsViewModel.uiShowEpgProgrammeProgress }, currentPlaybackEpgProgrammeProvider = { mainContentState.currentPlaybackEpgProgramme }, videoPlayerMetadataProvider = { videoPlayerState.metadata }, channelFavoriteEnabledProvider = { settingsViewModel.iptvChannelFavoriteEnable }, channelFavoriteListVisibleProvider = { settingsViewModel.iptvChannelFavoriteListVisible }, onChannelFavoriteListVisibleChange = { settingsViewModel.iptvChannelFavoriteListVisible = it }, onClose = { mainContentState.isChannelScreenVisible = false }, ) } PopupContent( visibleProvider = { mainContentState.isChannelScreenVisible && settingsViewModel.uiUseClassicPanelScreen }, onDismissRequest = { mainContentState.isChannelScreenVisible = false }, ) { ClassicChannelScreen( channelGroupListProvider = filteredChannelGroupListProvider, favoriteChannelListProvider = favoriteChannelListProvider, currentChannelProvider = { mainContentState.currentChannel }, currentChannelLineIdxProvider = { mainContentState.currentChannelLineIdx }, showChannelLogoProvider = { settingsViewModel.uiShowChannelLogo }, onChannelSelected = { mainContentState.isChannelScreenVisible = false mainContentState.changeCurrentChannel(it) }, onChannelFavoriteToggle = onChannelFavoriteToggle, epgListProvider = epgListProvider, epgProgrammeReserveListProvider = { EpgProgrammeReserveList(settingsViewModel.epgChannelReserveList) }, showEpgProgrammeProgressProvider = { settingsViewModel.uiShowEpgProgrammeProgress }, supportPlaybackProvider = { mainContentState.supportPlayback(it, null) }, currentPlaybackEpgProgrammeProvider = { mainContentState.currentPlaybackEpgProgramme }, onEpgProgrammePlayback = { channel, programme -> mainContentState.isChannelScreenVisible = false mainContentState.changeCurrentChannel(channel, null, programme) }, onEpgProgrammeReserve = { channel, programme -> mainContentState.reverseEpgProgrammeOrNot(channel, programme) }, videoPlayerMetadataProvider = { videoPlayerState.metadata }, channelFavoriteEnabledProvider = { settingsViewModel.iptvChannelFavoriteEnable }, channelFavoriteListVisibleProvider = { settingsViewModel.iptvChannelFavoriteListVisible }, onChannelFavoriteListVisibleChange = { settingsViewModel.iptvChannelFavoriteListVisible = it }, onClose = { mainContentState.isChannelScreenVisible = false }, ) } EpgReverseScreen( epgProgrammeReserveListProvider = { settingsViewModel.epgChannelReserveList }, onConfirmReserve = { reserve -> filteredChannelGroupListProvider().channelList.firstOrNull { it.name == reserve.channel } ?.let { mainContentState.changeCurrentChannel(it) } }, onDeleteReserve = { reserve -> settingsViewModel.epgChannelReserveList = EpgProgrammeReserveList(settingsViewModel.epgChannelReserveList - reserve) }, ) } /** * 初始化X5内核 */ private fun preInitX5Core(context: Context) { // Accept context as a parameter // 预加载x5内核 val intent = Intent(context, X5CorePreLoadService::class.java) X5CorePreLoadService.enqueueWork(context, intent) } private fun getKeyDownEvent(actionEvent: KeyDownAction, settingsViewModel: SettingsViewModel, mainContentState: MainContentState, isLoading: Boolean) { when (actionEvent) { KeyDownAction.ChangeCurrentChannelToNext -> { if (settingsViewModel.iptvChannelChangeFlip) mainContentState.changeCurrentChannelToPrev() else mainContentState.changeCurrentChannelToNext() } KeyDownAction.ChangeCurrentChannelToPrev -> { if (settingsViewModel.iptvChannelChangeFlip) mainContentState.changeCurrentChannelToNext() else mainContentState.changeCurrentChannelToPrev() } KeyDownAction.ChangeCurrentChannelLineIdxToPrev -> { if (mainContentState.currentChannel.lineList.size > 1) { mainContentState.changeCurrentChannel( mainContentState.currentChannel, mainContentState.currentChannelLineIdx - 1, ) } } KeyDownAction.ChangeCurrentChannelLineIdxToNext -> { if (mainContentState.currentChannel.lineList.size > 1) { mainContentState.changeCurrentChannel( mainContentState.currentChannel, mainContentState.currentChannelLineIdx + 1, ) } } KeyDownAction.ToIptvSourceScreen -> { mainContentState.isIptvSourceScreenVisible = true } KeyDownAction.ToChannelScreen -> { if (!isLoading) mainContentState.isChannelScreenVisible = true } KeyDownAction.ToQuickOpScreen -> { mainContentState.isQuickOpScreenVisible = true } KeyDownAction.ToEpgScreen -> { mainContentState.isEpgScreenVisible = true } KeyDownAction.ToChannelLineScreen -> { mainContentState.isChannelLineScreenVisible = true } KeyDownAction.ToVideoPlayerControllerScreen -> { mainContentState.isVideoPlayerControllerScreenVisible = true } } }请将以上代码转换为java
07-03
non zero exit code 1 Python 3.11.2 pip 25.2 from F:\ESP32-1\Espressif\python_env\idf5.5_py3.11_env\Lib\site-packages\pip (python 3.11) WARNING: The following issue occurred while accessing the ESP-IDF version file in the Python environment: [Errno 2] No such file or directory: 'F:\\ESP32-1\\Espressif\\python_env\\idf5.5_py3.11_env\\idf_version.txt'. (Diagnostic information. It can be ignored.) Looking in indexes: http://mirrors.aliyun.com/pypi/simple/ Requirement already satisfied: pip in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (25.2) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Looking in indexes: http://mirrors.aliyun.com/pypi/simple/ Requirement already satisfied: setuptools in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (71.0.0) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Looking in indexes: http://mirrors.aliyun.com/pypi/simple/, https://dl.espressif.com/pypi Ignoring importlib_metadata: markers 'python_version < "3.8"' don't match your environment Requirement already satisfied: setuptools in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 7)) (71.0.0) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Requirement already satisfied: packaging in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 8)) (25.0) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Requirement already satisfied: click in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 11)) (8.1.8) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Requirement already satisfied: pyserial in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 12)) (3.5) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Requirement already satisfied: cryptography in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 13)) (44.0.3) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Requirement already satisfied: pyparsing in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 14)) (3.2.3) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Requirement already satisfied: pyelftools in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 15)) (0.32) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Requirement already satisfied: idf-component-manager~=2.2 in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 16)) (2.2.2) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. Requirement already satisfied: esp-coredump in f:\esp32-1\espressif\python_env\idf5.5_py3.11_env\lib\site-packages (from -r f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt (line 17)) (1.14.0) WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host and is being ignored. If this repository is available via HTTPS we recommend you use HTTPS instead, otherwise you may silence this warning and allow it anyway with '--trusted-host mirrors.aliyun.com'. ERROR: Cannot install esptool because these package versions have conflicting dependencies. The conflict is caused by: The user requested esptool The user requested (constraint) esptool~=4.10.dev1 To fix this you could try to: 1. loosen the range of package versions you've specified 2. remove package versions to allow pip to attempt to solve the dependency conflict ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts Traceback (most recent call last): File "f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\idf_tools.py", line 3592, in <module> Skipping the download of F:\ESP32-1\Espressif\espidf.constraints.v5.5.txt because it was downloaded recently. Upgrading pip... Upgrading setuptools... Installing Python packages Constraint file: F:\ESP32-1\Espressif\espidf.constraints.v5.5.txt Requirement files: - f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\requirements\requirements.core.txt main(sys.argv[1:]) File "f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\idf_tools.py", line 3582, in main action_func(args) File "f:\ESP32-1\Espressif\frameworks\esp-idf-v5.5\v5.5\esp-idf\tools\idf_tools.py", line 2914, in action_install_python_env subprocess.check_call(run_args, stdout=sys.stdout, stderr=sys.stderr, env=env_copy) File "subprocess.py", line 413, in check_call subprocess.CalledProcessError: Command '['F:\\ESP32-1\\Espressif\\python_env\\idf5.5_py3.11_env\\Scripts\\python.exe', '-m', 'pip', 'install', '--no-warn-script-location', '-r', 'f:\\ESP32-1\\Espressif\\frameworks\\esp-idf-v5.5\\v5.5\\esp-idf\\tools\\requirements\\requirements.core.txt', '--upgrade', '--constraint', 'F:\\ESP32-1\\Espressif\\espidf.constraints.v5.5.txt', '--extra-index-url', 'https://dl.espressif.com/pypi']' returned non-zero exit status 1.
最新发布
08-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值