1. 程式人生 > >Android-第一行程式碼CoolWeather案例實戰

Android-第一行程式碼CoolWeather案例實戰

                                        CoolWeather仿寫

環境資訊

           AndroidStudio 3.2      JDK1.8      執行與Android7.1 (專案本身不需要申請許可權)

準備工作

           1 新建專案     需要新增的四個庫(均是到目前為止最新的 庫  新增到build.gradle )

    implementation 'com.squareup.okhttp3:okhttp:3.11.0'
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.github.bumptech.glide:glide:4.8.0'
    implementation 'org.litepal.android:core:2.0.0'

                郵箱免費註冊➡ 登陸➡控制檯

專案編寫

                     1  定義各種包和檔案

                     2   編寫dbsql 下的 省市縣 實體類 並 配置 litepal庫

                  3   設定 litepal.xml 對映檔案 並編寫 Util包下 網路請求 方法

                  4  Util包下新建Utility類 解析和儲存網路獲取的資料 

                              (因為上面圖片都可以看完程式碼,所以不再附上程式碼)

                      (這裡的程式碼較多 ,圖片顯示不完全,所以下面會附上程式碼)

public class Utility {
    //解析和處理伺服器返回的省級資料
    public static boolean handleProvinceResponse(String response) {
        if (!TextUtils.isEmpty(response)) {
            try {
                JSONArray allProvinces = new JSONArray(response);
                for (int i = 0; i < allProvinces.length(); i++) {
                    JSONObject provinceObject = allProvinces.getJSONObject(i);
                    Province province = new Province();
                    province.setProvinceName(provinceObject.getString("name"));
                    province.setProvinceCode(provinceObject.getInt("id"));
                    province.save();
                }
                return true;
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
    //解析和處理伺服器返回的市級資料
    public static boolean handleCityResponse(String response, int provinceId) {
        if (!TextUtils.isEmpty(response)) {
            try {
                JSONArray allCities = new JSONArray(response);
                for (int i = 0; i < allCities.length(); i++) {
                    JSONObject cityObject = allCities.getJSONObject(i);
                    City city = new City();
                    city.setCityName(cityObject.getString("name"));
                    city.setCityCode(cityObject.getInt("id"));
                    city.setProvinceId(provinceId);
                    city.save();
                }
                return true;
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
    //解析和處理伺服器返回的縣級資料
    public static boolean handleCountyResponse(String response, int cityId) {
        if (!TextUtils.isEmpty(response)) {
            try {
                JSONArray allCounties = new JSONArray(response);
                for (int i = 0; i < allCounties.length(); i++) {
                    JSONObject countyObject = allCounties.getJSONObject(i);
                    County county = new County();
                    county.setCountyName(countyObject.getString("name"));
                    county.setWeatherId(countyObject.getString("weather_id"));
                    county.setCityId(cityId);
                    county.save();
                }
                return true;
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
   

}

                        5   下面就開始 編寫介面了 Choose_area.xml

                           6  使用碎片載入佈局

public class ChooseAreaFragment extends Fragment {
    public static final int LEVEL_PROVINCE = 0;
    public static final int LEVEL_CITY = 1;
    public static final int LEVEL_COUNTY = 2;
    private ProgressDialog progressDialog;
    private ArrayAdapter<String> adapter;
    private List<String> dataList = new ArrayList<>();
    //省列表
    private List<Province> provinceList;
    //市列表
    private List<City> cityList;
    //縣列表
    private List<County> countyList;
    //選中的省份
    private Province selectedProvince;
    //選中的城市
    private City selectedCity;
    //當前選中的級別
    private int currentLevel;

    private TextView titleText;
    private Button backButton;
    private ListView listView;
    private View view;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.choose_area, container, false);
        initView();
        adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, dataList);
        listView.setAdapter(adapter);
        return view;

    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
        //需要寫上這句話,來建立資料庫,要不然會報空指標
        Connector.getDatabase();
       //列表監聽器
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {          
             //如果選擇的是省
                if (currentLevel == LEVEL_PROVINCE) {
                    selectedProvince = provinceList.get(position);
                    queryCities();
                    //如果選擇的是市
                } else if (currentLevel == LEVEL_CITY) {
                    selectedCity = cityList.get(position);
                    queryCounties();
                } 
            }
        });
        //回退按鈕監聽器
        backButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (currentLevel == LEVEL_COUNTY) {
                    queryCities();
                } else if (currentLevel == LEVEL_CITY) {
                    queryProvinces();
                }
            }
        });
        //呼叫 下面的方法
        queryProvinces();
    }

    // 查詢全國所有的省,優先從資料庫查詢,如果沒有查詢到再去伺服器上查詢
    private void queryProvinces() {
        //設定 顯示標題為中國
        titleText.setText("中國");
        //設定回退按鈕隱藏
        backButton.setVisibility(View.GONE);
        //查詢 省實體類的資料 
        provinceList = LitePal.findAll(Province.class);
        //判斷是不是有資料 如果有
        if (provinceList.size() > 0) {
            //把要顯示在listview的資料列表清空
            dataList.clear();
            //遍歷省資料
            for (Province province : provinceList) {
                //把資料加到 要顯示在listview的資料列表 上
                dataList.add(province.getProvinceName());
            }
            //通知資料更新
            adapter.notifyDataSetChanged();
            //設定listview 預設選擇第一個
            listView.setSelection(0);
            // flag  設定為省    為回退做flag
            currentLevel = LEVEL_PROVINCE;
            //如果沒有資料
        } else {
            //去伺服器獲取資料
            String address = "http://guolin.tech/api/china";
            queryFromServier(address, "province");
        }
    }

    //查詢全國所有的市,優先從資料庫查詢,如果沒有查詢到再去伺服器上查詢
    private void queryCities() {
        titleText.setText(selectedProvince.getProvinceName());
        backButton.setVisibility(View.VISIBLE);
        cityList = LitePal.where("provinceid=?", String.valueOf(selectedProvince.getId())).find(City.class);
        if (cityList.size() > 0) {
            dataList.clear();
            for (City city : cityList) {
                dataList.add(city.getCityName());
            }
            adapter.notifyDataSetChanged();

            listView.setSelection(0);
            currentLevel = LEVEL_CITY;

        } else {
            int provinceCode = selectedProvince.getProvinceCode();
            String address = "http://guolin.tech/api/china/" + provinceCode;
            queryFromServier(address, "city");
        }
    }

    // 查詢全國所有的縣,優先從資料庫查詢,如果沒有查詢到再去伺服器上查詢
    private void queryCounties() {
        titleText.setText(selectedCity.getCityName());
        backButton.setVisibility(View.VISIBLE);
        countyList = LitePal.where("cityid=?", String.valueOf(selectedCity.getId())).find(County.class);
        if (countyList.size() > 0) {
            dataList.clear();
            for (County county : countyList) {
                dataList.add(county.getCountyName());
            }
            adapter.notifyDataSetChanged();
            listView.setSelection(0);
            currentLevel = LEVEL_COUNTY;
        } else {
            int cityCode = selectedCity.getCityCode();
            int provinceCode = selectedProvince.getProvinceCode();
            String address = "http://guolin.tech/api/china/" + provinceCode + "/" + cityCode;
            queryFromServier(address, "county");
        }
    }

    // 根據傳入的地址和型別 從伺服器查詢省市縣資訊資料
    private void queryFromServier(String address, final String type) {
        //顯示對話方塊
        showProgressDialog();
        HttpUtil.sendOkHttpRequest(address, new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //把獲取到的資料設定為字串
                String responseText = response.body().string();
                //設定結果
                boolean result = false;
                
                if ("province".equals(type)) {
                    //返回的省資料處理結果
                    result = Utility.handleProvinceResponse(responseText);
                } else if ("city".equals(type)) {
                 //返回的市資料處理結果
                    result = Utility.handleCityResponse(responseText, selectedProvince.getId());
                } else if ("county".equals(type)) {
                          //返回的縣資料處理結果
                    result = Utility.handleCountyResponse(responseText, selectedCity.getId());
                }
                //根據返回的資料結果更新資料
                if (result) {
                    //獲取主執行緒(UI必須在主執行緒更新)
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            closeProgressDialog();
                            if ("province".equals(type)) {
                                queryProvinces();
                            } else if ("city".equals(type)) {
                                queryCities();
                            } else if ("county".equals(type)) {
                                queryCounties();
                            }
                        }
                    });
                }


            }

            @Override
            public void onFailure(Call call, IOException e) {
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        closeProgressDialog();
                        Toast.makeText(getContext(), "載入失敗", Toast.LENGTH_LONG).show();
                    }
                });
            }
        });
    }

    private void showProgressDialog() {
        if (progressDialog == null) {
            progressDialog = new ProgressDialog(getActivity());
            progressDialog.setMessage("正在載入...");
            progressDialog.setCanceledOnTouchOutside(false);
        }
        progressDialog.show();
    }

    private void closeProgressDialog() {
        if (progressDialog != null) {
            progressDialog.dismiss();
        }
    }

    private void initView() {
        titleText = view.findViewById(R.id.title_text);
        backButton = view.findViewById(R.id.back_button);
        listView = view.findViewById(R.id.list_view);
    }


}

                             7   在 activity_main.xml 載入碎片

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/choose_area_fragment"
        android:name="com.gzl.exception.weatherreview.ChooseAreaFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


