导航:首页 > 操作系统 > android单线程模型

android单线程模型

发布时间:2022-12-16 00:33:21

A. 搭配handlerthread可以在handler

每个android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线(MainThread),主线程负责处理和ui相关的事件,因此主线程通常又叫UI线程。
而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操作,则会报错: :only the original thread that created aviewhierarchy can touch its views Android为我们提供了消息循环的机制,我们可以利用这个机制来实现线程间的通信。那么,我们就可以在非UI线程发送消息到UI线程,最终让Ui线程来进行ui的操作。

B. android 怎么处理线程安全

UI线程及Android的单线程模型原则当应用启动,系统会创建一个主线程(main thread)。这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,应用和Android的UI组件(components from the Android UI toolkit (components from the android.widget and android.view packages))发生交互。 当App做一些比较重(intensive)的工作的时候,除非合理地实现,否则单线程模型的performance会很poor。特别的是,如果所有的工作都在UI线程,做一些比较耗时的工作比如访问网络或者数据库查询,都会阻塞UI线程,导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程blocked的时间太长(大约超过5秒),用户就会看到ANR(application not responding)的对话框。 另外,Andoid UI toolkit并不是线程安全的,所以不能从非UI线程来操纵UI组件。必须把所有的UI操作放在UI线程里,所以Android的单线程模型有两条原则: 1.不要阻塞UI线程。 2.不要在UI线程之外访问Android UI toolkit(主要是这两个包中的组件:android.widget and android.view)。

C. Android进程和线程的区别

Android进程和线程的区别

下面我先介绍下Android进程和线程各是什么,然后再一一比较区别下

  1. Android进程基本知识:

    当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。 同时,Android会为每个应用程序分配一个单独的LINUX用户。Android会尽量保留一个正在运行进程,只在内存资源出现不足时,Android会尝试停止一些进程从而释放足够的资源给其他新的进程使用, 也能保证用户正在访问的当前进程有足够的资源去及时地响应用户的事件。

    我们可以将一些组件运行在其他进程中,并且可以为任意的进程添加线程。组件运行在哪个进程中是在manifest文件里设置的,其中<Activity>,<Service>,<receiver>和<provider>都有一个process属性来指定该组件运行在哪个进程之中。我们可以设置这个属性,使得每个组件运行在它们自己的进程中,或是几个组件共同享用一个进程,或是不共同享用。<application>元素也有一个process属性,用来指定所有的组件的默认属性。


    Android中的所有组件都在指定的进程中的主线程中实例化的,对组件的系统调用也是由主线程发出的。每个实例不会建立新的线程。对系统调用进行响应的方法——例如负责执行用户动作的View.onKeyDown()和组件的生命周期函数——都是运行在这个主线程中的。这意味着当系统调用这个组件时,这个组件不能长时间的阻塞主线程。例如进行网络操作时或是更新UI时,如果运行时间较长,就不能直接在主线程中运行,因为这样会阻塞这个进程中其他的组件,我们可以将这样的组件分配到新建的线程中或是其他的线程中运行。

    Android会根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,Android会首先停止那些不重要的进程。按照重要性从高到低一共有五个级别:


    1.1前台进程

    前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说, 在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。

    1.2可见进程

    可见进程不包含前台的组件但是会在屏幕上显示一个可见的进程是的重要程度很高,除非前台进程需要获取它的资源,不然不会被中止。

    1.3服务进程

    运行着一个通过startService() 方法启动的service,这个service不属于上面提到的2种更高重要性的。service所在的进程虽然对用户不是直接可见的,但是他们执行了用户非常关注的任务(比如播放mp3,从网络下载数据)。只要前台进程和可见进程有足够的内存,系统不会回收他们。


    1.4后台进程

    运行着一个对用户不可见的activity(调用过 onStop() 方法).这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进 程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在LRU (least recently used) 列表中,以便内存不足的时候被第一时间回收。如果一个activity正 确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。


    1.5空进程

    未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。


  2. 单线程模型

    线程在代码是使用标准的java Thread对象来建立,那么在Android系统中提供了一系列方便的类来管理线程——Looper用来在一个线程中执行消息循环,Handler用来处理消息,HandlerThread创建带有消息循环的线程。具体可以看下面的详细介绍。

    当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

    在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。


2.1 子线程更新UI Android的UI是单线程(Single-threaded)的。

为了避免拖住GUI,一些较费时的对象应该交给独立的线程去执行。如果幕后的线程来执行UI对象,Android就会发出错误讯息 。以后遇到这样的异常抛出时就要知道怎么回事了!

