【Android】Jetpack入门知识总结(LifeCycle,ViewModel,LiveData,DataBinding等)

文章目录

  • LifeCycle
    • 使用Lifecycle解耦页面与组件
      • 自定义控件实现LifecycleObserver接口
      • 注册生命周期监听器
    • 使用LifecycleService解耦Service与组件
    • 使用ProcessLifecycleOwner监听应用程序生命周期
  • ViewModel
    • 用法
    • 在 Fragment 中使用 ViewModel
  • LiveData
  • DataBinding
    • 导入依赖
    • 基本用法
      • 在布局文件中生成
      • 使用DataBinding的布局文件
      • 要绑定的数据类
      • 在Activity使用
    • 事件绑定
      • 定义事件处理类
      • 在Activity中设置绑定
      • 布局文件
    • 使用类方法
  • BindingAdapter
    • 示例
  • 双向绑定
    • BaseObservable
      • 示例
    • ObservableField

LifeCycle

使用Lifecycle解耦页面与组件

自定义控件实现LifecycleObserver接口

这里实现一个自定义的Chronometer,并实现能够响应生命周期事件的功能

public class MyChronometer extends Chronometer implements LifecycleObserver {
    private long elapsedtime;

    public MyChronometer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    // 添加注解与生命周期事件关联
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private void startMeter() {

        setBase(SystemClock.elapsedRealtime() - elapsedtime);
        start();
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    private void stopMeter() {

        elapsedtime = SystemClock.elapsedRealtime() - getBase();
        stop();
    }
}

注册生命周期监听器

在代码中使用自定义组件并为其注册生命周期监听器

public class MainActivity extends AppCompatActivity {
    private MyChronometer chronometer;
    private long elapsedtime;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
      
        chronometer = findViewById(R.id.chronometer);
        // 注册生命周期监听器
        getLifecycle().addObserver(chronometer);
    }
} 

activityfragment都是直接实现了LifecycleOwner接口,所以直接调用getLifecycle方法得到LifeCycle对象。addObserver 注册了一个生命周期观察者,当 MainActivity 的生命周期发生变化时,chronometer 会自动响应这些事件。

使用LifecycleService解耦Service与组件

1. 添加依赖

生命周期 | Jetpack | Android Developers (google.cn)

    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"

实现了 LifecycleObserver 接口的类,用于监听 Service 的生命周期事件。

public class MyLocationObserver implements LifecycleObserver {
    private Context mContext;
    private LocationManager locationManager;
    private MyLocationlistener myLocationlistener;

    public MyLocationObserver(Context context) {
        this.mContext = context;
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void startGetLocation() {
        Log.d("TagA", "start位置");
        if (ActivityCompat.checkSelfPermission(mContext, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
        myLocationlistener = new MyLocationlistener();
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1, myLocationlistener);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void stopGetLocation() {
        Log.d("TagA", "stop位置");
        locationManager.removeUpdates(myLocationlistener);
    }

    static class MyLocationlistener implements LocationListener {
        @Override
        public void onLocationChanged(@NonNull Location location) {
            Log.d("TagA", "位置变了");
        }
    }
}

扩展自 LifecycleService 的服务

public class MyLocationService extends LifecycleService {
    public MyLocationService() {
        Log.d("TagA", "MyLocationService空参构造");
        MyLocationObserver myLocationObserver = new MyLocationObserver(this);
        getLifecycle().addObserver(myLocationObserver);
    }
}

启动服务

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Log.d("TagA", "启动");
        Intent intent = new Intent(this, MyLocationService.class);
        startService(intent);

    } 
}

使用ProcessLifecycleOwner监听应用程序生命周期

ProcessLifecycleOwner 是 Android Jetpack 中的一个类,用于监听整个应用进程的生命周期事件。与 Activity 和 Fragment 的生命周期不同,ProcessLifecycleOwner 关注的是整个应用的前后台状态切换。它实现了 LifecycleOwner 接口,因此你可以像使用 Activity 或 Fragment 的生命周期一样,监听应用的生命周期事件。

ProcessLifecycleOwner 允许你监听以下两个主要状态:

  1. 前台:当应用至少有一个 Activity 可见时,进入前台。
  2. 后台:当所有 Activity 都不可见时,应用进入后台。
public class MyApp extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();

