第一行代码---Jetpack笔记

1.ViewModel

1.ViewModel的用法

ViewModel的作用:

专门用于存放与界面相关的数据,帮助Activity分担一部分工作;

生命周期与Activity不同,保证在手机屏幕发生旋转的时候不会被重新创建

1.添加依赖

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

2.给MainActivity创建一个对应的MainViewModel类

import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {
    public int counter = 0;
}

3.修改布局文件

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

    <TextView
        android:id="@+id/infoText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="32sp" />

    <Button
        android:id="@+id/plusOneBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Plus One" />

</LinearLayout>

4.修改MainActivity

在Android开发中如果出现android.content.res.Resources$NotFoundException: String resource ID #0x1这样的错误,你想也不用想,一定是Textview控件显示数据出了问题:mTextview.setText(这里的传入的数据一定写成int类型了)。我们需要做的是eg:mTextview.setText(1+""),也就是参数转化成字符串
————————————————
版权声明:本文为CSDN博主「合抱之木,生于毫末,九层之台,起于累土」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014133119/article/details/80989479

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    public MainViewModel mainViewModel;
    private TextView infoText;
    private Button plusOneBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        plusOneBtn = findViewById(R.id.plusOneBtn);
        infoText = findViewById(R.id.infoText);
        mainViewModel = new ViewModelProvider(this).get(MainViewModel.class);
        plusOneBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mainViewModel.counter++;
                refreshCounter();
            }
        });
        refreshCounter();
    }

    private void refreshCounter(){
        //这里一定要传一个String类型的参数,否则启动应用的时候会报错
        infoText.setText(mainViewModel.counter+"");
    }
}

2.向ViewModel传递参数

现在的计数器虽然在屏幕旋转的时候不会丢失数据,但是如果退出程序之后再重新打开,那么之前的计数就会被清零了

请注意:现在按返回键后,onDestroy方法不会执行,所以在onBackPressed方法里手动finish一下活动。Android 12 Ondestroy不执行 - 简书 (jianshu.com)

1.修改MainViewModel

import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {

    public int counter = 0;

    public MainViewModel(int counter) {
        this.counter = counter;
    }
}

2.新建一个MainViewModelFactory类,并让它实现ViewModelProvider.Factory接口

import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

public class MainViewModelFactory implements ViewModelProvider.Factory {

    private final int countReserved;

    public MainViewModelFactory(int countReserved) {
        this.countReserved = countReserved;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        return (T) new MainViewModel(countReserved);
    }
}

3.界面上添加一个清零按钮,方便用户手动将计数器清零

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

    <TextView
        android:id="@+id/infoText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="32sp" />

    <Button
        android:id="@+id/plusOneBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Plus One" />

    <Button
        android:id="@+id/clearBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Clear" />

</LinearLayout>

4.修改MainActivity

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "zhangke";
    public MainViewModel mainViewModel;
    public SharedPreferences sp;
    private TextView infoText;
    private Button plusOneBtn, clearBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate方法");
        setContentView(R.layout.activity_main);
        plusOneBtn = findViewById(R.id.plusOneBtn);
        clearBtn = findViewById(R.id.clearBtn);
        infoText = findViewById(R.id.infoText);
        plusOneBtn.setOnClickListener(this);
        clearBtn.setOnClickListener(this);
        sp = getPreferences(Context.MODE_PRIVATE);
        int countReserved = sp.getInt("count_reserved", 0);
        mainViewModel = new ViewModelProvider(this, new MainViewModelFactory(countReserved)).get(MainViewModel.class);
        refreshCounter();
    }

    private void refreshCounter() {
        infoText.setText(mainViewModel.counter + "");
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.plusOneBtn:
                mainViewModel.counter++;
                Log.d(TAG, mainViewModel.counter + "");
                refreshCounter();
                break;
            case R.id.clearBtn:
                mainViewModel.counter = 0;
                refreshCounter();
                break;
            default:
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy方法");
        SharedPreferences.Editor edit = sp.edit();
        edit.putInt("count_reserved", mainViewModel.counter);
        edit.apply();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        Log.d(TAG, "onBackPressed");
        finish();
    }
}

2.Lifecycle

可以让任何一个类都能轻松感知到Activity的生命周期,同时又不需要在Activity中编写大量的逻辑处理

1.新建一个MyObserver类,并让它实现LifecycleObserver接口

我们在方法上使用了@OnLifecycleEvent注解,并传入了一种生命周期事件。生
命周期事件的类型一共有7种:ON_CREATE、ON_START、ON_RESUME、ON_PAUSE、
ON_STOP和ON_DESTROY分别匹配Activity中相应的生命周期回调;另外还有一种ON_ANY类型,表示可以匹配Activity的任何生命周期回调

import android.util.Log;

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;

public class MyObserver implements LifecycleObserver {

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

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

2.在MainActivity的onCreate方法中添加

首先调用LifecycleOwner的getLifecycle()方法,得到一个Lifecycle对象,然后调用它
的addObserver()方法来观察LifecycleOwner的生命周期,再把MyObserver的实例传进去
就可以了

只要你的Activity是继承自AppCompatActivity的,或者你的Fragment是继承自
androidx.fragment.app.Fragment的,那么它们本身就是一个LifecycleOwner的实例,
这部分工作已经由AndroidX库自动帮我们完成了

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate方法");
        setContentView(R.layout.activity_main);
        ...
        getLifecycle().addObserver(new MyObserver());
    }

