關於Handler+Thread的使用終於有了一個比較完善的解決方案,自己也一直在自己的項目中使用,不斷改進。這次的結構是前所未有的清新,在Activity不會再出現臃腫的代碼,條理清晰。線程還是還是延續上次的設計進行集中管理。
這次的重要改動:
使用接口替代了抽象類來實現回調方法(對於java不能多繼承,這無疑是個更好的設計)
接口的泛型粒度更小(原來的設計泛型是類級別,現在設計到方法級別)
新增等待提示
這次改動同樣也支持Fragment,而且對同一Activity或Fragment中多個線程有更好的支持。
下個版本的計劃:
添加線程的控制(大家應該注意到,線程返回一個Future對象,下次會在這上面做文章)
等待提示控制(現在提示窗口,是由現在程序自己控制,控制力度感覺不夠)
這次涉及的知識主要是 接口、回調、泛型。
還是老慣例,上demo.
先是定義的一個回調接口
package com.sc.htdemo.app;
/**
* @author SunnyCoffee
* @date 2013-9-30
* @version 1.0
* @desc 回調接口
*/
public interface AppCallback<T> {
public void call(T result);
}
登錄的Activity
MainActivity:
package com.sc.htdemo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.sc.htdemo.app.AppCallback;
import com.sc.htdemo.app.RestImpl;
import com.sc.htdemo.bean.User;
public class MainActivity extends Activity implements OnClickListener,
AppCallback<User> {
private EditText nameEt;
private EditText pwdEt;
private Button loginBtn;
private TextView resultTv;
private User user;
private RestImpl rest = new RestImpl();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
nameEt = (EditText) findViewById(R.id.nameEt);
pwdEt = (EditText) findViewById(R.id.pwdEt);
loginBtn = (Button) findViewById(R.id.loginBtn);
resultTv = (TextView) findViewById(R.id.resultTv);
loginBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
String name = nameEt.getText().toString();
String pwd = pwdEt.getText().toString();
user = new User();
user.setName(name);
user.setPwd(pwd);
// 這裏傳遞兩個this,一個是context,一個是接口實例。
rest.login(this, this, user);
}
@Override
public void call(User result) {
String text = "username:" + result.getName() + "\n" + "photo:"
+ result.getPhoto() + "\n" + "sign:" + result.getSign();
resultTv.setText(text);
}
}
當類中有多個線程時,以如下方式調用接口rest.login(this, new AppCallback<User>() {
@Override
public void call(User result) {
}
}, user);
各種接口的實現類
RestImpl:
package com.sc.htdemo.app;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import com.sc.htdemo.bean.User;
/**
* @Author SunnyCoffee
* @Date 2013-7-29
* @version 1.0
* @Desc 獲取數據的接口實現類。(爲了清晰,Rest接口就沒再寫)
*/
public class RestImpl {
private ExecutorService service = RestHelper.getThreadPool();
/**
* 用戶登錄
*
* @param context
* @param callback
* @param user
* @return 可以用來終止線程
*/
public <T> Future<?> login(final Context context,
final AppCallback<T> callback, final User user) {
// 創建一個handler和一個Thread
final AppHandler<T> handler = new AppHandler<T>(context, callback);
Runnable task = new Runnable() {
@Override
public void run() {
// 模擬網絡延時,服務器返回數據
try {
String name = user.getName();
String pwd = user.getPwd();
Thread.sleep(3000);
User u = new User();
u.setSign("hello,大家好,我是" + name);
u.setName(name);
u.setPwd(pwd);
u.setPhoto("http://www.isunnycoffee.com");
sendData(handler, u);// 通過handler傳遞服務器返回的數據
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
return service.submit(task);
}
private void sendData(Handler handler, Object obj) {
Message msg = handler.obtainMessage();
msg.obj = obj;
handler.sendMessage(msg);
}
}
// 提供線程池
final class RestHelper {
private static final int THREAD_POOL_SIZE = 5;
private static ExecutorService service = null;
private RestHelper() {
}
public static ExecutorService getThreadPool() {
if (service == null)
service = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
return service;
}
}
Handler類,新增了等待提示
AppHandler:
package com.sc.htdemo.app;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
/**
* @Author SunnyCoffee
* @Date 2013-7-29
* @version 1.0
* @Desc Handler。獲得返回值,通過執行回調方法更新UI
*/
public class AppHandler<T> extends Handler {
private Context context;
private AppCallback<T> callback;
private ProgressDialog dialog;// 等待窗口
public AppHandler(Context context, AppCallback<T> callback) {
this.context = context;
this.callback = callback;
showWaiting();
}
@Override
public void handleMessage(Message msg) {
closeWaiting();
if (msg.obj == null)
return;
@SuppressWarnings("unchecked")
T result = (T) msg.obj;
if (callback != null) {
callback.call(result);
}
}
private void showWaiting() {
dialog = new ProgressDialog(context);
dialog.setCancelable(true);
dialog.setMessage("waiting...");
dialog.setCancelable(false);
dialog.show();
}
private void closeWaiting() {
if (dialog != null) {
dialog.dismiss();
}
}
}
測試的javaBean
package com.sc.htdemo.bean;
/**
* @author SunnyCoffee
* @date 2013-9-30
* @version 1.0
* @desc userbean
*/
public class User {
private int id;
private String name;
private String pwd;
private String photo;// 頭像圖片的url
private String sign;// 個性簽名
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getPhoto() {
return photo;
}
public void setPhoto(String photo) {
this.photo = photo;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
}
最後是佈局代碼:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/nameEt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:hint="Enter the username" />
<EditText
android:id="@+id/pwdEt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:hint="Enter the password"
android:inputType="textPassword" />
<Button
android:id="@+id/loginBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Login" />
<TextView
android:id="@+id/resultTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="left" />
</LinearLayout>
本人自己在項目中設計的不僅僅只有這些,還包括緩存機制、異常處理機制等。這種線程的集中管理方式對其他設計有很好的支持。我也會在項目結束後和大家分享項目的整體設計。