接上文
遺留問題
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