You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

当用户信息来自RxJava的BehaviorSubject时,Glide无法显示用户头像

MVP模式下Realm存储用户信息+Glide加载字节数组头像完整方案

嗨,我看你已经搭好了MVP架构的基础流程,还有Glide加载头像的代码片段,那我来帮你补全整个链路的实现逻辑,从登录存储到主页显示全流程打通~

一、先补全你提到的Glide加载字节数组头像核心方法

你给出的代码片段差了最后加载字节数组的关键部分,完整的loadProfileUserImage方法应该是这样的——Glide原生支持直接加载字节数组,无需额外转换:

private void loadProfileUserImage() {
    // 先从Presenter拿到已存储的用户对象
    if (mCurrentUser == null || mCurrentUser.getAvatarBytes() == null) {
        // 无头像时显示占位图
        Glide.with(this)
             .load(R.drawable.shadow)
             .apply(RequestOptions.circleCropTransform())
             .into(mAvatarIv);
        return;
    }

    RequestOptions requestOptions = new RequestOptions()
            .placeholder(R.drawable.shadow)
            .circleCropTransform(); // 圆形裁剪效果

    // Glide直接传入字节数组加载图片
    Glide.with(this)
         .load(mCurrentUser.getAvatarBytes())
         .apply(requestOptions)
         .into(mAvatarIv);
}

二、MVP各层职责划分与完整实现

1. Model层:Realm用户实体与数据操作

首先定义Realm的用户实体类,存储用户信息(含字节数组头像):

public class User extends RealmObject {
    @PrimaryKey
    private String userId;
    private String username;
    private byte[] avatarBytes; // 头像字节数组字段

    // Getter & Setter方法
    public String getUserId() { return userId; }
    public void setUserId(String userId) { this.userId = userId; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public byte[] getAvatarBytes() { return avatarBytes; }
    public void setAvatarBytes(byte[] avatarBytes) { this.avatarBytes = avatarBytes; }
}

然后实现Model层的数据操作类,负责Realm的增删改查:

public class UserModel {
    private Realm mRealm;

    public UserModel() {
        mRealm = Realm.getDefaultInstance();
    }

    // 保存/更新用户信息到Realm
    public void saveUser(User user) {
        mRealm.executeTransaction(realm -> realm.copyToRealmOrUpdate(user));
    }

    // 获取当前登录用户(假设登录后仅存储一个用户,可根据业务调整查询逻辑)
    public User getCurrentUser() {
        return mRealm.where(User.class).findFirst();
    }

    // 关闭Realm实例,避免内存泄漏
    public void closeRealm() {
        if (!mRealm.isClosed()) {
            mRealm.close();
        }
    }
}

2. Presenter层:业务逻辑中转

Presenter作为View和Model的中间层,处理登录后的存储、主页的用户信息获取:

登录Presenter

public class LoginPresenter {
    private LoginView mLoginView;
    private UserModel mUserModel;

    public LoginPresenter(LoginView loginView) {
        mLoginView = loginView;
        mUserModel = new UserModel();
    }

    // 模拟登录流程(实际替换为网络请求)
    public void login(String username, String password) {
        // 这里模拟从服务器获取用户信息(含头像字节数组)
        User user = new User();
        user.setUserId("1001");
        user.setUsername(username);
        // 模拟生成头像字节数组(实际从网络请求返回)
        user.setAvatarBytes(getMockAvatarBytes());

        // 保存到Realm
        mUserModel.saveUser(user);
        mLoginView.onLoginSuccess();
    }