        // 注册观察者来监听应用的生命周期
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onEnterForeground() {
        Log.d("AppLifecycle", "应用进入前台");
        // 这里可以添加当应用进入前台时需要执行的操作
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onEnterBackground() {
        Log.d("AppLifecycle", "应用进入后台");
        // 这里可以添加当应用进入后台时需要执行的操作
    }
}
  1. 继承 Application 类
    创建一个自定义的 Application 类,并在其中注册生命周期观察者。

  2. 添加生命周期观察者
    使用 ProcessLifecycleOwner.get().getLifecycle().addObserver() 来添加生命周期观察者。这样可以监听整个应用的生命周期。

  3. 处理生命周期事件
    通过 @OnLifecycleEvent(Lifecycle.Event.ON_START)@OnLifecycleEvent(Lifecycle.Event.ON_STOP) 监听应用进入前台和后台的事件。在进入前台或后台时,可以执行相应的逻辑,比如暂停或恢复任务、更新UI、数据同步等。

优点

  1. 应用级生命周期管理:不再需要逐个监听每个 Activity 或 Fragment 的生命周期事件,ProcessLifecycleOwner 提供了一种应用级别的生命周期事件处理方式。

  2. 前后台切换检测:可以用来检测应用是否进入后台或回到前台,非常适合在应用进入后台时暂停某些服务,或者在进入前台时恢复。

注意:ProcessLifecycleOwner 只监听应用的前台和后台状态,不会捕捉到具体的 Activity 或 Fragment 的生命周期事件。如果需要监听具体的页面状态,仍然需要使用各自的 LifecycleOwner

ViewModel

ViewModel 是 Android Jetpack 提供的架构组件之一,主要用于存储和管理与 UI 相关的数据,以确保在配置更改(如屏幕旋转)时数据能够持久化并且不会丢失。ViewModel 可以帮助我们更好地分离 UI 控制逻辑与数据处理逻辑,增强代码的可维护性。

  1. 生命周期感知ViewModel 是生命周期感知的,它的生命周期比 ActivityFragment 更长,因此即使 UI 控件销毁重建,数据依然可以保存。
  2. 持久化数据:它可以避免在配置更改(如屏幕旋转)时重新加载数据。
  3. UI 与数据逻辑分离:ViewModel 主要处理数据逻辑,使 UI 控制代码和业务逻辑分离,保持代码结构清晰。

用法

  1. 创建 ViewModel
  2. ActivityFragment 中创建 ViewModel 实例
  3. 使用 ViewModel 提供的数据更新 UI
import androidx.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
   private int counter = 0;

   // 增加计数
   public void increaseCounter() {
      counter++;
   }

   // 获取当前计数
   public int getCounter() {
      return counter;
   }
}
  1. 在 Activity 中使用 ViewModel
public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private MyViewModel myViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);

        // 通过 ViewModelProvider 获取 MyViewModel 的实例
        myViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
        textView.setText(String.valueOf(myViewModel.getCounter()));
    }
    public void plus(View view) {
        myViewModel.increaseCounter();
        textView.setText(String.valueOf(myViewModel.getCounter()));
    }
}

在 Fragment 中使用 ViewModel

如果是在 Fragment 中使用 ViewModel,方式与 Activity 类似,只需确保使用的是 FragmentViewModelProvider,代码如下:

myViewModel = new ViewModelProvider(getActivity()).get(MyViewModel.class);

LiveData

ViewModel 通常与 LiveData 结合使用,这样可以让 UI 自动监听数据变化并更新,而不需要手动刷新界面。

示例:

实现两个fragment中的seekbar同步变化

public class MyViewModel3 extends ViewModel {
    private MutableLiveData<Integer> progerss;

    public MutableLiveData<Integer> getProgerss() {
        if (progerss == null) {
            progerss = new MutableLiveData<Integer>();
        }
        return progerss;
    }
}

fragment处理

public class Fragment1 extends Fragment {

    private MyViewModel3 myViewModel3;

    public Fragment1() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_1, container, false);
        SeekBar seekBar = v.findViewById(R.id.seekBar);
        myViewModel3 = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory()).get(MyViewModel3.class);
        myViewModel3.getProgerss().observe(getActivity(), new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                seekBar.setProgress(integer);
            }
        });

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                myViewModel3.getProgerss().setValue(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
        return v;
    }
}

两个fragment中都需要进行修改

实现效果:

image-20241014183933983

DataBinding

导入依赖

android {
    ...
    buildFeatures {
        dataBinding true
    }
}

基本用法

在布局文件中生成

image-20241014221456001

