首页 > 代码库 > Android 通过广播来异步更新UI

Android 通过广播来异步更新UI

之前的项目里要做一个异步更新UI的功能,但是结果出现了ANR,所以想写个demo来测试到底是哪个地方出现了问题,结果发现原来的思路是没有问题,郁闷~~

现在这个demo 就是模拟项目里面 的步骤

1、接收到系统的广播(现在模拟为人工发送)

2、广播接收到后,handler通知异步线程从网上下载数据,是异步(模拟为sleep)

3、数据下载完后handler再通知UI更新


下面是主要的两个代码,能够正确运行


package com.example.testanr;

import android.support.v7.app.ActionBarActivity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends ActionBarActivity {
	public 	TextView hellworld = null;
	public 	Button sendBroadcast = null;
	public final static String MY_ACTION = "com.example.testanr.MY_ACTION";
	public static int i = 0;
	public Handler updateUI  =new Handler(){

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			if(msg.arg1 ==0){
				hellworld.setText("更新UI - "+ i);
				i++;
			}
		}
		
	};
	public Handler mHandler =new Handler(){

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			if (msg.arg1 == 1  ){
				new Thread(new Runnable(){

					@Override
					public void run() {
						// TODO Auto-generated method stub
						System.out.println("Thread id is "+Thread.currentThread().getId()+",and Thread name is "+Thread.currentThread().getName());
						try {
							Thread.currentThread().sleep(15000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						
						Message msg =new Message();
						msg.arg1 =0;
						updateUI.sendMessage(msg);
					}
					
					
					
				}).start();
				
			}
		}
		
	};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        hellworld = (TextView)findViewById(R.id.hello_world);
        sendBroadcast = (Button)findViewById(R.id.sendBroadcast);
        //生成一个BroadcastReceiver对象
        TestReceiver  testReceiver = new TestReceiver(mHandler);
		//生成一个IntentFilter对象
		IntentFilter filter = new IntentFilter(); 		
		filter.addAction(MainActivity.MY_ACTION);
		//将BroadcastReceiver对象注册到系统当中
		MainActivity.this.registerReceiver(testReceiver, filter); 
		System.out.println("Thread id is "+Thread.currentThread().getId()+",and Thread name is "+Thread.currentThread().getName());
        sendBroadcast.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				//发送广播
				Intent intent = new Intent();  
				intent.setAction(MainActivity.MY_ACTION);  
                sendBroadcast(intent);  
			}
        	
        });
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}


还有reciever

package com.example.testanr;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;

public class TestReceiver extends BroadcastReceiver {
	public Handler handler;
	public Message message = null;
	public TestReceiver(Handler handler){
		this.handler = handler;
		
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		//这里每次都要new,否者会报错
		message = new Message();
		message.arg1 = 1;
		handler.sendMessage(message);
	}

}

现在才发现一个message是不能往MessageQueue里面发送多次的,否则会报这样的错

java.lang.IllegalStateException: The specified message queue synchronization  barrier token has not been posted or has already been removed.

就说这个message的synchronization  barrier token 已经发送过了的

但是项目里面的问题还没有解决,回头找出原因再发上来


我们知道ANR一般有三种类型

1KeyDispatchTimeout(5 seconds) --主要类型

按键或触摸事件在特定时间内无响应

2BroadcastTimeout(10 seconds)

BroadcastReceiver在特定时间内无法处理完成

3ServiceTimeout(20 seconds) --小概率类型

Service在特定的时间内无法处理完成

所以原因还是应该是第二种,可能没有模拟对


ANR的分析

如何调查并解决ANR

1:首先分析log

2: trace.txt文件查看调用stack.

3: 看代码

4:仔细查看ANR的成因(iowait?block?memoryleak?


但是项目里面log没有输出,是最奇怪的~~~


Android 通过广播来异步更新UI