Jetpack全套

news2024/9/22 15:44:32

Jetpack全套

    • 一.Jetpack介绍
      • 1.特性:
      • 2.分类:
    • 二.应用架构
    • 三.LifeCycle:
      • 1.简介
      • 2.简单使用
      • 3.实战:Dialog内存泄漏
      • 4.Lifecycle的应用
        • (0)activity/fragment上面案例都是
        • (1)Service
        • (2)Application:ProcessLifecycleOwner
      • 6.总结
    • 四.VideModel
      • 1.介绍
      • 2.简单使用
      • 3.AndroidViewModel使用
      • 4.使用viewmodel实现fragment直接数据共享
    • 五.LiveData
      • 1.介绍
      • 2.4个常用方法
      • 3.使用场景
      • 4.结合viewModel使用
      • 5.加强练习:
      • 6.结合room使用:后面会用到
    • 五.DataBinding
      • 1.mvvm介绍
      • 2.Data Binding介绍
      • 3.简单使用
      • 4.图片处理
      • 5.重点:recyclerview结合使用
      • 6.其他
        • (1)sub二级标签使用
        • (2)事件监听
        • (3)双向绑定
    • 六.Room数据库
      • 1.介绍
      • 2.优点
      • 3.简单使用CRUD
      • (1)app的gradle配置
      • (2)Student实体类:@Entity
      • (3)StudentDao抽象类:@Dao
      • (4)AppDataBase抽象类:@Database
      • (5)增删改查操作
    • 4.数据库升级
      • 1.异常现象
      • 2.解决办法
      • 3.代码实现
      • 4.Schema文件
      • 5.预填充数据
    • 七.请跳转到:MVVM+JetPack综合使用
    • 八.Hit依赖注入
    • 九.WorkManager
    • 十.Paging

一.Jetpack介绍

在这里插入图片描述
Jetpack是一套库、工具和指南,可以帮助开发者更轻松地编写优质应用。这些组件可以帮助开发者遵循最佳做法、
让开发者摆脱编写样板代码的工作并简化复杂任务,以便开发者将精力集中放在所需的代码上。

1.特性:

  • 加速开发: 组件可以单独采用(不过这些组件是为协同工作而构建的),同时利用Kotlin语言功能帮助开发者提高工作效率。
  • 消除样板代码: Android Jetpack可管理繁琐的Activity(如后台任务、导航和生命周期管理),以便开发者可以专注于如何让自己的应用出类拔萃。
  • 构建高质量的强大应用 Android Jetpack组件围绕现代化设计实践构建而成,具有向后兼容性,可以减少崩溃和内存泄漏。

2.分类:

(1)Architecture(架构组件)可帮助您设计稳健、可测试且易维护的应用

  • Data Binding: 是一种支持库,借助该库,可以以声明方式将可观察数据绑定到界面元素。
  • Lifecycles: 管理Activity 和 Fragment的生命周期,能够帮助开发者轻松的应对Activity/Fragment的生命周期
    变化问题,帮助开发者生成更易于维护的轻量级代码。
  • LiveData: 在底层数据库更改时通知视图,是可观察的数据持有者类。与常规的可观察对象不同,LiveData具
    有生命周期感知功能(例如Activity,Fragment或Service的生命周期)。
  • Navigation: 处理应用内导航所需的一切。
  • Paging: 逐步从您的数据源按需加载信息,帮助开发者一次加载和显示小块数据。按需加载部分数据可减少网
    络带宽和系统资源的使用。
  • Room: 流畅地访问 SQLite 数据库。在SQLite上提供了一个抽象层,以在利用SQLite的全部功能的同时允许更
    健壮的数据库访问。
  • ViewModle: 以注重生命周期的方式管理界面相关的数据。ViewModel类允许数据幸免于配置更改(例如屏幕
    旋转)。通常和DataBinding配合使用,为开发者实现MVVM架构提供了强有力的支持。
  • WorkManager: 管理 Android 的后台作业,即使应用程序退出或设备重新启动,也可以轻松地调度预期将要
    运行的可延迟异步任务。
    (2)Foundation(基础组件)可提供横向功能,例如向后兼容性、测试和 Kotlin 语言支持。
  • Android KTX: 编写更简洁、惯用的 Kotlin 代码,是一组Kotlin扩展程序。优化了供Kotlin使用的Jetpack和
    Android平台API。旨在让开发者利用 Kotlin 语言功能(例如扩展函数/属性、lambda、命名参数和参数默认
    值),以更简洁、更愉悦、更惯用的方式使用 Kotlin 进行 Android 开发。Android KTX 不会向现有的 Android
    API 添加任何新功能。
  • AppCompat: 帮助较低版本的Android系统进行兼容。
  • Auto: 有助于开发 Android Auto 应用的组件。是 Google推出的专为汽车所设计之 Android 功能,旨在取代
    汽车制造商之原生车载系统来执行 Android应用与服务并访问与存取Android手机内容。
  • Benchmark: 从 Android Studio 中快速检测基于 Kotlin 或 Java 的代码。
  • Multidex: 为具有多个 DEX 文件的应用提供支持。
  • Security: 按照安全最佳做法读写加密文件和共享偏好设置。
  • Test: 用于单元和运行时界面测试的 Android 测试框架。
  • TV: 有助于开发 Android TV 应用的组件。
  • Wear OS by Google: 有助于开发 Wear 应用的组件。
    (3)Behavior(行为组件)可帮助您的应用与标准 Android 服务(如通知、权限、分享和 Google 助理)相集成。
  • CameraX: 简化相机应用的开发工作。它提供一致且易于使用的 API 界面,适用于大多数 Android 设备,并可向后兼容至 Android 5.0(API 级别 21)。
  • DownloadManager: 是一项系统服务,可处理长时间运行的HTTP下载。客户端可以请求将URI下载到特定的
    目标文件。下载管理器将在后台进行下载,处理HTTP交互,并在出现故障或在连接更改和系统重新启动后重试下载。
  • Media & playback: 用于媒体播放和路由(包括 Google Cast)的向后兼容 API。 4. Notifications: 提供向后兼容的通知 API,支持 Wear 和 Auto。 5. Permissions: 用于检查和请求应用权限的兼容性 API。 6. Preferences: 创建交互式设置屏幕,建议使用 AndroidX Preference Library 将用户可配置设置集成至应用中。
  • Sharing: 提供适合应用操作栏的共享操作。
  • Slices: 是UI模板,可以通过启用全屏应用程序之外的互动来帮助用户更快地执行任务,即可以创建在应用外
    部显示应用数据的灵活界面。
    (4)UI(界面组件)可提供微件和辅助程序,让您的应用不仅简单易用,还能带来愉悦体验。了解有助于简化界面开发的
    Jetpack Compose。
  • Animation & transitions: 使开发者可以轻松地为两个视图层次结构之间的变化设置动画。该框架通过随时间更改其某些属性值在运行时为视图设置动画。该框架包括用于常见效果的内置动画,并允许开发者创建自定义动画和过渡生命周期回调。
  • Emoji: 使Android设备保持最新的最新emoji表情,开发者的应用程序用户无需等待Android OS更新即可获取最新的表情符号。
  • Fragment: Activity的模块化组成部分。
  • Layout: 定义应用中的界面结构。可以在xml中声明界面元素,也可以在运行时实例化布局元素。
  • Palette: 是一个支持库,可从图像中提取突出的颜色,以帮助开发者创建视觉上引人入胜的应用程序。开发者可以使用调色板库设计布局主题,并将自定义颜色应用于应用程序中的视觉元素。

