莆仙生活网
当前位置: 莆仙生活网 > 知识库 >

messagequeue(5)

时间:2025-02-09 20:23:11 编辑:莆仙君
3.2 wait()、notify()和synchronizedwaite()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。例18:class TestThreadMethod extends Thread{public int shareVar = 0;public TestThreadMethod(String name){super(name);new Notifier(this);}public synchronized void run(){if(shareVar==0){for(int i=0; i<5; i++){shareVar++;System.out.println("i = " + shareVar);try{System.out.println("wait......");this.wait();}catch(InterruptedException e){}}}}}class Notifier extends Thread{private TestThreadMethod ttm;Notifier(TestThreadMethod t){ttm = t;start();}public void run(){while(true){try{sleep(2000);}catch(InterruptedException e){}/*1 要同步的不是当前对象的做法 */synchronized(ttm){System.out.println("notify......");ttm.notify();}}}}public class TestThread{public static void main(String[] args){TestThreadMethod t1 = new TestThreadMethod("t1");t1.start();}}运行结果为:i = 1wait......notify......i = 2wait......notify......i = 3wait......notify......i = 4wait......notify......i = 5wait......notify......4. wait()、notify()、notifyAll()和suspend()、resume()、sleep()的讨论4.1 这两组函数的区别1) wait()使当前线程进入停滞状态时,还会释放当前线程所占有的“锁标志”,从而使线程对象中的synchronized资源可被对象中别的线程使用;而suspend()和sleep()使当前线程进入停滞状态时不会释放当前线程所占有的“锁标志”。2) 前一组函数必须在synchronized函数或synchronized block中调用,否则在运行时会产生错误;而后一组函数可以non-synchronized函数和synchronized block中调用。4.2 这两组函数的取舍Java2已不建议使用后一组函数。因为在调用suspend()时不会释放当前线程所取得的“锁标志”,这样很容易造成“死锁”。


android主线程中是不是只有一个Looper,一个MessageQueue

Android中的Runnable并不一定是新开的线程,比如下面调用的方法就是运行在UI主线程中Hanlder handler = new Handler();handler.post(new Runnable(){ public void run(){}});官方文档对此的解释是:The runnable will be run on the user interface thread. ”boolean android.view.View .post(Runnable action)Causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread.Parameters: action The Runnable that will be executed. Returns: Returns true if the Runnable was successfully placed in to the message queue. Returns false on failure, usually because the looper processing the message queue is exiting.我们可以通过handler的对象的post方法,把Runnable对象(一般是Runnable的子类)传过去,handler会在Looper中调用Runnable的run方法执行,Runnable是一个接口,不是一个线程,一般线程会实现Runnable接口这里我们看代码handler.post(new Runnable(){好像是new了一个interface,其实是new一个实现Runnable的匿名内部类(Inner Anoymous Class)}) 这是一个简练的方法Runnalbe是一个接口,不是一个线程,一般线程会实现Runnalbe接口,所以如果我们使用匿名内部类是运行在UI主线程的,如果我们使用实现这个Runnable接口的线程类,则是运行在对应的线程的。具体来说这个函数的工作原理如下:View.post(Runnalbe)方法,在post(Runanble action)方法中,View获得当前主线程(即UI线程)的handler,然后将action对象post到handler里面去,在Handler里,它将传递过来的action对象封装成一个Message(Message 的callback为action),然后将其投入到UI线程的消息循环中,在handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法,而此时,已经路由到UI线程里,因此我们可以毫无顾虑来更新UI。如下图,前面看到的代码,我们这里的Message的callback为一个Runnalbe的匿名内部类,这种情况下,由于不是在新的线程中使用,所以千万别做复杂的计算逻辑。


android 多个子线程有多个loop吗

handlerB当然不能接收到taskA中的handlerA发送的message,可能有同学会说了,handlerA和handlerB都是在主线程中创建的handler,他们都关联于主线程,每个线程都有一个队列messageQueue,looper管理这个队列并且分发消息,无论是handlerA还是handlerB都是发送消息到主线程中的messageQueue, 并且这两个handler的代码也是一样的,handlerA所在的视图处于后台,视图B在前台,handlerB应该能够接受handlerA发送的消息并且处理啊,测试一下,果然视图b中虽然没有启动taskB,但是依然弹出了toast,难道这种说法是对的吗?当然不对,主要有这么两个问题。  第一个问题:handlerB能否接收到handlerA发送的消息?  不能,看看Message的创建方式复制代码Message mes = new Message();mHandler.sendMessage(mes);//******************************** public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }//******************************** public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }//******************************** public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }复制代码通过代码我们可以看到Message会指定它的target为发送他的handler另外一种方式:复制代码Message mes2 = mHandler.obtainMessage();mes2.sendToTarget();//********************************** public final Message obtainMessage() { return Message.obtain(this); }//*********************************** public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; }复制代码这两种创建方式都是一样的。再来看一下消息是怎么分发的?Looper会不断的轮询消息队列,将消息发送给响应的handler进行处理复制代码 public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; while (true) { Message msg = queue.next(); // might block //if (!me.mRun) { // break; //} if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } if (me.mLogging!= null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); msg.target.dispatchMessage(msg); if (me.mLogging!= null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); msg.recycle(); } } }//*****************************************************************************复制代码  看上面的红色标注的代码,他会调用这个Message的target的dispatchMessage(msg)分发,上面就说过了这个target就是发送这个消息的handler本身.复制代码 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }复制代码至此结束,handlerB根本不会得到handlerA的消息第二个问题,既然handlerB不能获得handlerA的消息,那么又是如何弹的toast呢?复制代码 public static Toast makeText(Context context, CharSequence text, int duration) { Toast result = new Toast(context); LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null); TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message); tv.setText(text); result.mNextView = v; result.mDuration = duration; return result; }//********************************************* public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext.getPackageName(); TN tn = mTN; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } }