当前位置:首页 » 安卓系统 » androidui主线程

androidui主线程

发布时间: 2023-03-17 13:28:38

1. Android UI线程

思考:

先必须了解下面2个问题
1.顾名思义 UI线程 就是刷新UI 所在线程
2.UI是单线程刷新

1.对Activity 来说 UI线程就是其主线程
2.对View来说 UI线程就是创建ViewRootImpl所在的线程
可以通过 WindowManager 内部会创建ViewRootImpl对象

好了,进入主题。我们来慢慢揭开面纱。
我们可以分别从几个方面切入

我们可能都有使用过 runOnUiThread 现在来看看的源码实现。

可以从上面的源码 看到
不是UI线程 就用Handler切到Handler所在的线程中,如果是UI线程直接就调用run方法。

Activity的创建:
1.Activity创建:mInstrumentation.newActivity
2.创建Context :ContextImpl (r)

我们经常用这个方法干的事情就是,要么在onCreate中获取View宽高的值。要么就是在子线程中做一些耗时操作 ,然后post切到对应View所在的线程 来绘制UI操作。那么这个对应的线程就是UI线程了。
那么这个UI线程就一定是主线程吗?
接来继续来看。它的源码View:post

mAttachInfo 在dispatchAttachedToWindow 中被赋值 ,也就是在ViewRootImpl创建的时候,所以是创建ViewRootImpl所在的线程。
attachInfo 上面时候为null 呢?在ViewRootImpl 还没来得及创建的时候,ViewRootImpl 创建是在 “onResume" 之后。所以在 Activity 的 onCreate 去View.post 那么AttachInfo 是为null 。
当 AttachInfo == null 那么会调用 getRunQueue().post(action) 。
最终这个Runnable 被 缓存到 HandlerActionQueue 中。
直到ViewRootImpl 的 performTraversals 中 调用dispatchAttachedToWindow(mAttachInfo, 0);, 那么才会去处理 RunQueue() 中的Runnable。

来张图 便于理解这个流程

我们有时候去子线程操作UI的时候(如:requestLayout),会很经常见到下面的 报错日志:
Only the original thread that created a view hierarchy can touch its views

为什么会报这个错误呢?
翻译一下:只有创建视图层次结构的原始线程才能接触到它的视图。
也就是操作UI的线程要和ViewRootImpl创建的线程是同一个线程才行,并不是只有主线程才能更新UI啊。
ViewRootImpl创建的线程?那么 ViewRootImpl 在哪里被创建的呢?

从上图可以看到ViewRootImpl创建最开始是从 ActivityThread 的HandleResumeActivity中开始 一直 ViewRootImpl 创建,也就是说ViewRootImpl 对应的UI线程和 ActivityThread 在同一个线程 也就是主线程。

好了 通过上面的讲解,上面的问题相信你可以自己回答啦~

2. Android线程池的使用

在Android中有主线程和子线程的区分。主线程又称为UI线程,主要是处理一些和界面相关的事情,而子线程主要是用于处理一些耗时比较大的一些任务,例如一些网络操作,IO请求等。如果在主线程中处理这些耗时的任务,则有可能会出现ANR现象(App直接卡死)。

线程池,从名字的表明含义上我们知道线程池就是包含线程的一个池子,它起到新建线程、管理线程、调度线程等作用。

既然Android中已经有了线程的概念,那么为什么需要使用线程池呢?我们从两个方面给出使用线程池的原因。

在Android中线程池就是ThreadPoolExecutor对象。我们先来看一下ThreadPoolExecutor的构造函数。

我们分别说一下当前的几个参数的含义:
第一个参数corePoolSize为 核心线程数 ,也就是说线程池中至少有这么多的线程,即使存在的这些线程没有执行任务。但是有一个例外就是,如果在线程池中设置了allowCoreThreadTimeOut为true,那么在 超时时间(keepAliveTime) 到达后核心线程也会被销毁。
第二个参数maximumPoolSize为 线程池中的最大线程数 。当活动线程数达到这个数后,后续添加的新任务会被阻塞。
第三个参数keepAliveTime为 线程的保活时间 ,就是说如果线程池中有多于核心线程数的线程,那么在线程没有任务的那一刻起开始计时,如果超过了keepAliveTime,还没有新的任务过来,则该线程就要被销毁。同时如果设置了allowCoreThreadTimeOut为true,该时间也就是上面第一条所说的 超时时间
第四个参数unit为 第三个参数的计时单位 ,有毫秒、秒等。
第五个参数workQueue为 线程池中的任务队列 ,该队列持有由execute方法传递过来的Runnable对象(Runnable对象就是一个任务)。这个任务队列的类型是BlockQueue类型,也就是阻塞队列,当队列的任务数为0时,取任务的操作会被阻塞;当队列的任务数满了(活动线程达到了最大线程数),添加操作就会阻塞。
第六个参数threadFactory为 线程工厂 ,当线程池需要创建一个新线程时,使用线程工厂来给线程池提供一个线程。
第七个参数handler为 拒绝策略 ,当线程池使用有界队列时(也就是第五个参数),如果队列满了,任务添加到线程池的时候的一个拒绝策略。

