android组件化登录的思考

登录问题思考

很久没有写文章了,因为一些个人的原因,但是现在一切问题都解决了。1年前写了一篇android组件化的学习,里面提到了commonbusiness里面会包含登录模块,但是后来经过实践表明这是一个不理想的决定,因为在各个模块当然运行的时候大多数的功能都是会依赖登录模块的,这样会造成各个模块都要登录。
现在的做法是把登录模块单独提取出来,也能单独运行, 登录模块运行之后,登录成功之后其它模块的app也能感知到登录成功之后的用户数据。

运用内容提供者进行app之间通讯

登录模块定义一个UserContentProvider代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class UserContentProvider extends DaggerContentProvider {
private static final int SUCCESS = 1;
private final String[] columnNames = new String[]{AppConstats.TOKEN, AppConstats.UID};
static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
mUriMatcher.addURI("com.modularization.android.login", "user", SUCCESS); //uri规则可自己定义,但一定和清单文件一直
}
@Inject
RxSharedPreferences mRxSharedPreferences;
@Override
public boolean onCreate() {
return super.onCreate();
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
String token = mRxSharedPreferences.getString(AppConstats.TOKEN).get();
String uId = mRxSharedPreferences.getString(AppConstats.UID).get();
MatrixCursor cursor = new MatrixCursor(columnNames);
cursor.addRow(new String[] {token, uId});
return cursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}

我这里简单的使用了RxSharedPreferences去保存登录成功之后的数据,提供了query方法。

登录的伪代码

1
2
3
4
5
6
7
String token = UUID.randomUUID().toString();
String uId = "123456";
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(AppConstats.TOKEN, token);
editor.putString(AppConstats.UID, uId);
editor.apply();
getContentResolver().notifyChange(Uri.parse(AppConstats.USER_URI), null);

getContentResolver().notifyChange()方法来通知其它模块app变化了。

因为在commonbusiness的UserSystem类,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Singleton
public class UserSystem {
private static final String KEY_TOKEN = "token";
private static final String KEY_USER_ID = "uid";
private BehaviorSubject<TokenEntity> mTokenEntityBehaviorSubject = BehaviorSubject.create();
private Context mContext;
@Inject
public UserSystem(Context context) {
this.mContext = context;
loadTokenEntity();
context.getContentResolver().registerContentObserver(Uri.parse(AppConstats.USER_URI), false, new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
loadTokenEntity();
}
});
}
private void loadTokenEntity() {
ContentResolver resolver = mContext.getContentResolver();
Uri uri = Uri.parse(AppConstats.USER_URI);
Cursor cursor = resolver.query(uri, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String token = cursor.getString(0);
String uId = cursor.getString(1);
TokenEntity entity = new TokenEntity(uId, token);
mTokenEntityBehaviorSubject.onNext(entity);
}
cursor.close();
} else {
ToastUtils.show(mContext, "请安装登录模块");
}
}
public Observable<TokenEntity> getTokenEntityObservable() {
return mTokenEntityBehaviorSubject;
}
}

registerContentObserver方法能监听到数据的变化,再去从新获取数据这样在登录模块单独运行的时候,登录成功其它模块的app也能感知得到变化,这样就只要登录一次就可以了。

其它思考

因为每个app接口要是你没有登录你都会返回一个错误码,全局app都有个错误处理的方法,我的如下

1
2
3
4
5
6
7
8
9
10
11
public class ResponseListenerImpl implements ResponseErrorListener {
@Override
public void handleResponseError(Context context, Throwable t) {
Timber.e(t);
if (t instanceof ResponseException) {
}
final String msg = ErrorMessageFactory.create(context, (Exception) t);
ToastUtils.show(context, msg);
}
}

在这里面你就可以获取那个code,然后采用android:scheme 通过uri跳转到登录模块的LoginActivity,这样就算各个模块单独运行也会让你感觉在一个app之内一个,比如A模块app服务端返回要从新登录的code,这样跳转到Login模块的app中走完登录流程,采用内容提供者来通知A模块我登录完成了,A模块获取用户数据,再进行需要登录之后的操作,代码在我的github里面的ModularizationExample项目