</FrameLayout>

                            8 現在就可以執行出現這樣的結果

                                                            當然了  回退的圖示需要自己找

                      9   既然選擇城市的介面已經出來 那就應該點選縣 進入天氣介面

                                         在上面的  ChooseAreaFragment 碎片中已經定義了 listview 的點選事件具體處理方法不過只有點選了                                                省和點選了市的    現在要新增點選了縣的  

 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    //點選了省
                if (currentLevel == LEVEL_PROVINCE) {
                    selectedProvince = provinceList.get(position);
                    queryCities();
                    //點選了市
                } else if (currentLevel == LEVEL_CITY) {
                    selectedCity = cityList.get(position);
                    queryCounties();
                }
                //點選了縣
                else if (currentLevel == LEVEL_COUNTY) {
                    //獲取縣的實體類中的 WeatherId
                    String weatherId = countyList.get(position).getWeatherId();
                    //如果這是在 MainActivity 的xml佈局中的碎片
                    if (getActivity() instanceof MainActivity) {
                        //用 Intent 傳值 直接跳轉到 WeatherActivity 活動
                        Intent intent = new Intent(getActivity(), WeatherActivity.class);
                        intent.putExtra("weather_id", weatherId);
                        startActivity(intent);
                        //關閉這個活動
                        getActivity().finish();
                       // 如果這是在 WeatherActivity的xml佈局中的碎片 也就是抽屜佈局中的
                    } else if (getActivity() instanceof WeatherActivity) {
                        //獲得 活動例項
                        WeatherActivity activity = (WeatherActivity) getActivity();
                        //使用 SharedPreferences 把 weather_id 儲存起來
                        SharedPreferences.Editor editor =PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
                        editor.putString("weather_id", weatherId);
                        editor.apply();

                        //關閉抽屜
                        activity.drawerLayout.closeDrawers();
                        //顯示重新整理圖示
                        activity.swipeRefresh.setRefreshing(true);
                        //請求天氣(下面會有)
                        activity.requestWeather(weatherId);
                    }

                }
            }
        });

                        10  編寫天氣介面 書上是 分步寫的  ,所以新建檔案比較多,嫌麻煩就寫在一個裡

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimary">
  <!-- 背景圖 -->
    <ImageView
        android:id="@+id/background_img"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />
    <!-- 抽屜佈局  主要是不能點選一個去就再也不能選擇了吧 -->
    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
          <!-- 重新整理  要能實時重新整理資料 -->  
       <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swipe_refresh"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
              <!-- 防止一屏顯示不夠  android:fitsSystemWindows="true" 是給狀態列流出空間-->
            <ScrollView
                android:id="@+id/weather_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:overScrollMode="never"
                android:scrollbars="none">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:fitsSystemWindows="true"
                    android:orientation="vertical">
                      <!-- 這就是那五個分步寫的佈局  其實可以扎這裡直接寫出介面佈局 很簡單 -->
                    <include layout="@layout/title" />
                    <include layout="@layout/now" />
                    <include layout="@layout/forecast" />
                    <include layout="@layout/aqi" />
                    <include layout="@layout/suggestion" />
                </LinearLayout>
            </ScrollView>
        </android.support.v4.widget.SwipeRefreshLayout>
      <!-- 在抽屜中顯示 省市縣的選擇  -->
        <fragment
            android:id="@+id/choose_area_fragment"
            android:name="com.gzl.exception.weatherreview.ChooseAreaFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            tools:layout="@layout/choose_area" />
    </android.support.v4.widget.DrawerLayout>