二.应用架构

在这里插入图片描述

三.LifeCycle:

1.简介

一直以来,解藕都是软件开发永恒的话题。在Android开发中,解藕很大程度上表现为系统组件的生命周期与普通组件之间的解藕,因为普通组件在使用过程中需要依赖系统组件的的生命周期。

举个例子,我们经常需要在页面的onCreate()方法中对组件进行初始化,然后在onStop()中停止组件,或者在onDestory()方法中对进行进行销毁。事实上,这样的工作非常繁琐,会让页面和页面耦合度变高,但又不得不做,因为如果不即时的释放资源,有可能会导致内存泄露。例如,下面是一个在Activity的同生命周期方法中监听调用的例子,代码如下。

public class MyListener {
    private static final String TAG = "MyListener";

    public void start(){
        Log.d(TAG, "start: ");
    }
    public void stop(){
        Log.d(TAG, "stop: ");
    }
    public void resume(){
        Log.d(TAG, "resume: ");
    }
}
public class LifecycleActivity extends AppCompatActivity {
    private MyListener mMyListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycle);
        mMyListener = new MyListener();
        mMyListener.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMyListener.stop();
    }
}

虽然,代码看起来没什么问题,但在实际开发中可能会有多个组件在Activity的生命周期中进行回调,这样Activity的生命周期的方法中可能就需要编写大量的代码,这就使得它们难以维护。 我们希望在对组件进行管理不依赖页面的生命周期的回调方法,同时当页面生命周期发生改变时,也能够即时的收到通知。这在Android组件化和架构设计的时候表现的尤为明显。

那纠结什么是Lifecycle组件呢?总的来说,Lifecycle 就是具有生命周期感知能力的组件。简单的理解就是,当Activity/Fragment的生命周期产生变化时,Lifecycle组件会感应相应的生命周期变化,当然我们还可以通过使用Lifecycle组件来在自定义的类中管理Activity/fragment的生命周期。

目前,Lifecycle生命周期组件主要由Lifecycle、LifecycleOwner、LifecycleObserver三个对象构成。
(1)Lifecycle:是一个持有组件生命周期状态与事件(如Activity或Fragment)的信息的类。
(2)LifecycleOwner:Lifecycle的提供者,通过实现LifecycleOwner接口来访问Lifecycle生命周期对象。
Fragment和FragmentActivity类实现了LifecycleOwner接口,它具有访问生命周期的getLifecycle方法,使用时
需要在自己的类中实现LifecycleOwner。
(3)LifecycleObserver:Lifecycle观察者,可以使用LifecycleOwner类的addObserver()方法进行注册,被注册后
LifecycleObserver便可以观察到LifecycleOwner的生命周期事件。

2.简单使用

(1)依赖:

 def lifecycle_version = "2.5.1"
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")

(2)按照Lifecycle的使用流程,需要先定义观察者,并重写对应的生命周期:设计模式:观察者模式

public class MyObserver implements LifecycleObserver {
    private static final String TAG = "MyObserver";

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void start(){
        Log.d(TAG, "start: ");
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void stop(){
        Log.d(TAG, "stop: ");
    }
}

(3)activity

public class LifecycleActivity2 extends AppCompatActivity {
    private MyObserver mMyObserver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycle2);
        mMyObserver = new MyObserver();
        getLifecycle().addObserver(mMyObserver);
    }
}

3.实战:Dialog内存泄漏

当activity关闭的时候,dialog没有关闭,进而导致内存泄漏,下面使用lifecycle打造一个完美的dialog
(1)自定义dialog并实现LifecycleObserver接口

public class MyDiaLog extends Dialog  implements LifecycleObserver {
    public MyDiaLog(@NonNull Context context) {
        super(context);
    }

    public MyDiaLog(@NonNull Context context, int themeResId) {
        super(context, themeResId);
    }

    protected MyDiaLog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
        super(context, cancelable, cancelListener);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_dialog);
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void destory(){
        dismiss();
    }
}

(2)activity

public class DialogActivity extends AppCompatActivity {
    private MyDiaLog mMyDiaLog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);
        mMyDiaLog = new MyDiaLog(this);
        getLifecycle().addObserver(mMyDiaLog);
       mMyDiaLog.show();
    }
}

