1.在子线程中更新UI
Android中更新UI元素,必须在主线程中进行,否则就会出现异常。
changeBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);
new Thread(new Runnable() {
@Override
public void run() {
tv1.setText("改变了");
}
});
}
});
运行以上程序,你会发现程序果然崩溃了,由此证实Android确实不允许在子线程中进行UI操作。
使用异步消息处理机制进行UI操作问题。
public class MainActivity extends AppCompatActivity {
private Button changeBt;
private TextView tv1;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == 1) {
tv1.setText("改变了");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
changeBt = (Button) findViewById(R.id.ChangeButton);
tv1 = (TextView) findViewById(R.id.Tv1);
changeBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);
runOnUiThread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
});
}
});
}
}
或者使用runOnUiThread()方法,它其实就是一个异步消息处理机制的接口封装,背后的实现原理上面的代码一模一样。
changeBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);
runOnUiThread(new Runnable() {
@Override
public void run() {
tv1.setText("改变了");
}
});
}
});
Android还提供了另外一些好用的工具,比如AsyncTask,它背后的实现原理也是基于异步消息处理机制的,只是Android帮我们做了封装。
public class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
private int i=0;
private Context con = null;
ProgressDialog pd1;
DownloadTask(Context con){
this.con= con;
}
@Override
protected Boolean doInBackground(Void... params) {
try{
while(true){
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if(downloadPercent>=100000){
break;
}
}
}catch (Exception e){
return false;
}
return true;
}
@Override
protected void onPreExecute() {
pd1 = ProgressDialog.show(con, "提示", "正在下载");
}
@Override
protected void onProgressUpdate(Integer... values) {
pd1.setMessage("Downloaded"+values[0]+"%");
}
@Override
protected void onPostExecute(Boolean aBoolean) {
pd1.dismiss();
Toast.makeText(con,"下载成功",Toast.LENGTH_SHORT).show();
}
private int doDownload() {
for (int j =0;j<10;j++){
i++;
}
return i;
}
}
AsyncTask是一个抽象类,使用的时候必须继承它,并且我们要为AsyncTask类指定3个泛型参数,第一个是执行AsyncTask传入的参数,可以在后台任务中使用。第二个参数是如果需要在界面上显示当前的进度,以第二个参数的类型作为进度单位。第三个参数是使用这里指定的泛型作为返回值类型。
- onPreExecute()方法是在后台任务开始执行之前调用。可以用于进行一些界面初始化操作。
- onInBackground()方法中的所有代码都是在子线程中执行的。任务完成后通过return返回执行结构。但不可以进行UI操作,要进行UI操作可以调用publishProgress()方法。
- onProgressUpdate()方法,调用了publishProgress()方法后会调用该方法,在onProgressUpdate()方法里可以进行UI操作。
- onPostExecute()方法是在后台任务执行完,利用返回数据判断进行一些UI操作的。
- 如果要启动这个任务,需要以下代码:
new DownloadTask.execute(Context 类型);
2.Service的基本用法
什么时候需要Service呢?比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的。
或者可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。
public class MyService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.i("MyService","oncreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService","onstartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("MyService","ondestroy");
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class DownloadBinder extends Binder {
public void startDownload(){
Log.i("Downloadbinder", "startDownload: ");
}
public void getprogress(){
Log.i("Downloadbinder", "getprogress: ");
}
}
}
- onCreate()方法,顾名思义,在服务创建的时候调用。
- onStartCommand()方法在每次服务启动的时候调用。
- onDestroy()方法在服务销毁的时候调用。
- 启动服务的方法
Intent startIntent = new Intent(MainActivity.this, MyService.class);
startService(startIntent);
- 暂停服务的方法
Intent stopIntent = new Intent(MainActivity.this,MyService.class);
stopService(stopIntent);
-绑定服务和解绑服务
public class MainActivity extends AppCompatActivity {
private Button changeBt;
private TextView tv1;
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getprogress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
changeBt = (Button) findViewById(R.id.ChangeButton);
tv1 = (TextView) findViewById(R.id.Tv1);
Intent startIntent = new Intent(MainActivity.this, MyService.class);
bindService(startIntent, connection, BIND_AUTO_CREATE);//绑定服务
changeBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);//解绑服务
}
});
}
}
绑定了服务后,可以调用DownloadBinder类里所有public方法。任何一个服务在整个应用程序范围类都是通用的,即Myservice不仅可以和MainActivity绑定,还可以和其他后动绑定。获取到相同的的Binder实例。
使用IntentService,好处是onHandleIntent已经是在子线程中运行了。服务在运行结束后会自动停止。