天天看點

Android手機應用開發(九) | RxJava(RxAndroid)的簡單使用

實驗目的
  1. 學會使用MediaPlayer
  2. 學習RxJava,使用RxJava更新UI
  3. 學會使用Service進行背景工作
  4. 學會使用Service與Activity進行通信

效果

Android手機應用開發(九) | RxJava(RxAndroid)的簡單使用

RxJava簡介

GitHub位址:

ReactiveX團隊

RxJava

RxAndroid

RxJava

是主體,其實還有

RxAndroid

RxGo

RxPY

RxSwift

等适配

這裡使用的是

RxAndroid

RxJava

RxAndroid并不包括全部的RxJava,而是側重Android的特性進行添加,是以防止缺少依賴庫,還是都得使用

其它代碼環境

這裡使用的是 Android手機應用開發(八) | 制作簡單音樂播放 的大部分代碼

隻是将更新UI的操作從

Handler

更改為

RxJava

注冊服務

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="wang.janking.mymusic">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application
    ...
        <service
            android:name=".MusicService"
            android:exported="true" />
    ...
    </application>

</manifest>
           

建立服務

MusicService.java

package wang.janking.mymusic;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;

public class MusicService extends Service {
    public final IBinder binder = new MyBinder();
    private MediaPlayer mp =  new MediaPlayer();
    private int isFinish = -1;
    private final int PLAY_CODE = 1, STOP_CODE = 2, SEEK_CODE = 3, NEWMUSIC_CODE = 4, CURRENTDURATION_CODE = 5, TOTALDURATION = 6;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        try {
            mp.setDataSource(Environment.getExternalStorageDirectory() + "/data/山高水長.mp3");
            mp.prepare();
            mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    isFinish = 1;
                }
            });
        } catch (IOException e) {
            Log.e("prepare error", "getService: " + e.toString());
        }
        return binder;
    }


    public class MyBinder extends Binder {
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            switch (code) {
                //service solve
                case PLAY_CODE:
                    play_pause();
                    break;
                case STOP_CODE:
                    stop();
                    break;
                case SEEK_CODE:
                    mp.seekTo(data.readInt());
                    break;
                case NEWMUSIC_CODE:
                    newMusic(Uri.parse(data.readString()));
                    reply.writeInt(mp.getDuration());
                case TOTALDURATION:
                    reply.writeInt(mp.getDuration());
                    break;
                case CURRENTDURATION_CODE:
                    reply.writeInt(mp.getCurrentPosition());
                    reply.writeInt(isFinish);
                    break;
            }
            return super.onTransact(code, data, reply, flags);
        }
    }
    public void play_pause() {
        if (mp.isPlaying()) {
            mp.pause();
        } else {
            mp.start();
            isFinish = -1;
        }
    }
    public void stop() {
        if (mp != null) {
            mp.stop();
            try {
                mp.prepare();
                mp.seekTo(0);
            } catch (Exception e) {
                Log.d("stop", "stop: " + e.toString());
            }
        }
    }
    public void newMusic(Uri uri){
            try{
                mp.reset();
                mp.setDataSource(this, uri);
                mp.prepare();
            }
       catch (Exception e){
            Log.d("New Music", "new music: " + e.toString());
        }
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        if(mp!= null){
            mp.reset();
            mp.release();
        }
    }

}
           

布局檔案

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <de.hdodenhof.circleimageview.CircleImageView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/profile_image"
        android:layout_width="match_parent"
        android:layout_height="290dp"
        android:layout_marginTop="30dp"
        android:src="@drawable/img"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/music_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="山高水長"
        android:layout_marginTop="10dp"
        android:textColor="@android:color/black"
        app:layout_constraintTop_toBottomOf="@id/profile_image"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>
    <TextView
        android:id="@+id/music_singer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="中山大學合唱團"
        android:layout_marginTop="3dp"
        app:layout_constraintTop_toBottomOf="@id/music_title"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/playing_status"
        android:orientation="horizontal"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:layout_marginTop="10dp"
        app:layout_constraintTop_toBottomOf="@id/music_singer">
        <TextView
            android:id="@+id/current_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:00"/>

        <SeekBar
            android:id="@+id/seekbar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginEnd="5dp"
            android:layout_marginStart="5dp"
            android:layout_weight="1" />
        <TextView
            android:id="@+id/total_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="99:99"/>
    </LinearLayout>
    <ImageButton
        android:id="@+id/select_music"
        android:src="@mipmap/file"
        android:padding="0dp"
        app:layout_constraintTop_toBottomOf="@id/playing_status"
        android:layout_marginTop="20dp"
        android:layout_marginStart="20dp"
        android:scaleType="fitXY"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_width="40dp"
        android:layout_height="40dp"/>
    <LinearLayout
        app:layout_constraintTop_toBottomOf="@id/playing_status"
        app:layout_constraintStart_toEndOf="@id/select_music"
        android:layout_marginTop="30dp"
        app:layout_constraintEnd_toStartOf="@id/quit"
        android:layout_width="wrap_content"
        android:orientation="horizontal"
        android:layout_height="wrap_content">
        <ImageButton
            android:id="@+id/play_pause"
            android:src="@mipmap/play"
            android:padding="0dp"
            android:scaleType="fitStart"
            android:layout_width="50dp"
            android:layout_marginEnd="30dp"
            android:layout_height="50dp" />
        <ImageButton
            android:id="@+id/stop"
            android:src="@mipmap/stop"
            android:padding="0dp"
            android:scaleType="fitXY"
            android:layout_width="50dp"
            android:layout_height="50dp" />
    </LinearLayout>
    <ImageButton
        android:id="@+id/quit"
        android:src="@mipmap/back"
        android:padding="0dp"
        android:layout_marginTop="20dp"
        app:layout_constraintEnd_toEndOf="parent"
        android:scaleType="fitXY"
        android:layout_marginEnd="20dp"
        app:layout_constraintTop_toBottomOf="@id/playing_status"
        android:layout_width="40dp"
        android:layout_height="40dp" />
