2015-09-04 83 views
0

我对Android编程相对陌生,但我确实来自Java背景,为企业应用程序构建后端工具。现在,我正在开发一个Android项目,在该项目中,我想从服务器中检索餐馆的java.util.List。Android:实时获取数据而不是使用异步任务

目前,为了实现这些任务,我使用了一个简单的线程和一个新的Runnable,因为线程立即在线程池中,然后我可以简单地加入线程,并等待它从服务器收到回复然后发回。非常方便,这是工作良好。

但是,有两件事情我不能使用线程来检查客户端的Internet连接并显示一个进度条。据我了解和阅读,这两个东西可以通过AsyncTask来实现。

但是,这里是关键点,我不希望AsyncTask在不久的将来执行,然后返回结果,因为使用当前的Thread模型,我可以简单地加入Thread并放松。

现在,我的问题是,在AsyncTask中是可能的,我不是说在1000毫秒后使用get方法。我的意思是真的在等待它。我如何实现这一目标?就此,我想知道如何检查INternet,如果成功,那么只能提出请求。

这是我在项目中的异步方法之一,然后下一个是我在项目中使用的简单线程模型。

private class LoginUserViaRest extends AsyncTask<Void, Void, String> { 

     @Override 
     protected String doInBackground(Void... params) { 

      final EditText userEmail = (EditText) findViewById(R.id.usernameText); 
      final EditText userPassword = (EditText) findViewById(R.id.PasswordField); 

      rest.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); 
      StaticRestTemplate.jsessionid = rest.execute(StaticRestTemplate.baseURL+"j_spring_security_check", HttpMethod.POST, 
        new RequestCallback() { 
         @Override 
         public void doWithRequest(ClientHttpRequest request) throws IOException { 
          request.getBody().write(("j_username=" + userEmail.getText().toString() + "&j_password=" + userPassword.getText().toString()).getBytes()); 
         } 
        }, new ResponseExtractor<String>() { 
         @Override 
         public String extractData(ClientHttpResponse response) throws IOException { 
          List<String> cookies = response.getHeaders().get("Cookie"); 
          if (cookies == null) { 
           cookies = response.getHeaders().get("Set-Cookie"); 
          } 
          String cookie = cookies.get(cookies.size() - 1); 
          int start = cookie.indexOf('='); 
          int end = cookie.indexOf(';'); 

          return cookie.substring(start + 1, end); 
         } 
        }); 
      return null; 
     } 

     @Override 
     protected void onPostExecute(String aVoid) { 
      super.onPostExecute(aVoid); 
     } 
    } 

线程来检索图片:

@Override 
    public List<RestImage> getImagesForMenuCard(final int menuCardId) { 
     final RestTemplate restTemplate = StaticRestTemplate.getRest(); 

     Thread thread = new Thread(new Runnable() { 
      @Override 
      public void run() { 
       HttpHeaders requestHeaders = new HttpHeaders(); 
       requestHeaders.add("Cookie", "JSESSIONID=" + StaticRestTemplate.jsessionid); 
       requestHeaders.setAccept(Collections.singletonList(new MediaType("application", "json"))); 
       HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders); 
       restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); 
       menuCardImageEntity = restTemplate.exchange(menucardImageList + menuCardId, HttpMethod.GET, requestEntity, RestImage[].class); 
      } 
     }); 
     thread.start(); 
     try { 
      thread.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     RestImage[] restImages = menuCardImageEntity.getBody(); 
     List<RestImage> restImageList = new ArrayList<>(); 
     Collections.addAll(restImageList, restImages); 
     return restImageList; 
    } 

我找到线程模型很容易,因为我更好地了解它。我真的不明白需要创建一个类来发出网络请求。 我对AsyncTask的理解是否被破坏,或者这是它应该是的方式。对于大量文字的道歉,我想简明扼要地解释我的情况。非常感谢。

+0

不幸的是,Android不支持Java8,否则我只会在线程中添加Lambda表达式来创建一个更紧凑的版本。 –

+0

使用AsyncTask被称为无痛线程,以下是Android开发人员有关AsyncTask与线程:[无痛线程](http://android-developers.blogspot.com/2009/05/painless-threading.html)和另一个很好的阅读在这里:[处理器VS AsyncTask与线程](http://stackoverflow.com/a/9800870/593709) –

+0

@AdilSoomro:我没有长时间运行的任务,既不频繁的UI更新...你给的链接只是指以表明有人冻结视图。不相关,因为我想在进行网络请求时显示进度条。 –

回答

2

如果你调用join(),你的线程正在等待另一个线程死掉。由于您称为连接的线程是主线程或用户线程线程,这意味着您将阻止用户界面,这是您应该避免的。现在 - 我真的很讨厌AsyncTask,但它是执行(可能)长时间运行任务的Android标准方式。你可以看看IntentService(但你必须管理服务和UI之间的通信),或者你可以看看RxJava和RxAndroid(这是我最喜欢的框架)。

关于检查连接,你需要ConnectivityManager类,当然这需要一个上下文。

+0

RxJava和RxAndroid看起来很有前途,而且相当方便,而与AsyncTask混杂在一起。在我接受你的答案之前的最后一件事,你能帮我翻译我在主要Post中的登录代码到RxAndroid代码。我正在设置服务器返回的JSESSIONID的值。 –

+0

这是一个有点棘手的代码与登录,这就是为什么。非常感谢。 :-) –