2.2 Message Queue

在单线程模型下,为了解决类似的问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:


2..3 Message 消息

理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。

2.4. Handler 处理者

是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message) 方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。


2.5. Message Queue 消息队列

用来存放通过Handler发布的消息,按照先进先出执行。 每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。

2.6 Looper Looper是每条线程里的Message Queue的管家。

Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper()得到当前线程的Looper就有可能为NULL。


从以上几点,不难看出Android进程和线程的二者的区别所在。

D. 为什么 Android 的 UI 框架使用单线程模型,比多线程模型有什么优点

·如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。·的线程需要的内存空间。·线程可能会给程序带来“bug”,因此要小心使用。·线程的中止需要考虑其对程序运行的影响。·通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。一些线程模型的背景可以重点讨论一下在Win32环境中常用的一些模型。·单线程模型在这种线程模型中,一个进程中只能有一个线程,剩下的进程必须等待当前的线程执行完。这种模型的缺点在于系统完成一个很小的任务都必须占用很长的时间。·块线程模型(单线程多块模型STA)这种模型里,一个程序里可能会包含多个执行的线程。在这里,每个线程被分为进程里一个单独的块。每个进程可以含有多个块,可以共享多个块中的数据。程序规定了每个块中线程的执行时间。所有的请求通过Windows消息队列进行串行化,这样保证了每个时刻只能访问一个块,因而只有一个单独的进程可以在某一个时刻得到执行。这种模型比单线程模型的好处在于,可以响应同一时刻的多个用户请求的任务而不只是单个用户请求。但它的性能还不是很好,因为它使用了串行化的线程模型,任务是一个接一个得到执行的。·多线程块模型(自由线程块模型)多线程块模型(MTA)在每个进程里只有一个块而不是多个块。这单个块控制着多个线程而不是单个线程。这里不需要消息队列,因为所有的线程都是相同的块的一个部分,并且可以共享。这样的程序比单线程模型和STA的执行速度都要快,因为降低了系统的负载,因而可以优化来减少系统idle的时间。这些应用程序一般比较复杂,因为程序员必须提供线程同步以保证线程不会并发的请求相同的资源,因而导致竞争情况的发生。这里有必要提供一个锁机制。但是这样也许会导致系统死锁的发生。进程和线程都是操作系统的概念。进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。线程是进程内部的一个执行单元。系统创建好进程后,实际上就启动执行了该进程的主执行线程,主执行线程以函数地址形式,比如说main或WinMain函数,将程序的启动点提供给Windows系统。主执行线程终止了,进程也就随之终止。每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。多线程可以实现并行处理,避免了某项任务长时间占用CPU时间。要说明的一点是,到2015年为止,大多数的计算机都是单处理器(CPU)的,为了运行所有这些线程,操作系统为每个独立线程安排一些CPU时间,操作系统以轮换方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。由此可见,如果两个非常活跃的线程为了抢夺对CPU的控制权,在线程切换时会消耗很多的CPU资源,反而会降低系统的性能。这一点在多线程编程时应该注意。C++11标准中,STL类库也实现了多线程的类std::thread,使得多线程编程更加方便。

E. android 异步方法和子线程方法有什么区别

子线程没有控制并发数量,当并发过多的时候异步方法的作用就体现出来了。

异步是相对于同步而言的,顾名思义,同步就是各个通讯节点之间有统一的时钟,按照相同的时钟工作,异步相反,各节点之间没有统一的时钟,每个节点按照自己内部的时钟工作。
android在所有Thread当中,有一个Thread,我们称之为UI Thread。UI
Thread在Android程序运行的时候就被创建,是一个Process当中的主线程Main
Thread,主要是负责控制UI界面的显示、更新和控件交互。在Android程序创建之初,一个Process呈现的是单线程模型,所有的任务都在一个线程中运行。因此,我们认为,UI
Thread所执行的每一个函数,所花费的时间都应该是越短越好。而其他比较费时的工作(访问网络,下载数据,查询数据库等),都应该交由子线程去执行,以免阻塞主线程。

F. android 主线程和子线程有什么区别