</FrameLayout>

                                                                                            這五個佈局就不寫了很簡單

                                  11    接下來就是  WeatherActivity.java

                                                            在此之前要定義一個工具方法 用來將返回的 JSON 資料 解析稱實體類

                                                然後再進行WeatherActivity.java

public class WeatherActivity extends AppCompatActivity {
    private ScrollView weatherLayout;private TextView titleCity;
    private TextView titleUpdateTime;private TextView degreeText;
    private TextView weatherInfoText;private LinearLayout forecastLayout;
    private TextView aqiText;private TextView pm25Text;
    private TextView comfortText;private TextView carWashText;
    private TextView sportText;private ImageView backgroundImg;
    public SwipeRefreshLayout swipeRefresh;public DrawerLayout drawerLayout;
    private Button navButton;private Intent intent;
    private String mWeatherId;
    //設定沉浸式
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //沉浸模式
        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            getWindow().setStatusBarColor(Color.TRANSPARENT);
        }
        setContentView(R.layout.activity_weather);
        //呼叫初始化控制元件方法
        initView();
    }
    //獲取控制元件
    private void initView() {
        weatherLayout = findViewById(R.id.weather_layout);
        titleCity = findViewById(R.id.title_city);
        titleUpdateTime = findViewById(R.id.title_update_time);
        degreeText = findViewById(R.id.degree_text);
        weatherInfoText = findViewById(R.id.weather_info_text);
        forecastLayout = findViewById(R.id.forecast_layout);
        aqiText = findViewById(R.id.aqi_text);
        pm25Text = findViewById(R.id.pm25_text);
        comfortText = findViewById(R.id.comfort_text);
        carWashText = findViewById(R.id.car_wash_text);
        sportText = findViewById(R.id.sport_text);
        backgroundImg = findViewById(R.id.background_img);
        swipeRefresh = findViewById(R.id.swipe_refresh);
        swipeRefresh.setColorSchemeResources(R.color.colorPrimary);
        drawerLayout = findViewById(R.id.drawer_layout);
        navButton = findViewById(R.id.nav_button);
        navButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                drawerLayout.openDrawer(GravityCompat.START);
            }
        });


        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        String weatherString = prefs.getString("weather", null);
        String bing_pic = prefs.getString("bing_pic", null);
        if (bing_pic != null) {
            Glide.with(this).load(bing_pic).into(backgroundImg);
        } else {
            loadBingPic();
        }


        if (weatherString != null) {
            //有快取時直接解析天氣
            Weather weather = Utility.handleWeatherResponse(weatherString);
            mWeatherId = weather.basic.weatherId;
            ShowWeatherInfo(weather);
        } else {
            //沒有快取資訊去伺服器查詢資訊
            mWeatherId = getIntent().getStringExtra("weather_id");
            weatherLayout.setVisibility(View.INVISIBLE);
            requestWeather(mWeatherId);
        }

        swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                String weather_id = prefs.getString("weather_id", null);
                requestWeather(weather_id);
            }
        });
    }
    //載入每日一圖
    private void loadBingPic() {
        String requestBinfPic = "http://guolin.tech/api/bing_pic";
        HttpUtil.sendOkHttpRequest(requestBinfPic, new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {

                final String bingpic = response.body().string();
                SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this).edit();
                editor.putString("bing_pic", bingpic);
                editor.apply();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Glide.with(WeatherActivity.this).load(bingpic).into(backgroundImg);

                    }
                });

            }

            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }
        });
    }
    //根據天氣 ID 請求城市天氣
    public void requestWeather(final String weatherId) {
        String weatherUrl = "http://guolin.tech/api/weather?cityid=" + weatherId +
                "&key=d4892ac004c84b498d27455a89623852";

      //  http://guolin.tech/api/weather?cityid=CN101080507&key=d4892ac004c84b498d27455a89623852
        HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String responseText = response.body().string();
                final Weather weather = Utility.handleWeatherResponse(responseText);

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (weather != null && "ok".equals(weather.status)) {
                            SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this).edit();
                            editor.putString("weather", responseText);
                            editor.apply();
                            ShowWeatherInfo(weather);
                        } else {
                            Toast.makeText(WeatherActivity.this, "獲取天氣資訊失敗"
                                    , Toast.LENGTH_SHORT).show();
                        }
                        swipeRefresh.setRefreshing(false);
                    }
                });
            }

            @Override
            public void onFailure(Call call, IOException e) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(WeatherActivity.this, "獲取天氣資訊失敗"
                                , Toast.LENGTH_SHORT).show();
                        swipeRefresh.setRefreshing(false);
                    }
                });
            }
        });
    }
    // 處理並顯示Weather 實體類中的資料
    private void ShowWeatherInfo(Weather weather) {
        if (weather != null && "ok".equals(weather.status)) {
            String cityName = weather.basic.cityName;
            String upadteTime = weather.basic.update.updateTime.split(" ")[1];
            String degrss = weather.now.temperature + "℃";
            String weatherinfo = weather.now.more.info;

            titleCity.setText(cityName);
            titleUpdateTime.setText(upadteTime);
            degreeText.setText(degrss);
            weatherInfoText.setText(weatherinfo);
            forecastLayout.removeAllViews();
            for (Forecast forecast : weather.forecastList) {
                View view = LayoutInflater.from(this).inflate(R.layout.forecast_item, forecastLayout, false);
                TextView dateText = view.findViewById(R.id.date_text);
                TextView infoText = view.findViewById(R.id.info_text);
                TextView maxText = view.findViewById(R.id.max_text);
                TextView minText = view.findViewById(R.id.min_text);
                dateText.setText(forecast.date);
                infoText.setText(forecast.more.info);
                maxText.setText(forecast.temperature.max);
                minText.setText(forecast.temperature.min);
                forecastLayout.addView(view);
            }

            if (weather.aqi != null) {
                aqiText.setText(weather.aqi.city.aqi);
                pm25Text.setText(weather.aqi.city.pm25);
            }
            String comfort = "舒適度 :\n" + weather.suggestion.comfort.info;
            String carwach = "洗車指數:\n" + weather.suggestion.carWash.info;
            String sport = "運動建議:\n" + weather.suggestion.sport.info;

            comfortText.setText(comfort);
            carWashText.setText(carwach);
            sportText.setText(sport);
            weatherLayout.setVisibility(View.VISIBLE);
            
            intent = new Intent(this, AutoUpdateService.class);
            startService(intent);

        } else {
            Toast.makeText(WeatherActivity.this, "獲取天氣資訊失敗"
                    , Toast.LENGTH_SHORT).show();
        }

    }
    @Override
    protected void onPause() {
        super.onPause();
        stopService(intent);
    }
}

                        12   在Service包下 新建 Service

