Android 视频播放器dkplayer

列表播放如图所示:

一、依赖

     //添加RecyclerView的依赖包
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
    // 异步加载图片依赖
    implementation 'com.squareup.picasso:picasso:2.5.2'
    // 上拉刷新、下来加载依赖
    implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.3'
    // --  Android:视频播放器dkplayer
    //# 必选,内部默认使用系统mediaplayer进行解码
    implementation 'com.github.dueeeke.dkplayer:dkplayer-java:3.2.6'
    //# 可选,包含StandardVideoController的实现
    implementation 'com.github.dueeeke.dkplayer:dkplayer-ui:3.2.6'
    //# 可选,使用exoplayer进行解码
    implementation 'com.github.dueeeke.dkplayer:player-exo:3.2.6'
    //# 可选,使用ijkplayer进行解码
    implementation 'com.github.dueeeke.dkplayer:player-ijk:3.2.6'
    //# 可选,如需要缓存或者抖音预加载功能请引入此库
    implementation 'com.github.dueeeke.dkplayer:videocache:3.2.6'

二、工具类

Tag.java

package com.chy.permission;

/**
 * 播放器标签
 */
public final class Tag {
    //列表播放
    public static final String LIST = "list";
    //无缝播放
    public static final String SEAMLESS = "seamless";
    //画中画
    public static final String PIP = "pip";
}
Utils.java
package com.chy.permission;

import android.view.View;
import android.view.ViewParent;
import android.widget.FrameLayout;

import com.dueeeke.videoplayer.player.VideoView;
import com.dueeeke.videoplayer.player.VideoViewConfig;
import com.dueeeke.videoplayer.player.VideoViewManager;

import java.lang.reflect.Field;

public final class Utils {

    private Utils() {
    }


    /**
     * 获取当前的播放核心
     */
    public static Object getCurrentPlayerFactory() {
        VideoViewConfig config = VideoViewManager.getConfig();
        Object playerFactory = null;
        try {
            Field mPlayerFactoryField = config.getClass().getDeclaredField("mPlayerFactory");
            mPlayerFactoryField.setAccessible(true);
            playerFactory = mPlayerFactoryField.get(config);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return playerFactory;
    }

    /**
     * 将View从父控件中移除
     */
    public static void removeViewFormParent(View v) {
        if (v == null) return;
        ViewParent parent = v.getParent();
        if (parent instanceof FrameLayout) {
            ((FrameLayout) parent).removeView(v);
        }
    }

    /**
     * Returns a string containing player state debugging information.
     */
    public static String playState2str(int state) {
        String playStateString;
        switch (state) {
            default:
            case VideoView.STATE_IDLE:
                playStateString = "idle";
                break;
            case VideoView.STATE_PREPARING:
                playStateString = "preparing";
                break;
            case VideoView.STATE_PREPARED:
                playStateString = "prepared";
                break;
            case VideoView.STATE_PLAYING:
                playStateString = "playing";
                break;
            case VideoView.STATE_PAUSED:
                playStateString = "pause";
                break;
            case VideoView.STATE_BUFFERING:
                playStateString = "buffering";
                break;
            case VideoView.STATE_BUFFERED:
                playStateString = "buffered";
                break;
            case VideoView.STATE_PLAYBACK_COMPLETED:
                playStateString = "playback completed";
                break;
            case VideoView.STATE_ERROR:
                playStateString = "error";
                break;
        }
        return String.format("playState: %s", playStateString);
    }

    /**
     * Returns a string containing player state debugging information.
     */
    public static String playerState2str(int state) {
        String playerStateString;
        switch (state) {
            default:
            case VideoView.PLAYER_NORMAL:
                playerStateString = "normal";
                break;
            case VideoView.PLAYER_FULL_SCREEN:
                playerStateString = "full screen";
                break;
            case VideoView.PLAYER_TINY_SCREEN:
                playerStateString = "tiny screen";
                break;
        }
        return String.format("playerState: %s", playerStateString);
    }


}

VideoAdapter.java

package com.chy.demoprj.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.chy.demoprj.R;
import com.dueeeke.videocontroller.component.PrepareView;
import com.squareup.picasso.Picasso;

import java.util.HashMap;
import java.util.List;

public class VideoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private Context context;
    private List<HashMap<String,String>> datas;

    private OnItemChildClickListener mOnItemChildClickListener;// 播放控件点击事件

