Android 进程和线程
版权声明 华清远见教育集团版权所有 ; 未经华清远见明确许可, 不得为任何目的以任何形式复制或传播此文档的任何部分 ; 本文档包含的信息如有更改, 恕不另行通知 ; 华清远见教育集团保留所有权利
Android 进程和线程 在安装 Android 应用程序的时候,Android 会为每个程序分配一个 Linux 用户 ID, 并设置相应的权限, 这样其它应用程序就不能访问此应用程序所拥有的数据和资源了 下图中, 两个 Android 应用程序, 各自运行在其自己的基本沙箱或进程上 它们有不同的 Linux user ID
Android 进程和线程
Android 进程和线程 当一个程序第一次启动时,Android 会同时启动一个对应的主线程 (Main Thread), 主线程主要负责处理与 UI 相关的事件, 如 : 用户的按键事件, 用户接触屏幕的事件以及屏幕绘图事件, 并把相关的事件分发到对应的组件进行处理 所以主线程通常又被叫做 UI 线程 在开发 Android 应用时必须遵守单线程模型的原则 : Android UI 操作并不是线程安全的并且这些操作必须在 UI 线程中执行
Android 进程和线程 如果在非 UI 线程中直接操作 UI 线程, 会抛出 android.view.viewroot$calledfromwrongthrea dexception: Only the original thread that created a view hierarchy can touch its views 由于 UI 线程负责事件的监听和绘图, 因此, 必须保证 UI 线程能够随时响应用户的需求,UI 线程里的操作应该像中断事件那样短小, 费时的操作 ( 如网络连接 ) 需要另开线程, 否则, 如果 UI 线程超过 5s 没有响应用户请求, 会弹出对话框提醒用户终止应用程序
Android 进程与线程 如果在新开的线程中需要对 UI 进行设定, 就可能违反单线程模型, 因此 android 采用一种复杂的 Message Queue 机制保证线程间通信 Message Queue 是一个消息队列, 用来存放通过 Handler 发布的消息 Android 在第一次启动程序时会默认会为 UI thread 创建一个关联的消息队列, 可以通过 Looper.myQueue() 得到当前线程的消息队列, 用来管理程序的一些上层组件, 如 Activities BroadcastReceivers 等等 你可以在自己的子线程中创建 Handler 与 UI thread 通讯
Handler 消息传递机制 Android 通过 Looper Handler 来实现消息循环机制, Android 消息循环是针对线程的 ( 每个线程都可以有自己的消息队列和消息循环 ) Android 系统中,Looper 负责管理线程的消息队列和消息循环 我们可以通过 Loop.myLooper() 得到当前线程的 Looper 对象, 通过 Loop.getMainLooper() 可以获得当前进程的主线程的 Looper 对象 一个线程可以存在 ( 当然也可以不存在 ) 一个消息队列和一个消息循环 (Looper) 构造 Handler 的时候可以指定一个 Looper 对象, 如果不指定则利用当前线程的 Looper 创建
Handler 消息传递机制
Handler 消息传递机制 在 Android 平台中, 新启动的线程是无法访问 Activity 中的 Widget 的, 当然也不能将运行状态传递出来, 这就需要有 Handler 机制了 另外, 正如前面所说, 我们希望能在一个子线程中获取数据并将其显示到界面上, 如果直接在子线程中设置界面, 则将会报错 (android.view.viewroot$calledfromwrongthr eadexception), 此时也需要使用 Handler 来处理
Handler 方法 描述 void handlemessage(message message) boolean sendemptymessage(int what) 通过这个方法接收消息 发送只有一个 what 值的消息 boolean sendmessage(message message) boolean hasmessage(int what) 发送消息到 Handler,Handler 即可用 handlemessage 处理 判断是否有 what 值的消息 boolean post(runnable r) 将一个线程添加到消息队列
Handler Example 1
Handler Example 2
AsyncTask Handler 和 AsyncTask, 都是为了不阻塞主线程 (UI 线程 ), 且 UI 的更新只能在主线程中完成, 因此异步处理是不可避免的 要使用 AsyncTask, 需要自己编写一个类, 继承 AsyncTask 类, 并且实现其中的方法 AsyncTask 定义了三种泛型类型 Params,Progress 和 Result, 即 AsyncTask<Params,Progress,Result> Params 启动任务执行的输入参数, 比如 HTTP 请求的 URL Progress 后台任务执行的百分比 Result 后台执行任务最终返回的结果, 比如 String
AsyncTask AsyncTask 的执行分为四个步骤, 每一步都对应一个回调方法, 开发者需要实现至少一个方法 (doinbackground(params...)) 这些方法都是回调方法, 在任务的执行过程中, 这些方法被自动调用 : onpreexecute(): 该方法将在执行实际的后台操作前被 UI thread 调用 可以在该方法中做一些准备工作, 如在界面上显示一个进度条 doinbackground(params...),: 将在 onpreexecute 方法执行后马上执行, 该方法运行在后台线程中 这里将主要负责执行那些很耗时的后台计算工作 该方法是抽象方法, 子类必须实现 在这个方法中可以调用 publishprogress() 方法来更新实时的任务进度
AsyncTask onprogressupdate(progress...): 在 publishprogress 方法被调用后,UI thread 将调用这个方法从而在界面上展示任务的进展情况, 例如通过一个进度条进行展示 onpostexecute(result): 在 doinbackground 执行完成后,onPostExecute 方法将被 UI thread 调用, 后台的计算结果将通过该方法传递到 UI thread.
AsyncTask 使用 AsyncTask 类, 以下是几条必须遵守的准则 : Task 的实例必须在 UI thread 中创建 execute 方法必须在 UI thread 中调用 不要手动的调用 onpreexecute(), onpostexecute(result), doinbackground(params...), onprogressupdate(progress...) 这几个方法 该 task 只能被执行一次, 否则多次调用时将会出现异常
AsyncTask
AsyncTask 一开始要在 res 文件夹底下新增两个 XML 文件, 接着将一开始新增的两个 XML 文件, 作为两个 View, 并使用 ViewSwitcher 的方法去做两个 View 之间的切换 当按下 更多 的按钮时,ViewSwitcher 就会切换到另外一个 View, 当后台任务处理完成后, 才会切换回原本的 View
AsyncTask 布局文件 (res/layout/button.xml): <?xml version="1.0" encoding="utf-8"?> <!-- 使用 ViewSwitcher 切换的第一个 View--> <Button xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/btn_loadmorecontacts" android:text=" 更多..." android:layout_width="fill_parent" android:layout_height="wrap_content" android:textappearance="?android:attr/textappearancelarge" android:minheight="?android:attr/listpreferreditemheight" android:textcolor="#ffffff" android:background="@android:drawable/list_selector_backgro und" android:clickable="true" android:onclick="onclick" />
AsyncTask 布局文件 (res/layout/progress.xml): <?xml version="1.0" encoding="utf-8"?> <!-- 使用 ViewSwitcher 切换的第二个 View--> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:id="@+id/relativelayout1" android:minheight="?android:attr/listpreferreditemheight"> <ProgressBar android:id="@+id/progressbar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centervertical="true" /> <TextView android:text="loading " android:textappearance="?android:attr/textappearancelarge" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_torightof="@+id/progressbar" android:layout_centervertical="true" android:gravity="center" android:padding="10dip" android:textcolor="#ffffff" /> </RelativeLayout>
AsyncTask public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); switcher = new ViewSwitcher(this); button = (Button) View.inflate(this, R.layout.button, null); button.setonclicklistener(this); progress = (RelativeLayout) View.inflate(this, R.layout.progress, null); /* 将 button 与 progressbar 加入 switcher 中 */ switcher.addview(button); switcher.addview(progress); /* 取得 ListView 并将 switcher 加入 */ getlistview().addfooterview(switcher); /* 设定 ListAdapter, 其中第二个参数可选择样式 */ setlistadapter(new ArrayAdapter<String>(this, android.r.layout.simple_list_item_1, ITEMS)); }
AsyncTask private class GetMoreItemsTask extends AsyncTask<Object,Object,Object> { @Override protected Object doinbackground(object... params) { /* 此处可编写后台任务的程序代码 */ try { /* 线程调用 sleep 方法, 实际项目中应该是执行耗时操作 */ Thread.sleep(3000); } catch (InterruptedException e) { e.printstacktrace(); } return null; } @Override protected void onpostexecute(object result) { switcher.showprevious(); } }
Activity.runOnUiThread(Runnable ) 另外, 在 Activity 中也定义了一个 runonuithread(runnable) 方法, 用于处理 UI 线程的问题 : public void onclick( View v ) { new Thread( new Runnable() { public void run() { // 耗时操作 Activity.runOnUiThread( new Runnable() { mytext.settext( 来自网络的信息 ); }); } }).start(); }
View.post() 和 View.postDelayed() 在 View 中, 也定义了 View.post(Runnable)/ View.postDelayed(Runnable, long) 两个方法来处理 View 的线程问题 postdelayed 可以指定任务的延迟时间 ( 毫秒 ) public void onclick( View v ) { new Thread( new Runnable() { public void run() { // 耗时操作 mytext.post( new Runnable() { mytext.settext(...); }); } }).start(); }
Q&A