使用DataBinding的布局文件

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

        <variable
            name="user"
            type="com.example.livedatapractice.User" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">


        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"
            android:textSize="50dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="name" />

        <TextView
            android:id="@+id/tv_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="67dp"
            android:text="@{String.valueOf(user.age)}"
            android:textSize="50dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_name"
            app:layout_constraintVertical_bias="0.0"
            tools:text="age" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

要绑定的数据类

package com.example.livedatapractice;

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

在Activity使用

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding activityMainBinding;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        User user = new User("lixaing", 20);
        activityMainBinding.setUser(user);
    }
}

事件绑定

定义事件处理类

public class EventHandleListener {
    private Context context;

    public EventHandleListener(Context context) {
        this.context = context;
    }

    public void buttonOnClick(View view) {
        Toast.makeText(context, "点", Toast.LENGTH_SHORT).show();
    }
}

在Activity中设置绑定

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding activityMainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        User user = new User("lixaing", 20);
        activityMainBinding.setUser(user);
        // set
        activityMainBinding.setEventHandle(new EventHandleListener(this));
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

        <variable
            name="user"
            type="com.example.livedatapractice.User" />
        <variable
            name="eventHandle"
            type="com.example.livedatapractice.EventHandleListener" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">


        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"
            ...
            />

        <TextView
            android:id="@+id/tv_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(user.age)}"
            ...
            />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            android:onClick="@{eventHandle.buttonOnClick}"
            ...
            />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

使用类方法

定义工具类

package com.example.databindingpractice2;

public class StringUtils {

    public static String toUpperCase(String str) {
        return str.toUpperCase();
    }
}

在布局文件中使用

<data>

        <import type="com.example.databindingpractice2.StringUtils" />

        <variable
            name="user"
            type="com.example.databindingpractice2.ObservableUser" />
</data>
		   <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{StringUtils.toUpperCase(user.lastName)}" />

image-20241015204321757

BindingAdapter

有很多属性并非遵循了 javaBean 约定,例如 ImageView 可以通过android:src=“@{userViewModel.head}” 来绑定图片源,但是 ImageView 并没有提供 setSrc() 方法,而设置图片的方法是 setImageDrawable()、setImageURI() 等这些方法;但我们却也可以通过绑定,是因为google为我们提供了许多扩展标记行为的注解,帮助我们完成特殊需求下的匹配绑定,例如 @BindingAdapte可以扩展绑定行为,当然还有其他更丰富的的注解可以组合完成任何双向绑定的需求和复杂的中间过程。

示例

  1. 创建一个 BindingAdapter,将 URL 绑定到 ImageView
public class BindingAdapter {
    @androidx.databinding.BindingAdapter("app:src")
    public static void loadImage(ImageView view, String url) {
        Glide.with(view.getContext())
                .load(url)
                .error(R.drawable.ic_launcher_background)
                .into(view);
    }
}
  1. ImageView 中使用 BindingAdapter
<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

        <variable
            name="user"
            type="com.example.livedatapractice.User" />

        <variable
            name="eventHandle"
            type="com.example.livedatapractice.EventHandleListener" />

        <variable
            name="imageUrl"
            type="String" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        ...

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.097"
            app:src="@{imageUrl}"
            tools:srcCompat="@tools:sample/avatars" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
  1. 在Activity中使用
activityMainBinding.setImageUrl(url);

当 ImageView 控件的 url 属性值发生变化时,dataBinding 就会将 ImageView 实例以及新的 url 值传递给 loadImage() 方法,从而可以在此动态改变 ImageView 的相关属性

双向绑定

BaseObservable

示例

继承BaseObservable类,

get方法使用@Bindable注解,

set方法添加notifyPropertyChanged(BR.firstName)通知绑定的视图(View)数据已经发生了变化,需要更新视图显示的内容

// 被观察者    viewModel
public class ObservableUser extends BaseObservable {
    private String firstName;
    private String lastName;

    public ObservableUser(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Bindable
    public String getFirstName() {
        return firstName;
    }

    @Bindable
    public String getLastName() {
        return lastName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }
}

布局文件中采用@=符号,这里允许EditTextuser.firstName之间进行双向数据同步。

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

        <variable
            name="user"
            type="com.example.databindingpractice2.ObservableUser" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"
            android:textSize="30dp" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}"
            android:textSize="30dp" />

        <EditText
            android:id="@+id/et"
            android:layout_width="186dp"
            android:layout_height="54dp"
            android:text="@={user.lastName}"
            android:textSize="30dp" />

        <EditText
            android:id="@+id/et2"
            android:layout_width="186dp"
            android:layout_height="54dp"
            android:text="@={user.firstName}"
            android:textSize="30dp" />
    </LinearLayout>
