2010-05-27 193 views
12

我有一個單獨的線程運行以從互聯網獲取數據。之後,我想通過調用adapter.notifyDataSetChanged()來更新主線程中的ListView。但它不起作用。任何解決方法?謝謝。從另一個線程更新主線程中的ListView

+0

http://stackoverflow.com/a/33584826/1318946 – 2015-11-07 16:12:54

回答

12

總體不錯的建議是 http://android-developers.blogspot.com/2009/05/painless-threading.html

我個人使用我的自定義線程(一類擴展Thread),但通過消息發送響應到UI線程。所以在線程的run()函數中有:

Message msg; 
msg = Message.obtain(); 
msg.what = MSG_IMG_SET;      
mExtHandler.sendMessage(msg); 

UI線程定義了一個消息處理程序。

private Handler mImagesProgressHandler; 

public void onCreate(Bundle bundle) { 

    mImagesProgressHandler = new Handler() { 
     @Override 
     public void handleMessage(Message msg) { 
      switch (msg.what) {    
      case LoadImagesThread.MSG_IMG_SET: 
       mArrayAdapter.setBitmapList(mImagesList); 
       mArrayAdapter.notifyDataSetChanged(); 
       break; 
      case LoadImagesThread.MSG_ERROR: 
       break; 
      } 
      super.handleMessage(msg); 
     } 
    };     

這實際上比AsyncTask更容易。

+0

我更喜歡這種方法以及 – Mark 2011-02-14 01:47:04

+0

我忘了提及:你也可以添加一個對象到消息,如:msg.obj = someBitmap;並在處理程序中接收它(例如,可以使用接收到的位圖更新數組或列表,並通知適配器列表的底層數據已更改:mArrayAdapter.notifyDataSetChanged();)。在這種情況下,不會出現競賽狀況。 – Yar 2011-02-16 09:09:21

+2

當心 - 這裏是龍!這個內部Handler類對外部類有一個隱式引用。更好地使Handler靜態並將WeakReference引用到外部類。 – 2015-07-14 22:17:32

9

或者發佈消息ListView的消息隊列(這將在UI線程上執行):

list.post(new Runnable() {     
    @Override 
    public void run() { 
     adapter.notifyDataSetChanged(); 

    } 
}); 
2

假設 yourActivity是你的小部件已被置於它的活性, yourView是的小部件和 adapter是小工具的適配器:

yourActivity.runOnUiThread(new Runnable() { 
public void run() {  
     try{ 
       adapter.notifyDataSetChanged(); 
     } 
     catch{} 
} 
} 
3

您可以使用RxJava和組合爲此目的。

下面介紹如何做到這一點。我將使用GitHub API作爲示例。

爲您的HTTP API創建一個Java接口。

public interface GitHubService { 
    @GET("users/{user}/repos") 
    Observable<List<Repo>> listRepos(@Path("user") String user); 
} 

使用Observable會將響應轉換爲流。每個事件都是回購列表。

使用Retrofit類生成GitHubService接口的實現。您可能會或可能不會提供自定義HTTP客戶端。

Retrofit retrofit = new Retrofit.Builder() 
      .baseUrl("https://api.github.com/") 
      .client(okHttpClient) // OkHttp Client 
      .addConverterFactory(GsonConverterFactory.create()) 
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 
      .build(); 

GitHubService service = retrofit.create(GitHubService.class); 

現在來Rx部分。您必須添加一個訂閱服務器來收聽服務器發回的響應。換句話說,反應它。

service.listRepos("octocat") 
.subscribeOn(Schedulers.io()) // 1 
.observeOn(AndroidSchedulers.mainThread()) // 2 
.flatMap(Observable::from) // 3 
.subscribe(repoList -> { // 4 
    // Update the list view 
    // notifyDataSetChanged 
}); 

下面是用數字註釋行正在做 -

1 - 它告訴哪個線程被用來撥打電話的操作系統。我們正在爲此選擇IO線程。

2 - 它告訴操作系統使用哪個線程來偵聽響應。我們在主線程上執行此操作,並在接收響應時更新UI。

3 - 此行顯示了Rx的真正魔力。這個簡單的小行將轉換爲單個對象的響應列表。因此,我們將對每個對象做出反應,而不是整個列表。你可以閱讀更多關於它here

4 - 此行實際上定義了什麼樣的'反應'將顯示給事件。您可以在這裏更新適配器。

一個更詳盡的用戶看起來是這樣的 -

new Subscriber<Repo>() { 
    @Override 
    public void onCompleted() { 

    } 

    @Override 
    public void onError(Throwable e) { 

    } 

    @Override 
    public void onNext(Repo repo) { 

    } 
} 

附: - 我在上面的例子中使用過lambda表達式。您可以通過here添加。

-1

這裏的技巧是,你把

mAdapter.notifyDataSetChanged()的位置;

這裏一個簡單的例子:

mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view); 
mAdapter = new myAdapter(......); 
mAdapter.notifyDataSetChanged(); 
mRecyclerView.setAdapter(mAdapter); 

這個工作對我來說很好。