Android移动开发-使用HttpURLConnection和AsyncTask请求解析地理位置实现

使用HttpURLConnection和AsyncTask请求解析地理位置的原理其实很简单,就是利用AsyncTask发起请求异步网络请求,然后通过HttpURLConnection解析JSON字符串。现在我们利用 Google Map 的开放API。通过HTTP调用传入经纬度的数值,然后对方返回一个JSON格式的地址信息字符串,通过解析JSON串就能得到具体的地址。

{
   "results" : [
      {
         "address_components" : [
            {
               "long_name" : "天安门",
               "short_name" : "天安门",
               "types" : [ "premise" ]
            },
            {
               "long_name" : "东城区",
               "short_name" : "东城区",
               "types" : [ "political", "sublocality", "sublocality_level_1" ]
            },
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "中国",
               "short_name" : "CN",
               "types" : [ "country", "political" ]
            }
         ],
         "formatted_address" : "中国北京市东城区天安门",
         "geometry" : {
            "bounds" : {
               "northeast" : {
                  "lat" : 39.9088799,
                  "lng" : 116.3981847 },
               "southwest" : {
                  "lat" : 39.9085116,
                  "lng" : 116.3967974 }
            },
            "location" : {
               "lat" : 39.90869,
               "lng" : 116.3975334
            },
            "location_type" : "ROOFTOP",
            "viewport" : {
               "northeast" : {
                  "lat" : 39.9100447302915,
                  "lng" : 116.3988400302915 },
               "southwest" : {
                  "lat" : 39.9073467697085,
                  "lng" : 116.3961420697085 }
            }
         },
         "place_id" : "ChIJEcuXlb9S8DURhgBLykaGiuE",
         "types" : [ "premise" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "1",
               "short_name" : "1",
               "types" : [ "street_number" ]
            },
            {
               "long_name" : "西长安街",
               "short_name" : "西长安街",
               "types" : [ "route" ]
            },
            {
               "long_name" : "西城区",
               "short_name" : "西城区",
               "types" : [ "political", "sublocality", "sublocality_level_1" ]
            },
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "中国",
               "short_name" : "CN",
               "types" : [ "country", "political" ]
            },
            {
               "long_name" : "100031",
               "short_name" : "100031",
               "types" : [ "postal_code" ]
            }
         ],
         "formatted_address" : "中国北京市西城区西长安街1号 邮政编码: 100031",
         "geometry" : {
            "location" : {
               "lat" : 39.907871,
               "lng" : 116.3929394
            },
            "location_type" : "ROOFTOP",
            "viewport" : {
               "northeast" : {
                  "lat" : 39.9092199802915,
                  "lng" : 116.3942883802915 },
               "southwest" : {
                  "lat" : 39.9065220197085,
                  "lng" : 116.3915904197085 }
            }
         },
         "place_id" : "ChIJO-uUspVS8DURL89OOAGFbjc",
         "types" : [ "street_address" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "东城区",
               "short_name" : "东城区",
               "types" : [ "political", "sublocality", "sublocality_level_1" ]
            },
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "中国",
               "short_name" : "CN",
               "types" : [ "country", "political" ]
            }
         ],
         "formatted_address" : "中国北京市东城区",
         "geometry" : {
            "bounds" : {
               "northeast" : {
                  "lat" : 39.9740365,
                  "lng" : 116.4523907 },
               "southwest" : {
                  "lat" : 39.8586252,
                  "lng" : 116.3771342 }
            },
            "location" : {
               "lat" : 39.92835300000001,
               "lng" : 116.416357
            },
            "location_type" : "APPROXIMATE",
            "viewport" : {
               "northeast" : {
                  "lat" : 39.9740365,
                  "lng" : 116.4523907 },
               "southwest" : {
                  "lat" : 39.8586252,
                  "lng" : 116.3771342 }
            }
         },
         "place_id" : "ChIJLwKYcTNT8DUR8DZJ7J2CieA",
         "types" : [ "political", "sublocality", "sublocality_level_1" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "中国",
               "short_name" : "CN",
               "types" : [ "country", "political" ]
            }
         ],
         "formatted_address" : "中国北京市",
         "geometry" : {
            "bounds" : {
               "northeast" : {
                  "lat" : 41.0608157,
                  "lng" : 117.514625 },
               "southwest" : {
                  "lat" : 39.442758,
                  "lng" : 115.4234112 }
            },
            "location" : {
               "lat" : 39.90419989999999,
               "lng" : 116.4073963
            },
            "location_type" : "APPROXIMATE",
            "viewport" : {
               "northeast" : {
                  "lat" : 40.2164962,
                  "lng" : 116.7829835 },
               "southwest" : {
                  "lat" : 39.6612714,
                  "lng" : 116.0119343 }
            }
         },
         "place_id" : "ChIJuSwU55ZS8DURiqkPryBWYrk",
         "types" : [ "locality", "political" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "北京市",
               "short_name" : "北京市",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "中国",
               "short_name" : "CN",
               "types" : [ "country", "political" ]
            }
         ],
         "formatted_address" : "中国北京市",
         "geometry" : {
            "bounds" : {
               "northeast" : {
                  "lat" : 41.0608157,
                  "lng" : 117.514625 },
               "southwest" : {
                  "lat" : 39.442758,
                  "lng" : 115.4234112 }
            },
            "location" : {
               "lat" : 40.2373519,
               "lng" : 116.2304616
            },
            "location_type" : "APPROXIMATE",
            "viewport" : {
               "northeast" : {
                  "lat" : 41.0608157,
                  "lng" : 117.514625 },
               "southwest" : {
                  "lat" : 39.442758,
                  "lng" : 115.4234112 }
            }
         },
         "place_id" : "ChIJuSwU55ZS8DURp8_dOLuVd8A",
         "types" : [ "administrative_area_level_1", "political" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "中国",
               "short_name" : "CN",
               "types" : [ "country", "political" ]
            }
         ],
         "formatted_address" : "中国",
         "geometry" : {
            "bounds" : {
               "northeast" : {
                  "lat" : 53.56097399999999,
                  "lng" : 134.7728099 },
               "southwest" : {
                  "lat" : 17.9996,
                  "lng" : 73.4994136 }
            },
            "location" : {
               "lat" : 35.86166,
               "lng" : 104.195397
            },
            "location_type" : "APPROXIMATE",
            "viewport" : {
               "northeast" : {
                  "lat" : 53.56097399999999,
                  "lng" : 134.7728099 },
               "southwest" : {
                  "lat" : 17.9996,
                  "lng" : 73.4994136 }
            }
         },
         "place_id" : "ChIJwULG5WSOUDERbzafNHyqHZU",
         "types" : [ "country", "political" ]
      }
   ],
   "status" : "OK"
}