</layout>

在主活动中将user对象与视图绑定

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        ObservableUser user = new ObservableUser("Yuyu","Feng");

        binding.setUser(user);
    }
}

ObservableField

与直接继承 Observable 类相比,ObservableField 更适合简单场景下的单一数据绑定,不需要复杂的通知操作,可以理解为官方对 BaseObservable 中字段的注解和刷新等操作的封装。Android 为常见的基本数据类型(如 intboolean 等)提供了对应的 ObservableX 类型,方便开发者使用。

ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble

BaseObservable的示例修改

public class ObservableUser {
    private ObservableField<String> firstName;
    private ObservableField<String>  lastName;

    public ObservableUser(String firstName, String lastName) {
        this.firstName = new ObservableField<>(firstName);
        this.lastName = new ObservableField<>(lastName);
    }

    public ObservableField<String> getFirstName() {
        return firstName;
    }

    public void setFirstName(ObservableField<String> firstName) {
        this.firstName = firstName;
    }

    public ObservableField<String> getLastName() {
        return lastName;
    }

    public void setLastName(ObservableField<String> lastName) {
        this.lastName = lastName;
    }
}


感谢您的阅读
如有错误烦请指正


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

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

相关文章

构建后端为etcd的CoreDNS的容器集群(二)、下载最新的etcd容器镜像

在尝试获取etcd的容器的最新版本镜像时&#xff0c;使用latest作为tag取到的并非最新版本&#xff0c;本文尝试用实际最新版本的版本号进行pull&#xff0c;从而取到想的最新版etcd容器镜像。 一、用latest作为tag尝试下载最新etcd的镜像 1、下载镜像 [rootlocalhost opt]# …

找到连续赢 K 场比赛的第一位玩家

题目链接 找到连续赢 K 场比赛的第一位玩家 题目描述 注意 2 < n < 10^51 < skills[i] < 10^6skills 中的整数互不相同这个比赛的赢家是第一位连续赢下k场比赛的玩家 解答思路 双指针&#xff0c;一个指针maxIdx指向当前技能等级最高的玩家&#xff0c;另一个…

pagehelper 开启分页查询之后为什么total返回有误

场景重现 在controller中 使用了pageHelper 分页之后,巡查结果的确是10个,但是为什么total永远都是10?debug发现 没法获取到原本的total,获取的是list的长度热心网友的回答 网上的原因是:TableDataInfo(list)list的泛型是 T类型,但是Mapper中返回的List的泛型是M看了一…

想让前后端交互更轻松?alovajs了解一下?

作为一个前端开发者&#xff0c;我最近发现了一个超赞的请求库 alovajs&#xff0c;它真的让我眼前一亮&#xff01;说实话&#xff0c;我感觉自己找到了前端开发的新大陆。大家知道&#xff0c;在前端开发中&#xff0c;处理 Client-Server 交互一直是个老大难的问题&#xff…

如何提取视频文件中的音频(.mp4 to .mp3)

1.安装 FFmpeg&#xff08;windows 为例&#xff09; 官网地址 第一步 点击 windows 版 第二步 解压下载好的 .zip文件 第三步 解压之后进入 bin 目录下 第四步 点击导航栏 输入 cmd 回车 第五步 输入指令 ffmpeg -i input_video.mp4 -q:a 0 -map a output_audio.mp3将上面…

算法题总结(十六)—— 动态规划(上)

动态规划 动态规划理论基础 什么是动态规划 动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff0c;如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 所以动态规划中每一个状态一定是由上一个状态推导出来的&#xff…

实战 | 国外攻破大学数据库系统,暴露数千学生记录

实战 | 国外攻破大学数据库系统&#xff0c;暴露数千学生记录 引言 在这篇文章中&#xff0c;我将分享我是如何攻破一个大型大学解决方案门户服务器的&#xff0c;这个服务器服务于许多大学客户&#xff0c;并且涉及数千名学生的数据。 目标 这是一个由印度许多大学和学院使…

没有基础,学习HCIE难吗?

首先要清楚&#xff0c;华为 HCIE-Datacom 认证并非局限于特定专业背景&#xff0c;即便对专业基础有一定要求&#xff0c;无论你有无相关学习经历或者工作经验&#xff0c;皆有机会报考并争取通过这一认证。HCIE-Datacom 考试主要由笔试和实验两部分构成&#xff0c;涉及高级路…

