最近学习了github上下载了stylishmusicplayer
rxandroid
rxadnroid 可以用于各个组件之间传递消息的工具 类似于EventBus以及异步处理消息的绝佳框架。
了解axandroid的使用,需要了解观察者(订阅者)、被观察者的含义
被观察者(Observable)
被观察者执行一些操作和提供数据给订阅者,它有以下几个特点:
•每一段Rx代码都从被观察者开始
•被观察者向观察者抛出onNext,onCompleted,onError事件,onNext可以连续多个,但onCompleted/onError只能出现一次,且出现后就结束事件队列
•用一些操作符可以方便地生成Observable对象
观察者(Observe和Subscription)
观察者实现被观察者的回调,得到被观察者传递的数据进而利用线程控制做出相应的界面更新
一般调用Action(无返回值)或Func(又返回值)进行相应操作,例如从网络加载图片(当然这是一个示例,网络上已有很多成熟的图片加载框架):
public void showImage(final String url){
Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super Drawable> subscriber) {
//加载网络图片
Drawable drawable = loadDrawble(url);
subscriber.onNext(drawable);//回调传递图片
subscriber.onCompleted();//完成
}
}).subscribeOn(Schedulers.io())//设置上述被观察者操作在io线程(io线程用于读取文件或访问网络)
.observeOn(AndroidSchedulers.mainThread())//设置下面订阅者在主线程中操作
.subscribe(new Subscriber() {//提交
@Override
public void onCompleted() {
//传递完成
}
@Override
public void onError(Throwable e) {
//出错
Toast.makeText(getContext(),e.getMessage()).show();
}
@Override
public void onNext(Drawable drawable) {
//传递成功
imageView.setImageDrawble(drawable);
}
});
}
那么下面进入正题,利用mvp模式+rxandroid加载本地音乐
mvp模式
interface LocalMusicContract {
//View视图 界面处理
interface View {
//设置代理人
void setPresenter(Presenter presenter);
//获取加载器管理者(v4)
LoaderManager getLoaderManager();
//上下文
Context getContext();
//显示进度条
void showProgress();
//隐藏进度条
void hideProgress();
//没有本地音乐提示
void emptyView(boolean visible);
//出错
void handleError(Throwable error);
//音乐加载完成
void onLocalMusicLoaded(List songs);
}
//Presenter代理人 处理后台数据
interface Presenter {
//加载本地音乐
void loadLocalMusic();
//订阅
void subscribe();
//取消订阅
void unsubscribe();
}
}
通过LocalMusicContract 将View与Presnter联系起来,从而实现回调更新UI
代理人
package io.github.ryanhoo.music.ui.local.all;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import io.github.ryanhoo.music.data.model.Song;
import io.github.ryanhoo.music.data.source.AppRepository;
import io.github.ryanhoo.music.utils.FileUtils;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
import rx.subscriptions.CompositeSubscription;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class LocalMusicPresenter implements LocalMusicContract.Presenter, LoaderManager.LoaderCallbacks {
private static final String TAG = "LocalMusicPresenter";
private static final int URL_LOAD_LOCAL_MUSIC = 0;
private static final Uri MEDIA_URI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
private static final String WHERE = MediaStore.Audio.Media.IS_MUSIC + "=1 AND "
+ MediaStore.Audio.Media.SIZE + ">0";
private static final String ORDER_BY = MediaStore.Audio.Media.DISPLAY_NAME + " ASC";
private static String[] PROJECTIONS = {
MediaStore.Audio.Media.DATA, // the real path
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.MIME_TYPE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.IS_RINGTONE,
MediaStore.Audio.Media.IS_MUSIC,
MediaStore.Audio.Media.IS_NOTIFICATION,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.SIZE
};
//View 界面
private LocalMusicContract.View mView;
//数据仓库 此处封装了liteorm 用于储存音乐数据
private AppRepository mRepository;
//用于统一添加订阅 取消订阅
private CompositeSubscription mSubscriptions;
public LocalMusicPresenter(AppRepository repository, LocalMusicContract.View view) {
mView = view;
mRepository = repository;
mSubscriptions = new CompositeSubscription();
mView.setPresenter(this);
}
@Override
public void subscribe() {
loadLocalMusic();
}
@Override
public void unsubscribe() {
mView = null;
mSubscriptions.clear();
}
/**
* LoaderManager就是加载器的管理器,
* 一个LoaderManager可以管理一个或多个Loader,
* 一个Activity或者Fragment只能有一个LoadManager
*/
@Override
public void loadLocalMusic() {
mView.showProgress();
mView.getLoaderManager().initLoader(URL_LOAD_LOCAL_MUSIC, null, this);
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
if (id != URL_LOAD_LOCAL_MUSIC) return null;
return new CursorLoader(
mView.getContext(),
MEDIA_URI,
PROJECTIONS,
WHERE,
null,
ORDER_BY
);
}
@Override
public void onLoadFinished(Loader loader, Cursor cursor) {
/**
* 订阅
*/
Subscription subscription = Observable.just(cursor)
.flatMap(new Func1>>() {
@Override
public Observable> call(Cursor cursor) {
//被观察者加载本地音乐
List songs = new ArrayList<>();
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
do {
Song song = cursorToMusic(cursor);
songs.add(song);
} while (cursor.moveToNext());
}
//加载完成后发送消息给订阅者
return mRepository.insert(songs);
}
})
.doOnNext(new Action1>() {
@Override
public void call(List songs) {
//排序
Log.d(TAG, "onLoadFinished: " + songs.size());
Collections.sort(songs, new Comparator() {
@Override
public int compare(Song left, Song right) {
return left.getDisplayName().compareTo(right.getDisplayName());
}
});
}
})
.subscribeOn(Schedulers.io())//制定前面被观测者执行线程
.observeOn(AndroidSchedulers.mainThread())//指定后面订阅者线程
.subscribe(new Subscriber>() {
@Override
public void onStart() {
//开始显示进度条
mView.showProgress();
}
@Override
public void onCompleted() {
//完成后隐藏进度条
mView.hideProgress();
}
@Override
public void onError(Throwable throwable) {
mView.hideProgress();
Log.e(TAG, "onError: ", throwable);
}
@Override
public void onNext(List songs) {
//被观察者传递过来的音乐
//通知界面回调 更新adapter
mView.onLocalMusicLoaded(songs);
mView.emptyView(songs.isEmpty());
}
});
mSubscriptions.add(subscription);
}
@Override
public void onLoaderReset(Loader loader) {
// Empty
}
private Song cursorToMusic(Cursor cursor) {
String realPath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
File songFile = new File(realPath);
Song song;
if (songFile.exists()) {
// Using song parsed from file to avoid encoding problems
song = FileUtils.fileToMusic(songFile);
if (song != null) {
return song;
}
}
song = new Song();
song.setTitle(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)));
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME));
if (displayName.endsWith(".mp3")||displayName.endsWith(".amr")) {
displayName = displayName.substring(0, displayName.length() - 4);
}
song.setDisplayName(displayName);
song.setArtist(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)));
song.setAlbum(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)));
song.setPath(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)));
song.setDuration(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)));
song.setSize(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)));
return song;
}
}
AllLocalMusicFragment 实现View接口,通过代理人对界面实现更新
界面
package io.github.ryanhoo.music.ui.local.all;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.ryanhoo.music.R;
import io.github.ryanhoo.music.RxBus;
import io.github.ryanhoo.music.data.model.Song;
import io.github.ryanhoo.music.data.source.AppRepository;
import io.github.ryanhoo.music.event.PlayListUpdatedEvent;
import io.github.ryanhoo.music.event.PlaySongEvent;
import io.github.ryanhoo.music.ui.base.BaseFragment;
import io.github.ryanhoo.music.ui.base.adapter.OnItemClickListener;
import io.github.ryanhoo.music.ui.common.DefaultDividerDecoration;
import io.github.ryanhoo.music.ui.widget.RecyclerViewFastScroller;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import java.util.List;
public class AllLocalMusicFragment extends BaseFragment implements LocalMusicContract.View {
@BindView(R.id.recycler_view)
RecyclerView recyclerView;
@BindView(R.id.fast_scroller)
RecyclerViewFastScroller fastScroller;
@BindView(R.id.progress_bar)
ProgressBar progressBar;
@BindView(R.id.text_view_empty)
View emptyView;
LocalMusicAdapter mAdapter;
LocalMusicContract.Presenter mPresenter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_all_local_music, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
mAdapter = new LocalMusicAdapter(getActivity(), null);
mAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(int position) {
Song song = mAdapter.getItem(position);
/**
* 可以通过rxandroid 进行组件之间传递消息如通知播放音乐界面播放音乐
*/
}
});
recyclerView.setAdapter(mAdapter);
recyclerView.addItemDecoration(new DefaultDividerDecoration());
fastScroller.setRecyclerView(recyclerView);
//新建代理人 并 查询音乐,this实现View接口
new LocalMusicPresenter(AppRepository.getInstance(), this).subscribe();
}
// MVP View
@Override
public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
progressBar.setVisibility(View.GONE);
}
@Override
public void emptyView(boolean visible) {
emptyView.setVisibility(visible ? View.VISIBLE : View.GONE);
fastScroller.setVisibility(visible ? View.GONE : View.VISIBLE);
}
@Override
public void handleError(Throwable error) {
Toast.makeText(getActivity(), error.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onLocalMusicLoaded(List songs) {
//presenter 传递数据 更新适配器
mAdapter.setData(songs);
mAdapter.notifyDataSetChanged();
}
@Override
public void setPresenter(LocalMusicContract.Presenter presenter) {
//设置代理人
mPresenter = presenter;
}
}
RxAndroid是一个开发库、是一种代码风格、也是一种思维方式。
RxAndroid的特点是简洁、优雅、高效,它的优点是多线程切换简单、
数据变换容易、代码简洁可读性好、第三方支持丰富易于开发;
缺点是学习成本较高、出错难以排查。