4.Lifecycle的应用

  • 使用Lifecycle解藕activity/fragment与组件
  • 使用LifecycleService解藕Service与组件
  • 使用ProcessLifecycleOwner监听Application应用程序生命周期

(0)activity/fragment上面案例都是

(1)Service

举例:一般项项目获得用户的位置需要在Service中监听,当Service被创建的时候需要注册监听,当Service销毁的时候需要解除注册,不然很造成内存泄漏现象(常见的内存泄漏现象可以上网多了解下)

a.自定义LifecycleObserver类,service创建的时候注册监听,service销毁的时候解除注册监听

public class MyLocationObserver implements LifecycleObserver {

    private Context context;
    private LocationManager locationManager;
    private MyLocationListener locationListener;

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

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    private void startGetLocation() {
        Log.d("ning","startGetLocation");
        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        locationListener = new MyLocationListener();
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1, locationListener);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private void stopGetLocation() {
        Log.d("ning","stopGetLocation");
        locationManager.removeUpdates(locationListener);
    }


    static class MyLocationListener implements LocationListener{

        @Override
        public void onLocationChanged(Location location) {
            Log.d("ning","location changed:"+location.toString());
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

        }
    }

}

b。Service:继承LifecycleService

public class MyLocationService extends LifecycleService {

    public MyLocationService() {
        Log.d("ning","MyLocationService");
        MyLocationObserver observer = new MyLocationObserver(this);
        getLifecycle().addObserver(observer);
    }
}

c。activity启动服务和停止服务

public class Step3Activity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_two);
    }
    public void startGps(View view) {
        startService(new Intent(this,MyLocationService.class));
    }

    public void stopGps(View view) {
        stopService(new Intent(this,MyLocationService.class));
    }
}

d。权限以及动态权限,此处省略

(2)Application:ProcessLifecycleOwner

a。自定义LifecycleObserver类

public class ApplicationObserver implements LifecycleObserver {

    private String TAG = "ning";

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onCreate() {
        Log.d(TAG, "Lifecycle.Event.ON_CREATE");
    }


    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onStart() {
        Log.d(TAG, "Lifecycle.Event.ON_START");
    }


    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {
        Log.d(TAG, "Lifecycle.Event.ON_RESUME");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause() {
        Log.d(TAG, "Lifecycle.Event.ON_PAUSE");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onStop() {
        Log.d(TAG, "Lifecycle.Event.ON_STOP");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        Log.d(TAG, "Lifecycle.Event.ON_DESTROY");
    }

}

b。application添加观察者

public class MyApplication extends Application{
    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle()
                .addObserver(new ApplicationObserver());
    }
}

6.总结

在这里插入图片描述

四.VideModel

1.介绍

ViewModel 同样具有生命周期意识的处理跟UI相关的数据,并且,当设备的一些配置信息改变(例如屏幕旋转)它的数据不会消失。
通常情况下,如果我们不做特殊处理,当屏幕旋转的时候,数据会消失,那 ViewModel 管理的数据为什么不会消失呢,是因为 ViewModel 的生命周期:
在这里插入图片描述注意:不要向ViewModel中传入Context,会导致内存泄漏,如果使用Context请使用AndroidViewModel的Appliction

2.简单使用

案例:当屏幕方向发生改变的时候,数据保持不变

(1)自定义ViewModel

public class NumViewModel extends ViewModel {
    public int num;
}

(2)activity实现,并旋转屏幕测试

public class ViewModelActivity extends AppCompatActivity {
    private TextView tv;
    private  NumViewModel numViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model);
        tv = findViewById(R.id.tv);
        //获得viewmodel,页面切换后数据不变
        numViewModel = new ViewModelProvider(this).get(NumViewModel.class);
        tv.setText(numViewModel.num+"");
    }

    public void plus(View view) {
        tv.setText(numViewModel.num+++"");
    }
}

3.AndroidViewModel使用

public class MyAndriodViewModel extends AndroidViewModel {
    public int num;
    public MyAndriodViewModel(@NonNull Application application) {
        super(application);
    }
}
public class AndrodiViewModelActivity extends AppCompatActivity {
    private TextView tv;
    private MyAndriodViewModel mAndroidViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_androdi_view_model);
        tv = findViewById(R.id.tv);
        mAndroidViewModel = new ViewModelProvider(this).get(MyAndriodViewModel.class);
        tv.setText(mAndroidViewModel.num);
    }

    public void plus(View view) {
        tv.setText(mAndroidViewModel.num+++"");
    }
}

4.使用viewmodel实现fragment直接数据共享

public class FragmentViewModel extends ViewModel {
    public int num;
}
public class OneFragment extends Fragment {
    private Button bt;
    private View view;
    private  FragmentViewModel fragmentViewModel;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment_one, container, false);
        bt  = view.findViewById(R.id.tv);
        fragmentViewModel = new ViewModelProvider(getActivity()).get(FragmentViewModel.class);

        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                fragmentViewModel.num++;
                bt.setText(fragmentViewModel.num+"");
            }
        });

        return view;
    }
}
public class TwoFragment extends Fragment {
    private Button bt;
    private View view;
    private  FragmentViewModel fragmentViewModel;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment_two, container, false);
        bt  = view.findViewById(R.id.tv);
        fragmentViewModel = new ViewModelProvider(getActivity()).get(FragmentViewModel.class);

        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                fragmentViewModel.num++;
                bt.setText(fragmentViewModel.num+"");
            }
        });

        return view;
    }
}

五.LiveData

在讲 LiveData 之前,我们先看看 LiveData 和 ViewModel 的作用
在这里插入图片描述

1.介绍

从官网的介绍可以看到, LiveData 作用跟RxJava类似,是观察数据的类,相比RxJava,它能够在Activity、Fragment和Service之中正确的处理生命周期。那么 LiveData 有什么优点呢?
- 数据变更的时候更新UI
- 没有内存泄漏
- 不会因为停止Activity崩溃
- 无需手动处理生命周期
- 共享资源

