【问题标题】:Does waiting on threads defeat the purpose of a thread? Trouble with Android threading等待线程会破坏线程的目的吗? Android线程问题
【发布时间】:2013-07-07 23:30:59
【问题描述】:

这是我一直对线程有点困惑的事情。

我有一个线程,它使用 LocationManager 来查找 Android 设备的当前位置。然后我更新 UI 以显示该位置。

事实上,我已经构建了一个名为 LocationFinder 的完整类来为我获取位置。这样我就可以说

String yourLocation = (new LocationFinder).getLocation();

但是,LocationFinder 中的 getLocation() 方法在单独的线程上运行。所以我真的不能说

String yourLocation = (new LocationFinder).getLocation();

因为返回的立即值肯定不是位置,因为位置需要几分之一秒才能找到。 getLocation() 默认返回“notset”,直到内部方法将返回值设置为实际位置。

无论如何,我对如何处理这个问题感到困惑。在找到位置之前我不想阻止,因为应用程序锁定那几毫秒是非常烦人的。我不想使用 AsyncTask,因为在我调用 getLocation() 时已经使用了,而且我觉得嵌套 AsyncTask 是错误的。

这是此层次结构的伪代码:

public class MainActivity extends Activity {

    Button locationButton = new Button(locationButtonClickListener);
    LocationFinder locationFinder = new LocationFinder();

    OnClickListener   locationButtonClickListener = new OnClickListener() {

        locationButton.setText(locationFinder.getLocation());
    }

}

public class LocationFinder {

    String city = "notYetSet";

    public String getLastLocation() {
         (new LastKnownLocationFinder).execute();
         return city;
    }

    public String getGPSLocation() {
         (new GPSLocationFinder).execute();
         return city;
    }

    private class LastKnownLocationFinder extends AsyncTask {

          protected String doInBackground {
               city = [lots of code to get last known location];
          }
    }

    private class GPSLocationFinder extends AsyncTask {

          protected String doInBackground {
               city = [lots of code to get location using GPS];
          }
    }
}

希望这能说明我的意思。

谢谢。

【问题讨论】:

    标签: android multithreading concurrency android-asynctask blocking


    【解决方案1】:

    线程用于划分工作并且大部分时间并行运行(异步),几乎没有其他原因。在某些情况下,需要一个线程完成某件事,而第二个线程处理结果(同步)。

    现在,如果您知道 100% 的时间两个线程的工作都是连续的,那么理论上,不需要两个线程。除了有时人们希望将不同的工作封装在不同的组件中。

    对于Android,应用程序从一个线程(UI-thread / Main-thread)开始,该线程旨在控制所有UI元素的变化,并且永远不应该被阻塞或休眠或长时间操作生效它是为了减少用户体验到的任何延迟或无响应(这就是您目前正在做的事情)。

    在你的情况下,除了一件事之外,一切似乎都做对了,从我在你的伪代码中可以理解的,getLocation() 在调用时总是会返回“notset”,因为 AsyncTask 没有足够的时间来完全执行并更改值。

    您应该构建一个interface 来在LocationFinderMainActivity 之间进行通信,以便在找到后发送位置信号,甚至可以立即发送位置。

    public class LocationFinder {
    
        private CommunicateLocationFinder mCommunicate;
    
        public interface CommunicateLocationFinder {
            void LocationFinderSendGpsLocation(String location);
        }
    
        public LocationFinder (Activity activity){
            // This makes sure that the container activity has implemented
            // the callback interface. If not, it throws an exception
            try {
                mCommunicate = (CommunicateLocationFinder) activity;
            } catch (ClassCastException e) {
                throw new ClassCastException(activity.toString()
                        + " must implement CommunicateLocationFinder");
            }
        }
    
        private class GpsLocationFinder extends AsyncTask {
            protected String doInBackground {
                /*
                 * Your work here
                 */
            }
    
            protected void onPostExecute (String result){
                mCommunicate.LocationFinderSendGpsLocation(result);
            }
        }
    
        /*
         * Rest of your code
         */
    }
    
    public class MainActivity extends Activity implements 
            LocationFinder.CommunicateLocationFinder {
    
        @Override
        public void LocationFinderSendGpsLocation(String location){
            locationButton.setText(location);
        }
    
        /*
         * Rest of your code
         */
    
    }
    

    希望对您有所帮助,请告诉我,干杯。

    【讨论】:

    • 谢谢,这很有帮助!我还没有实现它,但它很有意义,我真的很喜欢这种方式。
    • 我仍然最喜欢这个解决方案,但我担心的是实现一个与我所有最重要的类(即活动)的接口。我知道接口是因为这个原因而存在的,但我觉得如果我可以在 LocationFinder 类中包含我需要的所有东西,而不需要接口,那会更干净。也许那是不可能的。
    • 您可以,您只需将您想要更改的任何内容的参考发送到LocationFinder 类,然后在那里进行更改。当然,如果您要发送 View 参考,请确保使用 runOnUiThread(..) 进行更改,如果您要在 doInBackground() 中进行更改,否则,只需进行更改即可。
    • 我发现了另一种方法!谢谢你的帮助,但这就是我决定做的事情:我在 LocationFinder 中创建了一个名为“uponCompletion()”的方法,并在我的 AsyncTasks 的“onPostExecute()”方法中调用了这个方法。但是,我在创建 LocationFinder 时覆盖了 onCompletion(),这样我就可以让它做任何我想做的事情!我自己想出了这个,所以也许这实际上是一个坏主意(@override 通常不用于接口吗?)但它确实有效,这让我很高兴。编辑是的,基本上这就是你所说的,但格式不同:P
    • 相同的概念,不同的实现,听起来应该可以正常工作。 @Override 可以用于重新创建方法,而不仅仅是接口,例如。 android onCreate(Bundle) 方法,有一个实现,这就是为什么你需要调用super.onCreate(Bundle) 以便实现执行。干杯。
    【解决方案2】:

    您可以使用 LocationListener。这样一来,您的代码将在新位置可用时立即运行,而您不必阻塞线程。

    阅读更多:http://developer.android.com/reference/android/location/LocationListener.html

    【讨论】:

      【解决方案3】:

      我会将应用程序使用的最后一个城市位置存储在首选项中。

      每次应用程序启动/重新启动时(或者更准确地说,每次应用程序恢复其 ui 线程时)我都会在它自己的后台线程中 getLastLocation(),并且我会将生成的城市存储在我上面提到的首选项中.由于该方法不会给天线加电,也不会耗尽电池,所以我经常这样做不会有任何问题。

      在回答您标题中的原始问题时,不,“等待线程不会破坏使用线程的目的”。在这种情况下,“等待”一词可能会令人困惑。 “等待”结果显示并同时渲染完美工作的 UI 与 UI 线程“等待”结果在它可以绘制单个像素并将所有内容从 UI 中冻结直到该结果出现之前是不同的进来。所以从这个意义上说,如果您使用两个不同的异步后台线程并不重要,只要它们是从初始 UI 线程分叉出来的(而不是嵌套的)。

      然后,我在 onclicklistener 中放置的唯一代码将是一个异步任务,它显示一个进度旋转轮,并在后台执行 getgpslocation 以将值插入首选项,并触发内容刷新。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-11
        • 2013-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-01
        • 1970-01-01
        相关资源
        最近更新 更多