繁体   English   中英

如何(RxJava)正确设置处理器将处理从Observable发出的数据的条件?

[英]How to (RxJava) properly set conditions on which handler will process data, emitted from Observable?

因此,我的任务是将设备的位置信息(如果更改)推送到远程服务器Json API服务。 如果远程服务器不可用,我的DatabaseManager必须将它们保存到本地数据库。

这是我的Retrofit API:

    public interface GpsService { 
        @POST("/v1/savelocationbatch") 
        SaveResponse saveLocationBatch(@Body LocationBatch locationBatch); 
    }

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(myBaseUrl)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

    GpsService service = retrofit.create(GpsService.class);

和一个POJO类:

    public class LocationBatch{

        @SerializedName("LocationPointList")
        ArrayList<LocationPoint> locationPointList;
        @SerializedName("ClientId")
        String clientId;
        @SerializedName("ClientSecret")
        String clientSecret;

        //setter & getter
    }

我的LocationPoint模型:

    @Table(name="LocationPoints", id = "_id")
    public class LocationPoint extends Model {

        @SerializedName("Latitude")
        @Column(name="latitude")
        public Double latitude;

        @SerializedName("Longitude")
        @Column(name="longitude")
        public Double longitude;

        @SerializedName("Altitude")
        @Column(name="altitude")
        public Double altitude;

        //... setters, getters etc
}

我的所有最后位置都存储在CurrentLocationHolder单例中(用于批量发送/保存到DB /从Observable发出)。 它的setLocation()方法更新currentLocation变量,然后将其放入locationBuffer,而不是检查缓冲区的大小,而不是缓冲区的大小超过我的MAX_BUFFER_SIZE变量,它会触发locationBufferChanged.onNext(使用locationBuffer的副本作为参数),然后清除它locationBuffer ...

    public class CurrentLocationHolder {

        private List<LocationPoint> locationBuffer = 
                            Collections.synchronizedList(new ArrayList<>());
        private LocationPoint currentLocation;
        private final PublishSubject<List<LocationPoint>> locationBufferFull = 
                            PublishSubject.create();

        public Observable<List<LocationPoint>> 
                            observeLocationBufferFull(boolean emitCurrentValue) {
                return emitCurrentValue ?
                    locationBufferFull.startWith(locationBuffer) :
                    locationBufferFull;
            }

        public void setLocation(LocationPoint point) {
            this.currentLocation = point;
            locationBuffer.add(point);
            if (locationBuffer.size() >= MAX_BUFFER_SIZE) {
                locationBufferChanged.onNext(new ArrayList<>(this.locationBuffer));
            }
            locationBuffer.clear();
        }
    }

这是我的DatabaseManager:

    public class DatabaseManager {    
        private Subscription locationBufferSubscription;
        private static DatabaseManager instance;    
        public static void InitInstance() {
            if (instance == null) 
                instance = new DatabaseManager();
            }
        }

        public void saveToDb(ArrayList<LocationPoint> locArray){
            ActiveAndroid.beginTransaction();
            try {
                for (int i = 0; i < locArray.size(); i++) {
                    locArray.get(i).save();
                }
                ActiveAndroid.setTransactionSuccessful();
            } 
            finally {
                ActiveAndroid.endTransaction();
            }
        }
    }

我的应用程序的主要目标:

通过Retrofit将所有已侦听的LocationPoints写入HTTP服务器。 如果远程服务器因某种原因突然停机(或者互联网连接丢失),我的应用程序应该无缝地将新的locationPoints写入本地数据库。 当服务器(或互联网)启动时,某些机制应该将已保存的本地数据提供给Retrofit的呼叫。