本文较为深入的分析了android中UI主线程与子线程。分享给大家供大家参考。具体如下:
在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的两种,除此之外还有Content Provider和Broadcast Receiver)都会跑在这个Process。
一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。在这么多Thread当中,有一个Thread,我们称之为UI Thread。UI Thread在Android程序运行的时候就被创建,是一个Process当中的主线程Main Thread,主要是负责控制UI界面的显示、更新和控件交互。在Android程序创建之初,一个Process呈现的是单线程模型,所有的任务都在一个线程中运行。因此,我们认为,UI Thread所执行的每一个函数,所花费的时间都应该是越短越好。而其他比较费时的工作(访问网络,下载数据,查询数据库等),都应该交由子线程去执行,以免阻塞主线程。
那么,UI Thread如何和其他Thread一起工作呢?常用方法是:诞生一个主线程的Handler物件,当做Listener去让子线程能将讯息Push到主线程的Message Quene里,以便触发主线程的handlerMessage()函数,让主线程知道子线程的状态,并在主线程更新UI。
例如,在子线程的状态发生变化时,我们需要更新UI。如果在子线程中直接更新UI,通常会抛出下面的异常:
11-07 13:33:04.393: ERROR/JavaBinder(1029):android.view.ViewRoot$:Only the original thread that created a view hierarchy can touch its views.
意思是,无法在子线程中更新UI。为此,我们需要通过Handler物件,通知主线程Ui Thread来更新界面。
如下,首先创建一个Handler,来监听Message的事件:

private final int UPDATE_UI = 1;
private Handler mHandler = new MainHandler();

private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI: {
Log.i("TTSDeamon", "UPDATE_UI");
showTextView.setText(editText.getText().toString());
ShowAnimation();
break;
}
default:
break;
}
}
}

或者:

private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI: {
Log.i("TTSDeamon", "UPDATE_UI");
showTextView.setText(editText.getText().toString());
ShowAnimation();
break;
}
default:
break;
}
}
}

当子线程的状态发生变化,则在子线程中发出Message,通知更新UI。

mHandler.sendEmptyMessageDelayed(UPDATE_UI, 0);

在我们的程序中,很多Callback方法有时候并不是运行在主线程当中的,所以如果在Callback方法中更新UI失败,也可以采用上面的方法。

G. android 中Bn 和Bp的区别

Bn意味着Binder Native 端
Bp是Binder Proxy端,
这两端会实现相同的接口,但Proxy端只是通过binder ipc发送一个binder transaction,
native端是真正做事情,再将结果返回。
Android用此机制实现高效的远程调用。

H. 为什么loop之后就可以子线程更新ui

我们常常听到这么一句话:更新UI要在UI线程(或者说主线程)中去更新,不要在子线程中更新UI,而Android官方也建议我们不要在非UI线程直接更新UI。

事实是不是如此呢,做一个实验:

更新之前:

代码:

package com.bourne.android_common.ServiceDemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.bourne.android_common.R;

public class ThreadActivity extends AppCompatActivity {

private Thread thread;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);

thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");
}
});

thread.start();
}

@Override
protected void onDestroy() {
super.onDestroy();
}
}
登录后复制

这里在Activity里面新建了一个子线程去更新UI,按理说会报错啊,可是执行结果是并没有报错,如图所示:

接下来让线程休眠一下:

package com.bourne.android_common.ServiceDemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import com.bourne.android_common.R;

public class ThreadActivity extends AppCompatActivity {

private Thread thread;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);

thread = new Thread(new Runnable() {
@Override
public void run() {

try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}

textView.setText("text text text");
}
});

thread.start();
}

}
登录后复制

应用报错,抛出异常:

android.view.ViewRootImpl$: Only the original thread that created a view hierarchy can touch its views

只有创建View层次结构的线程才能修改View,我们在非UI主线程里面更新了View,所以会报错

因为在OnCreate里面睡眠了一下才报错,这是为什么呢?

Android通过检查我们当前的线程是否为UI线程从而抛出一个自定义的AndroidRuntimeException来提醒我们“Only the original thread that created a view hierarchy can touch its views”并强制终止程序运行,具体的实现在ViewRootImpl类的checkThread方法中:

@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
// 省去海量代码…………………………

void checkThread() {
if (mThread != Thread.currentThread()) {
throw new (
"Only the original thread that created a view hierarchy can touch its views.");
}
}

// 省去巨量代码……………………
}
登录后复制

这就是Android在4.0后对我们做出的一个限制。

其实造成这个现象的根本原因是:

还没有到执行checkThread方法去检查我们的当前线程那一步。”Android对UI事件的处理需要依赖于Message Queue,当一个Msg被压入MQ到处理这个过程并非立即的,它需要一段事件,我们在线程中通过Thread.sleep(200)在等,在等什么呢?在等ViewRootImpl的实例对象被创建。”