乍看之下 LiveData 挺鸡肋的,事实也确实如此,因为 LiveData 能够实现的功能 RxJava 也可以实现,而且与LiveData 相比, RxJava 拥有着更加丰富的生态,当然,谷歌的官方架构仍然值得我们去学习。

2.4个常用方法

getValue:获得数据
setValue:主线程设置数据
postValue:子线程设置数据
observe:观察数据的变化

3.使用场景

  • viewModel结合使用:
  • room结合使用:数据库后面讲解

4.结合viewModel使用

(1)自定义ViewModel并定义MutableLiveData变量

public class NumViewModel2 extends ViewModel {
    private MutableLiveData<Integer> num;

    public MutableLiveData<Integer> getNum() {
        if(num == null){
            num = new MutableLiveData<>();
            num.setValue(0);//主线程设置默认值
        }
        return num;
    }
}

(2)activity代码:定时器不断修改数据,liveData观察数据变化并更新UI

public class MainActivity2 extends AppCompatActivity {
    private TextView mTextView;
    private NumViewModel2 mNumViewModel2;
    private  Timer timer = new Timer();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        mTextView = findViewById(R.id.tv);
        mNumViewModel2 = new ViewModelProvider(this).get(NumViewModel2.class);
        initTimer();
        /***************观察LiveData对象变化************/
        mNumViewModel2.getNum().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                mTextView.setText(integer+"");
            }
        });


    }
    //定时器:更新livedata数据
    private void initTimer() {
       timer.schedule(new TimerTask() {
           @Override
           public void run() {
               //子线程设置新的值:postValue
               int num = mNumViewModel2.getNum().getValue()+1;
               mNumViewModel2.getNum().postValue(num);

           }
       },1000,1000);
    }
}

5.加强练习:

2个fragment,第二个fragment进度条发生改变,第一个fragment值实时更新
在这里插入图片描述

6.结合room使用:后面会用到

五.DataBinding

1.mvvm介绍

MVVM(全称Model-View-ViewModel)同 MVC 和 MVP 一样,是逻辑分层解偶的模式

2.Data Binding介绍

Data Binding 不算特别新的东西,2015年Google就推出了,但即便是现在,很多人都没有学习过它,我就是这些工程师中的一位,因为我觉得MVP已经足够帮我处理日常的业务, Android Jetpack 的出现,是我研究 Data Binding 的一个契机。
在进行下文之前,我有必要声明一下,MVVM和Data Binding是两个不同的概念,MVVM是一种架构模式,而Data Binding是一个实现数据和UI绑定的框架,是构建MVVM模式的一个工具

3.简单使用

在这里插入图片描述

(1)app gradle配置

android {
	//....
    dataBinding {
        enabled true
    }
  }

(2)xml布局,在根标签快捷键选择第一个
在这里插入图片描述
自动生成 databinding 布局文件
在这里插入图片描述
databing标签介绍
在这里插入图片描述
(3)User类:

public class User {
    private String username;//用户名
    private int age;//年龄
    private boolean show;//是否显示
    private String imageUrl;//图片
    public User(String username, int age, String imageUrl) {
        this.username = username;
        this.age = age;
        this.imageUrl = imageUrl;
    }
    public boolean isShow() {
        return show;
    }

    public void setShow(boolean show) {
        this.show = show;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public User(String username, int age, boolean show, String imageUrl) {
        this.username = username;
        this.age = age;
        this.show = show;
        this.imageUrl = imageUrl;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

(4)activity布局

<?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.bawei.jetpackforjava.databing.User" />
        <import type="android.view.View"></import>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".databing.DataBingActivityOne">
        <EditText
            android:text="@{user.username}"
            android:id="@+id/et_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></EditText>
        <EditText
            android:text='@{user.age+""}'
            android:id="@+id/et_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></EditText>
        <ImageView
            android:background="@color/black"
            android:visibility="@{user.show ? View.VISIBLE :View.GONE}"
            android:id="@+id/iv"
            android:layout_width="80dp"
            android:layout_height="80dp"></ImageView>
        <Button
            android:id="@+id/bt_login"
            android:text="修改"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></Button>
    </LinearLayout>
</layout>

(5)activity代码:

public class DataBingActivityOne extends AppCompatActivity {
    private ActivityDataBingOneBinding mActivityDataBingOneBinding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_data_bing_one);
        mActivityDataBingOneBinding = DataBindingUtil.setContentView(this,R.layout.activity_data_bing_one);
        User user = new User("张三", 3,true,"");
        mActivityDataBingOneBinding.setUser(user);
    }
}

4.图片处理

思考问题:DataBind中如何显示图片
(1)ImageViewBinderAdapter:图片适配器

public class ImageViewBinderAdapter {
    //加载网络图片
    @BindingAdapter("image")
    public static void setImage(ImageView imageView, String url){
        if(!TextUtils.isEmpty(url)){
            Glide.with(imageView.getContext())//这里上下文建议使用application,避免内存泄漏
                    .load(url)
                    .placeholder(R.drawable.ic_launcher_background)
                    .into(imageView);
        }else{
            imageView.setBackgroundColor(Color.GRAY);
        }
    }

    //加载本地图片
    @BindingAdapter("image")
    public static void setImage(ImageView imageView, int resId){
        imageView.setImageResource(resId);
    }

    //参数可选,网络图片为空时,加载本地图片
    @BindingAdapter(value = {"image", "defaultImageResource"}, requireAll = false)
    public static void setImage(ImageView imageView, String url, int resId){
        if(!TextUtils.isEmpty(url)){
            Glide.with(imageView.getContext())这里上下文建议使用application,避免内存泄漏
                    .load(url)
                    .placeholder(R.drawable.ic_launcher_background)
                    .into(imageView);
        }else{
            imageView.setImageResource(resId);
        }
    }
}

(2)布局文件中设置image属性引用图片地址