可以看到FixedThreadPool的构建调用了ThreadPoolExecutor的构造函数。从上面的调用中可以看出FixedThreadPool的几个特点:

可以看到CacheThreadPool的构建调用了ThreadPoolExecutor的构造函数。从上面的调用中可以看出CacheThreadPool的几个特点:

可以看到ScheledThreadPoolExecutor的构建调用了ThreadPoolExecutor的构造函数。从上面的调用中可以看出ScheledThreadPoolExecutor的几个特点:

可以看到SingleThreadExecutor的构建调用了ThreadPoolExecutor的构造函数。从上面的调用中可以看出SingleThreadExecutor的几个特点:

3. Android开发之路-多线程

多线程作为Android开发中相对而言较为高阶的知识,其中用到相关的知识点是非常的多,所以在我们需要进行设计或者写多线程的代码就必须要进行相对谨慎的处理,这样就由必要对其要有着比较系统化的认知

我们一般将Android应用分成为两种:主线程和工作线程;主线程主要是用来进行初始化UI,而工作线程主要是进行耗时操作,例如读取数据库,网络连接等

Android系统是以进程为单位来对应用程序资源进行限制,这个问题的可以解释为:一个进程最多能够开几个线程?最好能开几个?但实则这个是没有上限这一说,主要是因为资源的限制

Android中关于主线程的理解:Android的主线程是UI线程,在Android中,四大组件运行在主线程中,在主线程中做耗时操作会导致程序出现卡顿甚至出现ANR异常,一个.

在一个程序中,这些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。多线程处理一个常见的例子就是用户界面。

线程总的来就是进程的一个实体,是CPU进行分派和调度的基本单位,拥有着比进程更小且能够独立运行的基本单位,线程本身基本上是不拥有系统资源,仅拥有一点在运行过程中必须拥有的资源,但它可与同属一个进程中的其他进程进行共享其所拥有的所有资源

线程状态有些地方将之分为5中状态,而且在Java Jdk中线程被其定义为6中状态,我们可以对其进行类比

普遍定义的5中状态:新建,就绪,运行,阻塞, 死亡

Java Jdk 定义状态

线程阻塞是指在某一时刻的某一个线程在进行运行一段代码的情况下,突然另一个线程也要进行运行,但在运行过程中,那个线程执行完全运行之前,另一个线程是不可能获取到CPU的执行权,就会导致线路阻塞的出现

死锁也称之为抱死,意思就是说一个进程锁定了另外一个进程所需要的页或表是,但第二个进程同时又锁定了第一个进程所需的一页,这样就会出现死锁现象

简要介绍实现线程的三种方式:继承Thread,实现runnable,实现callable。这里有一点需要注意的是,实现callable是与线程池相关联的而callable很重要的一个特性是其带有返回值。当我们只需实现单线程时实现runnable更加利于线程程序的拓展

在线程开启之前进行调用 thread.setDaemon(true); 将thread设定成当前线程中的守护线程 使用案例

线程让步【yield方法】让当前线程释放CPU资源,让其他线程抢占

这种具体某个对象锁 wait & notify 方法与Condition 的 await以及signal方法类似; 全面这种方法的阻塞等待都可以是释放锁,而且在唤醒后,这种线程都是能够获取锁资源的,而这个门栓就跟阀门类似

4. 每个Android 都应必须了解的多线程知识点~

进程是系统调度和资源分配的一个独立单位。

