Java平台原生Proxy代理把MVP中普通函数接口切换调度到Android UI主线程
在Android开发者,这种线程切换的需求场景很常见,比如在后台普通的Java线程展开了一项耗时的操作(比如下载一个大文件),下载时候需要实时更新下载进度。普通Java线程不能直接接触Android UI主线程的View,因此在后台获得下载进度后,需要切换到Android UI主线程把数值设置到Android View中。这次线程切换的场景在Android的MVP架构设计中必然成为一个问题,因为在MVP中,当M层数据完成后,经过P层设置到V层,在P层中更新V的状况,必须切换到Android UI主线程。
Java平台原生的Proxy支持这种操作。基本原理是把View层的函数,通过Java的Proxy,调度切换到Android UI主线程。
V层:
package zhangphil.demo;
import android.support.annotation.MainThread;
import android.support.annotation.UiThread;
public interface UiView<T> {
@MainThread
@UiThread
void onProgress(T t);
}
Java Proxy的代理转换:
package zhangphil.demo;
import android.support.v7.app.AppCompatActivity;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class UiHandler implements InvocationHandler {
private Object object;
private AppCompatActivity activity;
public UiHandler(AppCompatActivity activity, Object obj) {
this.object = obj;
this.activity = activity;
}
/**
* 把一个普通的函数包装成运行在Android UI主线程的函数。
*
* @param proxy
* @param method
* @param args
* @return
*/
@Override
public Object invoke(Object proxy, final Method method, final Object[] args) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
Object result = method.invoke(object, args);
} catch (Exception e) {
e.printStackTrace();
}
}
});
//唯一的不足,不支持包装后的UiView接口返回数据。
return null;
}
}
P层
package zhangphil.demo;
import android.support.v7.app.AppCompatActivity;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Presenter {
private Model model;
private UiView viewProxy;
public Presenter(AppCompatActivity activity, UiView v) {
this.model = new Model(this);
this.viewProxy = getUiViewProxy(activity, v);
}
public void startDownload() {
model.download();
}
public void progress(int n) {
viewProxy.onProgress(n);
}
/**
* 传入一个普通的UiView,然后把UiView的接口都转化为可直接在Android UI主线程运行的接口。
*
* @param activity
* @param view
* @return
*/
private UiView getUiViewProxy(AppCompatActivity activity, UiView view) {
InvocationHandler handler = new UiHandler(activity, view);
UiView proxy = (UiView) Proxy.newProxyInstance(view.getClass().getClassLoader(), view.getClass().getInterfaces(), handler);
return proxy;
}
}
M层:
package zhangphil.demo;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class Model {
private Presenter presenter;
public Model(Presenter p) {
this.presenter = p;
}
/**
* 模拟一个在普通Java线程中的耗时下载任务。
*
*/
public void download() {
CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
presenter.progress(i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
}
测试:
package zhangphil.demo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements UiView<Integer> {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
Presenter presenter = new Presenter(this, this);
presenter.startDownload();
}
@Override
public void onProgress(Integer integer) {
textView.setText(String.valueOf(integer));
}
}