目前MyObserver虽然能够感知到Activity的生命周期发生了变化,却没有办法主动获知当前的生命周期状态。

要解决这个问题也不难,只需要在MyObserver的构造函数中将Lifecycle对象传进来即可

3.LiveData

1.基本用法

LiveData是Jetpack提供的一种响应式编程组件,它可以包含任何类型的数据,并在数据发生
变化的时候通知给观察者

 1.修改MainViewModel

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {

    public final MutableLiveData<Integer> counter = new MutableLiveData<Integer>();

    public MainViewModel(int counter) {
        this.counter.setValue(counter);
    }

    public final void plusOne() {
        Integer count = counter.getValue();
        if (count == null) {
            count = 0;
        }
        counter.setValue(count + 1);
    }

    public final void clear() {
        counter.setValue(0);
    }
    
}

 2.修改MainActivity

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "zhangke";
    public MainViewModel mainViewModel;
    public SharedPreferences sp;
    private TextView infoText;
    private Button plusOneBtn, clearBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate方法");
        setContentView(R.layout.activity_main);
        plusOneBtn = findViewById(R.id.plusOneBtn);
        clearBtn = findViewById(R.id.clearBtn);
        infoText = findViewById(R.id.infoText);
        plusOneBtn.setOnClickListener(this);
        clearBtn.setOnClickListener(this);
        sp = getPreferences(Context.MODE_PRIVATE);
        int countReserved = sp.getInt("count_reserved", 0);
        mainViewModel = new ViewModelProvider(this, new MainViewModelFactory(countReserved)).get(MainViewModel.class);
        mainViewModel.counter.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                infoText.setText(integer+"");
            }
        });
        getLifecycle().addObserver(new MyObserver());
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.plusOneBtn:
                mainViewModel.plusOne();
                Log.d(TAG, mainViewModel.counter.getValue() + "");
                break;
            case R.id.clearBtn:
                mainViewModel.clear();
                break;
            default:
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy方法");
        SharedPreferences.Editor edit = sp.edit();
        edit.putInt("count_reserved", mainViewModel.counter.getValue()!=null?mainViewModel.counter.getValue():0);
        edit.apply();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        Log.d(TAG, "onBackPressed");
        finish();
    }
}

MainViewModelFactory和MyObserver不变

计数器功能同样是可以正常工作的。不同的是,现在我们的代码更科学,也更合理,而且不用担心ViewModel的内部会不会开启线程执行耗时逻辑。不过需要注意的是,如果你需要在子线程中给LiveData设置数据,一定要调用postValue()方法,而不能再使用setValue()方法,否则会发生崩溃

 2.需要优化的地方

以上就是LiveData的基本用法。虽说现在的写法可以正常工作,但其实这仍然不是最规范的
LiveData用法,主要的问题就在于我们将counter这个可变的LiveData暴露给了外部。这样
即使是在ViewModel的外面也是可以给counter设置数据的,从而破坏了ViewModel数据的
封装性,同时也可能带来一定的风险。

 1.修改MainViewModel,其实就是将变量声明为private,然后加一个get方法

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {

    private final MutableLiveData<Integer> _counter = new MutableLiveData<Integer>();

    public LiveData<Integer> get_counter() {
        return _counter;
    }

    public MainViewModel(int counter) {
        this._counter.setValue(counter);
    }

    public final void plusOne() {
        Integer count = _counter.getValue();
        if (count == null) {
            count = 0;
        }
        _counter.setValue(count + 1);
    }

    public final void clear() {
        _counter.setValue(0);
    }

}

2.修改MainActivity

将mainViewModel.counter修改为mainViewModel.get_counter()

3.map和switchMap

看map()方法,这个方法的作用是将实际包含数据的LiveData和仅用于观察数据的
LiveData进行转换

 1.创建数据类User

import java.util.Objects;

public class User {
    private String firstName;
    private String lastName;
    private int age;

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

    public String getFirstName() {
        return firstName;
    }

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

    public String getLastName() {
        return lastName;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(firstName, user.firstName) && Objects.equals(lastName, user.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName, age);
    }

    @Override
    public String toString() {
        return "User{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", age=" + age +
                '}';
    }
}

2.修改MainViewModel

import androidx.arch.core.util.Function;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {

    private final MutableLiveData<Integer> _counter = new MutableLiveData<Integer>();

    private final MutableLiveData<User> userLiveData = new MutableLiveData<>();


    public LiveData<Integer> get_counter() {
        return _counter;
    }

    public MainViewModel(int counter) {
        final LiveData<Object> userName = Transformations.map(userLiveData, new Function<User, Object>() {
            @Override
            public Object apply(User input) {
                return input.getFirstName() + input.getLastName();
            }
        });
        this._counter.setValue(counter);
    }

    public final void plusOne() {
        Integer count = _counter.getValue();
        if (count == null) {
            count = 0;
        }
        _counter.setValue(count + 1);
    }

    public final void clear() {
        _counter.setValue(0);
    }

}

前面我们所学的所有内容都有一个前提:LiveData对象的实例都是在ViewModel中创建的。然而在实际的项目中,不可能一直是这种理想情况,很有可能ViewModel中的某个LiveData对象是调用另外的方法获取的。

这个时候,switchMap()方法就可以派上用场了。正如前面所说,它的使用场景非常固定:如
果ViewModel中的某个LiveData对象是调用另外的方法获取的,那么我们就可以借助
switchMap()方法,将这个LiveData对象转换成另外一个可观察的LiveData对象。

 


未完待续

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值