AsyncTask获取数据库数据

来源:互联网 发布:网络与新媒体概论笔记 编辑:程序博客网 时间:2024/06/02 03:04

2016_04_25

—————R

关于
http://blog.csdn.net/u010045971/article/details/50853651 未尽之事
1. 为应用程序添加provider的访问权限。
在manifest标签内,定义上面的提到的 permission

    <permission        android:name="com.example.android.sunshine.app.LICENSE_TO_KILL"        android:protectionLevel="dangerous"        android:label="Licenced to Kill">    </permission>

在application标签内,

<provider            android:name="com.xxx.app.data.WeatherProvider"      android:authorities="com.example.android.sunshine.app"      android:enabled="true"      android:exported="true"      android:permission="com.xxx.app.LICENSE_TO_KILL"/><!--be available to the third app-->

AsyncTask类

public abstract class AsyncTask<Params , Progress , Result> {}

通常在学习android的某部分内容(类)时,我会先看看一些好的介绍的博客
http://blog.csdn.net/liuhe688/article/details/6532519
这个里面涉及一些源码,是有利于理解的。

如果博客看的差不多了,有了大致的思路,我会再去看一下Developer下的内容。
https://developer.android.com/reference/android/os/AsyncTask.html

然后,你会发现,很多博客都是在翻译Developer中的内容,然后加上一些例子等。当然,这也说明了别人掌握了这一部分。

虽然还只一知半解,还是希望可以记录下来。

Usage

AsyncTask must be subclassed to be used. The subclass will override at least one method (doInBackground(Params…)), and most often will override a second one (onPostExecute(Result).)

创建一个类,继承AsyncTask,至少要覆写方法 doInBackground(Param…),onPostExecute(Result)很多时候也是需要覆写的。

举个栗子
这是一个DownloadFileTask,继承了AsyncTask,也覆写了几个方法。

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {     protected Long doInBackground(URL... urls) {         int count = urls.length;         long totalSize = 0;         for (int i = 0; i < count; i++) {             totalSize += Downloader.downloadFile(urls[i]);             publishProgress((int) ((i / (float) count) * 100));             // Escape early if cancel() is called             if (isCancelled()) break;         }         return totalSize;     }     protected void onProgressUpdate(Integer... progress) {         setProgressPercent(progress[0]);     }     protected void onPostExecute(Long result) {         showDialog("Downloaded " + result + " bytes");     } }

它的执行方式也很简单,这个是需要在UI thread中调用的。

new DownloadFilesTask().execute(url1, url2, url3);

AsyncTask’s generic types

The three types used by an asynchronous task are the following:

1. Params, the type of the parameters sent to the task upon execution.2. Progress, the type of the progress units published during the background computation.3. Result, the type of the result of the background computation.

Not all types are always used by an asynchronous task. To mark a type as unused, simply use the type Void:

public class MyTask extends AsyncTask<Void , Void , Void>{ ... }

继承AsyncTask时,需要填写的几个参数,Params,是你传入的参数值,Progress是描述进度的,Result表示后台计算的结果。
如果没有传入值或者不需要得到结果,可以将不用的参数置为Void。

The 4 steps

When an asynchronous task is executed, the task goes through 4 steps:

  1. onPreExecute()
    invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.

  2. doInBackground(Params…)
    invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress…) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress…) step.

  3. onProgressUpdate(Progress…)
    invoked on the UI thread after a call to publishProgress(Progress…). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.

  4. onPostExecute(Result)
    invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.

这是类中执行的几个方法的次序的说明:

首先,前面提到过的excute方法需要在UI thread中调用,方法中加入要用的参数:excute(Params…params)。调用该函数,它内部就会去调用我们覆写的这些方法,而不能去直接调用覆写的方法。

onPreExecute(),在execute(Params… params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

doInBackground(Params… params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress… values)来更新进度信息。

onProgressUpdate(Progress… values),在调用publishProgress(Progress… values)时,此方法被执行,直接将进度信息更新到UI组件上。

onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

Cancelling a task

A task can be cancelled at any time by invoking cancel(boolean).

Invoking this method will cause subsequent calls to isCancelled() to return true.

After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns.

To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)

Threading rules

There are a few threading rules that must be followed for this class to work properly:

  1. The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.

  2. The task instance must be created on the UI thread.

  3. execute(Params…) must be invoked on the UI thread.

  4. Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) manually.

  5. The task can be executed only once (an exception will be thrown if a second execution is attempted.)

