1. UI为什么不是线程安全的
很好理解,先说说什么是线程安全,线程安全就是多个线程同时运行一段代码,运行结果不能存在二义性和不确定性,和单线程结果一样,就是线程安全的。 否则就不是,就需要考虑线程同步。
UI 就好比画板, 假如两个画家同时画, 你想画这样画,我想那样画,那如何的了。所以操作系统保证,一个界面同一时间只能有一个操作,其他操作将被阻塞。 包括windows, linux, android 所有UI肯定都不是线程安全的。
2. 在android中使用service时,需要注意哪些事项
我们都知道Android是单线程模型,这意味着Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行,所以你单纯的new一个Thrad并且start()不行的,因为这违背了Android的单线程模型。
很幸运的是Android为我们提供了在其他线程中访问UI线程的方法,相信大家都见过,这几个就是Activity的runOnUiThread方法,View的post和postDelayed方法,以及最常用的Hanlder和AsyncTask,这里我推荐使用Hanlder和AsyncTask,尤其是AsyncTask,因为使用他们你会发现你的代码很容易被理解,因为他们都有一些具有特定职责的方法,尤其是AsyncTask,有预处理的方法onPreExecute,有后台执行任务的方法doInBackground,有更新进度的方法publishProgress,有返回结果的方法onPostExecute等等,这就不像post这些方法,把所有的操作都一大坨的写在一个Runnable里。
有了Android为我们提供了这些方法我们就可以很好的解决一些长时间处理的任务了,但是在使用的时候我们还必须注意以下几点:
这些方法或者类必须在在UI线程中创建和调用
其实这些方法和类最终的实现都是Android的Message、MessageQueue和Looper的机制,所以不要期待你会马上看到结果(效果),因为这是一个Loop一直循环出MessageQueue中的Message执行的过程,如果你没有看到效果,那么等等吧,因为还没有轮到你。
有线程(多个)的地方就会有并发,会有资源共享冲突,所以在使用的时候谨慎点吧,说不准你的一个线程中使用的变量已经被另一个线程改的面目全非了
3. Android进程和线程的区别
Android进程和线程的区别
下面我先介绍下Android进程和线程各是什么,然后再一一比较区别下
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空进程
未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。
单线程模型
线程在代码是使用标准的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进程和线程的二者的区别所在。
4. android 中Bn 和Bp的区别
Bn意味着Binder Native 端
Bp是Binder Proxy端,
这两端会实现相同的接口,但Proxy端只是通过binder ipc发送一个binder transaction,
native端是真正做事情,再将结果返回。
Android用此机制实现高效的远程调用。
5. 为什么还说Android的UI操作并不是线程安全的
看到题主的问题,就明白了。是因为性能考虑。线程安全性能较差,线程不安全性能较好。
所以Android选择线程不安全。
Android主线程要注意的两点:
1.不要阻塞主线程
2.不要在其他线程调用UI操作方法(Android UI toolkit)
大概也就是这样
6. Android:在一个非主线程内直接调用UI线程的Handler实例,这样没问题吗
我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的。
下面说下有关Handler相关的知识。
多线程一些基础知识回顾:
在介绍Handler类相关知识之前,我们先看看在Java中是如何创建多线程的
方法有两种:
通过继承Thread类,重写Run方法来实现
通过继承接口Runnable实现多线程
接下来让我们看看Handler是什么?如何来用
Handler是这么定义的:
主要接受子线程发送的数据, 并用此数据配合主线程更新UI.
Handler的主要作用:主要用于异步消息的处理
Handler的运行过程:
当(子线程)发出一个消息之后,首先进入一个(主线程的)消息队列,发送消息的函数即刻返回,而在主线程中的Handler逐个的在消息队列中将消息取出,然后对消息进行处理。这样就实现了跨线程的UI更新(实际上还是在主线程中完成的)。
这种机制通常用来处理相对耗时比较长的操作,如访问网络比较耗时的操作,读取文大文件,比较耗时的操作处理等。
在大白话一点的介绍它的运行过程:
启动应用时Android开启一个主线程 (也就是UI线程) , 如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象(这也就是你在主线程中直接访问网络时会提示你异常的原因,如我们上篇文章所述Android主线程不能访问网络异常解决办法
)。
这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,更新UI只能在主线程中更新.。
这个时候,Handler就出现了,来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。
接下来我们看看关于Handler都有哪些方法(它都能干什么):
其中Handler对象的一些主要方法,如下:
post(Runnable) postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
正如方法名字中看到的,有两类方法:
(1)在某个主线程中执行Runnable
(2)在子线程中发送一个消息,在主线程中检测该消息处理
线程间传递Message对象的sendMessage方法和发送Runnable多线程对象的post方法。正对应着上面所说的两个特性1)、2)
下面开发个Handler实例做说明:
用post的方法执行一个Runnable对象,在该对象中随机产生一个10-100之间的随机数,赋值到UI主线程中的TextView中线程,执行5次,每次相隔5秒, 拼接每次的数字, 最后执行结果应该如:10 22 33 44 61
主要代码如下:
int i = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler.post(run);
}
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
String s = String.valueOf(msg.what);
TextView tv = (TextView)findViewById(R.id.textView);
tv.setText(tv.getText() + ” ” + s);
}
};
Runnable run = new Runnable(){
@Override
public void run(){
Random r = new Random();
int rnum = r.nextInt((100 – 10) + 1) + 10;
handler.sendEmptyMessage(rnum);
handler.postDelayed(run, 5000);
i++;
if (i==5){
handler.removeCallbacks(run);
}
}
};
7. 为什么说Android主线程是线程不安全的,既然不安全为什么要在主线程中更新UI,有点晕 求大师解答
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)。
8. 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)。
9. android中Invalidate和postInvalidate的区别
Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
1,利用invalidate()刷新界面
实例化一个Handler对象,并重写handleMessage方法调用invalidate()实现界面刷新;而在线程中通过sendMessage发送界面更新消息。
{
privateViewview;
publicclassHandlerhandler=newHandler(){
publicvoidhandleMessage(Messagemsg){
view.invalidate();//刷新界面
}
}
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
newThread(){
publicvoidrun(){
handler.sendEmptyMessage();
}
}.start();
}
2,使用postInvalidate()刷新界面 ,使用postInvalidate则比较简单,不需要handler,直接在线程中调用postInvalidate即可。
{
privateViewview;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
newThread(){
publicvoidrun(){
view.postInvalidate();
}
}.start();
}
两个方法都有重载函数,用于刷新指定区域的内容
public void invalidate(int l, int t, int r, int b) ;
public void invalidate(Rect dirty) ;
public void postInvalidate(int l, int t, int r, int b) ;
public void postInvalidate(Rect dirty) ;
此外postInvalidate还支持延迟刷新,
public void postInvalidateDelayed(long delayMilliseconds)
让视图在指定时间后刷新