当访问到上面的JSON代码后,就从“results”标志位开始解析到“status”标志位结束,当解析到“formatted_address”标志位后就显示“formatted_address”标志位里的文本信息。

因为网络访问不能在主线程中进行,所以要结合AsyncTask与HttpURLConnection实现地址的异步获取。获取地址信息的任务代码如下:

  • GetAddressTask.java逻辑代码如下:
package com.fukaimei.mapapi.task;

import android.location.Location;
import android.os.AsyncTask;
import android.util.Log;

import com.fukaimei.mapapi.http.HttpRequestUtil;
import com.fukaimei.mapapi.http.tool.HttpReqData;
import com.fukaimei.mapapi.http.tool.HttpRespData;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.text.MessageFormat;

public class GetAddressTask extends AsyncTask<Location, Void, String> {
    private final static String TAG = "GetAddressTask";
    private static String mAddressUrl = "http://maps.google.cn/maps/api/geocode/json?latlng={0},{1}&sensor=true&language=zh-CN";
    private OnAddressListener mListener;

    public GetAddressTask() {
        super();
    }

    @Override
    protected String doInBackground(Location... params) {
        Location location = params[0];
        String url = MessageFormat.format(mAddressUrl, location.getLatitude(), location.getLongitude());
        HttpReqData req_data = new HttpReqData(url);
        HttpRespData resp_data = HttpRequestUtil.getData(req_data);
        Log.d(TAG, "return json = " + resp_data.content);

        String address = "当前定位地址解析失败,请开启网络方可解析!";
        if (resp_data.err_msg.length() <= 0) {
            try {
                JSONObject obj = new JSONObject(resp_data.content);
                JSONArray resultArray = obj.getJSONArray("results");
                if (resultArray.length() > 0) {
                    JSONObject resultObj = resultArray.getJSONObject(0);
                    address = resultObj.getString("formatted_address");
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        Log.d(TAG, "address = " + address);
        return address;
    }

    @Override
    protected void onPostExecute(String address) {
        mListener.onFindAddress(address);
    }

    public void setOnAddressListener(OnAddressListener listener) {
        mListener = listener;
    }

    public interface OnAddressListener {
        void onFindAddress(String address);
    }

}
  • HttpRequestUtil.java逻辑代码如下:
package com.fukaimei.mapapi.http;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import android.graphics.BitmapFactory;
import android.util.Log;

import com.fukaimei.mapapi.http.tool.HttpReqData;
import com.fukaimei.mapapi.http.tool.HttpRespData;
import com.fukaimei.mapapi.http.tool.StreamTool;

public class HttpRequestUtil {

    private static final String TAG = "HttpRequestUtil";

    //设置http连接的头部信息
    private static void setConnHeader(HttpURLConnection conn, String method, HttpReqData req_data)
            throws ProtocolException {
        conn.setRequestMethod(method);
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(10000);
        conn.setRequestProperty("Accept", "*/*");
        //IE使用
//      conn.setRequestProperty("Accept-Language", "zh-CN");
//      conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C)");
        //firefox使用
        conn.setRequestProperty("Accept-Language", "zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
        conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0");
        conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
        if (req_data.content_type.equals("") != true) {
            conn.setRequestProperty("Content-Type", req_data.content_type);
        }
        if (req_data.x_requested_with.equals("") != true) {
            conn.setRequestProperty("X-Requested-With", req_data.x_requested_with);
        }
        if (req_data.referer.equals("") != true) {
            conn.setRequestProperty("Referer", req_data.referer);
        }
        if (req_data.cookie.equals("") != true) {
            conn.setRequestProperty("Cookie", req_data.cookie);
            Log.d(TAG, "setConnHeader cookie=" + req_data.cookie);
        }
    }

    private static String getRespCookie(HttpURLConnection conn, HttpReqData req_data) {
        String cookie = "";
        Map<String, List<String>> headerFields = conn.getHeaderFields();
        if (headerFields != null) {
            List<String> cookies = headerFields.get("Set-Cookie");
            if (cookies != null) {
                for (String cookie_item : cookies) {
                    cookie = cookie + cookie_item + "; ";
                }
            } else {
                cookie = req_data.cookie;
            }
        } else {
            cookie = req_data.cookie;
        }
        Log.d(TAG, "cookie=" + cookie);
        return cookie;
    }

    //get文本数据
    public static HttpRespData getData(HttpReqData req_data) {
        HttpRespData resp_data = new HttpRespData();
        try {
            URL url = new URL(req_data.url);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            setConnHeader(conn, "GET", req_data);

            conn.connect();
            resp_data.content = StreamTool.getUnzipStream(conn.getInputStream(),
                    conn.getHeaderField("Content-Encoding"), req_data.charset);
            resp_data.cookie = conn.getHeaderField("Set-Cookie");
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
            resp_data.err_msg = e.getMessage();
        }
        return resp_data;
    }

    //post的内容放在url中
    public static HttpRespData postUrl(HttpReqData req_data) {
        HttpRespData resp_data = new HttpRespData();
        String s_url = req_data.url;
        if (req_data.params != null) {
            s_url += "?" + req_data.params.toString();
        }
        Log.d(TAG, "s_url=" + s_url);
        try {
            URL url = new URL(s_url);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            setConnHeader(conn, "POST", req_data);
            conn.setDoOutput(true);

            conn.connect();
            resp_data.content = StreamTool.getUnzipStream(conn.getInputStream(),
                    conn.getHeaderField("Content-Encoding"), req_data.charset);
            resp_data.cookie = conn.getHeaderField("Set-Cookie");
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
            resp_data.err_msg = e.getMessage();
        }
        return resp_data;
    }

    //post的内容放在输出流中
    public static HttpRespData postData(HttpReqData req_data) {
        req_data.content_type = "application/x-www-form-urlencoded";
        HttpRespData resp_data = new HttpRespData();
        String s_url = req_data.url;
        Log.d(TAG, "s_url=" + s_url + ", params=" + req_data.params.toString());
        try {
            URL url = new URL(s_url);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            setConnHeader(conn, "POST", req_data);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();

            PrintWriter out = new PrintWriter(conn.getOutputStream());
            out.print(req_data.params.toString());
            out.flush();

            resp_data.content = StreamTool.getUnzipStream(conn.getInputStream(),
                    conn.getHeaderField("Content-Encoding"), req_data.charset);
            resp_data.cookie = getRespCookie(conn, req_data);
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
            resp_data.err_msg = e.getMessage();
        }
        return resp_data;
    }

    //post的内容分段传输
    public static HttpRespData postMultiData(HttpReqData req_data, Map<String, String> map) {
        HttpRespData resp_data = new HttpRespData();
        String s_url = req_data.url;
        Log.d(TAG, "s_url=" + s_url);
        String end = "\r\n";
        String hyphens = "--";
        try {
            URL url = new URL(s_url);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            setConnHeader(conn, "POST", req_data);
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + req_data.boundary);
            conn.setRequestProperty("Cache-Control", "no-cache");
            conn.setDoOutput(true);
            conn.setDoInput(true);

            StringBuffer buffer = new StringBuffer();
            Log.d(TAG, "map.size()=" + map.size());
            Iterator<String> it = map.keySet().iterator();
            while (it.hasNext()) {
                String str = it.next();
                buffer.append(hyphens + req_data.boundary + end);
                buffer.append("Content-Disposition: form-data; name=\"");
                buffer.append(str);
                buffer.append("\"" + end + end);
                buffer.append(map.get(str));
                buffer.append(end);
                Log.d(TAG, "key=" + str + ", value=" + map.get(str));
            }
            if (map.size() > 0) {
                buffer.append(hyphens + req_data.boundary + end);
                byte[] param_data = buffer.toString().getBytes(req_data.charset);
                OutputStream out = conn.getOutputStream();
                out.write(param_data);
                out.flush();
            }

            conn.connect();
            resp_data.content = StreamTool.getUnzipStream(conn.getInputStream(),
                    conn.getHeaderField("Content-Encoding"), req_data.charset);
            resp_data.cookie = conn.getHeaderField("Set-Cookie");
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
            resp_data.err_msg = e.getMessage();
        }
        return resp_data;
    }

}
  • layout/activity_http_request.xml界面布局代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp">

    <TextView
        android:id="@+id/tv_location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#ff000000"
        android:textSize="18sp" />

</LinearLayout>

接着在Activity代码中启动该任务,并实现OnAddressListener接口的onFindAddress方法,即可在页面上添加详细的地址信息。

  • HttpRequestActivity.java逻辑代码如下:
package com.fukaimei.mapapi;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.fukaimei.mapapi.http.tool.DateUtil;
import com.fukaimei.mapapi.task.GetAddressTask;

public class HttpRequestActivity extends AppCompatActivity implements GetAddressTask.OnAddressListener {
    private final static String TAG = "HttpRequestActivity";
    private TextView tv_location;
    private String mLocation = "";
    private LocationManager mLocationMgr;
    private Criteria mCriteria = new Criteria();
    private Handler mHandler = new Handler();
    private boolean bLocationEnable = false;
    private String mAddress = "";
    // 位置监听器
    private LocationListener mLocationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            setLocationText(location);
        }

        @Override
        public void onProviderDisabled(String arg0) {
        }

        @Override
        public void onProviderEnabled(String arg0) {
        }

        @Override
        public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
        }
    };
    private Runnable mRefresh = new Runnable() {
        @Override
        public void run() {
            if (bLocationEnable == false) {
                initLocation();
                mHandler.postDelayed(this, 1000);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_http_request);
        // 申请定位的动态权限
        locationPermissions();
        initWidget();
        initLocation();
        mHandler.postDelayed(mRefresh, 100);
    }

    // 定义定位的动态权限
    private void locationPermissions() {
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{
                    android.Manifest.permission.ACCESS_FINE_LOCATION}, 1);
        }
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{
                    android.Manifest.permission.ACCESS_COARSE_LOCATION}, 2);
        }
    }

    /**
     * 重写onRequestPermissionsResult方法
     * 获取动态权限请求的结果,再开启定位
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            initLocation();
        } else {
            Toast.makeText(this, "用户拒绝了权限", Toast.LENGTH_SHORT).show();
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    private void initWidget() {
        tv_location = (TextView) findViewById(R.id.tv_location);
        mLocationMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        mCriteria.setAccuracy(Criteria.ACCURACY_FINE);
        mCriteria.setAltitudeRequired(true);
        mCriteria.setBearingRequired(true);
        mCriteria.setCostAllowed(true);
        mCriteria.setPowerRequirement(Criteria.POWER_LOW);
    }

    private void initLocation() {
        String bestProvider = mLocationMgr.getBestProvider(mCriteria, true);
        if (bestProvider == null) {
            bestProvider = LocationManager.NETWORK_PROVIDER;
        }
        if (mLocationMgr.isProviderEnabled(bestProvider)) {
            tv_location.setText("正在获取" + bestProvider + "定位对象");
            mLocation = String.format("当前定位类型:%s", bestProvider);
            beginLocation(bestProvider);
            bLocationEnable = true;
        } else {
            tv_location.setText("\n" + bestProvider + "定位不可用");
            bLocationEnable = false;
        }
    }

    @Override
    public void onFindAddress(String address) {
        mAddress = address;
    }

    @SuppressLint("DefaultLocale")
    private void setLocationText(Location location) {
        if (location != null) {
            String desc = String.format("%s" + "\n定位时间:%s" + "\n经度:%f,纬度:%f" +
                            "\n海拔高度:%d米,定位精度:%d米" + "\n定位地址:%s",
                    mLocation, DateUtil.getNowDateTime("yyyy/MM/dd HH:mm:ss"),
                    location.getLongitude(), location.getLatitude(),
                    Math.round(location.getAltitude()), Math.round(location.getAccuracy()), mAddress);
            Log.d(TAG, desc);
            tv_location.setText(desc);
            GetAddressTask addressTask = new GetAddressTask();
            addressTask.setOnAddressListener(this);
            addressTask.execute(location);
        } else {
            tv_location.setText(mLocation + "\n暂未获取到定位对象");
        }
    }

    private void beginLocation(String method) {
        mLocationMgr.requestLocationUpdates(method, 300, 0, mLocationListener);
        Location location = mLocationMgr.getLastKnownLocation(method);
        setLocationText(location);
    }

    @Override
    protected void onDestroy() {
        if (mLocationMgr != null) {
            mLocationMgr.removeUpdates(mLocationListener);
        }
        super.onDestroy();
    }

}
  • 注意:由于该程序需要到定位和访问互联网,因此还需要在清单文件AndroidManifest.xml文件中授权定位、访问互联网以及查看网络状态的权限:
    <!-- 互联网 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 查看网络状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- 定位 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  • Demo程序运行效果界面截图如下:

这里写图片描述


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值