当用户信息来自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




