Android平台调用Web Service:引入线程
接上文
遗留问题
MainActivity的onCreate方法中如果没有有这段代码:
// 强制在UI线程中操作
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().penaltyLog().penaltyDeath()
.build());
会报错误如下:
FATAL EXCEPTION:main
java.lang.NullPointerException
atcom.example.demoservice.MainActivity.getRemoteInfo(MainActivity.java:91)
atcom.example.demoservice.MainActivity$1.onClick(MainActivity.java:51)
这是因为android 3.0+以上 已经不建议在activity中添加耗时操作,要界面和数据脱离。4.0以上的通信都必须放到线程里去做,不能在UI线程。解决办法是另起线程,如果一定要想在UI线程操作,就需要添加如上代码。
显然这样做是不可取的,因为通信消耗时间长,可能会让用户傻傻的等待,那么接下来就通过引入线程来解决这个问题。
通过Runnable接口和Thread类创建线程
我们可以用Runnable接口和Thread类创建线程,从而舍弃强制使用UI主线程的方式,代码如下(同时对代码进行了整理,把nameSpace等变量抽出来)
public classMainActivity extends Activity {
public static final String TAG ="webService_pj";
private EditText phoneSecEditText;
private TextView resultView;
private Button queryButton;
@Override
public void onCreate(BundlesavedInstanceState) {
// StrictMode.setThreadPolicy(newStrictMode.ThreadPolicy.Builder()
// .detectDiskReads().detectDiskWrites().detectNetwork()
// .penaltyLog().build());
//
// StrictMode.setVmPolicy(newStrictMode.VmPolicy.Builder()
// .detectLeakedSqlLiteObjects().penaltyLog().penaltyDeath()
// .build());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
phoneSecEditText = (EditText)findViewById(R.id.phone_sec);
resultView = (TextView)findViewById(R.id.result_text);
queryButton = (Button)findViewById(R.id.query_btn);
queryButton.setOnClickListener(newOnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG,"MainActivity线程ID:"+Thread.currentThread().getId());
// 手机号码(段)
String phoneSec =phoneSecEditText.getText().toString().trim();
// 简单判断用户输入的手机号码(段)是否合法
if("".equals(phoneSec) || phoneSec.length() < 7) {
// 给出错误提示
phoneSecEditText.setError("您输入的手机号码(段)有误!");
phoneSecEditText.requestFocus();
// 将显示查询结果的TextView清空
resultView.setText("");
return;
}
// 命名空间
String nameSpace = "http://WebXml.com.cn/";
// 调用的方法名称
String methodName ="getMobileCodeInfo";
// EndPoint
String endPoint = "http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx";
// SOAP Action
String soapAction = "http://WebXml.com.cn/getMobileCodeInfo";
// method params and values
ArrayList<String> params= new ArrayList<String>();
ArrayList<Object> vals =new ArrayList<Object>();
params.add("mobileCode");
params.add("userId");
vals.add(phoneSec);
vals.add("");
// 通过Runnable接口和Thread类 创建线程调用WebService
newMyThread(nameSpace,methodName,endPoint,soapAction,
params,vals).start();
//将WebService返回的结果显示在TextView中
resultView.setText(getResult());
}
});
}
//通过Runnable接口和Thread类,得到线程返回值
privateString result;
publicString getResult(){
returnresult;
}
private class MyThread extends Thread
{
private String nameSpace;
private String methodName;
private String endPoint;
private String soapAction;
private ArrayList<String> params;
private ArrayList<Object> vals;
public MyThread(String nameSpace, String methodName,
StringendPoint, String soapAction, ArrayList<String> params,ArrayList<Object> vals){
this.nameSpace = nameSpace;
this.methodName = methodName;
this.endPoint = endPoint;
this.soapAction = soapAction;
this.params = params;
this.vals = vals;
}
@Override
publicvoid run()
{
Log.i(TAG,"MyService线程ID:"+Thread.currentThread().getId());
result= getRemoteInfo(nameSpace, methodName, endPoint,
soapAction,params,vals);
}
}
/**
*@MethodName : getRemoteInfo
*@Description : 调用远程webservice方法
* @param nameSpace
* @param methodName
* @param endPoint
* @param soapAction
* @param params
* @param vals
* @return
*/
public String getRemoteInfo(StringnameSpace, String methodName,
StringendPoint, String soapAction, ArrayList<String> params,
ArrayList<Object>vals) {
// 指定WebService的命名空间和调用的方法名
SoapObject rpc = newSoapObject(nameSpace, methodName);
//设置需调用WebService接口需要传入的两个参数mobileCode、userId
for (int i = 0; i < params.size();i++) {
rpc.addProperty(params.get(i),vals.get(i));
}
//生成调用WebService方法的SOAP请求信息,并指定SOAP的版本
SoapSerializationEnvelope envelope =new SoapSerializationEnvelope(SoapEnvelope.VER10);
envelope.bodyOut = rpc;
// 设置是否调用的是dotNet开发的WebService
// envelope.dotNet = true;
// 等价于envelope.bodyOut = rpc;
envelope.setOutputSoapObject(rpc);
HttpTransportSE transport = newHttpTransportSE(endPoint);
try {
// 调用WebService
transport.call(soapAction,envelope);
} catch (Exception e) {
e.printStackTrace();
}
// 获取返回的数据
SoapObject object = (SoapObject)envelope.bodyIn;
String result = "";
if (object != null) {
// 获取返回的结果
result =object.getProperty(0).toString();
}
return result;
}
}
通过线程进行通信,得到同样结果
出现新的问题
可以发现,执行线程中需要在线程中返回一个值,通过在run()中保存返回值,存储返回值的变量应该是MainActivity的成员变量,然后在主线程中用一个get方法取得该值。
但是run何时完成是未知的,很可能当第一次点击按钮后,依然看不到结果,直到第二次或者更多才看到,所以我们需要一定的机制来保证。
而在Java se5就开始用Callable和Future来管理多线程了,可以解决这个问题,接下文。。。
源码下载
http://download.csdn.net/detail/tcl_6666/7365341