</android.support.constraint.ConstraintLayout>
           

代碼檔案

package wang.janking.mymusic;

import android.Manifest;


import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;

import org.reactivestreams.Subscriber;

import java.text.SimpleDateFormat;
import java.util.Date;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.Schedulers;

public class MainActivity extends AppCompatActivity {
    //
    private IBinder mBinder;
    private final int PLAY_CODE = 1, STOP_CODE = 2, SEEK_CODE = 3, NEWMUSIC_CODE = 4, CURRENTDURATION_CODE = 5, TOTALDURATION = 6;
    private int total_duration = 0;
    //讀寫權限
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE};
    //請求狀态碼
    private static int REQUEST_PERMISSION_CODE = 1;
    ImageView imageView;
    SeekBar seekbar;
    ImageButton play_pause, stop, select, quit;
    TextView current_time, total_time, music_title, music_singer;
    boolean isPlay = false;
    boolean isStop = false;
    private SimpleDateFormat time = new SimpleDateFormat("mm:ss");
    private String time_format = "mm:ss";
	
    
    private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            mBinder = service;
            //與服務通信
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                mBinder.transact(TOTALDURATION, data, reply, 0);
            }catch (Exception e){
                Log.d("SERVICE CONNECTION", "onServiceConnected: " + e.toString());
            }
            total_duration = reply.readInt();
            seekbar.setProgress(0);
            seekbar.setMax(total_duration);
            //兩種方法實作毫秒轉時間
            //total_time.setText(time.format(new Date(ms.mp.getDuration())));
            total_time.setText(DateFormat.format(time_format, total_duration));
            current_time.setText(time.format(new Date(0)));
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
    /*public Runnable  myThread = new Runnable() {
        @Override
        public void run() {
            Message msg = handler.obtainMessage();
            if(isStop){
                msg.what = -1;
                handler.sendMessage(msg);
                return;
            }
            try{
                //與服務通信
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                mBinder.transact(CURRENTDURATION_CODE, data, reply, 0);
                msg.arg1 = reply.readInt();
            }catch (Exception e){
                Log.d("Run", "run: " + e.toString());
                return;
            }
            handler.sendMessage(msg);
        }
    };
    @SuppressLint("HandlerLeak")
    private final Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) { // 根據消息類型進行操作
                case -1:
                    handler.removeCallbacks(myThread);
                    seekbar.setProgress(0);
                    current_time.setText(time.format(0));
                    imageView.setRotation(0);
                    break;
                default:
                    if(msg.arg1 >= total_duration)
                        stop.performClick();
                    seekbar.setProgress(msg.arg1);
                    current_time.setText(time.format(new Date(msg.arg1)));
                    imageView.setPivotX(imageView.getWidth()/2);
                    imageView.setPivotY(imageView.getHeight()/2);//支點在圖檔中心
                    imageView.setRotation(msg.arg1/30);
                    handler.postDelayed(myThread, 1);
            }
        }
    };*/
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_PERMISSION_CODE);
        play_pause = findViewById(R.id.play_pause);
        stop = findViewById(R.id.stop);
        select = findViewById(R.id.select_music);
        quit = findViewById(R.id.quit);
        seekbar = findViewById(R.id.seekbar);
        imageView = findViewById(R.id.profile_image);
        current_time = findViewById(R.id.current_time);
        total_time = findViewById(R.id.total_time);
        music_singer = findViewById(R.id.music_singer);
        music_title = findViewById(R.id.music_title);
        imageView.setPivotX(imageView.getWidth()/2);
        imageView.setPivotY(imageView.getHeight()/2);//支點在圖檔中心
        //設定一些UI
        setSomething();
    }
    void setSomething(){
        play_pause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //與服務通信
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                try{
                    mBinder.transact(PLAY_CODE, data, reply, 0);
                }catch (RemoteException e){
                    Log.e("STOP:", "onClick: " + e.toString() );
                }
                if(isPlay){
                    isPlay = false;
                    play_pause.setImageResource(R.mipmap.play);
                }
                else {
                    isPlay = true;
                    isStop = false;
                    play_pause.setImageResource(R.mipmap.pause);
                    //訂閱觀察者
                   //......
                }

            }
        });
        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               isPlay = false;
               isStop = true;
               play_pause.setImageResource(R.mipmap.play);
                seekbar.setProgress(0);
                current_time.setText(time.format(0));
                imageView.setRotation(0);
                //與服務通信
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                try{
                    mBinder.transact(STOP_CODE, data, reply, 0);
                }catch (RemoteException e){
                    Log.e("STOP:", "onClick: " + e.toString() );
                }

            }
        });

        seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if(fromUser){
                    imageView.setPivotX(imageView.getWidth()/2);
                    imageView.setPivotY(imageView.getHeight()/2);//支點在圖檔中心
                    imageView.setRotation(progress/30);
                    current_time.setText(time.format(progress));
                    //與服務通信
                    Parcel data = Parcel.obtain();
                    Parcel reply = Parcel.obtain();
                    data.writeInt(progress);
                    try{
                        mBinder.transact(SEEK_CODE, data, reply, 0);
                    }catch (RemoteException e){
                        Log.e("STOP:", "onClick: " + e.toString() );
                    }
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

        select.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("audio/*");
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                startActivityForResult(intent,1);
            }
        });

        quit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
    @Override
    public void onDestroy(){
        super.onDestroy();
        if(sc != null){
            unbindService(sc);
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION_CODE) {
            Intent intent = new Intent(this, MusicService.class);
            bindService(intent, sc, BIND_AUTO_CREATE);
        }
    }
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (data != null) {
            try{
                //與服務通信
                Parcel send_data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                send_data.writeString(data.getData().toString());
                try{
                    mBinder.transact(NEWMUSIC_CODE, send_data, reply, 0);
                }catch (RemoteException e){
                    Log.e("STOP:", "onClick: " + e.toString() );
                }
                //設定資訊
                total_duration = reply.readInt();
                seekbar.setProgress(0);
                seekbar.setMax(total_duration);
                total_time.setText(DateFormat.format(time_format, total_duration));
                current_time.setText(time.format(new Date(0)));
                //設定歌曲資訊
                MediaMetadataRetriever mmr = new MediaMetadataRetriever();
                mmr.setDataSource(MainActivity.this,data.getData());
                music_title.setText(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE));
                music_singer.setText(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST));
                byte[] picture = mmr.getEmbeddedPicture();
                if(picture.length!=0){
                    Bitmap bitmap = BitmapFactory.decodeByteArray(picture, 0, picture.length);
                    imageView.setImageBitmap(bitmap);
                }
                mmr.release();
                //自動播放
				isPlay = false;
				play_pause.performClick();
				//不自動播放
				//模拟停止
				//stop.performClick();
            }catch (Exception e){
                Log.d("Open file", "onActivityResult: " + e.toString());
            }

        }
        super.onActivityResult(requestCode, resultCode, data);
    }
    //點選傳回鍵
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode==KeyEvent.KEYCODE_BACK){
            moveTaskToBack(true);
            return false;
        }
        return super.onKeyDown(keyCode, event);
    }
}
           

