Android:使用AsyncTask实现天气预报功能

http://www.webxml.com.cn/WebServices/WeatherWebService.asmx 是一个免费的天气预报服务接口(限访问次数),本文以此为例,实现简单的天气预报功能。


实现效果:

实现效果


重点如下:

  • 天气预报接口的使用
  • AsyncTask的使用
  • LruCache的使用

源码:

activity_load_weather.xml

显示最近三天的天气。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@mipmap/r_t1"/>

    <Button
        android:id="@+id/btn_select_city"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="广东广州"
        android:textSize="20sp"
        android:textColor="@color/black"
        android:layout_marginTop="5dp"
        android:background="@color/lightseagreen"/>

    <TextView
        android:id="@+id/tv_today_pes"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="今日天气实况:"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:layout_marginTop="20dp"
        android:padding="10dp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="5dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:gravity="top"
            android:layout_marginTop="5dp"
            android:layout_weight="1">

            <ImageView
                android:id="@+id/iv_today_pic1"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:src="@mipmap/ic_launcher"/>

            <ImageView
                android:id="@+id/iv_today_pic2"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:src="@mipmap/ic_launcher"/>

            <TextView
                android:id="@+id/tv_today_weather"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:textColor="@color/black"
                android:textSize="16sp"
                android:layout_marginLeft="10dp"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:padding="5dp"
            android:gravity="top"
            android:layout_weight="1">

            <ImageView
                android:id="@+id/iv_tomorrow_pic1"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:src="@mipmap/ic_launcher"/>

            <ImageView
                android:id="@+id/iv_tomorrow_pic2"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:src="@mipmap/ic_launcher"/>

            <TextView
                android:id="@+id/tv_tomorrow_weather"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:textColor="@color/black"
                android:textSize="16sp"
                android:layout_marginLeft="10dp"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:padding="5dp"
            android:layout_gravity="top"
            android:layout_weight="1">

            <ImageView
                android:id="@+id/iv_after_tomorrow_pic1"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:src="@mipmap/ic_launcher"/>

            <ImageView
                android:id="@+id/iv_after_tomorrow_pic2"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:src="@mipmap/ic_launcher"/>

            <TextView
                android:id="@+id/tv_after_tomorrow_weather"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:textColor="@color/black"
                android:textSize="16sp"
                android:layout_marginLeft="10dp"/>

        </LinearLayout>

    </LinearLayout>

</LinearLayout>

LoadWeatherActivity.java

切换城市、显示天气。

public class LoadWeatherActivity extends Activity {