在使用的时候,有几点需要格外注意:

  1. 异步任务的实例必须在UI线程中创建。
  2. execute(Params… params)方法必须在UI线程中调用。
  3. 不要手动调用onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法。
  4. 不能在doInBackground(Params… params)中更改UI组件的信息。
  5. 一个任务实例只能执行一次,如果执行第二次将会抛出异常。

FetchWeatherTask

实现异步任务机制有两种方式 :AsyncTask 和 Handler

通过API获得天气数据,然后对数据进行处理。
这里是根据location获取一定天数的天气情况。

openweatherMap的获取1-16天的天气的api
http://openweathermap.org/forecast16

http://api.openweathermap.org/data/2.5/forecast/daily?q=xxx&mode=xxx&units=xxx&cnt=xxx&APPID=xxxx

采用上述API,参数分别为
q为地点,在location表中对应的locationSetting,如 “Xi’an,China”
mode 可选 Json 、Xml

Example of API repond:

{“cod”:”200”,”message”:0.0032,
“city”:{“id”:1851632,”name”:”Shuzenji”,
“coord”:{“lon”:138.933334,”lat”:34.966671},
“country”:”JP”},
“cnt”:10,
“list”:[{
“dt”:1406080800,
“temp”:{
“day”:297.77,
“min”:293.52,
“max”:297.77,
“night”:293.52,
“eve”:297.77,
“morn”:297.77},
“pressure”:925.04,
“humidity”:76,
“weather”:[{“id”:803,”main”:”Clouds”,”description”:”broken clouds”,”icon”:”04d”}],}
]}

doInBackground(String… params){}

// String... params 意思是很多个String类型的参数,params[0],params[1],...@Overrideprotected Void doInBackground(String... params) {        // If there's no zip code, there's nothing to look up.  Verify size of params.        if (params.length == 0) {            return null;        }        String locationQuery = params[0];        // 为了可以在finally块中可以执行close操作,这两个变量需要在try/catch外定义        HttpURLConnection urlConnection = null;        BufferedReader reader = null;        // 将包含原始JSON响应当做字符串.        String forecastJsonStr = null;        String format = "json";        String units = "metric";        int numDays = 14;        String appId = "xxxxxxxxxxxxx"; //用自己的APPID替换这个        try {            // Construct the URL for the OpenWeatherMap query            // Possible parameters are avaiable at OWM's forecast API page, at            // http://openweathermap.org/API#forecast            final String FORECAST_BASE_URL =                   "http://api.openweathermap.org/data/2.5/forecast/daily?";            final String QUERY_PARAM = "q";            final String FORMAT_PARAM = "mode";            final String UNITS_PARAM = "units";            final String DAYS_PARAM = "cnt";            final String APP_ID = "APPID";        //根据定义的数据,拼接成访问的uri            Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon()                    .appendQueryParameter(QUERY_PARAM, params[0])                    .appendQueryParameter(FORMAT_PARAM, format)                    .appendQueryParameter(UNITS_PARAM, units)                    .appendQueryParameter(DAYS_PARAM, Integer.toString(numDays))                    .appendQueryParameter(APP_ID , appId)                    .build();            URL url = new URL(builtUri.toString());        /*            urlConnection = url.openConnection();                    setRequestMethod("GET");                    connect();            urlConnection.getInputStream();                    处理返回所得的响应。        */            // Create the request to OpenWeatherMap, and open the connection            urlConnection = (HttpURLConnection) url.openConnection();            urlConnection.setRequestMethod("GET");            urlConnection.connect();            // Read the input stream into a String            InputStream inputStream = urlConnection.getInputStream();            StringBuffer buffer = new StringBuffer();            if (inputStream == null) {                // Nothing to do.                return null;            }            reader = new BufferedReader(new InputStreamReader(inputStream));            String line;            while ((line = reader.readLine()) != null) {                // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)                // But it does make debugging a *lot* easier if you print out the completed                // buffer for debugging.                buffer.append(line + "\n");            }            if (buffer.length() == 0) {                // Stream was empty.  No point in parsing.                return null;            }            forecastJsonStr = buffer.toString(); //响应变为String类型            getWeatherDataFromJson(forecastJsonStr, locationQuery);        } catch (IOException e) {            Log.e(LOG_TAG, "Error ", e);            // If the code didn't successfully get the weather data, there's no point in attemping            // to parse it.            return null;        } catch (JSONException e) {            e.printStackTrace();        } finally {            if (urlConnection != null) {                urlConnection.disconnect();            }            if (reader != null) {                try {                    reader.close();                } catch (final IOException e) {                    Log.e(LOG_TAG, "Error closing stream", e);                }            }        }        // This will only happen if there was an error getting or parsing the forecast.        return null;    }

