简要说明
本人接触rxjava、retrofit、realm时间其实也不长,学习的时候也有点疑惑,所以干脆写一个能把几个功能模块融合起来的demo,根据以往做的小demo来看,这个demo非天气预报莫属了,功能很简单,主要是获取数据、保存数据以及数据展示,和第一行代码的天气预报很类似,只是实现差别很大,这里也参考了[ Android 简易版天气预报app的实现](http://blog.csdn.net/new_one_object/article/details/51993822),写这个博客写是希望对正在学习这些框架的同学有一点点帮助。源码地址
项目介绍
首先需要在gradle里面到如下rxjava+retrofit依赖:本人的依赖包如下:
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
。
realm依赖:首先是添加依赖classpath “io.realm:realm-gradle-plugin:2.2.1”,以及添加android配置apply plugin: ‘realm-android’。这里就不贴图了。
获取城市数据
本demo的天气Api是采用的聚合数据的全国天气预报接口,能免费使用500次调用,足够开发用了,只需要注册一个账号,就能拿到聚合数据的key值访问数据了,具体内容在聚合数据里有demo教你怎么访问,这里不做赘述了。这里获取数据流程是,先获取全国各个城市,然后用realm保存到数据库中,再用城市Id获取天气数据信息进行展示,从后端获取的城市json数据格式如下:
{“resultcode”:”200”,”reason”:”successed”,”result”:[{“id”:”1”,”province”:”北京”,”city”:”北京”,”district”:”北京”},{“id”:”2”,”province”:”北京”,”city”:”北京”,”district”:”海淀”}..,],”error_code”:0}
贴出来主要是为了将相应数据封装成javaBean方便在retrofit中直径进行数据转换。由于数据格式是固定code、reson、result、errorCode,而result数据类型会有所不同,所以要将整体返回结果用泛型先封装,下面的JsonResuly是对城市数据result的封装,泛型封装如下:
public class HttpResult<T>{
public int resultcode;
public String reason;
public T result;
public int error_code;
}
返回全国城市数据的泛型T 也就是result封装如下:
`public class JsonResuly {
private Integer id;
private String province;
private String city;
private String district;
public JsonResuly(){}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public JsonResuly(Integer id, String province, String city, String district) {
this.id = id;
this.province = province;
this.city = city;
this.district = district;}
然后编写服务接口,因为rxjava与retrofit结合后,接口返回不再是call,而是 Observable了,这里利用上面封装的数据类型,写一个接口。
public interface WeatherService {
@GET("weather/citys")
//这里的key指的是从聚合数据上注册后拿到的key,必须有这个key才可以访问到数据
Observable<HttpResult<ArrayList<JsonResuly>>> getTopMovie(@Query("key") String key);
}
然后为rxjava封装好一个帮助类,用于获取数据,以及将数据写入到数据库当中,帮助类如下:
public class HttpMethods {
//聚合数据提供的Url
public static final String BASE_URL = UrlUtils.City_Url;
private static final int DEFAULT_TIMEOUT = 5;
private Retrofit retrofit;
private WeatherService movieService;
private Context context;
private static HttpMethods INSTANCE;
private Realm myRealm=null;
//构造方法私有
private HttpMethods(Context context) {
this.context=context;
//手动创建一个OkHttpClient并设置超时时间
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
movieService = retrofit.create(WeatherService.class);
}
//单例模式拿到帮助类实例
public static HttpMethods singleton(Context context)
{
synchronized (HttpMethods.class)
{
if( INSTANCE == null)
{
INSTANCE = new HttpMethods(context);
}
INSTANCE.context = context;
return INSTANCE;
}
}
//realm的初始化,获取到realm对象采可以操作对应的数据库表
public Realm getRealm(Context context)
{
byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
Realm.init(context);
RealmConfiguration config = new RealmConfiguration.Builder()
.name("realmdb.realm") //文件名
.schemaVersion(0) //版本号
.deleteRealmIfMigrationNeeded()//声明版本冲突时自动删除原数据库(当调用了该方法时,上面的方法将失效)。
.build();//创建
return Realm.getInstance(config);
}
//将获取到的数据添加到数据库当中
public void addNote(Realm realm,HttpResult<ArrayList<JsonResuly>> arrayListHttpResult){
City city=null;
realm.beginTransaction();//必须先开启事务
ArrayList<JsonResuly> result=arrayListHttpResult.result;
for(int i=0;i<result.size();i++){
city= new City();
city.setId(i);
city.setCity_name(result.get(i).getCity());
city.setProvince_name(result.get(i).getProvince());
city.setDistrict_name(result.get(i).getDistrict());
city.setCode(result.get(i).getId());
realm.copyToRealmOrUpdate(city);
city=null;
}
realm.commitTransaction();//提交事务
City dog = realm.where(City.class).equalTo("id", 45).findFirst();
}
//为下面跳转界面时获取到对应的下一层数据
public ArrayList<String> getData(){
myRealm=getRealm(context);
RealmResults<City> dogs = myRealm.where(City.class).findAll();
ArrayList<String> cities=new ArrayList<>();
String name=null;
for(City city:dogs){
if(name==null){
name=city.getProvince_name();
cities.add(name);
}else if(!name.equals(city.getProvince_name())){
name=city.getProvince_name();
cities.add(name);
}
}
return cities;
}
//为下面跳转界面时获取到对应的下一层城市数据
public ArrayList<String> getCityData(String province){
myRealm=getRealm(context);
RealmResults<City> dogs = myRealm.where(City.class).equalTo("Province_name", province).findAll();
ArrayList<String> cities=new ArrayList<String>();
String name=null;
for(City city:dogs){
if(name==null){
name=city.getCity_name();
cities.add(name);
}else if(!name.equals(city.getCity_name())){
name=city.getCity_name();
cities.add(name);
}
}
return cities;
}
//为下面跳转界面时获取到对应的下一层市区数据
public ArrayList<String> getDistrcitData(String city){
myRealm=getRealm(context);
RealmResults<City> dogs = myRealm.where(City.class).equalTo("City_name", city).findAll();
ArrayList<String> cities=new ArrayList<String>();
String name=null;
for(City data:dogs){
if(name==null){
name=data.getDistrict_name();
cities.add(name);
}else if(!name.equals(data.getDistrict_name())){
name=data.getDistrict_name();
cities.add(name);
}
}
return cities;
}
public void getTopMovie(Subscriber<HttpResult<ArrayList<JsonResuly>>> subscriber, String key){
//这里并不是数据转换,只是将耗时任务,插入数据库操作写到了该函数中,用于在IO线程中插入数据
movieService.getTopMovie(key).flatMap(new Func1<HttpResult<ArrayList<JsonResuly>>, Observable<HttpResult<ArrayList<JsonResuly>>>>() {
public Observable<HttpResult<ArrayList<JsonResuly>>> call(HttpResult<ArrayList<JsonResuly>> arrayListHttpResult) {
myRealm=getRealm(context);
addNote(myRealm,arrayListHttpResult);
return Observable.just(arrayListHttpResult);
}
})
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
最后在mainActivity中调用方法即可。这里的数据显示是用的Recycleview代码如下:
public class MainActivity extends AppCompatActivity {
private TextView resultTV;
private Subscriber<HttpResult<ArrayList<JsonResuly>>> subscriber;
private Boolean IsFirstStart=false;
private RecyclerView ry_province;
private ArrayList<String> mProvince;
private MyRecyclerAdapter recycleAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resultTV = (TextView) findViewById(R.id.tv_content);
ry_province=(RecyclerView)findViewById(R.id.ry_province);
getMovie();
resultTV.setText("中国");
mProvince= HttpMethods.singleton(getApplicationContext()).getData();
recycleAdapter= new MyRecyclerAdapter(MainActivity.this ,mProvince);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//设置布局管理器
ry_province.setLayoutManager(layoutManager);
//设置为垂直布局,这也是默认的
layoutManager.setOrientation(OrientationHelper.VERTICAL);
ry_province.addItemDecoration(new MyDecoration(this, MyDecoration.VERTICAL_LIST));
//设置Adapter
recycleAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
String province=mProvince.get(position);
Intent intent=new Intent(MainActivity.this, CityActivity.class);
intent.putExtra("province",province);
startActivity(intent);
}
});
ry_province.setAdapter( recycleAdapter);
}
private void getMovie() {
//第一次就从网上获取数据,以后的数据读取都在数据库中拿就可以了
if (IsFirstStart) {
subscriber = new Subscriber<HttpResult<ArrayList<JsonResuly>>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(), "获取数据失败", Toast.LENGTH_LONG).show();
}
@Override
public void onNext(HttpResult<ArrayList<JsonResuly>> arrayListHttpResult) {
resultTV.setText(arrayListHttpResult.result.get(0).getCity());
}
};
HttpMethods.singleton(this).getTopMovie(subscriber, UrlUtils.Key);
IsFirsStart=true;
}
}
}
这里已经将关键获取数据和存入数据查询数据都介绍完了,后面的过程只不过是类似的封装数据、获取数据、展示数据,具体内容在我的github上具体源码地址
最后希望能帮到大家一点点。