 <ImageView
            image="@{user.imageUrl}"
            android:background="@color/black"
            android:visibility="@{user.show ? View.VISIBLE :View.GONE}"
            android:id="@+id/iv"
            android:layout_width="80dp"
            android:layout_height="80dp"></ImageView>

5.重点:recyclerview结合使用

(1)User实体类:

public class User   {
    private String username;//用户名
    private String  caption;//描述
    private String imageUrl;//图片

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getCaption() {
        return caption;
    }
    public void setCaption(String caption) {
        this.caption = caption;
    }
    public String getImageUrl() {
        return imageUrl;
    }
    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
    public User(String username, String caption, String imageUrl) {
        this.username = username;
        this.caption = caption;
        this.imageUrl = imageUrl;
    }
}

(2)item布局:
在这里插入图片描述

<?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.bawei.jetpackforjava.databing_recyclerview.User" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingVertical="10dip">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="100dip"
            android:layout_height="100dip"
            image="@{user.imageUrl}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/guideline2"
            app:layout_constraintHorizontal_bias="0.432"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.054"
            tools:srcCompat="@tools:sample/avatars" />

        <TextView
            android:id="@+id/textViewChName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.username}"
            android:textSize="24sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.138"
            app:layout_constraintStart_toStartOf="@+id/guideline2"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.063"
            tools:text="斯嘉丽.约翰逊" />

        <TextView
            android:id="@+id/textViewEnName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="28dp"
            android:text='@{user.caption}'
            android:textSize="18sp"
            app:layout_constraintStart_toStartOf="@+id/textViewChName"
            app:layout_constraintTop_toBottomOf="@+id/textViewChName"
            tools:text="Scarlett Johansson" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.4" />
    </androidx.constraintlayout.widget.ConstraintLayout>


</layout>

(3)ImageViewBinderAdapter

public class ImageViewBinderAdapter {
    //加载网络图片
    @BindingAdapter("image")
    public static void setImage(ImageView imageView, String url){
        if(!TextUtils.isEmpty(url)){
            Glide.with(imageView.getContext())//这里上下文建议使用application,避免内存泄漏
                    .load(url)
                    .placeholder(R.drawable.ic_launcher_background)
                    .into(imageView);
        }else{
            imageView.setBackgroundColor(Color.GRAY);
        }
    }

    //加载本地图片
    @BindingAdapter("image")
    public static void setImage(ImageView imageView, int resId){
        imageView.setImageResource(resId);
    }

    //参数可选,网络图片为空时,加载本地图片
    @BindingAdapter(value = {"image", "defaultImageResource"}, requireAll = false)
    public static void setImage(ImageView imageView, String url, int resId){
        if(!TextUtils.isEmpty(url)){
            Glide.with(imageView.getContext())这里上下文建议使用application,避免内存泄漏
                    .load(url)
                    .placeholder(R.drawable.ic_launcher_background)
                    .into(imageView);
        }else{
            imageView.setImageResource(resId);
        }
    }
}

(4)适配器

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
    private List<User> mUserList;

    public RecyclerViewAdapter(List<User> userList) {
        mUserList = userList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ItemBinding itemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),R.layout.item,parent,false);
        return new MyViewHolder(itemBinding);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        User user = mUserList.get(position);
        holder.mItemBinding.setUser(user);
    }

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

    class MyViewHolder extends RecyclerView.ViewHolder{
        ItemBinding mItemBinding;
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
        }
        public MyViewHolder(ItemBinding itemBinding) {
            super(itemBinding.getRoot());
            this.mItemBinding = itemBinding;
        }
    }
}

(5)activity

public class RecyclerViewActivity extends AppCompatActivity {
    private ActivityRecyclerViewBinding  viewDataBinding;
    private RecyclerViewAdapter mRecyclerViewAdapter;
    private List<User> list = new ArrayList<>();
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_view);
        initData();
        mRecyclerViewAdapter = new RecyclerViewAdapter(list);
        viewDataBinding.rv.setAdapter(mRecyclerViewAdapter);
        viewDataBinding.rv.setLayoutManager(new LinearLayoutManager(this));
    }
    private void initData() {

        User i1 = new User("斯嘉丽.约翰逊","Scarlett Johansson","https://5b0988e595225.cdn.sohucs.com/images/20190624/d93dbf866aa2405f8b9b1d660c15db9d.jpeg");
        list.add(i1);

        User i2 = new User("安吉丽娜·朱莉","Angelina Jolie","https://5b0988e595225.cdn.sohucs.com/images/20190624/0657ccc0066b4e1797ead2b3293230b0.jpeg");
        list.add(i2);

        User i3 = new User("杰西卡·辛普森","Jessica Simpson","https://5b0988e595225.cdn.sohucs.com/images/20190624/49c95e9b542a4854b2232e67579b9215.jpeg");
        list.add(i3);

        User i4 = new User("萨尔玛·海耶克","Salma Hayek","https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3893590240,2013198505&fm=26&gp=0.jpg");
        list.add(i4);

        User i5 = new User("卡门·伊莱克特拉","Carmen Electra","https://5b0988e595225.cdn.sohucs.com/images/20190624/1399d0fda46c467dbd988f2996dccaad.jpeg");
        list.add(i5);

        User i6 = new User("凯瑟琳·海格尔","Katherine Heigl","https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180116%2F74c81a087a28446590734ca257e3eacf.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1618118692&t=6c5c1eecb1b0db8664810bfc51f8609b");
        list.add(i6);

        User i7 = new User("珍妮佛·安妮斯顿","Jennifer Aniston","https://5b0988e595225.cdn.sohucs.com/images/20190624/98e8b18a86004eb79bef58318e93446d.jpeg");
        list.add(i7);

        User i8 = new User("梅根·福克斯","Megan Fox","https://5b0988e595225.cdn.sohucs.com/images/20190624/37991ee797e6496d99cdee5315082b76.jpeg");
        list.add(i8);

        User i9 = new User("杰西卡·阿尔芭","Jessica Alba","https://5b0988e595225.cdn.sohucs.com/images/20190624/eb49bf15b9634d579ff89f596c54e0ca.jpeg");
        list.add(i9);

        User i10 = new User("詹妮弗·洛芙·休伊特","Jennifer Love Hewitt","https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=791729948,2390587761&fm=26&gp=0.jpg");
        list.add(i10);
    }
}