注意到getWeatherDataFromJson(forecastJsonStr , locaitonQuery);
locationQuery是我们传进来的参数params[0],forecastJsonStr是我们得到的响应值。

这个方法主要是将我们得到的14天的天气情况解析后,存到数据库中。

先看看我们需要哪些东西,前面API的JSON格式中,有哪些描述呢?
city 、name、coord
lat、lon
list
pressure、humidity、speed、deg
temp、max、min
weather、main、id
这里将要用到的常量部分定义好,这是个很好的习惯。

private void getWeatherDataFromJson(String forecastJsonStr,
String locationSetting)

        //Location information        final String OWM_CITY = "city";        final String OWM_CITY_NAME = "name";        final String OWM_COORD = "coord";        // Location coordinate        final String OWM_LATITUDE = "lat";        final String OWM_LONGITUDE = "lon";        // Weather information.  Each day's forecast info is an element of the "list" array.        final String OWM_LIST = "list";        final String OWM_PRESSURE = "pressure";        final String OWM_HUMIDITY = "humidity";        final String OWM_WINDSPEED = "speed";        final String OWM_WIND_DIRECTION = "deg";        // All temperatures are children of the "temp" object.        final String OWM_TEMPERATURE = "temp";        final String OWM_MAX = "max";        final String OWM_MIN = "min";        final String OWM_WEATHER = "weather";        final String OWM_DESCRIPTION = "main";        final String OWM_WEATHER_ID = "id";

接下来,我们需要把forecastJsonStr转化为JSON对象,然后从中按格式提取出各部分内容,再根据需要进行处理,最终将list部分存入数据库中。

处理Json数据部分是在try/catch部分中。

    JSONObject forecastJson = new JSONObject(forecastJsonStr);

list是一个Array,需要使用getJSONArray方法从JSONObject中提取出来。

    JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST);

其余的我们依次看

    //city里包含name、id等元素     JSONObject cityJson = forecastJson.getJSONObject(OWM_CITY);     String cityName = cityJson.getString(OWM_CITY_NAME);    //coord中包含latitude和longitude     JSONObject cityCoord = cityJson.getJSONObject(OWM_COORD);     double cityLatitude = cityCoord.getDouble(OWM_LATITUDE);     double cityLongitude = cityCoord.getDouble(OWM_LONGITUDE);

location的信息加入location表中
location表中需要有locationSetting,cityName,cityLatitude ,cityLongitude。

    long locationId = addLocation(locationSetting, cityName ,cityLatitude, cityLongitude);

location小插曲


