关于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>
本人自己在项目中设计的不仅仅只有这些,还包括缓存机制、异常处理机制等。这种线程的集中管理方式对其他设计有很好的支持。我也会在项目结束后和大家分享项目的整体设计。