安卓的用户界面线程(user interface thread)

1.1 主线程

安卓修改用户界面并从一个单一用户界面线程中处理输入事件,这个线程也被称作主线程(main thread)

Android将所有的事件都收集进一个队列中,并且作为Looper类的一个实例来进行处理

1.2为什么要利用并发性

如果程序不使用任何并发结构,那么一个安卓app的所有代码都运行在一个主线程中,而且每一段代码都要在等待它前面的代码运行完之后才能运行。

如果有一个持续时间比较长的操作,比如从网络上加载数据,那么这个应用就会被卡住,必须一直等到相应的操作完成。

要想有一个好的用户体验,所有潜在的,运行较慢的操作都应该构建异步。

在安卓中使用java线程结构

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
}
}

aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkJDQzA1MTVGNkE2MjExRTRBRjEzODVCM0Q0NEVFMjFBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkJDQzA1MTYwNkE2MjExRTRBRjEzODVCM0Q0NEVFMjFBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QkNDMDUxNUQ2QTYyMTFFNEFGMTM4NUIzRDQ0RUUyMUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QkNDMDUxNUU2QTYyMTFFNEFGMTM4NUIzRDQ0RUUyMUEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6p+a6fAAAAD0lEQVR42mJ89/Y1QIABAAWXAsgVS/hWAAAAAElFTkSuQmCC" alt="" data-s="300,640" data-type="png" data-src="http://mmbiz.qpic.cn/mmbiz/2U1wplxhy5z9qeMK0Wy4eic0TpM5n2WuWDFibSdkGPAWUH9xKW9N2hUS1MenzSAGqSolcaEia6JptPNqjT7rbslYg/0?wx_fmt=png" data-ratio="1.4857768052516411" data-w="457" />

利用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 { }
}
} }

安卓利用Handlers,AsyncTask和Loaders运行后台程序的更多相关文章

  1. ansible shell 之运行后台程序

    最近在使用ansible shell模块启动一个shell编写的脚本,该脚本主要功能式加载java的classpath并在后台运行这个java程序. 该脚本在linux shell中可以正常启动和停止 ...

  2. 利用btrace工具监控在线运行java程序

     一.作用 可以用于对运行中java程序进行诊断监控分析,也可以用于开发阶段查看一些异常信息或者调用过程(如有些第三方代码没有源代码,不便于debug调试). 注:如果用于对在线运行系统的诊断,需 ...

  3. nohup 运行后台程序

    写了个程序,大概就是日志文件快写满的时候自动删日记.然后 java -jar  log_delete.jar  &  跑起来.过两天ps发现程序没有了, 网上一查明白是因为:通过终端(shel ...

  4. [转]【安卓笔记】AsyncTask源码剖析

    [转][安卓笔记]AsyncTask源码剖析 http://blog.csdn.net/chdjj/article/details/39122547 前言: 初学AsyncTask时,就想研究下它的实 ...

  5. 解决Linux关闭终端(关闭SSH等)后运行的程序或者服务自动停止【后台运行程序】

    问题描述:当SSH远程连接到服务器上,然后运行一个服务 ./catalina.sh start,然后把终端开闭(切断SSH连接)之后,发现该服务中断,导致网页无法访问.   解决方法:使用nohup命 ...

  6. 【翻译】利用Qt设计师窗体在运行时创建用户界面(Creating a user interface from a Qt Designer form at run-time)

    利用Qt设计师窗体在运行时创建用户界面 我们利用Calculator窗体例子中创建的窗体(Form)来展示当一个应用(application)已经生成后,是可以在其运行时产生与例子中相同的用户界面. ...

  7. 利用jdk自带的运行监控工具JConsole观察分析Java程序的运行

    利用jdk自带的运行监控工具JConsole观察分析Java程序的运行 原文链接 一.JConsole是什么 从Java 5开始 引入了 JConsole.JConsole 是一个内置 Java 性能 ...

  8. 安卓手机上运行 PC-E500 程序

    目录 第1章安卓手机上运行 PC-E500 程序    1 1 PockEmul    1 2 下载    1 3 打包BASIC程序    2 4 配置PC-E500模拟器    5 5 载入e50 ...

  9. 让java程序在后台一直执行(例如putty关闭后后台程序继续运行)

    如果在终端中执行java -jar xxx.jar&命令,当终端关闭时,xxx.jar也会同时结束运行,但是如果执行nohup java -jar xxx.jar&命令,则程序会在后台 ...

随机推荐

  1. python学习笔记(十四): unittest

    Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作. 在说unittest之前,先说几个概念: TestC ...

  2. 仅用CSS3创建h5预加载交错圈

    <head> <meta charset="UTF-8"> <title></title> <style type=" ...

  3. springcloud (一) 介绍

    开启springcloud 之旅... 传统单体架构: 微服务架构: 每个模块独立运行,就是独立的进程. 接下来基于springboot 来构建微服务: 1, 客户端->订单微服务->用户 ...

  4. Tkinter画布-Canvas

    Python - Tkinter画布-Canvas: Canvas是一个长方形的面积,图画或其他复杂的布局.可以放置在画布上的图形,文字,部件,或是帧 Canvas是一个长方形的面积,图画或其他复杂的 ...

  5. SpringMVC配置过程中出现的问题!

    <c:set var="ctx" value="${pageContext.request.contextPath}" />不起作用,原因是web. ...

  6. 关于 Apache Shiro 详解

    1.1  简介 Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Securi ...

  7. <转>Linux 环境进程间通信(六)

    http://www.ibm.com/developerworks/cn/linux/l-ipc/part6/ 一个套接口可以看作是进程间通信的端点(endpoint),每个套接口的名字都是唯一的(唯 ...

  8. linux运维笔记——curl

    ** 1.获取网站返回码 ** [root@Cacti ~]# curl -I www.qq.com HTTP/1.1 200 OK Server: squid/3.4.1 Date: Wed, 08 ...

  9. ubuntu 下安装nanomsg和nnpy

    nanomsg nanomsg是ZeroMQ作者用C语言重写的一个Socket库,其用法和模式和ZeroMQ差不多,但是具有更好的性能和更完善的接口. 首先下载源码 wget https://gith ...

  10. Sqlserver2005中的varchar,varchar,char,nchar的比较

    C#窗体中的TextBox 的MaxLength:与Nvarchar类似,不论是什么,最多只能为2.我我11我1