添加依賴

有兩種辦法

  • 直接在

    Build.gradle(Module app)

    裡添加
    //這是截止到目前的最新版
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
    implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
               
  • 下載下傳

    Jar

    包手動添加依賴

    這個我試過,但是從一些小網站上下載下傳的缺少很多東西,而且很多jar包在CSDN上下載下傳要積分!!!知識怎麼能收費呢!

但是直接添加依賴代碼的方法可能會出現下載下傳失敗的情況,因為要經過谷歌的庫……

這樣子需要更改

Build.gradle(Module Project)

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        // maven庫
        def cn = "http://maven.aliyun.com/nexus/content/groups/public/"
        def abroad = "http://central.maven.org/maven2/"
        // 先從url中下載下傳jar若沒有找到,則在artifactUrls中尋找
        maven {
            url cn
            artifactUrls abroad
        }
        maven { url "http://maven.aliyun.com/nexus/content/repositories/jcenter"}
        // 保留google源
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.4'


        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
allprojects {
    repositories {
        // maven庫
        def cn = "http://maven.aliyun.com/nexus/content/groups/public/"
        def abroad = "http://central.maven.org/maven2/"
        // 先從url中下載下傳jar若沒有找到,則在artifactUrls中尋找
        maven {
            url cn
            artifactUrls abroad
        }
        maven { url "http://maven.aliyun.com/nexus/content/repositories/jcenter"}
        // 保留google源
        google()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}
           

添加被觀察者(Observable)

現在新的版本不是簡單添加一個

Observable

就好,而是需要寄放在

CompositeDisposable

裡面

是以在

MainActivity.java

中添加兩個成員變量

//RxJAVA變量
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
private Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
            while (true) {
                //與服務通信
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                try {
                    //擷取資訊
                    mBinder.transact(CURRENTDURATION_CODE, data, reply, 0);
                    //每隔一毫秒查詢一次歌曲的狀态
                    Thread.sleep(1);
                } catch (Exception exception){
                    Log.d("SERVICE CONNECTION", "onServiceConnected: " + exception.toString());
                }
                //讀取目前進度
                observableEmitter.onNext(reply.readInt());
                //reply.readInt() == 1 讀取 isFinish判斷歌曲是否被動停止
                // isStop 判斷歌曲是否主動停止
                if(reply.readInt() == 1 || isStop)
                    break;
            }
            observableEmitter.onComplete();
    }
});
           