在Android中,一个应用程序就是一个独立的集成,应用运行在一个独立的环境中,可以避免其他应用程序/进程的干扰。当我们启动一个应用程序时,系统就会创建一个进程(该进程是从Zygote中fork出来的,有独立的ID),接着为这个进程创建一个主线程,然后就可以运行MainActivity了,应用程序的组件默认都是运行在其进程中。开发者可以通过设置应用的组件的运行进程,在清单文件中给组件设置:android:process = "进程名";可以达到让组件运行在不同进程中的目的。让组件运行在不同的进程中,既有好处,也有坏处。我们依次的说明下。

好处:每一个应用程序(也就是每一个进程)都会有一个内存预算,所有运行在这个进程中的程序使用的总内存不能超过这个值,让组件运行不同的进程中,可以让主进程可以拥有更多的空间资源。当我们的应用程序比较大,需要的内存资源比较多时(也就是用户会抱怨应用经常出现OutOfMemory时),可以考虑使用多进程。

坏处:每个进程都会有自己的虚拟机实例,因此让在进程间共享一些数据变得相对困难,需要采用进程间的通信来实现数据的共享。

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

在Android中,线程会有那么几种状态:创建、就绪、运行、阻塞、结束。当应用程序有组件在运行时,UI线程是处于运行状态的。默认情况下,应用的所有组件的操作都是在UI线程里完成的,包括响应用户的操作(触摸,点击等),组件生命周期方法的调用,UI的更新等。因此如果UI线程处理阻塞状态时(在线程里做一些耗时的操作,如网络连接等),就会不能响应各种操作,如果阻塞时间达到5秒,就会让程序处于ANR(application not response)状态。

1.线程作用

减少程序在并发执行时所付出的时空开销,提高操作系统的并发性能。

2.线程分类

守护线程、非守护线程(用户线程)

2.1 守护线程

定义:守护用户线程的线程,即在程序运行时为其他线程提供一种通用服务
常见:如垃圾回收线程
设置方式:thread.setDaemon(true);//设置该线程为守护线程

2.2 非守护线程(用户线程)

主线程 & 子线程。

2.2.1 主线程(UI线程)

定义:Android系统在程序启动时会自动启动一条主线程
作用:处理四大组件与用户进行交互的事情(如UI、界面交互相关)
因为用户随时会与界面发生交互,因此主线程任何时候都必须保持很高的响应速度,所以主线程不允许进行耗时操作,否则会出现ANR。

2.2.2 子线程(工作线程)

定义:手动创建的线程
作用:耗时的操作(网络请求、I/O操作等)

2.3 守护线程与非守护线程的区别和联系

区别:虚拟机是否已退出,即
a. 当所有用户线程结束时,因为没有守护的必要,所以守护线程也会终止,虚拟机也同样退出
b. 反过来,只要任何用户线程还在运行,守护线程就不会终止,虚拟机就不会退出

3.线程优先级

3.1 表示

线程优先级分为10个级别,分别用Thread类常量表示。

3.2 设置

通过方法setPriority(int grade)进行优先级设置,默认线程优先级是5,即 Thread.NORM_PRIORITY。

4.线程状态

创建状态:当用 new 操作符创建一个线程的时候

就绪状态:调用 start 方法,处于就绪状态的线程并不一定马上就会执行 run 方法,还需要等待CPU的调度

运行状态:CPU 开始调度线程,并开始执行 run 方法

阻塞(挂起)状态:线程的执行过程中由于一些原因进入阻塞状态,比如:调用 sleep/wait 方法、尝试去得到一个锁等

结束(消亡)状态:run 方法执行完 或者 执行过程中遇到了一个异常

(1)start()和run()的区别

通过调用Thread类的start()方法来启动一个线程,这时此线程是处于就绪状态,并没有运行。调用Thread类调用run()方法来完成其运行操作的,方法run()称为线程体,它包含了要执行的这个线程的内容,run()运行结束,此线程终止,然后CPU再调度其它线程。

(2)sleep()、wait()、yield()的区别

sleep()方法属于Thread类,wait()方法属于Object类。
调用sleep()方法,线程不会释放对象锁,只是暂停执行指定的时间,会自动恢复运行状态;调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池,不调用notify()方法,线程永远处于就绪(挂起)状态。

yield()直接由运行状态跳回就绪状态,表示退让线程,让出CPU,让CPU调度器重新调度。礼让可能成功,也可能不成功,也就是说,回到调度器和其他线程进行公平竞争。

1.Android线程的原则

