【问题标题】:How to force main thread to wait until Looper will end its work如何强制主线程等到 Looper 结束工作
【发布时间】:2014-08-04 00:11:41
【问题描述】:

我在 Android 应用程序的 Service 中使用 Looper。通常它可以正常工作。但在某些情况下(例如,当我经常尝试调试我的应用程序时)它显示应用程序内部存在竞争。 Looper 正在启动,ThreadHandler 正在方法 startMessageThread() 中创建,该方法在 Service.onCreate() 方法结束时被调用。

比赛是因为在Looper.loop();之前初始化的还有其他方法和类使用这个类。在某些情况下,这些方法在Looper 结束之前运行。结果为NullPointerException

public class MyService extends Service {

    private Thread thread;
    private Handler handler;

    @Override
    public void onCreate() {
        super.onCreate();
        //some code
        startMessageThread();
    }

    private void startMessageThread() {
        thread = new Thread() {

            @Override
            public void run() { 
                Looper.prepare();
                handler = new Handler() {

                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what) {
                            //message handling
                        }
                    }
                };
                MyObject myObject = new MyObject(arg1, arg2);
                myObject.init();
                myObject.attr = myAttr;
                Looper.loop();
            }
        }              
    }
}

如果我是对的,这个问题How to create a Looper thread, then send it a message immediately? 非常相似,但我不想在创建“HandlerThread”后立即发送任何消息。只是为了强制主线程在onCreate方法结束时等待Looper结束。

我已经尝试在 Service.onCreate 方法的末尾添加这个:

synchronized (thread) {
    try {
        if (looperRun) {
            wait();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

looperRun 当然是boolean。我在Looper.loor(); 方法调用之前添加了looperRun = true;。而这段代码在startMessageThread()方法的末尾。

if (looperRun) {            
    notify();
    looperRun = false;
}

在某些情况下我遇到了这个异常:

java.lang.RuntimeException: Unable to create service com.myapplication.service.MyService:
java.lang.IllegalMonitorStateException: object not locked by thread before wait()
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2582)
    at android.app.ActivityThread.access$1800(ActivityThread.java:135)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5017)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalMonitorStateException: object not locked by thread before wait()
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:364)
    at com.myapplication.service.MyService.onCreate(MyService.java:162)
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2572)
    at android.app.ActivityThread.access$1800(ActivityThread.java:135)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5017)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
    at dalvik.system.NativeStart.main(Native Method)

编辑

protected void startMessageThread() {
    MessageThread messageThread = new MessageThread("messageThread");
    messageThread.start();
    messageHandler = new Handler(messageThread.getLooper()) {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                //message handling
            }
        }
    };
}

private class MessageThread extends HandlerThread {

    public MessageThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        Looper.prepare();
        MyObject myObject = new MyObject(arg1, arg2);
        myObject.init();
        myObject.attr = myAttr;
        Looper.loop();
    }
}

编辑 2

我尝试过类似的解决方案:How to create a Looper thread, then send it a message immediately? 我的代码如下。但是我的应用在启动后似乎被冻结了。

private class BackgroundThread extends HandlerThread {

    private Handler handler;

    public BackgroundThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        Looper.prepare();
        backgroundHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                log.finest("DEAService message:" + msg.what);
                switch (msg.what) {
                    case MSG_INIT:                            
                        MyObject myObject = new MyObject(arg1, arg2);
                        myObject.init();
                        myObject.attr = myAttr;
                        break;
                    //message handling
                }
            }
        };
        Looper.loop();
    };

    public synchronized void waitUntilReady() {
        handler = new Handler(getMainLooper());
    }
}

我像以前一样在这个方法中启动这个HandlerThread

protected void startBackgroundThread() {
    BackgroundThread backgroundThread = new BackgroundThread("backgroundThread");
    backgroundThread.start();
    backgroundThread.waitUntilReady();
    backgroundHandler.sendEmptyMessage(MSG_INIT);
}

正如我上面提到的 - 应用程序似乎在启动后被冻结。什么都没有发生。

【问题讨论】:

  • 你想做什么?
  • @OfekRon 我想在onCreate 里面等一会儿,因为Looper 将完成

标签: java android multithreading


【解决方案1】:

你永远不应该阻塞主线程。从来没有!

我已经使用了好几次没有任何问题的方便的HandlerThread 类,如下所示:

HandlerThread ht = new HandlerThread("myThread");
ht.start();
Handler handler = new Handler(ht.getLooper());
handler.post(... stuff

也许你应该试一试

编辑: 抱歉,处理程序有两种方法,我原来的帖子是另一种。你的使用方式是这样的。

HandlerThread ht = new HandlerThread("myThread");
ht.start();
Handler handler = new Handler(ht.getLooper()){
      @Override
      public void handleMessage(Message msg) {
        switch (msg.what) {
            //message handling
        }
   }
};

新编辑:

查看代码。

protected void startMessageThread() {
    HandlerThread messageThread = new HandlerThread("messageThread");
    messageThread.start();
    messageHandler = new Handler(messageThread.getLooper()) {

        @Override
        public void handleMessage(Message msg) {
          switch (msg.what) {
              case INIT:
                 MyObject myObject = new MyObject(arg1, arg2);
                 myObject.init();
                 myObject.attr = myAttr; 
              break;
            }
        }
    };
   messageHandler.sendEmptyMessage(INIT);
}

【讨论】:

  • 嗯,我可以将我的Thread 切换为HandlerThread。但是我可以通过handler 发布以这种方式创建的哪些内容?我不能以这种方式放置在 onCreate 完成后运行的任何代码。
  • 我还有一个问题:我应该以这种方式将我提到的代码放在哪里,该代码放在run() 方法中Looper.loop() 之前?如果我是对的,我可以扩展 HandlerThread 并将其放在 run() 方法中。但是我是否也应该将run() 方法放在Looper.prepare()Looper.loop() 中?
  • 我不确定我是否理解您的问题。根据我在答案中发布的内容,您必须保留 handler 对象的引用并调用 handler.sendMessage(...),这将在 switch 语句中处理。无需扩展 HandlerThread 您已经在扩展 Handler
  • 好吧,我的英语不是很清楚和完美。 :-) 我已经更新了我的问题。我在Looper.loop() 之前的第一个版本中添加了三行代码,并且我还添加了我想知道的解决方案。这就是我在之前的评论中所描述的。这三行代码非常重要,所以我应该把它们放在这个Thread 中。
  • 我已经重新编辑了答案,这样的事情似乎是可能的。
【解决方案2】:

我已尝试使用此代码,一切似乎都运行良好。

private class BackgroundThread extends HandlerThread {

    private Handler handler;

    public BackgroundThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        Looper.prepare();
        backgroundHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                log.finest("DEAService message:" + msg.what);
                switch (msg.what) {
                    //message handling
                }
            }
        };
        MyObject myObject = new MyObject(arg1, arg2);
        myObject.init();
        myObject.attr = myAttr;
        Looper.loop();
    };

    public synchronized void waitUntilReady() {
        handler = new Handler(getMainLooper());
    }
}

然后用这个代码开始这个HandlerThread

protected void startBackgroundThread() {
    BackgroundThread backgroundThread = new BackgroundThread("backgroundThread");
    backgroundThread.start();
    backgroundThread.waitUntilReady();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-12
    相关资源
    最近更新 更多