6.其他

(1)sub二级标签使用

(2)事件监听

(3)双向绑定

在这里插入图片描述

六.Room数据库

1.介绍

Room 是一个持久性库,属于 Android Jetpack 的一部分。Room 是 SQLite 数据库之上的一个抽象层。Room 并不直接使用 SQLite,而是负责简化数据库设置和配置以及与数据库交互方面的琐碎工作。此外,Room 还提供 SQLite 语句的编译时检查
在这里插入图片描述
**数据实体(Entity)**表示应用的数据库中的表。数据实体用于更新表中的行所存储的数据以及创建新行供插入。
数据访问对象 (DAO) 提供应用在数据库中检索、更新、插入和删除数据所用的方法。
**数据库类(DataBase)***持有数据库,并且是应用数据库底层连接的主要访问点。数据库类为应用提供与该数据库关联的 DAO 的实例。

2.优点

使用编译时注解,能够对 @Query 和 @Entity 里面的SQL语句等进行验证。
与SQL语句的使用更加贴近,能够降低学习成本。
对 RxJava 2 的支持(大部分都Android数据库框架都支持),对 LiveData 的支持。
@Embedded 能够减少表的创建。

3.简单使用CRUD

(1)app的gradle配置

依赖:

 def room_version = "2.4.0"

    implementation("androidx.room:room-runtime:$room_version")
    annotationProcessor("androidx.room:room-compiler:$room_version")

    // To use Kotlin annotation processing tool (kapt)
    // To use Kotlin Symbol Processing (KSP)
//    ksp("androidx.room:room-compiler:$room_version")
    // optional - Kotlin Extensions and Coroutines support for Room
    implementation("androidx.room:room-ktx:$room_version")
    // optional - RxJava2 support for Room
    implementation("androidx.room:room-rxjava2:$room_version")
    // optional - Paging 3 Integration
    implementation("androidx.room:room-paging:$room_version")

defaultConfig里面配置:这里注意一定是+=不是= ,后面会介绍

javaCompileOptions {
            annotationProcessorOptions {
                //room的数据库概要、记录
                arguments += ["room.schemaLocation":
                                      "$projectDir/schemas".toString()]
            }
        }

(2)Student实体类:@Entity

@Entity(tableName = "student")
public class Student {
    @PrimaryKey(autoGenerate = true)//主键自增
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    private long id;
    @ColumnInfo(name = "username", typeAffinity = ColumnInfo.TEXT)
    private String username;
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    private int age;
    public Student(long id, String username, int age) {
        this.id = id;
        this.username = username;
        this.age = age;
    }
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }



}

在这里插入图片描述

(3)StudentDao抽象类:@Dao

@Dao
public interface StudentDao {
    //添加一条数据
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(Student student);
    //添加多条数据
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(List<Student> students);
    //删除一条数据
    @Delete
    void delete(Student student);
    //删除多条数据
    @Delete
    void deleteList(List<Student> students);
    //删除所有数据
    @Query("delete from Student")
    void deleteAll();
    //修改数据
    @Update
    void update(Student student);
    //查询所有
    @Query("select * from student")
    List<Student> getAllStudents();
    //根据姓名查找
    @Query("select * from student where username =  :username ")
    List<Student> getStudentByName(String username);
}

(4)AppDataBase抽象类:@Database

/**
 * entities:数据表实体类
 * version:版本号
 * exportSchema:是否导出Schema文件
 */
@Database(entities = {Student.class},version = 2,exportSchema = true)
public abstract class AppDataBase extends RoomDatabase {
    public static final String DB_NAME = "StudentDatabase.db";//数据库名称
    //注意构造不能私有化
    private static AppDataBase appDataBase;
    public static AppDataBase getInstance(){
        if(appDataBase == null){
            synchronized (AppDataBase.class){
                appDataBase = Room.databaseBuilder(App.instance,AppDataBase.class,DB_NAME)
                        .allowMainThreadQueries()数据库耗时操作,默认不能在主线程执行
                        .build();
            }
        }
        return appDataBase;
    }

    private StudentDao studentDao;
    public abstract StudentDao getStudentDao();
}

(5)增删改查操作

public class RoomActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_room);
        List<Student> list = new ArrayList<>();
        Student student1 = new Student(1,"张三1",18);
        Student student2 = new Student(2,"张三2",28);
        Student student3 = new Student(3,"张三3",38);
        Student student4 = new Student(4,"张三4",48);
        list.add(student1);
        list.add(student2);
        list.add(student3);
        list.add(student4);

        AppDataBase.getInstance().getStudentDao().insert(list);
        student2.setUsername("李四");
        AppDataBase.getInstance().getStudentDao().update(student2);
        AppDataBase.getInstance().getStudentDao().delete(student2);
        List<Student> allStudents = AppDataBase.getInstance().getStudentDao().getAllStudents();
        List<Student> lisi = AppDataBase.getInstance().getStudentDao().getStudentByName("李四");
    }
}

4.数据库升级

1.异常现象

如果直接将数据库版本升级到4,却没有写对应的Migration,则会出现IllegalStateException异常 在这里插入图片描述

2.解决办法

第一种,直接添加fallbackToDestructiveMigration方法,该方法在出现异常时,重建数据表同时数据会丢失,不建议
第二种,写出对应的版本升级Migration,Room 会判断当前有没有直接1到4的升级方案,如果有,直接执行1到4的升级方案,如果没有,Room就会按照顺序执行Migration(1,2)、Migration(2,3)、Migration(3,4)已完成升级

3.代码实现

