Android学习笔记
Android 开发者基础知识 (Java) —— Google Developers 培训团队
文章目录
第3单元 在后台运行
第7课 后台任务
87.AsyncTask 和 AsyncTaskLoader
你会做什么
- 使用 Google APIs Explorer 了解 Books API。
- 创建“谁写的?” 应用程序,它使用工作线程查询 Books API 并在 UI 中显示结果。
- 修改“谁写的?” 应用程序使用 an
AsyncTaskLoader
而不是AsyncTask
.
87.1 探索Google Books API
-
发送Books API请求
地址:https://developers.google.com/apis-explorer/
-
分析Books API的响应
JSON格式
87.2 创建“谁写的?”应用程序
-
创建项目和用户界面UI
布局activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="16dp" tools:context=".MainActivity"> <TextView android:id="@+id/instructions" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/instructions" android:textAppearance="@style/TextAppearance.AppCompat.Title" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/bookInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:hint="@string/input_hint" android:inputType="text" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/instructions" /> <Button android:id="@+id/searchButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:onClick="searchBooks" android:text="@string/button_text" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/bookInput" /> <TextView android:id="@+id/titleText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:textAppearance="@style/TextAppearance.AppCompat.Headline" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/searchButton" /> <TextView android:id="@+id/authorText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:textAppearance="@style/TextAppearance.AppCompat.Headline" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/titleText" /> </androidx.constraintlayout.widget.ConstraintLayout>
-
获取用户输入
package com.dingjiaxiong.whowroteit; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private EditText mBookInput; private TextView mTitleText; private TextView mAuthorText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBookInput = (EditText) findViewById(R.id.bookInput); mTitleText = (TextView) findViewById(R.id.titleText); mAuthorText = (TextView) findViewById(R.id.authorText); } public void searchBooks(View view) { String queryString = mBookInput.getText().toString(); } }
-
创建AsyncTask类
FetchBook.java
package com.dingjiaxiong.whowroteit; import android.os.AsyncTask; import android.widget.TextView; import java.lang.ref.WeakReference; public class FetchBook extends AsyncTask<String,Void,String> { private WeakReference<TextView> mTitleText; private WeakReference<TextView> mAuthorText; public FetchBook(TextView mTitleText, TextView mAuthorText) { this.mTitleText = new WeakReference<>(mTitleText); this.mAuthorText = new WeakReference<>(mAuthorText); } @Override protected String doInBackground(String... strings) { return null; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); } }
-
创建Networkutils类并构建URL
-
发起请求
package com.dingjiaxiong.whowroteit; import android.net.Uri; import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class NetworkUtils { private static final String LOG_TAG = NetworkUtils.class.getSimpleName(); // Constants for the various components of the Books API request. // // Base endpoint URL for the Books API. private static final String BOOK_BASE_URL = "https://www.googleapis.com/books/v1/volumes?"; // Parameter for the search string. private static final String QUERY_PARAM = "q"; // Parameter that limits search results. private static final String MAX_RESULTS = "maxResults"; // Parameter to filter by print type. private static final String PRINT_TYPE = "printType"; /** * Static method to make the actual query to the Books API. * * @param queryString the query string. * @return the JSON response string from the query. */ static String getBookInfo(String queryString) { // Set up variables for the try block that need to be closed in the // finally block. HttpURLConnection urlConnection = null; BufferedReader reader = null; String bookJSONString = null; try { // Build the full query URI, limiting results to 10 items and // printed books. Uri builtURI = Uri.parse(BOOK_BASE_URL).buildUpon() .appendQueryParameter(QUERY_PARAM, queryString) .appendQueryParameter(MAX_RESULTS, "10") .appendQueryParameter(PRINT_TYPE, "books") .build(); // Convert the URI to a URL, URL requestURL = new URL(builtURI.toString()); // Open the network connection. urlConnection = (HttpURLConnection) requestURL.openConnection(); urlConnection.setRequestMethod("GET"); urlConnection.connect(); // Get the InputStream. InputStream inputStream = urlConnection.getInputStream(); // Create a buffered reader from that input stream. reader = new BufferedReader(new InputStreamReader(inputStream)); // Use a StringBuilder to hold the incoming response. StringBuilder builder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { // Add the current line to the string. builder.append(line); // Since this is 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. builder.append("\n"); } if (builder.length() == 0) { // Stream was empty. Exit without parsing. return null; } bookJSONString = builder.toString(); } catch (IOException e) { e.printStackTrace(); } finally { // Close the connection and the buffered reader. if (urlConnection != null) { urlConnection.disconnect(); } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } // Write the final JSON response to the log Log.d(LOG_TAG, bookJSONString); return bookJSONString; } }
-
添加网络权限
-
解析JSON字符串
MainActivity.java
package com.dingjiaxiong.whowroteit; import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private EditText mBookInput; private TextView mTitleText; private TextView mAuthorText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBookInput = (EditText) findViewById(R.id.bookInput); mTitleText = (TextView) findViewById(R.id.titleText); mAuthorText = (TextView) findViewById(R.id.authorText); } public void searchBooks(View view) { // Get the search string from the input field. String queryString = mBookInput.getText().toString(); // Hide the keyboard when the button is pushed. InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (inputManager != null) { inputManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } // Check the status of the network connection. ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = null; if (connMgr != null) { networkInfo = connMgr.getActiveNetworkInfo(); } // If the network is available, connected, and the search field // is not empty, start a FetchBook AsyncTask. if (networkInfo != null && networkInfo.isConnected() && queryString.length() != 0) { new FetchBook(mTitleText, mAuthorText).execute(queryString); mAuthorText.setText(""); mTitleText.setText(R.string.loading); } // Otherwise update the TextView to tell the user there is no // connection, or no search term. else { if (queryString.length() == 0) { mAuthorText.setText(""); mTitleText.setText(R.string.no_search_term); } else { mAuthorText.setText(""); mTitleText.setText(R.string.no_network); } } } }
运行程序
【当然手机用的另一个网,肯定是获取不到数据的…】
87.3 实施UI最佳实践
-
隐藏键盘并更新TextView
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (inputManager != null ) { inputManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } new FetchBook(mTitleText, mAuthorText).execute(queryString); mAuthorText.setText(""); mTitleText.setText(R.string.loading)
-
管理网络状态和空搜索字段的示例
ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = null; if (connMgr != null) { networkInfo = connMgr.getActiveNetworkInfo(); }
87.4 迁移到AsyncTaskLoader
项目地址:https://github.com/google-developer-training/android-fundamentals-apps-v2/tree/master/WhoWroteItLoader
- 创建一个 AsyncTaskLoader类
- 实现方法
- 修改MainActivity
- 实现加载器回调
87.5 小结
- 连接到网络的任务不应在 UI 线程上执行。如果您尝试在 UI 线程上进行网络连接或文件访问,Android 运行时通常会引发异常。
- 使用图书搜索 API 以编程方式访问 Google 图书。对 Google 图书的 API 请求采用 URL 的形式,响应是 JSON 字符串。
- 使用 Google APIs Explorer 以交互方式探索 Google APIs。
- 用于
getText()
从EditText
视图中检索文本。要将文本转换为简单字符串,请使用toString()
. - 该
Uri.buildUpon()
方法返回URI.Builder
可用于构造 URI 字符串的 a。 - 要连接到互联网,您必须在 Android 清单文件中配置网络权限:
<uses-permission android:name="android.permission.INTERNET" />
该类 AsyncTask
允许您在后台而不是在 UI 线程上运行任务:
- 要使用
AsyncTask
,您必须对其进行子类化。子类覆盖该doInBackground(Params...)
方法。通常子类也会覆盖该onPostExecute(Result)
方法。 - 要启动
AsyncTask
,请使用execute()
。 - 如果正在控制的活动停止(例如由于设备配置更改),
AsyncTask
则无法更新 UI 。AsyncTask
执行时AsyncTask
,它经过四个步骤:
onPreExecute()
在执行任务之前在 UI 线程上运行。此步骤通常用于设置任务,例如通过在 UI 中显示进度条。doInBackground(Params...)
完成后立即在后台线程上运行onPreExecute()
。此步骤执行可能需要很长时间的后台计算。onProgressUpdate(Progress...)
调用后在 UI 线程上运行publishProgress(Progress...)
。onPostExecute(Result)
后台计算完成后在 UI 线程上运行。计算结果传递给onPostExecute()
.
AsyncTaskLoader
是 loader 的等价物AsyncTask
。
AsyncTaskLoader
提供该loadInBackground()
方法,该方法在单独的线程上运行。- 的结果通过回调
loadInBackground()
的方式传递给 UI 线程。onLoadFinished()
LoaderManager
- 要创建和解析 JSON 字符串,请使用内置 Java JSON 类
JSONObject
和JSONArray
. - An
AsyncTaskLoader
使用AsyncTask
辅助类在后台完成工作,脱离主线程。 AsyncTaskLoader
实例由LoaderManager
.- 允许
LoaderManager
您使用.Activity``getSupportLoaderManager().initLoader()
【这种实现方式有点太那个了,现在应该还是OKhttp吧】