所以,我的问题是:

  1. 如何创建一个Rx-Observable对象,它会将List正常发送到Retrofit服务,但是当服务器(或互联网)出现故障时,它应该向DatabaseManager.saveToDb()方法提供未保存的LocationPoints?
  2. 如何捕获互联网连接或服务器“上升”状态? 创建一些Observable是一个好主意,它会ping我的远程服务器,结果应该为它的订阅者发出一些布尔值? 实现此行为的最佳方法是什么?
  3. 当互联网连接(服务器)变为“向上”时,是否有一种简单的方法可以使用本地保存的数据(来自本地数据库)将Retrofit调用排入队列?
  4. 如何不在服务器端丢失任何LocationPoints? (最后我的客户端应用程序必须发送所有这些!
  5. 难道我做错了什么? 我是Android,Java和Java的新手
    特别是对RxJava ......

有趣的任务! 首先:您不需要创建DB来存储这么小的信息。 Android具有存储任何Serializable数据的好地方。 因此,为了保存本地数据包模型,如:

public class MyLocation implements Serializable {

   @Nonnull
   private final String id;
   private final Location location;
   private final boolean isSynced;

   // constructor...
   // getters...
}

单身人士课程:

public class UserPreferences {
    private static final String LOCATIONS = "locations";
    @Nonnull
    private final SharedPreferences preferences;
    @Nonnull
    private final Gson gson;
    private final PublishSubject<Object> locationRefresh = PublishSubject.create();

public void addLocation(MyLocation location) {
    final String json = preferences.getString(LOCATIONS, null);

    final Type type = new TypeToken<List<MyLocation>>() {
    }.getType();

    final List<MyLocation> list;
    if (!Strings.isNullOrEmpty(json)) {
        list = gson.fromJson(json, type);
    } else {
        list = new ArrayList<MyLocation>();
    }
    list.add(lication);

    final String newJson = gson.toJson(set);
    preferences.edit().putString(LOCATIONS, newJson).commit();
    locationRefresh.onNext(null);
}

private List<String> getLocations() {
    final String json = preferences.getString(LOCATIONS, null);

    final Type type = new TypeToken<List<MyLocation>>() {
    }.getType();

    final List<MyLocation> list = new ArrayList<MyLocation>();
    if (!Strings.isNullOrEmpty(json)) {
        list.addAll(gson.<List<MyLocation>>fromJson(json, type));
    }

    return list;
}

@Nonnull
public Observable<List<MyLocation>> getLocationsObservable() {
    return Observable
            .defer(new Func0<Observable<List<MyLocation>>>() {
                @Override
                public Observable<List<MyLocation>> call() {
                    return Observable.just(getLocations())
                            .filter(Functions1.isNotNull());
                }
            })
            .compose(MoreOperators.<List<MyLocation>>refresh(locationRefresh));
}

// also You need to create getLocationsObservable() and getLocations() methods but only for not synced Locations.

}

更改:

public interface GpsService { 
    @POST("/v1/savelocationbatch") 
    Observable<SaveResponse> saveLocationBatch(@Body LocationBatch locationBatch); 
}

现在最有趣的......让一切顺利。 扩展 RxJava。 它有很多“酷工具”(btw,UserPref类中的MoreOperators来自那里),它也有处理改造错误的东西。

因此,假设当Observable saveLocationObservable发出一些东西时,就会发生位置保存。 在这种情况下,您的代码如下所示:

final Observable<ResponseOrError<SaveResponse>> responseOrErrorObservable = saveLocationObservable
        .flatMap(new Func1<MyLocation, Observable<ResponseOrError<SaveResponse>>>() {
            @Override
            public Observable<ResponseOrError<SaveResponse>> call(MyLocation myLocation) {
                final LocationBatch locationBatch = LocationBatch.fromMyLocation(myLocation);  // some method to convert local location to requesr one
                return saveLocationBatch(locationBatch)
                        .observeOn(uiScheduler)
                        .subscribeOn(networkScheduler)
                        .compose(ResponseOrError.<SaveResponse>toResponseOrErrorObservable());
            }

        })
        .replay(1)
        .refCount();
final Observable<Throwable> error = responseOrErrorObservable
     .compose(ResponseOrError.<SaveResponse>onlyError())
     .withLatestFrom(saveLocationObservable, Functions2.<MyLocation>secondParam())
     .subscribe(new Action1<MyLocation>() {
            @Override
            public void call(MyLocation myLocation) {
                   // save location to UserPref with flag isSynced=flase
            }
        });
final Observable<UserInfoResponse> success = responseOrErrorObservable
     .compose(ResponseOrError.<SaveResponse>onlySuccess())
     .subscribe(new Action1<SaveResponse>() {
            @Override
            public void call(SaveResponse response) {
                // save location to UserPref with flag isSynced=true
            }
        });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM