Rxjava+Retrofit+Realm的天气预报

简要说明

本人接触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上具体源码地址
最后希望能帮到大家一点点。

猜你喜欢

转载自blog.csdn.net/firesmog/article/details/60471669