elf加载,动态库加载

elf加载 ELF&#xff08;Executable and Linkable Format&#xff0c;可执行与可链接格。 所以我们写代码生成的可执行文件&#xff0c;以及写的动态库都是elf格式的文件。 我们重点要关注的就是红色框框里面的section节。 而节保存的就有我们的代码段和数据段。所以我们链接…

Redis 性能优化选择:Pika 的配置与使用详解

引言 在我们日常开发中 redis是我们开发业务场景中不可缺少的部分。Redis 凭借其内存存储和快速响应的特点&#xff0c;广泛应用于缓存、消息队列等各种业务场景。然而&#xff0c;随着数据量的不断增长&#xff0c;单节点的 Redis 因为内存限制和并发能力的局限&#xff0c;逐…

ONLYOFFICE文档8.2:开启无缝PDF协作

ONLYOFFICE 开源办公套件的最新版本新增约30个新功能&#xff0c;并修复了超过500处故障。 什么是 ONLYOFFICE 文档 ONLYOFFICE 文档是一套功能强大的文档编辑器&#xff0c;支持编辑处理文档、表格、幻灯片、可填写的表单和PDF。可多人在线协作&#xff0c;支持插件和 AI 集…

论文解读 | ECCV2024 AutoEval-Video:一个用于评估大型视觉-语言模型在开放式视频问答中的自动基准测试...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 陈修元&#xff0c;上海交通大学清源研究院硕士生 概述 总结来说&#xff0c;我们提出了一个新颖且具有挑战性的基准测试AutoEvalVideo&#xff0c;用于全…

红队-安全见闻篇(上)

声明 学习视频来自B站UP主 泷羽sec的个人空间-泷羽sec个人主页-哔哩哔哩视频,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 一.编程与开发 1.后端语言学习 C语⾔&#xff1a;⼀种通⽤的…

NVR小程序接入平台/设备EasyNVR多个NVR同时管理的高效解决方案

在当今的数字化安防时代&#xff0c;视频监控系统的需求日益复杂和多样化。为了满足不同场景下的监控需求&#xff0c;一种高效、灵活且兼容性强的安防视频监控平台——NVR批量管理软件/平台EasyNVR应运而生。本篇探讨这一融合所带来的创新与发展。 一、NVR监测软件/设备EasyNV…

053_python基于深度学习的短视频内容理解与推荐系统

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

如何实现海内外工厂的EDI对接?

企业在国际市场的扩张需要高效的信息流动&#xff0c;以便快速响应客户需求。但国内工厂和海外工厂使用的系统可能存在兼容性问题&#xff0c;且数据格式各不相同&#xff0c;集成过程复杂。 近期国内主营高精度滚动轴承的汽车行业C公司就遇到了这样的挑战&#xff0c;如何实现…

linux查看文件命令

查看文件命令 显示命令 cat 语法&#xff1a;cat 【选项】 文件 选项 命令含义n显示行号包括空行b显示行号不包括空行s压缩空行为一行A显示隐藏字符 cat -n 文件&#xff1a;显示行号包括空行 cat -b 文件 cat -s 文件 cat -A 文件 more和less是 分页查看 tac和rev都…

python实战项目47:Selenium采集百度股市通数据

python实战项目47:Selenium采集百度股市通数据 一、思路分析二、完整代码一、思路分析 这里以获取百度股市通股评下的投票数据为例,页面中的其他数据同理。由于此页面数据是js动态加载的,所以采用Selenium获取数据。思路很简单,通过Selenium打开页面,然后定位到“股评”选…

502 错误码通常出现在什么场景?

服务器过载场景 高流量访问&#xff1a;当网站遇到突发的高流量情况&#xff0c;如热门产品促销活动、新闻热点事件导致网站访问量激增时&#xff0c;服务器可能会因承受过多请求而无法及时响应。例如&#xff0c;电商平台在 “双十一” 等购物节期间&#xff0c;大量用户同时…

Java 多线程(七)—— 定时器

定时器介绍与使用 先简单介绍一下什么是定时器&#xff1a;定时器类似生活中的闹钟&#xff0c;当时间一到&#xff0c;我们就会去做某些事情。 在代码层面理解就是&#xff0c;当我们设置的时间一到&#xff0c;程序就会执行我们固定的代码片段&#xff08;也就是任务&#x…