    // 模拟头像字节数组生成(实际场景从服务器获取)
    private byte[] getMockAvatarBytes() {
        try {
            InputStream is = mLoginView.getActivityContext().getResources().openRawResource(R.raw.default_avatar);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    // 生命周期清理
    public void onDestroy() {
        mUserModel.closeRealm();
        mLoginView = null;
    }

    // 登录View接口定义
    public interface LoginView {
        void onLoginSuccess();
        void onLoginFail(String error);
        Context getActivityContext();
    }
}

主页Presenter

public class MainPresenter {
    private MainView mMainView;
    private UserModel mUserModel;

    public MainPresenter(MainView mainView) {
        mMainView = mainView;
        mUserModel = new UserModel();
    }

    // 获取当前登录用户信息
    public void getCurrentUser() {
        User user = mUserModel.getCurrentUser();
        if (user != null) {
            mMainView.onGetUserSuccess(user);
        } else {
            mMainView.onGetUserFail("未找到用户信息");
        }
    }

    // 生命周期清理
    public void onDestroy() {
        mUserModel.closeRealm();
        mMainView = null;
    }

    // 主页View接口定义
    public interface MainView {
        void onGetUserSuccess(User user);
        void onGetUserFail(String error);
        Context getActivityContext();
    }
}

3. View层:Activity实现

LoginActivity

public class LoginActivity extends AppCompatActivity implements LoginPresenter.LoginView {
    private EditText mUsernameEt, mPasswordEt;
    private Button mLoginBtn;
    private LoginPresenter mLoginPresenter;

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

        mUsernameEt = findViewById(R.id.et_username);
        mPasswordEt = findViewById(R.id.et_password);
        mLoginBtn = findViewById(R.id.btn_login);

        mLoginPresenter = new LoginPresenter(this);

        mLoginBtn.setOnClickListener(v -> {
            String username = mUsernameEt.getText().toString().trim();
            String password = mPasswordEt.getText().toString().trim();
            if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
                Toast.makeText(this, "请输入用户名和密码", Toast.LENGTH_SHORT).show();
                return;
            }
            mLoginPresenter.login(username, password);
        });
    }

    @Override
    public void onLoginSuccess() {
        Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
        startActivity(new Intent(this, MainActivity.class));
        finish();
    }

    @Override
    public void onLoginFail(String error) {
        Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
    }

    @Override
    public Context getActivityContext() {
        return this;
    }

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

MainActivity

public class MainActivity extends AppCompatActivity implements MainPresenter.MainView {
    private ImageView mAvatarIv;
    private MainPresenter mMainPresenter;
    private User mCurrentUser;

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

        mAvatarIv = findViewById(R.id.iv_avatar);
        mMainPresenter = new MainPresenter(this);

        // 获取用户信息并加载头像
        mMainPresenter.getCurrentUser();
    }

    @Override
    public void onGetUserSuccess(User user) {
        mCurrentUser = user;
        loadProfileUserImage(); // 调用头像加载方法
    }

    @Override
    public void onGetUserFail(String error) {
        Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
    }

    @Override
    public Context getActivityContext() {
        return this;
    }

    // 补全后的头像加载方法
    private void loadProfileUserImage() {
        if (mCurrentUser == null || mCurrentUser.getAvatarBytes() == null) {
            Glide.with(this)
                 .load(R.drawable.shadow)
                 .apply(RequestOptions.circleCropTransform())
                 .into(mAvatarIv);
            return;
        }

        RequestOptions requestOptions = new RequestOptions()
                .placeholder(R.drawable.shadow)
                .circleCropTransform();

        Glide.with(this)
             .load(mCurrentUser.getAvatarBytes())
             .apply(requestOptions)
             .into(mAvatarIv);
    }

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

三、关键注意事项

  • Realm线程安全:如果在子线程操作Realm,要获取对应线程的Realm实例,避免线程异常。
  • 内存优化:字节数组头像如果过大,会占用较多内存,建议服务器端先压缩再返回,或本地压缩后存储。
  • Glide版本:确保使用Glide 4.x及以上版本,原生支持字节数组加载。
  • MVP生命周期:在Activity的onDestroy中调用Presenter的清理方法,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Cyrus the Great

火山引擎 最新活动