数据预取小轮子

在Android开发中,预取数据可以说是一个比较通用的功能,通过预取数据可以有效的减少用户等待的时间,提升用户体验,是一种常见的以空间换取时间的策略。虽然说预取数据这个功能比较好实现,但一直没有发现一个好用的开源库实现,于是自己花了点时间写了一个,尽量做到简单优雅、轻量和侵入性低

Prefetch

一个通用的数据预取库

  • 简单优雅、轻量和侵入性低
  • 如果之前使用RxJava,那么完全可以重用之前的数据加载逻辑

预览

Preview

Gradle依赖

1
implementation 'com.tubb:prefetch:1.1.0'

如何使用

  • 先要继承 FetchTask 类, 定义好数据加载任务,可以看到execute()方法返回的是RxJava中的Obserable类型,默认的执行线程为Schedulers.io(),执行结果通知线程为AndroidSchedulers.mainThread(),当然这两者都可以通过覆写相应的方法来指定
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class UserInfoFetchTask extends FetchTask<UserInfo> {
    @Override
    public Observable<UserInfo> execute() {
    return Observable.create(new ObservableOnSubscribe<UserInfo>() {
    @Override
    public void subscribe(ObservableEmitter<UserInfo> emitter) throws Exception {
    // just for test
    Thread.sleep(5000);
    UserInfo userInfo = new UserInfo();
    userInfo.name = "BingBing";
    emitter.onNext(userInfo);
    }
    });
    }
    }
1
2
3
4
5
6
7
protected Scheduler subscribeOnScheduler() {
return SchedulerProvider.io();
}

protected Scheduler observeOnScheduler() {
return SchedulerProvider.ui();
}
  • 然后就可以执行定义好的任务,框架会帮你生成一个taskId,作为任务的唯一标识,后续注册数据加载结果监听器要指定对应的taskId

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class MainActivity extends AppCompatActivity {
    private long taskId;

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

    @Override
    protected void onResume() {
    super.onResume();
    // prefetch user info data
    taskId = Prefetch.instance().executeTask(new UserInfoFetchTask());
    }

    public void viewClick(View view) {
    Intent intent = new Intent(this, UserInfoActivity.class);
    intent.putExtra("taskId", taskId);
    startActivity(intent);
    }
    }
  • 注册数据加载结果监听器,拿到预取到的数据

    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
    public class UserInfoActivity extends AppCompatActivity implements FetchTask.Listener<UserInfo> {
    private static final String TAG = "Prefetch";
    private long taskId;
    private TextView tv_user_name;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_user_info);
    tv_user_name = findViewById(R.id.tv_user_name);
    taskId = getIntent().getLongExtra("taskId", 0L);
    }

    @Override
    protected void onResume() {
    super.onResume();
    Prefetch.instance().registerListener(taskId, this);
    }

    @Override
    protected void onPause() {
    super.onPause();
    Prefetch.instance().unregisterListener(taskId);
    }

    @Override
    public void onExecuting() {
    tv_user_name.setText(String.format("%s task executing", taskId));
    }

    @Override
    public void onSuccess(UserInfo userInfo) {
    tv_user_name.setText(userInfo.name);
    Prefetch.instance().finishTask(taskId);
    }

    @Override
    public void onError(Throwable throwable) {
    tv_user_name.setText(String.format("%s task error", taskId));
    Log.e(TAG, taskId + " task", throwable);
    Prefetch.instance().finishTask(taskId);
    }
    }

Custom

可以通过PrefetchConfig 来配置TaskIdGeneratorTaskExecutor,实现自己特殊的业务场景

1
2
3
4
5
6
7
8
9
10
11
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
// init Prefetch
Prefetch.instance().init(new PrefetchConfig.Builder()
.taskIdGenerator(new UUIDTaskIdGenerator())
.taskExecutor(new TestTaskExecutor())
.build());
}
}

注意事项

  • 一个预取任务只能执行一次,如果想重用,必须结束已经执行过的预取任务

    1
    Prefetch.instance().finishTask(taskId);
  • 必须正确的注销数据加载结果监听器,否则会导致内存泄漏,可能会泄漏Activity,或其他监听器对象持有者实例

    1
    Prefetch.instance().unregisterListener(taskId);