+0

好的。在[Retrofit](http://square.github.io/retrofit/)中获得战利品,这样你就可以忘记所有与http相关的东西。图书馆可以为您提供每个网络请求的Observable 。当你有Observable时,你只需要订阅它来获得对象:) –

1

没有特别要求使用AsyncTask。您可以使用您喜欢的任何Java并发类/技术/框架。

AsyncTask的好处是它给你一个很好的抽象为保证某些线程(doInBackground()在后台线程; onPreExecute()onPostExecute()onProgressUpdate()在主线程上)执行某些代码,所以如果你想更新基于AsyncTask的进度/完成的UI,这很容易实现。通常我建议AsyncTask用于可能对用户界面有直接影响的相对较短的操作,例如在背景中模糊一个位图,然后将其设置为内容ImageView

重要的是要避免阻塞主(UI)线程。这意味着在该线程上调用join()总是一个坏主意。您需要设计代码,以便在异步操作完成时通知您,这通常包括回调接口或其他可观察模式。这样想:仅仅因为你“等待”结果并不意味着线程必须被阻止,你可能必须确保用户不能与UI的某个部分进行交互。

关于这个问题有一些很好的书,我建议你去搜索一下。这里

编辑是一个:http://shop.oreilly.com/product/0636920029397.do

+0

这是正确的答案。永远不要使用连接的线程导致你阻塞UI线程。 Android 101说永远不会妨碍用户的体验,如果你必须使用微调,并使用AsyncTasks ......节省你很多时间和精力 –

0

永远不要使用join()在主线程,因为如果你正在等待被困在循环或永远不返回(从它的run方法返回)线,你会得到ANR。

至于你的问题,AsyncTask类和Java中的其他并发方法没有区别。 AsyncTask所做的是抽象背景中的操作,这是一种在后台通知操作并发布后台操作结果的方法。

我自己更喜欢使用Thread类,并使用接口回调我的操作状态。考虑像下面的东西:

public class BackgroundOperationWorker implements Runnable{ 

     private Thread thread; 
     private IOperationListener listener; 
     Handler handler; 

     public BackgroundOperationWorker(Handler mainThreadHandler,IOperationListener listener) 
     { 
      this.handler = mainThreadHandler; 
      this.listener = listener; 
     } 

     public void doIt(){ 
     thread = new Thread(this); 
     thread.start(); 
     } 

     @Override 
     public void run(){ 
     /** 
     do what you have to do in the thread 
     **/ 

     if(success){ 
      handler.post(new Runnable(){ 
      listener.operationSuccess(); 
      }); 
      }else{ 
      handler.post(new Runnable(){ 
      listener.operationFailed(); 
      }); 
      } 
     } 
} 

警惕的是,如果听众在您的活动类中实现,就像旋转的配置改变,接口的实例将变为无效,必须在活动的onConfigurationChanged(Bundle bundle)方法重新设置实例。

编辑: 编辑上面的示例代码,使其更加简单。

考虑下面的代码:

public class MyClass implements IOperationListener{ 

public BackgroundOperationWorker worker; 

public MyClass(Context context){ 

this.worker = new BackgroundOperationWorker(new Handler(context.getMainLooper()),this/*as an instance of IOperationListener*/); 

this.worker.doIt(); 
} 

@Override 
public void onOperationSuccess(){ 
//notify the user that the operation is successful 
} 

@Override 
public void onOperationFailed(){ 
//notify the user that the operation has failed 
} 

} 

这MyClass的就是一个例子。这可以是您执行“IOperationListener”的活动。

+0

你如何从上面的类调用方法,你在哪里得到答复?你有没有使用上述类的代码?非常感谢!!! –

+0

@WeareBorg我编辑的答案更清晰。 MyClass使用BackgroundWorker类。 BackgroundWorker类的doIt方法初始化后台线程并启动它。由于BackgroundWorker实现了Runnable,因此我将BackgroundWorker实例传递给后台线程。线程启动后,BackgroundWorker的run方法将从线程中调用。 –