    private Button btnSelectCity = null;
    private ImageView ivTodayPic1 = null;
    private ImageView ivTodayPic2 = null;
    private TextView tvTodayWeather = null;
    private TextView tvTodayPES = null;
    private ImageView ivTomorrowPic1 = null;
    private ImageView ivTomorrowPic2 = null;
    private TextView tvTomorrowWeather = null;
    private ImageView ivAfterTomorrowPic1 = null;
    private ImageView ivAfterTomorrowPic2 = null;
    private TextView tvAfterTomorrowWeather = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_load_weather);

        btnSelectCity = (Button) findViewById(R.id.btn_select_city);
        ivTodayPic1 = (ImageView) findViewById(R.id.iv_today_pic1);
        ivTodayPic2 = (ImageView) findViewById(R.id.iv_today_pic2);
        tvTodayWeather = (TextView) findViewById(R.id.tv_today_weather);
        tvTodayPES = (TextView) findViewById(R.id.tv_today_pes);
        ivTomorrowPic1 = (ImageView) findViewById(R.id.iv_tomorrow_pic1);
        ivTomorrowPic2 = (ImageView) findViewById(R.id.iv_tomorrow_pic2);
        tvTomorrowWeather = (TextView) findViewById(R.id.tv_tomorrow_weather);
        ivAfterTomorrowPic1 = (ImageView) findViewById(R.id.iv_after_tomorrow_pic1);
        ivAfterTomorrowPic2 = (ImageView) findViewById(R.id.iv_after_tomorrow_pic2);
        tvAfterTomorrowWeather = (TextView) findViewById(R.id.tv_after_tomorrow_weather);

        // 切换城市
        btnSelectCity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ProvinceManager.getInstance(LoadWeatherActivity.this).showProvinceDialog();
            }
        });

        // 注册天气改变的事件
        WeatherManager.getInstance(LoadWeatherActivity.this).setToldWeatherChangedListener(new WeatherManager.ToldWeatherChangedListener() {
            @Override
            public void toldWeatherChanged(WeatherBean weather) {
                refresh(weather);
            }
        });

        // 初始化天气
        WeatherManager.getInstance(LoadWeatherActivity.this).getWeather("广州");
    }

    /**
     * 刷新天气信息
     * @param weather
     */
    private void refresh(WeatherBean weather) {
        if (weather != null) {
            btnSelectCity.setText(weather.Province + weather.City);

            String today = "今天:" + weather.Today.Day
                    + "\n" + "天气:" + weather.Today.Weather
                    + "\n" + "气温:" + weather.Today.Temperature
                    + "\n" + "风力:" + weather.Today.WindPower;
            tvTodayWeather.setText(today);
            tvTodayPES.setText(weather.Today.PES);
            ivTodayPic1.setImageResource(getImageIdByName("a_" + weather.Today.Picture1));
            ivTodayPic2.setImageResource(getImageIdByName("a_" + weather.Today.Picture2));

            String tomorrow = "明天:" + weather.Tomorrow.Day
                    + "\n" + "天气:" + weather.Tomorrow.Weather
                    + "\n" + "气温:" + weather.Tomorrow.Temperature
                    + "\n" + "风力:" + weather.Tomorrow.WindPower;
            tvTomorrowWeather.setText(tomorrow);
            ivTomorrowPic1.setImageResource(getImageIdByName("a_" + weather.Tomorrow.Picture1));
            ivTomorrowPic2.setImageResource(getImageIdByName("a_" + weather.Tomorrow.Picture2));

            String afterTomorrow = "后天:" + weather.AfterTomorrow.Day
                    + "\n" + "天气:" + weather.AfterTomorrow.Weather
                    + "\n" + "气温:" + weather.AfterTomorrow.Temperature
                    + "\n" + "风力:" + weather.AfterTomorrow.WindPower;
            tvAfterTomorrowWeather.setText(afterTomorrow);
            ivAfterTomorrowPic1.setImageResource(getImageIdByName("a_" + weather.AfterTomorrow.Picture1));
            ivAfterTomorrowPic2.setImageResource(getImageIdByName("a_" + weather.AfterTomorrow.Picture2));
        }
    }

    /**
     * 根据图片名称获取R.java中对应的id
     *
     * @param name
     * @return
     */
    public static int getImageIdByName (String name) {
        int value = 0;
        if (null != name) {
            if (name.indexOf(".") != -1) {
                name = name.substring(0, name.indexOf("."));
            }
            Class<R.mipmap> cls = R.mipmap.class;
            try {
                value = cls.getDeclaredField(name).getInt(null);
            } catch (Exception e) {

            }
        }
        return value;
    }
}

ProvinceManager.java

使用AsyncTask下载城市,保存在内存中,以免重复下载。

选择省份后,弹出城市选择的对话框。

public class ProvinceManager {

    private static final String PROVINCE_URL = "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getSupportProvince";

    private Context ctx = null;

    /**
     * 选择省份对话框
     */
    private AlertDialog dialog = null;

    /**
     * 省份列表
     */
    private String[] provinces = null;

    private static ProvinceManager instance = null;
    public static ProvinceManager getInstance(Context context) {
        if (instance == null) {
            instance = new ProvinceManager(context);
        }
        return instance;
    }
    private ProvinceManager(Context context) {
        ctx = context;
    }

    /**
     * 弹出省份选择对话框
     */
    public void showProvinceDialog() {
        if (provinces == null) {
            new LoadProvinceAsyncTask().execute();
        } else {
            showDialog();
        }
    }