    /**
     * 构造函数
     * */
    public VideoAdapter(Context context,List<HashMap<String,String>> datas){
        this.context = context;
        this.datas = datas;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_video_layout,parent,false);
         ViewHolder viewHolder = new ViewHolder(view);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,int position) {
        ViewHolder vh = (ViewHolder) holder;
        HashMap<String,String> entity = datas.get(position);

        // 设置播放占位图(不设置默认黑色)
        Picasso.with(context)
                .load(entity.get("thumbUrl"))
                .into(vh.mThumb);

        vh.mPosition = position;
    }

    @Override
    public int getItemCount() {
        return datas.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
        public FrameLayout mPlayerContainer;// 容器
        public PrepareView mPrepareView;// 播放视图
        public ImageView mThumb;// 缩略图
        public int mPosition;// 当前视图index

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            mPlayerContainer = itemView.findViewById(R.id.player_container);
            mPrepareView = itemView.findViewById(R.id.prepare_view);
            mThumb = mPrepareView.findViewById(R.id.thumb);

            /**
             * 添加点击事件
             * */
            if (mOnItemChildClickListener != null) {
                mPlayerContainer.setOnClickListener(this);
            }


            //通过tag将ViewHolder和itemView绑定
            itemView.setTag(this);
        }

        @Override
        public void onClick(View v) {
            if (v.getId() == R.id.player_container){

                if (mOnItemChildClickListener != null){
                    mOnItemChildClickListener.onItemChildClick(mPosition);
                }

            }
        }
    }

    /**
     * 点击事件接口
     * */
    public interface OnItemChildClickListener{
        void onItemChildClick(int position);
    }

    /**
     * 点击事件回调函数
     * */
    public void setOnItemChildClickListener(OnItemChildClickListener onItemChildClickListener) {
        mOnItemChildClickListener = onItemChildClickListener;
    }

}

三、布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<com.scwang.smartrefresh.layout.SmartRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/refreshLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


</com.scwang.smartrefresh.layout.SmartRefreshLayout>

item_video_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/player_container"
        android:layout_width="match_parent"
        android:layout_height="187dp"
        android:layout_marginTop="8dp"
        android:background="@android:color/black"
        app:layout_constraintDimensionRatio="16:9"
        app:layout_constraintTop_toTopOf="parent">

        <com.dueeeke.videocontroller.component.PrepareView
            android:id="@+id/prepare_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </FrameLayout>


</LinearLayout>

四、实现