(1)为什么不能再主线程中做耗时操作
防止ANR, 不能在UI主线程中做耗时的操作,因此我们可以把耗时的操作放在另一个工作线程中去做。操作完成后,再通知UI主线程做出相应的响应。这就需要掌握线程间通信的方式了。 在Android中提供了两种线程间的通信方式:一种是AsyncTask机制,另一种是Handler机制。

(2)为什么不能在非UI线程中更新UI 因为Android的UI线程是非线程安全的,应用更新UI,是调用invalidate()方法来实现界面的重绘,而invalidate()方法是非线程安全的,也就是说当我们在非UI线程来更新UI时,可能会有其他的线程或UI线程也在更新UI,这就会导致界面更新的不同步。因此我们不能在非UI主线程中做更新UI的操作。

2.Android实现多线程的几种方式

3.为何需要多线程

多线程的本质就是异步处理,直观一点说就是不要让用户感觉到“很卡”。

4.多线程机制的核心是啥

多线程核心机制是Handler

推荐Handler讲解视频: 面试总被问到Handler?带你从源码的角度解读Handler核心机制

根据上方提到的 多进程、多线程、Handler 问题,我整理了一套 Binder与Handler 机制解析的学习文档,提供给大家进行学习参考,有需要的可以 点击这里直接获取!!! 里面记录许多Android 相关学习知识点。

5. Android的主线程是UI线程吗

是的,只是叫法上的不同。因为Android处于安全性考虑,只允许在主线程中操作UI,所以也叫UI线程。

6. android 为什么要设计ui线程

UI线程,即主线程。为何将UI线程和其他事物线程,是由于其他事务处理会需要更多的时间去加载,也就是所谓的耗时操作,这样不会导致页面加载需要很久(至少画面出来了,数据可以略慢)。设计UI线程是为了使得程序能够独立完成界面加载不受其他线程影响速度。

7. android怎么更新UI

首先,android的UI刷新是在主线程(UI线程)中完成的。四大组件中,activity和service运行在主线程中。现在总结自己在项目中常用到的UI刷新方式。
第一,利用子线程发消息刷新UI。
子线程负责处理UI需要的数据,然后发消息到主线程来刷新UI。代码结构如下:
new Thread(new Runnable() {

@Override
public void run() {
Person person=new Person();
person.setName(mName.getText().toString().trim());
person.setPhone(mPhone.getText().toString().trim());
Log.i("person",person.toString());
DatabaseInfoFactory.getPersonDao(mContext).addPerson(person);
Looper.prepare();
Message msg=Message.obtain();
msg.what=0x123456;
handler.sendMessage(msg);
Looper.loop();

}
}).start();
主线程中:
private Handler mHandler=new Handler(){

@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(msg.what==0x123456||msg.what==0x123){
fillData();
setListener();
}

}
};
第二,利用异步任务更新UI。代码结构如下:
new AsyncTask<void,void,void>() {

@Override
protected void onPostExecute(Void result) {

if(mAdapter==null){
mAdapter=new LeaveInfoAdapter();
//设置数据适配器
mLVleaveInfos.setAdapter(mAdapter);
Log.i("测试", "异步任务显示后台获得数据库数据");
}
else {
mAdapter.notifyDataSetChanged();

}

super.onPostExecute(result);
}

@Override
protected Void doInBackground(Void... params) {
//获得要显示的数据
mleaveInfos=mLeaveInfosDao.findAll();
if (mleaveInfos==null) {
Toast.makeText(HomeActivity.this,"请假数据不存在或是已经清除!", 500).show();

}

Log.i("测试", "异步任务后台获得数据库数据"+mleaveInfos.size());

return null;
}
}.execute();</void,void,void>
第三,利用配置文件+activity的生命周期方法刷新UI。

热点内容
数据库宿舍管理系统 发布:2025-05-10 07:24:23 浏览:868
c语言遍历二维数组 发布:2025-05-10 07:17:49 浏览:622
sql合并两列 发布:2025-05-10 07:07:01 浏览:821
linuxmysqlsql 发布:2025-05-10 07:06:12 浏览:917
恒波u盘加密器 发布:2025-05-10 06:55:24 浏览:443
服务器ip需要解析吗 发布:2025-05-10 06:54:26 浏览:427
编译型语言的执行速度 发布:2025-05-10 06:53:43 浏览:219
免费开我的世界国际服服务器 发布:2025-05-10 06:51:35 浏览:276
压缩机拍卖 发布:2025-05-10 06:50:04 浏览:838
服务器映射器怎么固定ip 发布:2025-05-10 06:35:31 浏览:637