ViewRootImpl的实例对象是在OnResume中创建的啊!

看onResume方法的调度,其在ActivityThread中通过handleResumeActivity调度:

public final class ActivityThread {
// 省去海量代码…………………………

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
unscheleGcIdler();

ActivityClientRecord r = performResumeActivity(token, clearHide);

if (r != null) {
final Activity a = r.activity;

// 省去无关代码…………

final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}

} else if (!willBeVisible) {
// 省去无关代码…………

r.hideForNow = true;
}

cleanUpPendingRemoveWindows(r);

if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
// 省去无关代码…………

performConfigurationChanged(r.activity, r.newConfig);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.newConfig));
r.newConfig = null;
}

// 省去无关代码…………

WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}

if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;

// 省去无关代码…………

Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;

// 省去与ActivityManager的通信处理

} else {
// 省略异常发生时对Activity的处理逻辑
}
}

// 省去巨量代码……………………
}
登录后复制

handleResumeActivity方法逻辑相对要复杂一些,除了对当前显示Window的逻辑判断以及没创建的初始化等等工作外其在最终会调用Activity的makeVisible方法

public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2 {
// 省去海量代码…………………………

void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}

// 省去巨量代码……………………
}
登录后复制

在makeVisible方法中逻辑相当简单,获取一个窗口管理器对象并将根视图DecorView添加到其中,addView的具体实现在WindowManagerGlobal中:

public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 省去很多代码

ViewRootImpl root;

// 省去一行代码

synchronized (mLock) {
// 省去无关代码

root = new ViewRootImpl(view.getContext(), display);

// 省去一行代码

// 省去一行代码

mRoots.add(root);

// 省去一行代码
}

// 省去部分代码
}
}
登录后复制

在addView生成了一个ViewRootImpl对象并将其保存在了mRoots数组中,每当我们addView一次,就会生成一个ViewRootImpl对象,其实看到这里我们还可以扩展一下问题一个APP是否可以拥有多个根视图呢?答案是肯定的,因为只要我调用了addView方法,我们传入的View参数就可以被认为是一个根视图,但是!在framework的默认实现中有且仅有一个根视图,那就是我们上面makeVisible方法中addView进去的DecorView,所以为什么我们可以说一个APP虽然可以有多个Activity,但是每个Activity只会有一个Window一个DecorView一个ViewRootImpl,看到这里很多童鞋依然会问,也就是说在onResume方法被执行后我们的ViewRootImpl才会被生成对吧,但是为什么下面的代码依然可以正确运行呢:

package com.bourne.android_common.ServiceDemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import com.bourne.android_common.R;

public class ThreadActivity extends AppCompatActivity {

private Thread thread;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
textView = (TextView) findViewById(R.id.textView);

thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");

}
});

thread.start();
}

@Override
protected void onResume() {
super.onResume();
}
}
登录后复制

Activity.onResume前,ViewRootImpl实例没有建立,所以没有checkThread检查。但是使用了Thread.sleep(200)的时候,ViewRootImpl已经被创建完毕了,自然checkThread就起作用了,抛出异常顺理成章。

第一种做法中,虽然是在子线程中setText,但是这时候View还没画出来呢,所以并不会调用之后的invalidate,而相当于只是设置TextView的一个属性,不会invalidate,就没有后面的那些方法调用了,归根结底,就不会调用ViewRootImpl的checkThread,也就不会报错。而第二种方法,调用setText之后,就会引发后面的一系列的方法调用,VIew要刷新界面,ViewGroup要更新布局,计算子View的大小位置,到最后,ViewRootImpl就会checkThread,就崩了。

所以,严格上来说,第一种方法虽然在子线程了设置View属性,但是不能够归结到”更新View”的范畴,因为还没画出来呢,就没有所谓的更新。

当我们执行Thread.sleep时候,这时候onStart、onResume都执行了,子线程再调用setText的时候,就会崩溃。

那么说,在onStart()或者onResume()里面执行线程操作UI也是可以的:

@Override
protected void onStart() {
super.onStart();
thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");

}
});
thread.start();
}
登录后复制

@Override
protected void onResume() {
super.onResume();
thread = new Thread(new Runnable() {
@Override
public void run() {
textView.setText("text text text");

}
});
thread.start();
}
登录后复制

注意的是:当你在来回切换界面的时候,onStart()和onResume()是会再执行一遍的,这时候程序就崩溃了!

1、能不能在非UI线程中更新UI呢?
答案:能、当然可以