這裡其實做的主要操作就是每隔一毫秒讀取歌曲的進度,然後調用

onNext()

讓觀察者更新UI,如果歌曲已經停止就調用

onComplete()

結束觀察過程

因為我想實作歌曲播放完畢自動回到起點,同時UI置位,是以這裡用了很多變量(如,

isFinish

isStop

等)

但是我知道這些變量有備援,沒有精簡,但是功能是沒問題的!

添加觀察者(DisposableObserver)

現在的版本不是叫

Observer

了,而是加了個

Disposable

,表示可處理,就把它當做對事件的具體處理來了解就好了

更改

MainActivity.java

播放按鈕的監聽事件

//觀察者
play_pause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               //...
                if(isPlay){
                    //...
                }
                else {
                    //...
                    //訂閱觀察者
                    DisposableObserver<Integer> disposableObserver = new DisposableObserver<Integer>() {
                        @Override
                        //設定UI
                        public void onNext(Integer integer) {
                            Log.d("onNext", "" + integer);
                            seekbar.setProgress(integer);
                            current_time.setText(time.format(new Date(integer)));
                            imageView.setPivotX(imageView.getWidth()/2);
                            imageView.setPivotY(imageView.getHeight()/2);//支點在圖檔中心
                            imageView.setRotation(integer/30);
                        }
						//模拟點選停止按鍵
                        @Override
                        public void onComplete() {
                            stop.performClick();
                        }

                        @Override
                        public void onError(Throwable e) {
                            Log.d("onError", "" + e.toString());
                        }
                    };
                    //在新線程監聽
                    observable.subscribeOn(Schedulers.newThread())
                        //在主線程更新
                        .observeOn(AndroidSchedulers.mainThread())
                        //綁定
                        .subscribe(disposableObserver);
                    //管理DisposableObserver的容器
                    mCompositeDisposable.add(disposableObserver);
                }

            }
        });
           

為什麼這個觀察者變量不像被觀察者一樣作為一個成員變量呢?

因為它們隻能訂閱一次!

這裡是每次點選播放按鈕就開始播放,并且開始監聽UI改變,然後歌曲播放完畢(或者點選停止按鈕)就調用

onComplete()

方法,那麼這對觀察者和被觀察者的生命也就終止了……

但是重新播放或者選擇新的歌曲的話會報錯

12-03 19:59:08.238 28919-28919/wang.janking.mymusic E/AndroidRuntime: FATAL EXCEPTION: main
    Process: wang.janking.mymusic, PID: 28919
    io.reactivex.exceptions.ProtocolViolationException: It is not allowed to subscribe with a(n) wang.janking.mymusic.MainActivity$1 multiple times. Please create a fresh instance of wang.janking.mymusic.MainActivity$1 and subscribe that to the target source instead.
           

是以要在每次需要監聽給的時候動态建立一個局部變量

disposableObserver

以後再調用的時候就又是一個新的變量了

銷毀

如果

Activity

要被銷毀時,我們的背景任務沒有執行完,那麼就會導緻

Activity

不能正常回收,而對于每一個

Observer

,都會有一個

Disposable

對象用于管理,而

RxJava

提供了一個

CompositeDisposable

類用于管理這些

Disposable

,我們隻需要将其将入到該集合當中,在

Activity

onDestroy

方法中,調用它的

clear

方法,就能避免記憶體洩漏的發生。

修改

onDestroy

方法

@Override
public void onDestroy(){
    super.onDestroy();
    //清除所有的觀察者
    mCompositeDisposable.clear();
    if(sc != null){
        unbindService(sc);
    }
}