package com.chy.demoprj;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.Manifest;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.chy.demoprj.adapter.VideoAdapter;
import com.chy.permission.PermissionUtils;
import com.chy.permission.Tag;
import com.chy.permission.Utils;
import com.dueeeke.videocontroller.StandardVideoController;
import com.dueeeke.videocontroller.component.CompleteView;
import com.dueeeke.videocontroller.component.ErrorView;
import com.dueeeke.videocontroller.component.GestureView;
import com.dueeeke.videocontroller.component.TitleView;
import com.dueeeke.videocontroller.component.VodControlView;
import com.dueeeke.videoplayer.player.VideoView;
import com.dueeeke.videoplayer.player.VideoViewManager;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_PERMISSION_CODE = 0;// 权限所用
    // 动态申请权限
    private String[] permissions = {
            Manifest.permission.INTERNET,// 网络权限
            Manifest.permission.WRITE_EXTERNAL_STORAGE,// 写入数据权限
            Manifest.permission.READ_EXTERNAL_STORAGE,// 读取数据权限
            Manifest.permission.ACCESS_FINE_LOCATION,// 定位权限
            Manifest.permission.ACCESS_COARSE_LOCATION // 获取基站的服务信号权限,以便获取位置信息
    };


    private RefreshLayout refreshLayout;
    private RecyclerView recyclerView;// 列表
    private LinearLayoutManager layoutManager;
    private List<HashMap<String,String>> datas = new ArrayList<>();

    // 视频播放-控件
    protected VideoView mVideoView;
    protected StandardVideoController mController;
    protected ErrorView mErrorView;
    protected CompleteView mCompleteView;
    protected TitleView mTitleView;

    /**
     * 当前播放的位置
     * */
    protected int mCurPos = -1;
    /**
     * 上次播放的位置,用于页面切换回来继续播放
     * */
    protected int mLastPos = mCurPos;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getPermission();


        initData();
        initControls();
        initVideoView();

    }

    /**
     * 权限
     * */
    private void getPermission(){
        boolean flage = PermissionUtils.hasPermissions(MainActivity.this,permissions);
        if (flage) {
            System.out.println("权限获取成功!");
        } else {
            PermissionUtils.requestPermissions(MainActivity.this, REQUEST_PERMISSION_CODE, permissions);
        }
    }


    /**
     * 数据初始化
     * */
    private void initData(){
        HashMap<String,String> hashMap = null;
        // 占位图
        String thumbUrl = "https://p1-xg.byteimg.com/img/tos-cn-p-0000/527b08d0f31d4705a4d8f4a72120948c~tplv-crop-center:1041:582.jpg";
        // 视频播放地址
        String playerUrl = "https://vd4.bdstatic.com/mda-pdhb52ikamv3bdb7/sc/cae_h264/1681819063478400576/mda-pdhb52ikamv3bdb7.mp4";
        // 循环添加数据
        for (int i=0,len=8;i<=len;i++){
            hashMap = new HashMap<>();
            hashMap.put("thumbUrl",thumbUrl);
            hashMap.put("playerUrl",playerUrl);
            hashMap.put("vtitle","播放标题"+i);

            datas.add(hashMap);
        }

    }

    /**
     * 初始化控件
     * */
    private void initControls(){
        refreshLayout = findViewById(R.id.refreshLayout);
        // 下拉刷新事件
        refreshLayout.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(@NonNull RefreshLayout refreshLayout) {
                refreshLayout.finishRefresh(2000/*,false*/);// 传入false表示刷新失败

            }
        });
        // 上拉加载事件
        refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
                refreshLayout.finishLoadMore(2000/*,false*/);// 传入false表示加载失败
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplication(),"没有更多数据",Toast.LENGTH_SHORT).show();
                    }
                });

            }
        });


        recyclerView = findViewById(R.id.recyclerView);
        // 设置布局
        layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);

        // 配置器
        VideoAdapter videoAdapter = new VideoAdapter(this,datas);
        // 设置点击事件
        videoAdapter.setOnItemChildClickListener(new VideoAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(int position) {
                /**
                 * PrepareView被点击
                 */
                startPlay(position);
            }
        });

        recyclerView.setAdapter(videoAdapter);
        // RecyclerView监听事件
        recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
            @Override
            public void onChildViewAttachedToWindow(@NonNull View view) {

            }

            @Override
            public void onChildViewDetachedFromWindow(@NonNull View view) {
                FrameLayout playerContainer = view.findViewById(R.id.player_container);
                View v = playerContainer.getChildAt(0);
                if (v != null && v == mVideoView && !mVideoView.isFullScreen()){
                    releaseVideoView();
                }
            }
        });

    }


    /**
     * 播放控件初始化
     * */
    protected void initVideoView() {
        mVideoView = new VideoView(this);
        mVideoView.setOnStateChangeListener(new VideoView.SimpleOnStateChangeListener() {
            @Override
            public void onPlayStateChanged(int playState) {
                //监听VideoViewManager释放,重置状态
                if (playState == com.dueeeke.videoplayer.player.VideoView.STATE_IDLE) {
                    Utils.removeViewFormParent(mVideoView);
                    mLastPos = mCurPos;
                    mCurPos = -1;
                }
            }
        });

        mController = new StandardVideoController(this);
        mErrorView = new ErrorView(this);
        mController.addControlComponent(mErrorView);
        mCompleteView = new CompleteView(this);
        mController.addControlComponent(mCompleteView);
        mTitleView = new TitleView(this);
        mController.addControlComponent(mTitleView);
        mController.addControlComponent(new VodControlView(this));
        mController.addControlComponent(new GestureView(this));
        mController.setEnableOrientation(true);
        mVideoView.setVideoController(mController);
    }

    @Override
    public void onPause() {
        super.onPause();
        pause();
    }

    /**
     * 由于onPause必须调用super。故增加此方法,
     * 子类将会重写此方法,改变onPause的逻辑
     */
    protected void pause() {
        releaseVideoView();
    }

    @Override
    public void onResume() {
        super.onResume();
        resume();
    }


    /**
     * 由于onResume必须调用super。故增加此方法,
     * 子类将会重写此方法,改变onResume的逻辑
     */
    protected void resume() {
        if (mLastPos == -1)
            return;
        //恢复上次播放的位置
        startPlay(mLastPos);
    }



    /**
     * 开始播放
     *
     * @param position 列表位置
     */
    protected void startPlay(int position) {
        if (mCurPos == position) return;
        if (mCurPos != -1) {
            releaseVideoView();
        }
        HashMap<String,String> entity = datas.get(position);
        //边播边存
//        String proxyUrl = ProxyVideoCacheManager.getProxy(getActivity()).getProxyUrl(videoBean.getUrl());
//        mVideoView.setUrl(proxyUrl);
        String playurl = entity.get("playerUrl");
        mVideoView.setUrl(playurl);
        mTitleView.setTitle(entity.get("vtitle"));
        View itemView = layoutManager.findViewByPosition(position);
        if (itemView == null) return;
        VideoAdapter.ViewHolder viewHolder = (VideoAdapter.ViewHolder) itemView.getTag();
        //把列表中预置的PrepareView添加到控制器中,注意isPrivate此处只能为true。
        mController.addControlComponent(viewHolder.mPrepareView, true);
        Utils.removeViewFormParent(mVideoView);
        viewHolder.mPlayerContainer.addView(mVideoView, 0);
        //播放之前将VideoView添加到VideoViewManager以便在别的页面也能操作它
        getVideoViewManager().add(mVideoView, Tag.LIST);
        mVideoView.start();
        mCurPos = position;

    }


    /**
     * 释放播放控件
     * */
    private void releaseVideoView() {
        mVideoView.release();
        if (mVideoView.isFullScreen()) {
            mVideoView.stopFullScreen();
        }
        if (this.getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
            this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
        mCurPos = -1;
    }

    /**
     * 创建播放管理类
     * */
    protected VideoViewManager getVideoViewManager(){
        return VideoViewManager.instance();
    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/66972.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql之存储过程

目录 一、mysql之存储过程的相关知识 1&#xff09;存储过程的概念 2&#xff09;存储过程的优点 二、存储过程的管理 1&#xff09;创建存储过程 基本格式&#xff1a; 2&#xff09;调用存储过程 格式&#xff1a; call 存储过程名称 3&#xff09;查看存储过程 查…

Leetcode-每日一题【剑指 Offer 13. 机器人的运动范围】

题目 地上有一个m行n列的方格&#xff0c;从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动&#xff0c;它每次可以向左、右、上、下移动一格&#xff08;不能移动到方格外&#xff09;&#xff0c;也不能进入行坐标和列坐标的数位之和大于k的格子。例…

51单片机学习--红外遥控(外部中断)

需要利用下面这个红外接收头&#xff0c;OUT口会发出红外信号对应的高低电平&#xff0c;由于发送的速度很快&#xff0c;所以需要把OUT引脚接在外部中断引脚上&#xff0c;当OUT一旦产生下降沿&#xff0c;马上进中断&#xff0c;这样响应会更及时。 外部中断引脚位于P3_2和P…

【云原生】kubernetes控制器deployment的使用

目录 ​编辑 1 Controller 控制器 1.1 什么是 Controller 1.2 常见的 Controller 控制器 1.3 Controller 如何管理 Pod 2 Deployment 2.1 创建 deployment 2.2 查看 deployment 2.3 扩缩 deployment 2.4 回滚 deployment 2.5 删除 deployment 1 Controller 控制器 …

条条大路通罗马系列—— 使用 Hiredis-cluster 连接 Amazon ElastiCache for Redis 集群

前言 Amazon ElastiCache for Redis 是速度超快的内存数据存储&#xff0c;能够提供亚毫秒级延迟来支持 实时应用程序。适用于 Redis 的 ElastiCache 基于开源 Redis 构建&#xff0c;可与 Redis API 兼容&#xff0c;能够与 Redis 客户端配合工作&#xff0c;并使用开放的 Re…

【算法篇C++实现】五大常规算法

文章目录 &#x1f680;一、分治法⛳&#xff08;一&#xff09;算法思想⛳&#xff08;二&#xff09;相关代码 &#x1f680;二、动态规划算法⛳&#xff08;一&#xff09;算法思想⛳&#xff08;二&#xff09;相关代码 &#x1f680;三、回溯算法⛳&#xff08;一&#xf…

数据挖掘具体步骤

数据挖掘具体步骤 1、理解业务与数据 2、准备数据 数据清洗&#xff1a; 缺失值处理&#xff1a; 异常值: 数据标准化&#xff1a; 特征选择&#xff1a; 数据采样处理&#xff1a; 3、数据建模 分类问题&#xff1a; 聚类问题&#xff1a; 回归问题 关联分析 集成学习 image B…

区块链学习6-长安链部署:如何创建特定共识节点数和同步节点数的链

正常prepare的时候只支持4 7 13 16个节点个数&#xff0c;想要创建10个节点&#xff0c;其中5个是共识节点&#xff0c;如何实现&#xff1f; 1. 注释掉prepare.sh的这几行&#xff1a; 2. 修改 crytogen的模板文件&#xff1a; 如果是cert模式&#xff1a;chainmaker-crypt…

idea+gradle阅读spring5.2.9源码之源码构建报错解决方案

注意 1、先确保gradle版本和spring、jdk版本对应 本文:gradle:5.6.4/spring 5.2.9/jdk1.8&#xff08;gradle和jdk都要先安装好&#xff0c;gradle还要配置好本地资源文件路径&#xff09; 2、原来项目乱了的话&#xff0c;先重新导入下载的源码项目 3、进入源码所在根目录&…

Redis数据库的下载和安装

目录 第一章、Redis数据库的下载和安装1.1&#xff09;nosql数据库和 Redis 介绍1.2&#xff09;Windows中下载安装Redis数据库1.3&#xff09;Linux中安装Redis数据库1.4&#xff09;Linux中启动redis1.5&#xff09;Linux中关闭redis 第二章、三种Redis客户端连接Redis数据库…

java代码审计9之XXE

文章目录 1、简介2、 java XXE审计函数3、漏洞3.1、正常的业务3.2、有回显的情况3.3、无回显的情况3.4、修复 之前的文章&#xff0c; php代码审计9之XXE 1、简介 XXE&#xff08;XML外部实体注⼊&#xff0c;XML External Entity) &#xff0c;在应⽤程序解析XML输⼊时&…

【Java-16】动态代理的使用方法及原理实现

代理模式&#xff1a;静态代理 目标 了解静态代理模式实现 路径 静态代理概述静态代理案例 静态代理概述 静态代理&#xff1a; 是由程序员创建或工具生成代理类的源码&#xff0c;再编译成为字节码 &#xff08;字节码文件在没有运行java之前就存在了&#xff09; 在编译…

深度学习:使用卷积神经网络CNN实现MNIST手写数字识别

引言 本项目基于pytorch构建了一个深度学习神经网络&#xff0c;网络包含卷积层、池化层、全连接层&#xff0c;通过此网络实现对MINST数据集手写数字的识别&#xff0c;通过本项目代码&#xff0c;从原理上理解手写数字识别的全过程&#xff0c;包括反向传播&#xff0c;梯度…

【第一阶段】kotlin语言的Nothing类型

fun main() {show(60) } //两种写法一样 private fun show(num:Int){when(num){//下面这句话不是注释提示&#xff0c;会终止程序-1->TODO("不符合")in 0..59->println("不及格")in 60..89->println("及格")in 90..100->println(&qu…

桥接模式(C++)

定义 将抽象部分(业务功能)与实现部分(平台实现)分离&#xff0c;使它们都可以独立地变化。 使用场景 由于某些类型的固有的实现逻辑&#xff0c;使得它们具有两个变化的维度&#xff0c;乃至多个纬度的变化。如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型…

idea - 刷新 Git 分支数据 / 命令刷新 Git 分支数据

一、idea - 刷新 Git 分支数据 idea 找到 fetch 选项&#xff0c;重新获取分支数据 二、命令刷新 Git 分支数据 git fetch参考链接 1. 远程Gitlab新建的分支在IDEA里不显示

OSPF工作原理及其配置命令

目录 一、OSPF&#xff08;开放式最短路径优先协议&#xff09;&#xff1a; 作用&#xff1a;防环 弊端&#xff1a; 结构化部署: 更新方式&#xff1a; 二、OSPF的数据包 三、OSPF的状态机 Down Init 2way 条件&#xff1a; Exstart Exchange Loadi…

Pytorch量化之Post Train Static Quantization(训练后静态量化)

使用Pytorch训练出的模型权重为fp32&#xff0c;部署时&#xff0c;为了加快速度&#xff0c;一般会将模型量化至int8。与fp32相比&#xff0c;int8模型的大小为原来的1/4, 速度为2~4倍。 Pytorch支持三种量化方式&#xff1a; 动态量化&#xff08;Dynamic Quantization&…

Android 13 Hotseat定制化修改

一.背景 由于需求是需要自定义修改Hotseat,所以此篇文章是记录如何自定义修改hotseat的,应该可以覆盖大部分场景,修改点有修改hotseat布局方向,hotseat图标数量,hotseat图标大小,hotseat布局位置,hotseat图标禁止形成文件夹,hotseat图标禁止移动到Launcher中,下面开始…

Gpt微信小程序搭建的前后端流程 - 前端小程序部分-2.确定交互所需的后端API(二)

Gpt微信小程序搭建的前后端流程 - 前端小程序部分-2.确定交互所需的后端API(二) 参考微信小程序-小柠AI智能聊天&#xff0c;可自行先体验。 根据上一节的小程序静态页面设计&#xff0c;需要从后端获取数据的主要4个点&#xff1a; 登录流程&#xff1b;获取今日已提问次数&a…