2、View的运行和Activity的生命周期有什么必然联系吗?
答案:没有、或者隐晦地说没有必然联系

3、除了Handler外是否还有更简便的方式在非UI线程更新UI呢?
答案:有、而且还不少,Activity.runOnUiThread(Runnable)、View.Post(Runnable)、View.PostDelayed(Runnable,long)、AsyncTask、其内部实现原理都是向此View的线程的内部消息队列发送一个Message消息,并传送数据和处理方式,省去了自己再写一个专门的Handler去处理。

4、在子线程里面用Toast也会报错,加上Looper.prepare和Looper.loop就可以了,这里可以这样做吗?
答案当然是不可以。Toast和View本质上是不一样的,Toast在子线程报错,是因为Toast的显示需要添加到一个MessageQueue中,然后Looper取出来,发给Handler调用显示,子线程因为没有Looper,所以需要加上Looper.prepare和Looper.loop创建一个Looper,但是实质上,这还是在子线程调用,所以还是会报错的!

5、为什么Android要求只能在UI主线程中更改View呢
这就要说到Android的单线程模型了,因为如果支持多线程修改View的话,由此产生的线程同步和线程安全问题将是非常繁琐的,所以Android直接就定死了,View的操作必须在UI线程,从而简化了系统设计。

参考文章
为什么我们可以在非UI线程中更新UI
【Android开发经验】来来来,同学,咱们讨论一下“只能在UI主线程更新View”这件小事
线程
android
ui
女式凉鞋,时尚,优雅,透气,货到付款!
精选推荐
广告

可能是全网最简单透彻的安卓子线程更新 UI 解析
71阅读·0评论·0点赞
2019年4月24日
android 不能在子线程中更新ui的讨论和分析
1.5W阅读·7评论·14点赞
2016年1月26日
android.view.ViewRootImpl$: Only the original thread that created
140阅读·0评论·0点赞
2022年9月23日
Looper.prepare()和Looper.loop(),在子线程中更新UI
2520阅读·0评论·0点赞
2016年5月2日
Android为什么能在子线程中更新UI
305阅读·0评论·1点赞
2020年4月9日
android多线程中更新ui,Android 在子线程中更新UI
146阅读·0评论·0点赞
2021年6月3日
Android子线程更新UI就会Crash么
1781阅读·0评论·4点赞
2017年4月1日
为什么只能在主线程中操作UI?为什么子线程中setText不报错?
3970阅读·1评论·5点赞
2017年6月27日
Android 子线程更新TextView的text 不抛出异常原因 分析总结
1211阅读·0评论·3点赞
2019年7月10日
非主线程更新UI
210阅读·0评论·0点赞
2018年4月12日
非 UI 线程中更新 UI
215阅读·0评论·0点赞
2021年4月29日
【Android】 Handler——子线程更新UI
722阅读·1评论·4点赞
2019年12月29日
android-如何在子线程中更新ui
4019阅读·4评论·2点赞
2016年8月23日
SurfaceView
251阅读·0评论·0点赞
2019年3月8日
非UI线程中更新UI
373阅读·0评论·0点赞
2018年7月10日
QT非UI线程更新UI(跨线程更新UI)
275阅读·0评论·0点赞
2022年9月21日
Android开发之UI线程和非UI线程
1619阅读·0评论·1点赞
2020年4月5日
非UI线程可不可以更新UI(一)
1265阅读·0评论·2点赞
2016年2月29日
为什么我们可以在非UI线程中更新UI
2.8W阅读·56评论·35点赞
2015年2月3日
去首页
看看更多热门内容

I. android启动后怎么查看其里面的进程和线程

1)一个 Android 程序开始运行时,会单独启动一个Process。
默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。
默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。
2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。

3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)

一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?
【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。

Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。

原文地址:Android中的进程与线程 原文作者:江鹏

当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:

1、进程

组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。

所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。

public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。

参数

keyCode-表示按钮被按下的键码,来自KeyEvent
event-定义了按钮动作的KeyEvent对象

返回值

如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。

当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。

当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。

2、线程

虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。

线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。

Looper类

该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互:

1. Android中进程与进程、线程与线程之间如何通信?
1)一个 Android 程序开始运行时,会单独启动一个Process。
默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。
默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。
2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。

3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)

一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?
【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。

Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。

原文地址:Android中的进程与线程 原文作者:江鹏

当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:

1、进程

组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。

所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。

public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。

参数

keyCode-表示按钮被按下的键码,来自KeyEvent
event-定义了按钮动作的KeyEvent对象