1-2:添加新的字段
2-3:数据销毁以及重建策略
在这里插入图片描述

/**
 * entities:数据表实体类
 * version:版本号
 * exportSchema:是否导出Schema文件
 */
@Database(entities = {Student.class},version = 2,exportSchema = true)
public abstract class AppDataBase extends RoomDatabase {
    public static final String DB_NAME = "StudentDatabase.db";//数据库名称
    //注意构造不能私有化
    private static AppDataBase appDataBase;
    public static AppDataBase getInstance(){
        if(appDataBase == null){
            synchronized (AppDataBase.class){
                appDataBase = Room.databaseBuilder(App.instance,AppDataBase.class,DB_NAME)
                        .allowMainThreadQueries()//数据库耗时操作,默认不能在主线程执行
                        .addMigrations(MIGRATION_1_2)//数据库升级
                        .addMigrations(MIGRATION_2_3)//数据库升级
                      //  .fallbackToDestructiveMigration()//表重新建立,数据销毁,
                        .build();
            }
        }
        return appDataBase;
    }

    private StudentDao studentDao;
    public abstract StudentDao getStudentDao();
    //1->2:增加sex字段
    static final Migration MIGRATION_1_2 = new Migration(1,2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE student ADD COLUMN sex INTEGER NOT NULL DEFAULT 1");
        }
    };
    //2->3:销毁与重建策略
    static final Migration MIGRATION_2_3 = new Migration(2,3) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("CREATE TABLE temp_student (" +
                    "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"+
                    "name TEXT,"+
                    "age INTEGER NOT NULL,"+
                    "sex TEXT DEFAULT 'M',"+
                    "bar_data INTEGER NOT NULL DEFAULT 1)");
            database.execSQL("INSERT INTO temp_student (name,age,sex,bar_data)" +
                    "SELECT name,age,sex,bar_data FROM student");
            database.execSQL("DROP TABLE student");
            database.execSQL("ALTER TABLE temp_student RENAME TO student");
        }
    };
    
}
@Entity(tableName = "student")
public class Student {
    @PrimaryKey(autoGenerate = true)//主键自增
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    private long id;
    @ColumnInfo(name = "username", typeAffinity = ColumnInfo.TEXT)
    private String username;
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    private int age;
    //V2.0
    @ColumnInfo(name = "sex", typeAffinity = ColumnInfo.INTEGER)
    public int sex;
    //V4.0
    /*@ColumnInfo(name = "sex", typeAffinity = ColumnInfo.TEXT)
    public String sex;*/
    public Student(long id, String username, int age) {
        this.id = id;
        this.username = username;
        this.age = age;
    }
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

4.Schema文件

在这里插入图片描述

5.预填充数据

在这里插入图片描述

