Rxjava+Retrofit+Realm的天气预报

来源:互联网 发布:印刷排版用什么软件 编辑:程序博客网 时间:2024/06/06 02:06

简要说明

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

1 0