1.多线程编程
1.1线程的基本用法
服务是Android中实现程序后台运行的解决方案。服务依赖于创建服务时所在的应用进程,当某个应用进程被杀掉时,所有依赖于该进程的服务也会停止运行。服务的所有的代码都是默认运行在主线程中,我们需要在服务的内部手动创建子线程,并在这里执行具体任务,否则就有可能出现主线程被阻塞。
Android是不允许在子线程中进行UI操作的,为此,提供了一套异步消息处理机制,解决在子线程中进行UI操作的问题。
新建AndroidThreadTest项目
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/change_text"
android:text="Change Text"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/text"
android:text="HelloWorld"
android:textSize="25sp"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final int UPDATE_TEXT = 1;
private TextView text;
private Handler handler = new Handler(){
public void handleMessage(Message msg){
switch (msg.what){
case UPDATE_TEXT :
text.setText("Nice to meet you");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text);
Button changeText = (Button) findViewById(R.id.change_text);
changeText.setOnClickListener(this);
}
@Override
public void onClick(View v){
switch (v.getId()){
case R.id.change_text:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message); //将Message对象发送出去
}
}).start();
break;
default:
break;
}
}
}
先是定义了一个常量UPDATE_TEXT用于表示更新TextView这个动作,然后新增一个Handler对象,并重写父类的handlerMessage()方法,在这里对具体的Message进行处理,如果message.what 字段的值等于UPDATE_TEXT,就将TextView显示的内容改成Nice to meet you
按钮的点击事件,创建了一个Message(android.os.Message)对象,并将what字段指定为UPDATE_TEXT,然后调用Handler的setMessage()方法将这条Message发送出去。很快Handler就会收到这条Message,并在handlerMessage()方法中进行处理。此时handlerMessage()方法中的代码就是在主线程当中运行,可以进行UI操作。
1.2解析异步消息处理机制
Android中的异步消息处理主要由4部分组成,Message,Handler,MessageQueue和Looper
1.Message:在线程之间传递的消息,可以在内部携带少量的信息,用于在不同线程之间交换数据,除了what字段,还有arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Onject对象。
2.Handler:处理者,用于发送和处理消息,Handler的sendMessage()方法,发出的消息经过一系列处理,最终会传递到Handler的handleMessage()方法中。
3.MessageQueue:消息队列,用于存放所有通过Handler发送的消息,这部分消息会一直在消息队列中,等待被处理,每个线程中只会有一个MessageQueue对象。
4.Looper:每个线程中的MessageQueue管家,调用Looper的loop()方法后,就会进入到一个无限循环中,每当发现MessageQueue中存在一条消息,就会将他取出,并传递到Handler的handleMessage()方法中,每个线程只有一个Looper对象。
整个流程:首先在主线程中创建一个Handler对象,并重写handleMessage()方法,然后当子线程需要UI操作时,就创建一个Message对象,并通过Handler将这条信息发送出去,之后这条信息会被添加到MessageQueue的队列中等待被处理,Looper会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,就可以进行UI操作。
之前用到的runOnUiThread()方法就是一个异步消息处理机制。
2.使用AsyncTask
AsyncTask是一个抽象类,使用时必须创建一个子类去继承它,继承时可以为AsyncTask类指定3个泛型参数
- Params:执行AsyncTask时需要传入的参数,可用于在后台任务中使用
- Progress:后台任务执行时,如果需要在界面上显示当前的进度,使用这里指定的泛型作为进度单位
- Result:当任务执行完毕,需要对结果进行返回,使用这里指定的泛型作为返回值类型
需要重写AsyncTask中的几个方法才能完成对任务的定制
1.onPreExecute():在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框
2.doInBackground(Params…):这个方法中的所有代码会在子线程里去执行,我们应该在这里去处理所有耗时任务,任务完成,可以通过return语句将执行结果返回,若AsyncTask第三个泛型参数是Void,就不用返回,在这个方法里不可以进行UI操作,若需要更新UI元素,如反馈当前任务的执行进度,可调用publishProgress(Progress…)方法来完成。
3.onProgressUpdate(Progress…):当在后台任务中调用了publishProgress(Progress…)方法后,onProgressUpdate(Progress…)方法会很快被调用,该方法里的参数就是从后台传递过来,在这个方法里可对UI进行操作,利用参数中的数值可对界面元素进行更新。
4.onPostExecute(Result):当后台任务执行完毕带着参数返回时,这个方法很快被调用,返回的数据会作为参数传递到此方法中,可利用返回的数据进行UI操作,比如提醒任务执行的结果,关闭掉进度条对话框等。
public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
@Override
protected void onPreExecute(){
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... params){
try{
while(true){
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if(downloadPercent>=100){
break;
}
}
}catch (Exception e){
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values){
progressDialog.setMessage("Download "+values[0]+"%");
}
@Override
protected void onPostExecute(Boolean result){
progressDialog.dismiss();
if(result){
Toast.makeText(context,"Download succeeded",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show();
}
}
}
在doInBackground()方法中执行具体的耗时任务,在onProgressUpDate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。
AsyncTask,只需要调用一下publishProgress()方法,就可以从子线程切换到UI线程了。
3.服务的基本用法
3.1启动和停止服务
onCreate():在服务创建的时候调用
onStartCommand():在每次服务启动时候调用
onDestroy():在服务销毁的时候调用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/start_service"
android:text="Start Service"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/stop_service"
android:text="Stop Service"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = (Button) findViewById(R.id.start_service);
Button stopService = (Button) findViewById(R.id.stop_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
}
@Override
public void onClick(View v){
switch (v.getId()){
case R.id.start_service:
Intent startIntent = new Intent(this,MyService.class);
startService(startIntent);
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent);
break;
default:
break;
}
}
}
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService","onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService","onStart");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyService","onDestroy");
}
}
3.2活动和服务进行通信
在MyService中新建了一个DownloadBinder类,并让他继承Binder,然后在他的内部提供了开始下载以及查看下载进度的方法,接着创建了DownloadBinder的实例,在onBind()方法里返回了这个实例。
......
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder{
public void startDownload(){
Log.d("MyService","startDownLoad executed");
}
public int getProgress(){
Log.d("MyService","getProgress executed");
return 0;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
......
新增两个按钮
......
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/bind_service"
android:text="Bind Service"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/unbind_service"
android:text="UnBind Service"/>
......
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = (Button) findViewById(R.id.start_service);
Button stopService = (Button) findViewById(R.id.stop_service);
Button bindService = (Button) findViewById(R.id.bind_service);
Button unbindService = (Button) findViewById(R.id.unbind_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v){
switch (v.getId()){
case R.id.start_service:
Intent startIntent = new Intent(this,MyService.class);
startService(startIntent);
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent);
break;
case R.id.bind_service:
Intent bindIntent = new Intent(this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE);
//三个参数,第三个参数是一个标志位,传入BIND_AUTO_CREATE表示活动和服务进行绑定后自动创建服务,MyService中的
//onCreate()方法执行,但onStartCommand()方法不会。
break;
case R.id.unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}
首先创建了一个ServiceConnection的匿名类,在里面重写了onServiceConnected()和onServiceDisconnected()方法,这两个方法分别会在活动与服务成功绑定以及断开连接时调用。在onServiceConnected()方法里,又通过向下转型得到了DownloadBinder的实例,有了这个实例,活动与服务的关系更紧密了,可以在活动中根据具体场景调用DownloadBinder中的任何public方法,即指挥服务干什么,服务就干什么。
4.服务的生命周期
之前我们学习过了活动以及碎片的生命周期。类似地,服务也有自己的生命周期,前面我们使用到的 onCreate()、onStartCommand()、onBind()和 onDestroy()等方法都是在服务的生命周期内可能回调的方法。 一旦在项目的任何位置调用了 Context的 startService()方法,相应的服务就会启动起来, 并回调 onStartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于 onStartCommand()方法执行。服务启动了之后会一直保持运行状态,直到 stopService()或 stopSelf()方法被调用。
注意,虽然每调用一次 startService()方法,onStartCommand()就 会执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次 startService() 方法,只需调用一次 stopService()或 stopSelf()方法,服务就会停止下来了。 另外,还可以调用 Context的 bindService()来获取一个服务的持久连接,这时就会回调服 务中的 onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于 onBind()方法执行。之后,调用方可以获取到 onBind()方法里返回的 IBinder 对象的实例, 这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持 运行状态。 当调用了 startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy()方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后, 又去调用 unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是 需要注意,我们是完全有可能对一个服务既调用了 startService()方法,又调用了 bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据 Android系统的机制,一 个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满 足,服务才能被销毁。所以,这种情况下要同时调用 stopService()和 unbindService()方法, onDestroy()方法才会执行。 这样你就已经把服务的生命周期完整地走了一遍。
5.服务的更多技巧
5.1使用前台服务
前台服务会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息。
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
NotificationChannel channel = new NotificationChannel("1", "channel1", NotificationManager.IMPORTANCE_MAX);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, "1")
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_background))
.setContentText("this is contentText")
.setContentTitle("this is contentTitle")
.setWhen(System.currentTimeMillis())
.build();
startForeground(1, notification);
startForeground()方法接收两个参数,第一个参数是通知的id,第二个参数是构建出的Notification对象,调用startForeground()会让MyService变成一个前台服务,并在系统状态栏显示出来
IntentService
IntentService实现了让一个服务在执行完毕后自动停止的功能
新建一个MyService类继承自IntentService
public class MyIntentService extends IntentService {
public MyIntentService(){
super("MyIntentService"); //调用父类的有参构造函数
}
@Override
protected void onHandleIntent(Intent intent){
Log.d("MyIntentService","Thread id is "+Thread.currentThread().getId());
}
@Override
public void onDestroy(){
super.onDestroy();
Log.d("MyIntentService","onDestroy executed");
}
}
onHandleIntent()是个抽象方法,可以在onHandleIntent()里处理一些具体的逻辑,不用担心ANR问题,这个方法已经是在子线程中运行的。
......
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/start_intent_service"
android:text="Start Intent Service"/>
......
@Override
protected void onCreate(Bundle savedInstanceState) {
......
Button startIntentService = (Button) findViewById(R.id.start_intent_service);
startIntentService.setOnClickListener(this);
......
}
......
@Override
public void onClick(View view){
switch (view.getId()){
......
case R.id.start_intent_service:
Log.d("MainActivity","Thread id is "+Thread.currentThread().getId());
Intent intentService = new Intent(this,MyIntentService.class);
startService(intentService);
break;
default:
break;
}
}
在AndroidManifest注册
<service android:name=".MyIntentService"/>
点击Start Intent Service按钮
IntentService集开启线程和停止线程于一身。