安卓的用户界面线程(user interface thread)java
1.1 主线程android
安卓修改用户界面并从一个单一用户界面线程中处理输入事件,这个线程也被称做主线程(main thread)程序员
Android将全部的事件都收集进一个队列中,而且做为Looper类的一个实例来进行处理数据库
1.2为何要利用并发性apache
若是程序不使用任何并发结构,那么一个安卓app的全部代码都运行在一个主线程中,并且每一段代码都要在等待它前面的代码运行完以后才能运行。缓存
若是有一个持续时间比较长的操做,好比从网络上加载数据,那么这个应用就会被卡住,必须一直等到相应的操做完成。网络
要想有一个好的用户体验,全部潜在的,运行较慢的操做都应该构建异步。并发
在安卓中使用java线程结构app
2.1使用java中的Thread和Thread pools异步
安卓支持使用Thread类来实现异步运行,安卓也支持用java.util.concurrent包中的类来实现后台运行,好比使用ThreadPools和Executor类,若是你想在新的线程中更新用户界面,你须要与用户界面线程进行同步。
2.2在Android中使用java Thread类的弊端
若是使用Thread类,就必须在你的代码中处理如下需求
若是要将结果返回到用户界面,则须要和主线程同步
默认不会取消线程
没有默认的线程池
没有默认的处理配置更改
由于这些限制,安卓开发者通常会使用android特殊的处理方式。
安卓中的并发结构
Android提供了另外的结构来处理异步性,可使用android.os.Handler类或者AsyncTasks类,更加复杂的方法是基于Loader类的留存片断及服务
Handler
3.1使用Handler类的目的
Handler能够登记到一个线程中而且提供一个简单的通道来传送数据到这个线程中。
一个Handler对象将本身与它建立的线程相关联。好比说在onCreate()方法中建立Handler类的实例,则此Handler能够传送数据到主线程中。
经过Handler的数据能够是Message的一个实例,或者是Runnable的一个实例
3.2建立并重用Handler实例
要想使用handler必须重写handleMessage()方法来处理messages线程能够经过sendMessage(Message)来传送messages,或者使用sendEmptyMessage()
在Runnable中,可使用post()方法,为了不建立对象,能够重用activity中已经存在的Handler对象。
Handler = getWindow().getDecorView().getHandler();
View类容许经过post()方法传递Runnable类型的对象。
3.3举例
如下代码演示如何经过View来使用Handler
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ProgressBar android:id="@+id/progressBar1" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:indeterminate="false" android:max="10" android:padding="4dip" > </ProgressBar> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" > </TextView> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="startProgress" android:text="Start Progress" > </Button> </LinearLayout>
package de.vogella.android.handler; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; public class ProgressTestActivity extends Activity { private ProgressBar progress; private TextView text; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); progress = (ProgressBar) findViewById(R.id.progressBar1); text = (TextView) findViewById(R.id.textView1); } public void startProgress(View view) { // do something long Runnable runnable = new Runnable() { @Override public void run() { for (int i = 0; i <= 10; i++) { final int value = i; doFakeWork(); progress.post(new Runnable() { @Override public void run() { text.setText("Updating"); progress.setProgress(value); } }); } } }; new Thread(runnable).start(); } // Simulating something timeconsuming private void doFakeWork() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }
AsyncTask
4.1使用AsyncTask类的目的
AsyncTask类将建立后台运行程序和与主线程同步的全部操做都封装起来。它还支持通知运行中的程序的进度
4.2使用AsyncTask类
想使用AsyncTask必须继承它,AsyncTask使用泛型和可变参数。参数以下AsyncTask <TypeOfVarArgParams , ProgressValue , ResultValue>
一个AsyncTask经过execute方法开始
Execute()方法调用doInBackground()和onPostExecute()方法。
TypeOfVarArgParams 传递到doInBackground()方法中,ProgressValue做为进度信息,ResultValue必须从doInBackground()方法中返回并做为参数传递到onPostExecute()方法中。
doInBackground()方法包含在后台线程中执行的程序,这个方法在一个单独的线程中自动运行。
onPostExecute()方法与主线程同步,并更新主线程,这个方法在doInBackground()完成后被调用。
4.3多个异步任务的并行执行
在Android中可使用executeOnExecutor()方法来实现并行执行,将AsyncTask.THREAD_POOL_EXECUTOR做为第一个参数。
// ImageLoader extends AsyncTask ImageLoader imageLoader = new ImageLoader(imageView); // Execute in parallel imageLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "http://url.com/image.png");
4.4AsyncTask的弊端
AsyncTask不会自动处理配置的变化,若是活动被从新建立,程序员必须在代码中本身处理。解决这种问题的通常方法是在一个保留片断中声明AsyncTask
4.5例子
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/readWebpage" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onClick" android:text="Load Webpage" > </Button> <TextView android:id="@+id/TextView01" android:layout_width="match_parent" android:layout_height="match_parent" android:text="Placeholder" > </TextView> </LinearLayout>
package de.vogella.android.asynctask; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import de.vogella.android.asyntask.R; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.TextView; public class ReadWebpageAsyncTask extends Activity { private TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView = (TextView) findViewById(R.id.TextView01); } private class DownloadWebPageTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... urls) { String response = ""; for (String url : urls) { DefaultHttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url); try { HttpResponse execute = client.execute(httpGet); InputStream content = execute.getEntity().getContent(); BufferedReader buffer = new BufferedReader(new InputStreamReader(content)); String s = ""; while ((s = buffer.readLine()) != null) { response += s; } } catch (Exception e) { e.printStackTrace(); } } return response; } @Override protected void onPostExecute(String result) { textView.setText(result); } } public void onClick(View view) { DownloadWebPageTask task = new DownloadWebPageTask(); task.execute(new String[] { "http://www.vogella.com" }); } }
后台程序运行及生命周期处理
5.1当配置发生改变时恢复状态
在使用线程的时候,面临的一个挑战,就是须要考虑到应用的生命周期。安卓系统会杀死activity或者产生一个能够触发activity重启的配置变化。
同时,你还要处理已经打开的对话框,由于对话框也与activity相关联,当activity重启的时候,访问现有的对话框时,会获得一个View not attached to window manager 的异常
可使用onRetainNonConfigurationInstance()方法来保存对象,经过getLastNonConfigurationInstance()方法来取回以前保存的对象。若是活动是第一次启动,或者经过finish()方法结束,则getLastNonConfigurationInstance()方法返回null,可是onRetainNonConfigurationInstance()在API13以后就被废除了,建议使用fragment和setRetainInstace()方法来保存数据。
5.2使用appliccation来存储对象
若是当配置发生变化时,多于一个对象要被存储在活动中,则能够经过继承Application类来实现,须要在AndroidManifest.xml中配置,将继承了Application类的类名分配给android:name属性。
<application android:icon="@drawable/icon" android:label="@string/app_name" android:name="MyApplicationClass"> <activity android:name=".ThreadsLifecycleActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
应用类将会被Android runtime自动建立,而且一直可用,除非整个应用进程终止。
这个类能够用来获取那些能够在活动之间传递或者在整个应用周期中均可用的数据,在onCreate()方法里,你能够建立对象而且将他们设为public或者建立getter方法以便获取他们。
onTerminate()方法只是被用来测试。若是Android终止进程,则全部的相关资源都会被释放。
能够经过getApplication()来获得Application类
Loader
6.1 使用Loader的目的
Loader类能够容许你在活动或者碎片中异步加载数据,他们能够监控数据源而且当内容发送变化时,将最新的结果传递出来。可以保持数据,即便环境发送变化。
若是结果被Loader恢复,当对象和它的父(活动或者碎片)已经失去联系的时候,Loader能够缓存数据。
6.2 实现一个Loader
可使用一抽象类AsyncTaskLoader做为你本身的Loader的基础,并实现它
LoaderManager管理一个或多个Loader实例,能够如下面的方法来建立一个Loader
# start a new loader or re-connect to existing one getLoaderManager().initLoader(0, null, this);
第一个参数是一个惟一的id,用于在回调时辨别是哪一个Loader。第二个参数是一个bundle,用于携带信息,第三个参数回调类,必须继承LoaderManger.LoaderCallbacks接口、
Loader不是直接经过调用getLoaderManager().initLoader()方法建立的,而是必须在回调方法onCreateLoader()中建立。一旦Loader完成了读取数据的工做,就会触发onLoadFinished()方法,在这里方法里能够更新用户界面
6.3 SQLite数据库和CursorLoader
Android提供了一个Loader,这个Loader默认实现了如何和SQlite数据库相关联的操做,即
CursorLoader类
若是Cursor没法使用时,onLoaderReset()方法就会被触发。
Custom Loader for preference
下面的代码将建立一个自定义的loader,并实现管理偏好。
package com.vogella.android.loader.preferences; import android.content.AsyncTaskLoader; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; public class SharedPreferencesLoader extends AsyncTaskLoader<SharedPreferences> implements SharedPreferences.OnSharedPreferenceChangeListener { private SharedPreferences prefs = null; public static void persist(final SharedPreferences.Editor editor) { editor.apply(); } public SharedPreferencesLoader(Context context) { super(context); } // Load the data asynchronously @Override public SharedPreferences loadInBackground() { prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); prefs.registerOnSharedPreferenceChangeListener(this); return (prefs); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // notify loader that content has changed onContentChanged(); } /** * starts the loading of the data * once result is ready the onLoadFinished method is called * in the main thread. It loader was started earlier the result * is return directly * method must be called from main thread. */ @Override protected void onStartLoading() { if (prefs != null) { deliverResult(prefs); } if (takeContentChanged() || prefs == null) { forceLoad(); } } }
在活动中使用此loader
package com.vogella.android.loader.preferences; import android.annotation.SuppressLint; import android.app.Activity; import android.app.LoaderManager; import android.content.Loader; import android.content.SharedPreferences; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity implements LoaderManager.LoaderCallbacks<SharedPreferences> { private static final String KEY = "prefs"; private TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.prefs); getLoaderManager().initLoader(0, null, this); } @Override public Loader<SharedPreferences> onCreateLoader(int id, Bundle args) { return (new SharedPreferencesLoader(this)); } @SuppressLint("CommitPrefEdits") @Override public void onLoadFinished(Loader<SharedPreferences> loader, SharedPreferences prefs) { int value = prefs.getInt(KEY, 0); value += 1; textView.setText(String.valueOf(value)); // update value SharedPreferences.Editor editor = prefs.edit(); editor.putInt(KEY, value); SharedPreferencesLoader.persist(editor); } @Override public void onLoaderReset(Loader<SharedPreferences> loader) { // NOT used } }
利用service运行后台任务
下面的代码将实现下载图片,而且展现有个dialog,直到下载完成,这个dialog才会消失。要确保即便活动重启线程也被保留下来。
public class ThreadsLifecycleActivity extends Activity { // Static so that the thread access the latest attribute private static ProgressDialog dialog; private static Bitmap downloadBitmap; private static Handler handler; private ImageView imageView; private Thread downloadThread; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // create a handler to update the UI handler = new Handler() { @Override public void handleMessage(Message msg) { imageView.setImageBitmap(downloadBitmap); dialog.dismiss(); } }; // get the latest imageView after restart of the application imageView = (ImageView) findViewById(R.id.imageView1); Context context = imageView.getContext(); System.out.println(context); // Did we already download the image? if (downloadBitmap != null) { imageView.setImageBitmap(downloadBitmap); } // check if the thread is already running downloadThread = (Thread) getLastNonConfigurationInstance(); if (downloadThread != null && downloadThread.isAlive()) { dialog = ProgressDialog.show(this, "Download", "downloading"); } } public void resetPicture(View view) { if (downloadBitmap != null) { downloadBitmap = null; } imageView.setImageResource(R.drawable.icon); } public void downloadPicture(View view) { dialog = ProgressDialog.show(this, "Download", "downloading"); downloadThread = new MyThread(); downloadThread.start(); } // save the thread @Override public Object onRetainNonConfigurationInstance() { return downloadThread; } // dismiss dialog if activity is destroyed @Override protected void onDestroy() { if (dialog != null && dialog.isShowing()) { dialog.dismiss(); dialog = null; } super.onDestroy(); } // Utiliy method to download image from the internet static private Bitmap downloadBitmap(String url) throws IOException { HttpUriRequest request = new HttpGet(url); HttpClient httpClient = new DefaultHttpClient(); HttpResponse response = httpClient.execute(request); StatusLine statusLine = response.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode == 200) { HttpEntity entity = response.getEntity(); byte[] bytes = EntityUtils.toByteArray(entity); Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); return bitmap; } else { throw new IOException("Download failed, HTTP response code " + statusCode + " - " + statusLine.getReasonPhrase()); } } static public class MyThread extends Thread { @Override public void run() { try { // Simulate a slow network try { new Thread().sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } downloadBitmap = downloadBitmap("http://www.devoxx.com/download/attachments/4751369/DV11"); // Updates the user interface handler.sendEmptyMessage(0); } catch (IOException e) { e.printStackTrace(); } finally { } } } }