locationSetting就是我们的locationQuery值,先查询表中是否有该Location信息,如果有,返回该条记录的ID,如果没有,则返回add后的id值。

    long addLocation(String locationSetting, String cityName, double lat, double lon ){        long locationId;        //先检查表中是否有该location信息        Cursor locationCursor = mContext.getContentResolver().query(                WeatherContract.LocationEntry.CONTENT_URI,                new String[]{WeatherContract.LocationEntry._ID},                WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING + " = ?",                new String[]{locationSetting},                null        );          if(locationCursor.moveToFirst()){            int locationIndex = locationCursor.getColumnIndex(WeatherContract.LocationEntry._ID);            locationId = locationCursor.getLong(locationIndex);             } // if it exists ,return the current ID        else {            //构造ContentValues            ContentValues locationValues = new ContentValues();            locationValues.put(WeatherContract.LocationEntry.COLUMN_CITY_NAME , cityName);                   locationValues.put(WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING, locationSetting);                                                                                                     locationValues.put(WeatherContract.LocationEntry.COLUMN_COORD_LAT,lat);            locationValues.put(WeatherContract.LocationEntry.COLUMN_COORD_LONG,lon);            Uri insertedUri = mContext.getContentResolver().insert(                            WeatherContract.LocationEntry.CONTENT_URI,                            locationValues            );            locationId = ContentUris.parseId(insertedUri);        }        locationCursor.close();        return locationId;  }

插曲结束

得到返回的locationId后,我们开始要给weather表中添数据了。

    Vector<ContentValues> cVVector = new Vector<ContentValues>(weatherArray.length());

关于返回的时间的一些说法,

OWM returns daily forecasts based upon the local time of the city that is being asked for, which means that we need to know the GMT offset to translate this data properly.

Since this data is also sent in-order and the first day is always the current day, we’re going to take advantage of that to get a nice normalized UTC date for all of our weather.

    Time dayTime = new Time();    dayTime.setToNow();    //we start at the day returned by local time.Otherwise this is a mess.    int julianStartDay = Time.getJulianDay(System.currentTimeMillis(), dayTime.gmtoff);    //now we work exclusively in UTC    dayTime = new Time();

我还是没太看懂。。。

填充今天和之后13天的天气数据,填充数据时,依据返回的JSON格式
所以,务必要准确判断是否为Array,以及它的名字,

不过,名字常量已经在前面定义过了。
list为array,大部分都同前面的Example差别不大.

    for(int i = 0; i < weatherArray.length() ; i++){        //这些是对应于weather表中的列项        long dateTime; //日期        double pressure;         int humidity;        double windSpeed;        double windDirection;        double hign;        double low;        String description;        int weatherId; //这个Id其实是表示哪个icon的(☀️,⛅️等)        ...    }

Get the JSON object representing the day

    JSONObject dayForecast = weatherArray.getJSONObject(i);    //Cheating to convert this to UTC time , which is what we want anyhow    dateTime = dayTime.setJulianDay(julianStartDay+i);    pressure = dayForecast.getDouble(OWM_PRESSURE);    humidity = dayForecast.getInt(OWM_HUMIDITY);    windSpeed = dayForecast.getDouble(OWM_WINDSPEED);    windDirection = dayForecast.getDouble(OWM_WIND_DIRECTION);    //weather:[{}]是个array,但只有一个Object    JSONObject weatherObject = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0);    description = weatherObject.getString(OWM_DESCRIPTION);    weatherId = weatherObject.getInt(OWM_WEATHER_ID);    //temp:{}     JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE);    high = temperatureObject.getDouble(OWM_MAX);    low = temperatureObject.getDouble(OWM_MIN);     ContentValues weatherValues = new ContentValues();    weatherValues.put(WeatherEntry.COLUMN_LOC_KEY, locationId);    weatherValues.put(WeatherEntry.COLUMN_DATE, dateTime);    weatherValues.put(WeatherEntry.COLUMN_HUMIDITY, humidity);    weatherValues.put(WeatherEntry.COLUMN_PRESSURE, pressure);    weatherValues.put(WeatherEntry.COLUMN_WIND_SPEED, windSpeed);                   weatherValues.put(WeatherEntry.COLUMN_DEGREES, windDirection);    weatherValues.put(WeatherEntry.COLUMN_MAX_TEMP, high);    weatherValues.put(WeatherEntry.COLUMN_MIN_TEMP, low);                       weatherValues.put(WeatherEntry.COLUMN_SHORT_DESC, description);    weatherValues.put(WeatherEntry.COLUMN_WEATHER_CONTIDITION_ID, weatherId);    cVVector.add(weatherValues);

等待list中全部填充 cVVector后,

    int inserted = 0;    if( cVVector.size() > 0 ){        // bulkInsert        ContentValues[] cvArray = new ContentValues[cVVector.size()];        cVVector.toArray(cvArray);                                                                                            mContext.getContentResolver().bulkInsert(WeatherEntry.CONTENT_URI, cvArray);    }

最后一部分

catch(JSONException e){    Log.e(LOG_TAG , e.getMessage() , e);    e.printStackTrace();}

现在对其上用到过的没有定义的一些变量做说明

全局变量:

private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();private final Context mContext;//构造函数public FetchWeatherTask(Context context){    mContext = context ;}

至此,对AsyncTask的工作完毕。
在哪里调用异步任务呢,前面说了,需要在UI thread中。

在ForecastFragment中,定义了一个updateWeather方法,这个方法里面,调用了FetchWeatherTask的excute方法。

一旦涉及到location的变化时,或者是Refresh时,我们就要调用此方法。

public void updateWeather(){    FetchWeatherTask weatherTask = new FetchWeatherTask(getActivity());    String location = Utility.getPreferredLocation(getActivity());    weatherTask.execute(location);}

比如说,menu中的refresh按钮

@Overridepublic boolean onOptionsItemSelected(MenuItem item){    int id = item.getItemId();    if(id == R.id.action_refresh){        updateWeather();        return true;    }    if(id == R.id.action_settings){        ...    }    return super.onOptionsItemSelected(item);} 

location变化时,UI 需要重新 load

void onLocationChanged(){    updateWeather();    getLoaderManager().restartLoader(FORECAST_LOADER , null ,this);}
0 0
原创粉丝点击