標籤:android平台 webservice 線程 runnable
接上文
遺留問題
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; } @Overridepublicvoid 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