 public static AppDataBase getInstance(){
        if(appDataBase == null){
            synchronized (AppDataBase.class){
                appDataBase = Room.databaseBuilder(App.instance,AppDataBase.class,DB_NAME)
                        .allowMainThreadQueries()//数据库耗时操作,默认不能在主线程执行
                        .addMigrations(MIGRATION_1_2)//数据库升级
                        .addMigrations(MIGRATION_2_3)//数据库升级
                      //  .fallbackToDestructiveMigration()//表重新建立,数据销毁,
                        .createFromAsset("xxx.db")//从assets目录中预填充数据
                        .createFromFile(new File("xxx.db"))//从sdcard目录中预填充数据
                        .build();
            }
        }
        return appDataBase;
    }

注意:
1.预填充的数据库db文件中的表名和自己创建的表名名称不能一致
2.查看生成的数据库结构需要打开三个文件:https://blog.csdn.net/u014521739/article/details/90672082

七.请跳转到:MVVM+JetPack综合使用

八.Hit依赖注入

九.WorkManager

十.Paging

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【部署Ruoyi微服务】

IP机器与部署组件 1 安装mysql wget https://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm rpm -ivh https://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022systemctl enab…

Mysql列的类型定义——整形类型

文章目录 前言一、整数类型的附带属性 类型名称后面的小括号unsignedauto_increment总结 前言 1&#xff09;采用26字母和0-9的自然数加上下互相 ‘_’ 组成&#xff0c;命名简洁明确&#xff0c;多个单词用下划线 ‘_’ 隔开 2&#xff09;全部小写命名&#xff0c;尽量避免…

R语言混合效应(多水平/层次/嵌套)模型及贝叶斯实现技术应用

回归分析是科学研究中十分重要的数据分析工具。随着现代统计技术发展&#xff0c;回归分析方法得到了极大改进。混合效应模型&#xff08;Mixed effect model&#xff09;&#xff0c;即多水平模&#xff08;Multilevel model&#xff09;/分层模型(Hierarchical Model)/嵌套模…

<IBM DB2>《DB2 进程技术模型》

《DB2 进程技术模型》 1 概念说明2 引擎可分派单元EDU3 多线程体系结果优点4 协调代理程序5 防火墙6 客户机程序7 侦听器8 代理程序9 db2fmp10 db2vend10.1 数据库 EDU10.2 事件监视器线程的标识方式如下&#xff1a;10.3 备份和复原线程的标识方式如下&#xff1a; 11 数据库服…

苹果手机没有声音怎么回事?3分钟解决!

案例&#xff1a;苹果手机听不见声音怎么回事&#xff1f; 【朋友们&#xff0c;苹果手机没有声音&#xff0c;不知道我是不是按错了什么。】 如果你的苹果手机没有声音&#xff0c;可能是由于多种原因导致的。苹果手机没有声音怎么回事&#xff1f;看这里&#xff0c;下面是一…

(转载)简述马尔可夫链

赶紧记录一下&#xff0c;通俗易懂。 参考&#xff1a;https://zhuanlan.zhihu.com/p/448575579 马尔科夫链的思想&#xff1a;过去所有的信息都已经被保存到了现在的状态&#xff0c;基于现在就可以预测未来。(用数学方法就能解释自然变化的一般规律模型) 马尔科夫链为状态空…

ROS学习第二十四节——rosbag

1 rosbag使用_命令行 需求: ROS 内置的乌龟案例并操作&#xff0c;操作过程中使用 rosbag 录制&#xff0c;录制结束后&#xff0c;实现重放 实现: 1.准备 创建目录保存录制的文件 mkdir ./xxx cd xxx2.开始录制 -a:all&#xff0c;录制所有话题消息 -o:out&#xff0c…

领跑行泊一体,纵目科技剑指自动驾驶L2到L4的规模化商业落地机遇

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 2019年&#xff0c;通用、丰田、特斯拉等11家车企承诺自动驾驶时间表&#xff0c;他们大都表示在2020年底实现高级别自动驾驶。以特斯拉为例&#xff0c;其CEO埃隆马斯克曾承诺在2020年实现自动驾驶食言后&#xff0c;随后在…

【工作记录】centos7.5环境下通过源码编译方式部署mysql5.7.25

前言 本文介绍centos7.5环境下通过源码编译安装mysql5.7.25的过程及安装过程中遇到的问题解决。 一、准备工作 # 新建目录 # data 数据 config 配置 boost 引导文件 log 日志文件 mkdir -p /opt/mysql/data /opt/mysql/config /opt/mysql/boost /opt/mysql/log /opt/mysql/ …

Python小姿势 - # 基础数据结构与算法

基础数据结构与算法 Python中基础的数据结构与算法是非常重要的&#xff0c;它们可以帮助我们解决很多实际问题。今天我们就来学习一下Python中的基础数据结构与算法。 首先&#xff0c;我们先来了解一下数据结构。数据结构是一种用来存储、组织、处理数据的方法。它可以帮助我…

理解Java程序的执行

main 方法 public class Solution {public static void main(String[] args) {Person person new Person();person.hello();} }class Person {public void hello() {System.out.println("hello");} }源文件名是 Solution.java&#xff0c;这是因为文件名必须与 pub…

初刷leetcode题目总结 -- 数据结构

魔王的介绍&#xff1a;&#x1f636;‍&#x1f32b;️一名双非本科大一小白。魔王的目标&#xff1a;&#x1f92f;努力赶上周围卷王的脚步。魔王的主页&#xff1a;&#x1f525;&#x1f525;&#x1f525;大魔王.&#x1f525;&#x1f525;&#x1f525; ❤️‍&#x1…

Leetcode38. 外观数列

一、题目描述&#xff1a; 给定一个正整数 n &#xff0c;输出外观数列的第 n 项。 「外观数列」是一个整数序列&#xff0c;从数字 1 开始&#xff0c;序列中的每一项都是对前一项的描述。 你可以将其视作是由递归公式定义的数字字符串序列&#xff1a; countAndSay(1) “…

4月19日第壹简报,星期三,农历闰二月廿九

4月19日第壹简报&#xff0c;星期三&#xff0c;农历闰二月廿九坚持阅读&#xff0c;静待花开1. 国家统计局&#xff1a;一季度GDP同比增长4.5%&#xff0c;当前中国经济没有通缩&#xff0c;下阶段也不会出现通缩&#xff0c;全国城镇调查失业率平均值为5.5%。2. 字节跳动发布…

海格里斯HEGERLS高速穿梭车按需定制|四向穿梭车货架和子母穿梭车货架别傻傻分不清?

随着物流行业和仓储行业的发展&#xff0c;越来越多的企业用户对仓储自动化程度要求越来越高。而近年来&#xff0c;各式各样的穿梭车AGV小车也现身各大物流展&#xff0c;备受各大中小型企业用户的青睐。且为了进一步提高仓库仓储的存储率&#xff0c;越来越多的仓储货架和仓储…

Docker之容器数据卷

Docker之容器数据卷 1. 容器数据卷的概念2. 数据卷的使用2.1 方式一2.2 测试22.3 方式二 3. 安装MySQL4. 具名挂载和匿名挂载4.1 匿名挂载4.2 具名挂载 5. 初识 Dockerfile6. 数据卷-容器之间 1. 容器数据卷的概念 将应用和环境打包成一个镜像数据&#xff1f;如果数据都在容器…

Java | 一分钟掌握JDK命令行工具 | 4 - 可视化分析工具

作者&#xff1a;Mars酱 声明&#xff1a;本文章由Mars酱编写&#xff0c;部分内容来源于网络&#xff0c;如有疑问请联系本人。 转载&#xff1a;欢迎转载&#xff0c;转载前先请联系我&#xff01; 前言 我们其实在分析的时候&#xff0c;也并不是必须使用命令行工具才能可以…

八、vue-基础之列表渲染v-for、v-for中的key属性的作用

一、v-for列表渲染 在真实开发中&#xff0c;我们往往会从服务器拿到一组数据&#xff0c;并且需要对其进行渲染。 这个时候我们可以使用v-for来完成&#xff1b;v-for类似于JavaScript的for循环&#xff0c;可以用于遍历一组数据&#xff1b; 二、v-for基本使用 &#xff0…

Kubeadm方式搭建K8s高可用集群【1.23.0版本】

文章目录 一、集群规划及架构二、系统初始化准备(所有节点同步操作)三、安装kubeadm(所有节点同步操作)四、高可用组件安装及配置1、安装Nginx及配置2、安装keepalived及配置 五、初始化Master集群六、扩容K8S集群1、扩容master节点2、扩容node节点 七、安装网络组件Calico八、…

电脑文件剪切到U盘,为什么不见了?这4个技巧帮你找回丢失文件

文件剪切是指对计算机中的文件进行移动操作&#xff0c;将文件从原先的位置剪切到新的位置&#xff0c;然后将这些文件粘贴到新的位置&#xff0c;以完成文件的整个剪切操作。虽然剪切操作起来并不难&#xff0c;但是近日&#xff0c;有位小伙伴遇到了将电脑文件剪切到u盘后出现…