android中的应用开发,不像是写控制台程序,他是一种和UI相关的程序。几乎所有的UI应用程序都会有这样的要求:不能在主线程(即UI线程)中做耗时的操作。因为一般情况下,主线程负责处理消息和更新界面。其实更新界面也是基于消息驱动的。
在android设备上, 我们做的每个操作,比如按下菜单键或返回键,或者点击了界面上的一个按钮,这些事件 都会被封装成一个消息,发送到主线程的消息队列中。而主线程监听在他的消息队列上, 如果消息队列中进入了一个消息,那么主线程便取出这个消息,调用这个消息上的回调方法,如果主线程的消息队列中没有消息,那么主线程便会阻塞在队列上,直到一个消息的到来。这种消息机制可以用下面的一张图来解释(该图片来自百度):
从这张图中可以看到android消息机制的几个角色:
其实主线程中的所有代码都是由这种消息机制驱动的。比如我们熟悉的onCreate等回调方法,是框架向该应用程序的主线程的消息队列中发送了一个消息,然后由主线程基于这个消息,调用onrCreate等回调方法。
如果在主线程中做耗时的操作,比如IO和网络,那么主线程就会被长时间的占用,他的消息队列中还有其他消息就不能被即使处理,导致应用程序崩溃,这就是著名的ANR(application no response)错误。举个例子,主线程正在从数据库中读取大量的数据,这时你点击了界面上的一个按钮,这个事件被封装成消息发送到主线程的消息队列,等待主线程处理,由于主线程正在读数据,所以这个消息得不到及时的处理。
所以,在安卓应用开发中, 为了避免主线程被阻塞,将耗时的操作放到子线程中是非常重要的。最主要的处理方式是:
Handler handlerMain = new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case 1: // ... break; case 2: // ... break; case 3: // ... break; default: break; } }; }; private void downloadFile(){ new Thread(new Runnable() { @Override public void run() { // 在子线程下载文件 //... //... //... //下载完成,发送通知 Message msg = handlerMain.obtainMessage(); msg.what = 1; //msg.sendToTarget();发送消息, 也可以这样写 handlerMain.sendMessage(msg); } }).start(); }
AsyncTask<String, String, String> task = new AsyncTask<String, String, String>(){ @Override protected String doInBackground(String... params) { // 在子线程下载文件 //... //... //... //下载任务完成后, 会自动发送消息 return null; } protected void onPostExecute(String result) { //主线程得到子线程发送的消息后,会回调到这个方法 //该方法在主线程中执行 //处理消息或更新界面 //... //... //... }; }; private void downloadFileAndUpdateUI(){ task.execute(null); }
/** * 登陆验证, 在主线程中直接调用, 主线程会等待后台线程验证的结果返回 * @param context * @return 验证成功返回true, 反之返回false */ public static boolean userLoginCheckWaited(final Context context){ //创建单个线程池, 将验证的网络操作放到子线程中 ExecutorService singleTheadPool = Executors.newSingleThreadExecutor(); //将验证任务提交到线程池中 Future<Boolean> fu = singleTheadPool.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return ESDKUtils.userLoginCheck1(context); } }); try { return fu.get(); //等待验证结果的返回 } catch (Exception e) { e.printStackTrace(); return false; } } /** * 网络操作放到后台线程中执行 */ private static boolean userLoginCheck1( Context context){ //设置登录验证的各项参数 List<BasicNameValuePair> params = new LinkedList<BasicNameValuePair>(); params.add(new BasicNameValuePair("yhid", userName)); params.add(new BasicNameValuePair("yhkl", passwd)); params.add(new BasicNameValuePair("sbid", DeviceTool.getDeviceId(context))); params.add(new BasicNameValuePair("clientIp", IPTool.getPsdnIp())); params.add(new BasicNameValuePair("ywxtbm", "BGPTNEW")); params.add(new BasicNameValuePair("ywxtmc", "办公平台升级")); URL url = null; StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try{ //设置url地址 url = new URL(URLConstant.USER_LOGIN_CHEAK_ADDRESS); String paramString = URLEncodedUtils.format(params, "GBK"); //请求参数编码为GBK byte[] dataToSend = paramString.getBytes(); //post请求中的实体数据 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true);
//开始业务登陆验证, 验证用户名和密码的正确性,在主线程直接调用 if(ESDKUtils.userLoginCheckWaited(this)){ //跳转到界面 }