返回值

如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。

当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。

当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。

2、线程

虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。

线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。

Looper类

该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互:

2.1、远程过程调用(Remote procere calls,RPCs)

Android有一个轻量级的远程过程调用机制——方法在本地调用却在远程(另外一个进程中)执行,结果返回给调用者。这需要将方法调用和它伴随的数据分解为操作系统能够理解的层次,从本地进程和地址空间传输到远程进程和地址空间,并重新组装调用。返回值以相反方向传输。Android提供了做这些工作的所有代码,这样我们可以专注于定义和执行RPC接口本身。

一个RPC接口仅包含方法。所有的方法同步地执行(本地方法阻塞直到远程方法执行完成),即使是没有返回值。简言之,该机制工作原理如下:首先,你用简单的IDL(interface definition language,接口定义语言)声明一个你想实现的RPC接口。从这个声明中,aidl工具生成一个Java接口定义,提供给本地和远程进程。它包含两个内部类,如下图所示:

内部类有管理你用IDL定义的接口的远程过程调用所需要的所有代码。这两个内部类都实现了IBinder接口。其中之一就是在本地由系统内部使用,你写代码可以忽略它。另外一个是Stub,扩展自Binder类。除了用于有效地IPC(interprocess communication)调用的内部代码,内部类在RPC接口声明中还包含方法声明。你可以定义Stub的子类实现这些方法,如图中所示。

通常情况下,远程过程有一个服务管理(因为服务能通知系统关于进程和它连接的其它进程的信息)。它有由aidl工具生成的接口文件和Stub子类实现的RPC方法。服务的客户端仅有由aidl工具生成的接口文件。

下面介绍服务如何与它的客户端建立连接:

· 服务的客户端(在本地端的)应该实现onServiceConnected() 和onServiceDisconnected() 方法,因此当与远程服务建立连接成功和断开连接是会通知它。然后调用bindService() 建立连接。

· 服务的onBind()方法将实现为接受或拒绝连接,者取决于它接受到的意图(该意图传送到binServive())。如果连接被接受,它返回一个Stub子类的实例。

· 如果服务接受连接,Android调用客户端的onServiceConnected()方法且传递给它一个IBinder对象,返回由服务管理的Stub子类的一个代理。通过代理,客户端可以调用远程服务。

这里只是简单地描述,省略了一些RPC机制的细节。你可以查阅相关资料或继续关注Android开发之旅,后面将为你奉上。

2.2、线程安全方法

在一些情况下,你实现的方法可能会被不止一个线程调用,因此必须写成线程安全的。这对远程调用方法是正确的——如上一节讨论的RPC机制。当从IBinder进程中调用一个IBinder对象中实现的一个方法,这个方法在调用者的线程中执行。然而,当从别的进程中调用,方法将在Android维护的IBinder进程中的线程池中选择一个执行,它不在进程的主线程中执行。例如,一个服务的onBind()方法在服务进程的主线程中被调用,在onBind()返回的对象中执行的方法(例如,实现RPC方法的Stub子类)将在线程池中被调用。由于服务可以有一个以上的客户端,所以同时可以有一个以上的线程在执行同一个IBinder方法。因此,IBinder的方法必须是线程安全的。

同样,一个内容提供者可以接受其它进程产生的数据请求。虽然ContentResolver 和 ContentProvider 类隐藏进程通信如何管理的,对应哪些请求的ContentResolver 方法——query()、insert()、delete()、update()、getType(),在内容提供者的进程的线程池中被调用,而不是在这一进程的主线程中。因为这些方法可以同时从任意数量的线程中调用,他们也必须实现为线程安全的。

阅读全文

与android单线程模型相关的资料

热点内容
公式保护后加密不了 浏览:82
java跳转到jsp 浏览:819
327平方根算法 浏览:216
win7美化命令行终端 浏览:797
免加密狗图片 浏览:485
一只透明的鸟是什么app 浏览:817
空气压缩机油批发商 浏览:69
linuxifexist 浏览:4
加密tf卡拷入文件 浏览:399
山西php工资 浏览:673
福州看病预约用什么小程序app 浏览:238
php保留两位小数不四舍五入 浏览:292
黑马程序员路径大全 浏览:1000
saas平台PHP 浏览:333
云服务器科学计算配置怎么选 浏览:649
jar解压命令 浏览:609
php正则问号 浏览:299
无线已加密不可上网是怎么了 浏览:465
什么app可以免费做手机 浏览:376
异性下载什么app 浏览:682