【Android 基础7.1 7.2】AsyncTask & AsyncTaskLoader

App Overview

I Use Douban Api instead of Google Book Api for this demo.

demoe

AsyncTask Inro

AsyncTask is an abstract class, which means you must subclass it in order to use it. In this example the AsyncTask performs a very simple background task: it sleeps for a random amount of time. In a real app, the background task could perform all sorts of work, from querying a database, to connecting to the internet, to calculating the next Go move to beat the current Go champion.

An AsyncTask subclass has the following methods for performing work off of the main thread:

  • onPreExecute(): This method runs on the UI thread, and is used for setting up your task (like showing a progress bar).
  • doInBackground(): This is where you implement the code to execute the work that is to be performed on the separate thread.
  • onProgressUpdate(): This is invoked on the UI thread and used for updating progress in the UI (such as filling up a progress bar)
  • onPostExecute(): Again on the UI thread, this is used for updating the results to the UI once the AsyncTask has finished loading.

asynctask

So, in a word, onPreExecute() , onProgressUpdate() and onPostExecute() is run on UI thread, doINBackground() run on worker thread.

Params

When you create an AsyncTask subclass, you can configure it using these parameters:

  • Params: The data type of the parameters sent to the task upon executing the doInBackground() override method.
  • Progress: The data type of the progress units published using the onProgressUpdated()override method.
  • Result: The data type of the result delivered by the onPostExecute() override method.

Simple implement

When onPreExecute() , onProgressUpdate() and onPostExecute() is run on UI thread, we can define a callback to expose these method, so we can call in Activity, and do not need to pass UI elements to AsyncTask. So In Java, a callback is a interface.

  • Define a callback:

      interface Callback<T, STATUS> {
          fun onNext(t: T)
          fun onUpdate(status: STATUS)
          fun onError(t: Throwable)
      }
    
  • Hold a weak reference in AsyncTask:

    class SearchMovieAsyncTask(private val callback: WeakReference<Callback<ArrayList<Movie>, Boolean>>) :
        AsyncTask<String, Boolean, ArrayList<Movie>?>() {
    }
    
      override fun doInBackground(vararg querys: String?): ArrayList<Movie>? {
          val query = querys[0] ?: return null
          publishProgress(true)
          return NetUtil.getMovieList(query)
      }
    
      override fun onProgressUpdate(vararg values: Boolean?) {
          callback.get()?.onUpdate(values[0] ?: true)
      }
    
      override fun onPostExecute(result: ArrayList<Movie>?) {
          super.onPostExecute(result)
          if (result != null) {
              callback.get()?.onNext(result)
          } else {
              callback.get()?.onError(NullPointerException(result))
          }
      }
    
  • Implement callback in MainActivity:

            callback = object : SearchMovieAsyncTask.Callback<ArrayList<Movie>, Int> {
                override fun onUpdate(status: Int) {
                    progressBar.visibility = View.VISIBLE
                }
    
                override fun onNext(movies: ArrayList<Movie>) {
                    adapter.clear()
                    adapter.addAll(movies)
                    progressBar.visibility = View.INVISIBLE
                }
    
                override fun onError(t: Throwable) {
                    Toast.makeText(this@MainActivity, "Error: ${t.message}", Toast.LENGTH_SHORT).show()
                }
    
            }
    

AsyncTaskLoader

This is same as define a custom callback, but Android already done it for us.It called AsyncTaskLoader.

If you want a more flexible interaction a custom callback is ok.

Create an AsyncTaskLoader

import android.support.v4.content.AsyncTaskLoader

class MovieLoader(val mContext: WeakReference<Context>, val query: String) :
    AsyncTaskLoader<ArrayList<Movie>>(mContext.get()!!) {
}

Implement loadInBackground

Notice the similarity between this method and the initial doInBackground() method from AsyncTask.

    override fun loadInBackground(): ArrayList<Movie>? {
        return NetUtil.getMovieList(query)
    }

Implement required methods

Inside the onStartLoading() method stub, call forceLoad() to start the loadInBackground() method. The loader will not start loading data until you call the forceLoad() method.

    override fun onStartLoading() {
        super.onStartLoading()
        forceLoad()
    }

Modify MainActivity

class MainActivity : AppCompatActivity(), LoaderManager.LoaderCallbacks<ArrayList<Movie>> {
        override fun onCreateLoader(id: Int, args: Bundle?): Loader<ArrayList<Movie>> {
        return MovieLoader(WeakReference(this), args?.getString("query")!!)
    }

    override fun onLoadFinished(loader: Loader<ArrayList<Movie>>, list: ArrayList<Movie>?) {
        adapter.clear()
        adapter.addAll(list ?: emptyList())
        progressBar.visibility = View.INVISIBLE
    }

    override fun onLoaderReset(list: Loader<ArrayList<Movie>>) {
    }
}

Start Loader

The restartLoader() method is defined by the LoaderManager, which manages all the loaders used in an activity or fragment. Each activity has exactly one LoaderManager instance that is responsible for the lifecycle of the Loaders that the activity manages.

val queryBundle = Bundle()
queryBundle.putString("query", s)
Log.d(TAG, "onQueryTextSubmit: restart loader")
this@MainActivity.supportLoaderManager.restartLoader(0, queryBundle, this@MainActivity)

Restore Loader

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_search)
        
        if (supportLoaderManager.getLoader<ArrayList<Movie>>(0) != null) {
            supportLoaderManager.initLoader(0, null, this);
        }
     }

NetUtil

Without any library, just use raw java to do network and parse json:

object NetUtil {

    const val TAG = "NetUtil"
    val API = "https://api.douban.com/v2/movie/search?q=%s&apikey=0b2bdeda43b5688921839c8ecb20399b&start=0&count=20"

    fun getMovieList(query: String): ArrayList<Movie>? {
        val url = URL(API.format(query))

        var urlConnection: URLConnection? = null
        var bufferReader: BufferedReader? = null
        var json: String = ""

        try {
            urlConnection = url.openConnection() as HttpURLConnection
            with(urlConnection) {
                requestMethod = "GET"
                connect()
            }
            val inputStream = urlConnection.inputStream
            bufferReader = BufferedReader(InputStreamReader(inputStream))
            Log.d(TAG, "doInBackground: start read")
            json = buildString {
                var line = bufferReader.readLine()
                while (line != null) {
                    append(line)
                    append("\n")
                    line = bufferReader.readLine()
                }
            }
            if (json.isEmpty()) {
                return null
            }
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            bufferReader?.close()
        }
        Log.d(TAG, "doInBackground: $json")

        val movieList = ArrayList<Movie>()
        try {
            val jsonObject = JSONObject(json)
            val movieArray = jsonObject.getJSONArray("subjects")
            var i = 0
            while (i < movieArray.length()) {
                val subject = movieArray.getJSONObject(i)
                val title = subject.getString("title")
                val year = subject.getString("year")
                movieList.add(Movie(subject.getString("id"), title, year))
                i++
                Log.d(TAG, "doInBackground: title: $title, year: $year")
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return movieList
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值