public class AutoUpdateService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        updateWeather();
        updateBingPic();
        AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
        int anHOur = 3*60 * 1000;
        long triggerAtTime = SystemClock.elapsedRealtime() + anHOur;
        Intent i = new Intent(this, AutoUpdateService.class);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
        manager.cancel(pi);
        return super.onStartCommand(intent, flags, startId);
    }

    //更新每日一圖 儲存到 SharedPreferences
    private void updateBingPic() {
        String requestBinfPic = "http://guolin.tech/api/bing_pic";
        HttpUtil.sendOkHttpRequest(requestBinfPic, new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {

                final String bingpic = response.body().string();
                SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(AutoUpdateService.this).edit();
                editor.putString("bing_pic", bingpic);
                editor.apply();
            }

            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }
        });
    }

    //更新天氣  儲存到SharedPreferences
    private void updateWeather() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        String weatherString = prefs.getString("weather", null);
        if (weatherString != null) {
            //有快取時直接解析天氣資料
            Weather weather = Utility.handleWeatherResponse(weatherString);

            String weatherId = weather.basic.weatherId;

            String weatherUrl = "http://guolin.tech/api/weather?cityid=" + weatherId +
                    "&key=d4892ac004c84b498d27455a89623852";

            HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
                @Override
                public void onResponse(Call call, Response response) throws IOException {

                    String responseText = response.body().string();

                    Weather weather = Utility.handleWeatherResponse(responseText);

                    if (weather != null && "ok".equals(weather.status)) {
                        SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(AutoUpdateService.this).edit();
                        editor.putString("weather", responseText);
                        editor.apply();

                    }
                }

                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

                                                     這樣 基本專案就完成了

還可以做的事

             可以 在Service 中 放   Notification  讓資料實時顯示到狀態列

              增加 設定介面  是否顯示   Notification

              增加一個 懸浮按鈕  顯示 選擇過的縣   不用再來回切換縣

              。。。