    /**
     * 弹出省份选择对话框
     */
    private void showDialog() {
        if (provinces != null) {
            if (dialog == null) {
                AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
                builder.setTitle("请选择省份:");
                builder.setSingleChoiceItems(provinces, 0, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        String province = provinces[i];

                        //选择城市后关闭省份的选择框
                        CityManager.getInstance(ctx).setToldDimissProvinceSelectDialogListener(new CityManager.ToldDimissProvinceSelectDialogListener() {
                            @Override
                            public void toldDimissProvinceSelectDialog() {
                                dismissDialog();
                            }
                        });
                        //选择城市
                        CityManager.getInstance(ctx).showCityDialog(province);
                    }
                });
                dialog = builder.create();
            }
            dialog.show();
        }
    }

    /**
     * 关闭对话框
     */
    private void dismissDialog() {
        if (dialog != null) {
            dialog.dismiss();
        }
    }

    /**
     * 加载省份的异步任务
     */
    class LoadProvinceAsyncTask extends AsyncTask<Void, Void, String[]> {

        @Override
        protected String[] doInBackground(Void... voids) {
            return getProvince(PROVINCE_URL);
        }

        @Override
        protected void onPostExecute(String[] strings) {
            super.onPostExecute(strings);

            provinces = strings;

            //显示选择对话框
            showDialog();
        }

        /**
         * 读取省份信息
         * @param urlStr
         * @return
         */
        private String[] getProvince(String urlStr) {
            List<String> result = new ArrayList<>();

            InputStream inputStream = null;
            InputStreamReader inputStreamReader = null;
            try {
                inputStream = new URL(urlStr).openStream();
                inputStreamReader = new InputStreamReader(inputStream, "utf-8");

                XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
                xmlPullParser.setInput(inputStreamReader);

                int eventType;
                while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
                    switch (eventType) {
                        case XmlPullParser.START_TAG:
                            String name = xmlPullParser.getName();
                            if (name.equalsIgnoreCase("string")) {
                                String value = xmlPullParser.nextText();
                                result.add(value);
                            }
                            break;
                        default:
                            break;
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    if (inputStreamReader != null) {
                        inputStreamReader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            int size = result.size();
            String[] resultArr = new String[size];
            for (int i=0;i<size;i++) {
                resultArr[i] = result.get(i);
            }

            return resultArr;
        }
    }
}

CityManager.java

异步下载城市信息,保存在缓存中,以免重复下载。

选择城市后,下载天气。

public class CityManager {

    private static final String CITY_URL = "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getSupportCity?byProvinceName=";

    private Context ctx = null;

    /**
     * 缓存,存储查询过的省份及对应城市
     */
    private LruCache<String, String[]> cityLruCache = null;

    /**
     * 通知关闭省份选择对话框的事件
     */
    public interface ToldDimissProvinceSelectDialogListener {
        void toldDimissProvinceSelectDialog();
    }
    private ToldDimissProvinceSelectDialogListener toldDimissProvinceSelectDialogListener = null;
    public void setToldDimissProvinceSelectDialogListener(ToldDimissProvinceSelectDialogListener listener) {
        toldDimissProvinceSelectDialogListener = listener;
    }

    private static CityManager instance = null;
    public static CityManager getInstance(Context context) {
        if (instance == null) {
            instance = new CityManager(context);
        }
        return instance;
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
    private CityManager(Context context) {
        ctx = context;

        // 获取最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        // 使用最大可用内存的十分之一
        int cacheSize = maxMemory / 10;
        // 定义缓存变量,重写sizeOf方法
        cityLruCache = new LruCache<String, String[]>(cacheSize){
            @Override
            protected int sizeOf(String key, String[] value) {
                // 在每次存入缓存时调用
                if (value != null && value.length > 0) {
                    int size = 0;
                    for (String str : value) {
                        size += str.length();
                    }
                    return size;
                }
                return 0;
            }
        };
    }

    /**
     * 从缓存中获取指定省份的城市列表
     * @param province
     * @return
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
    private String[] getCitysFromCache(String province) {
        return cityLruCache.get(province);
    }

    /**
     * 向缓存中添加省份的城市列表
     * @param province
     * @param citys
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
    private void addCitysToCache(String province, String[] citys) {
        if (getCitysFromCache(province) == null) {
            cityLruCache.put(province, citys);
        }
    }

    /**
     * 显示城市选择对话框
     * @param province
     */
    public void showCityDialog(String province) {
        String[] citys = getCitysFromCache(province);
        if (citys == null) {
            new LoadCityAsyncTask().execute(province);
        } else {
            showDialog(citys);
        }
    }

    private void showDialog(final String[] citys) {
        if (citys != null) {
            AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
            builder.setTitle("请选择城市:");
            builder.setSingleChoiceItems(citys, 0, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    String city = citys[i];

                    // 查询天气
                    WeatherManager.getInstance(ctx).getWeather(city);

                    // 关闭城市选择对话框
                    dialogInterface.dismiss();

                    // 关闭省份选择对话框
                    if (toldDimissProvinceSelectDialogListener != null) {
                        toldDimissProvinceSelectDialogListener.toldDimissProvinceSelectDialog();
                    }
                }
            });
            AlertDialog dialog = builder.create();
            dialog.show();
        }
    }

    class LoadCityAsyncTask extends AsyncTask<String, Void, String[]> {
        @Override
        protected String[] doInBackground(String... strings) {
            try {
                //使用URLEncoder.encode解决URL中存在中文导致openStream异常的问题
                String[] citys = getCity(CITY_URL + URLEncoder.encode(strings[0], "utf8"));

                //添加到缓存
                addCitysToCache(strings[0], citys);

                return citys;
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String[] strings) {
            super.onPostExecute(strings);

            //显示选择对话框
            showDialog(strings);
        }

        /**
         * 通过网络获取指定省份的城市列表
         * @param urlAddress
         * @return
         */
        private String[] getCity(String urlAddress) {
            List<String> result = new ArrayList<>();

            InputStream inputStream = null;
            InputStreamReader inputStreamReader = null;

            try {
                inputStream = new URL(urlAddress).openStream();
                inputStreamReader = new InputStreamReader(inputStream, "utf8");

                XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
                xmlPullParser.setInput(inputStreamReader);

                int eventType;
                while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
                    switch (eventType) {
                        case XmlPullParser.START_TAG:
                            String name = xmlPullParser.getName();
                            if (name.equalsIgnoreCase("string")) {
                                String value = xmlPullParser.nextText();
                                //String code = value.substring(value.length() - 6, value.length() - 1);
                                String city = value.substring(0, value.indexOf(" "));

                                result.add(city);
                            }
                            break;
                        default:
                            break;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    if (inputStreamReader != null) {
                        inputStreamReader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            int size = result.size();
            String[] resultArr = new String[size];
            for (int i=0;i<size;i++) {
                resultArr[i] = result.get(i);
            }
            return resultArr;
        }
    }
}

WeatherManager.java

异步下载天气信息,并通知界面进行更新。

public class WeatherManager {

    private static final String WEATHER_URL = "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=";

    private Context ctx = null;

    private static WeatherManager instance = null;
    public static WeatherManager getInstance(Context context) {
        if (instance == null) {
            instance = new WeatherManager(context);
        }
        return instance;
    }

    private WeatherManager(Context context) {
        ctx = context;
    }

    /**
     * 通知天气改变的事件
     */
    public interface ToldWeatherChangedListener {
        void toldWeatherChanged(WeatherBean weather);
    }
    private ToldWeatherChangedListener toldWeatherChangedListener = null;
    public void setToldWeatherChangedListener(ToldWeatherChangedListener listener) {
        toldWeatherChangedListener = listener;
    }

    /**
     * 获取指定城市的天气
     * @param city
     */
    public void getWeather(String city) {
        new LoadWeatherAsyncTask().execute(city);
    }

    class LoadWeatherAsyncTask extends AsyncTask<String, Void, WeatherBean> {

        @Override
        protected WeatherBean doInBackground(String... strings) {
            //使用URLEncoder.encode解决URL中存在中文导致openStream异常的问题
            return getWeather(WEATHER_URL + URLEncoder.encode(strings[0]));
        }

        @Override
        protected void onPostExecute(WeatherBean weatherBean) {
            super.onPostExecute(weatherBean);

            /**
             * 通知天气改变
             */
            if (toldWeatherChangedListener != null) {
                toldWeatherChangedListener.toldWeatherChanged(weatherBean);
            }
        }

        private WeatherBean getWeather(String urlAddress) {
            WeatherBean weatherBean = null;

            InputStream inputStream = null;
            InputStreamReader inputStreamReader = null;

            try {
                inputStream = new URL(urlAddress).openStream();
                inputStreamReader = new InputStreamReader(inputStream, "utf8");

                XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
                xmlPullParser.setInput(inputStreamReader);

                //读取天气信息
                List<String> weatherInfo = new ArrayList<>();
                int eventType;
                while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
                    switch (eventType) {
                        case XmlPullParser.START_TAG:
                            if (xmlPullParser.getName().equalsIgnoreCase("string")) {
                                weatherInfo.add(xmlPullParser.nextText());
                            }
                            break;
                        default:
                            break;
                    }
                }
                if (weatherInfo.size() >= 23) {
                    weatherBean = new WeatherBean();
                    weatherBean.Province = weatherInfo.get(0);
                    weatherBean.City = weatherInfo.get(1);
                    weatherBean.Today = new WeatherBeanItem();
                    weatherBean.Today.Temperature = weatherInfo.get(5);
                    String dateAndWeather = weatherInfo.get(6);
                    String[] dateAndWeatherArr = dateAndWeather.split(" ");
                    weatherBean.Today.Day = dateAndWeatherArr[0];
                    weatherBean.Today.Weather = dateAndWeatherArr[1];
                    weatherBean.Today.WindPower = weatherInfo.get(7);
                    weatherBean.Today.Picture1 = weatherInfo.get(8);
                    weatherBean.Today.Picture2 = weatherInfo.get(9);
                    weatherBean.Today.PES = weatherInfo.get(10);

                    weatherBean.Tomorrow = new WeatherBeanItem();
                    weatherBean.Tomorrow.Temperature = weatherInfo.get(12);
                    dateAndWeather = weatherInfo.get(13);
                    dateAndWeatherArr = dateAndWeather.split(" ");
                    weatherBean.Tomorrow.Day = dateAndWeatherArr[0];
                    weatherBean.Tomorrow.Weather = dateAndWeatherArr[1];
                    weatherBean.Tomorrow.WindPower = weatherInfo.get(14);
                    weatherBean.Tomorrow.Picture1 = weatherInfo.get(15);
                    weatherBean.Tomorrow.Picture2 = weatherInfo.get(16);

                    weatherBean.AfterTomorrow = new WeatherBeanItem();
                    weatherBean.AfterTomorrow.Temperature = weatherInfo.get(17);
                    dateAndWeather = weatherInfo.get(18);
                    dateAndWeatherArr = dateAndWeather.split(" ");
                    weatherBean.AfterTomorrow.Day = dateAndWeatherArr[0];
                    weatherBean.AfterTomorrow.Weather = dateAndWeatherArr[1];
                    weatherBean.AfterTomorrow.WindPower = weatherInfo.get(19);
                    weatherBean.AfterTomorrow.Picture1 = weatherInfo.get(20);
                    weatherBean.AfterTomorrow.Picture2 = weatherInfo.get(21);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    if (inputStreamReader != null) {
                        inputStreamReader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return weatherBean;
        }
    }
}

WeatherBean.java

public class WeatherBean {
    /**
     * 省份
     */
    public String Province;
    /**
     * 城市
     */
    public String City;
    /**
     * 今日天气
     */
    public WeatherBeanItem Today;
    /**
     * 明日天气
     */
    public WeatherBeanItem Tomorrow;
    /**
     * 后天天气
     */
    public WeatherBeanItem AfterTomorrow;
}

WeatherBeanItem.java

public class WeatherBeanItem {
    /**
     * 天气图片1
     */
    public String Picture1;
    /**
     * 天气图片2
     */
    public String Picture2;
    /**
     * 日期
     */
    public String Day;
    /**
     * 天气
     */
    public String Weather;
    /**
     * 气温
     */
    public String Temperature;
    /**
     * 风力
     */
    public String WindPower;
    /**
     * 天气实况
     */
    public String PES;
}

可进一步优化

  • 省份、城市下载后自动保存在本地。

  • 该服务接口的天气每2.5小时才刷新一次,因此一定时间内不需要重新下载天气信息,可添加计时器进行处理。

  • 使用 AsyncHttpResponseHandler 处理网络请求。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值