一、服务
1、服务
服务是Android实现程序后台运行的方案,其不依赖于任何用户界面
2、Android多线程
(1)线程的启动方法一:
A:
class MyThread extends Thread{
public void run(){
// 这里输入处理事件的逻辑
··············
}
}
//启动该线程
new MyThread().start();
(2)线程启动的方法二:
B:
class MyThread implements Runnable{
public void run(){
// 这里输入处理事件的逻辑
··············
}
}
//启动线程
MyThread myThread = new MyThread();
new Thread(myThread) .start();
(3)线程启动的方法三(常用):
C:
new Thread(new Runnable(){
public void run(){
// // 这里输入处理事件的逻辑
··············
}).start;
}
注意:UI元素必须在主线程中进行,不能在子线程中进行。
这里我们创建一个工程为AndroidThreadTest的工程来学习:
具体思想为创建一个按钮和文本显示内容,在点击事件中创建子线程,用该线程实现文本内容的变化,具体代码如下:
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/chang_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="改变文本内容"/>
<TextView
android:id="@+id/text"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Hello World!"
/>
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button changText = (Button)findViewById(R.id.chang_text);
text = (TextView)findViewById(R.id.text);
changText.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.chang_text:
//开始创建子线程
new Thread(new Runnable() {
@Override
//执行线程操作
public void run() {
Log.d("MainActivity","子线程创建成功,正在运行,下一步,执行UI操作");
text.setText("修改界面成功,欢迎看见你");
}
}).start();
}
}
}
然后运行,点击按钮后发现程序崩溃了。
查看错误信息可知,我们在子线程中进行UI操作这是不行的,UI操作应该切换至主线程中去完成。
那么,我们可以采用异步消息处理机制去解决在子线程中的UI操作。
异步通信机制通过在主线程中运行的handleMessage()方法进行处理子线程的想要完成的UI操作,我们通过消息(data数据)来通知主线程的进行UI显示。
在AndroidThreadTest的基础上进行修改。
在MainActivity中,
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView text;
//定义整形变量,用于更新TextView这个动作
public static final int UPDATE_TEXT = 1;
//定义一个Handler对象
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) { //重写父类的handleMessage()方法
/*如果接受字段等于UPDATE_TEXT,将修改UI内容*/
switch (msg.what) {
case UPDATE_TEXT:
Log.d("MainActivity","修改内容...");
text.setText("修改界面成功");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button changText = (Button)findViewById(R.id.chang_text);
text = (TextView)findViewById(R.id.text);
changText.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.chang_text:
//开始创建子线程
new Thread(new Runnable() {
@Override
//执行线程操作
public void run() {
Log.d("MainActivity","子线程创建成功");
//创建一个Message对象,
Message message = new Message();
//将对象的what字段设置为UPDATE_TEXT
message.what = UPDATE_TEXT;
//将信息发送出去,就会调用handler方法去修改文本内容。
handler.sendMessage(message);
/*Log.d("MainActivity","子线程创建成功,正在运行,下一步,执行UI操作");
text.setText("修改界面成功,欢迎看见你");*/
}
}).start();
break;
default:
break;
}
}
}
3:异步消息处理机制。
其处理过程主要有四部分组成:(1)Message、(2)Handler、(3)MessageQueue、(4)Looper。
下面我们就来具体介绍:
(1)Message
是在线程间传递的消息,其内部携带少量信息,也可采 用 arg1和arg2携带整形数据,obj携带Object对象。
(2)Handler
用于发送和处理消息,发送消息一般采用Handler的 sendMessage()方法,而消息会最终到达 handleMessage()方法中。
(3)MessageQueue
为消息队列,用于存放所有通过Handler发送的消息,每个线程只有一个MessageQueue对象。
(4)Looper
为每个线程中的,队列MessageQueue管理者。调用Looper的loop()方法后,就会进入一个无限循环,每发现MessageQueue队列中存在消息,就会将其取出,并传递到handleMessage()方法中
4、AsyncTask
是一个需要创建子类去继承的抽象类,继承时需指定三个泛行参数,分别为(1)params:AsyncTask执行时需要传入的参数,可在后台任务中使用。(2)Progress:进度单位,显示任务进度。(3)Result:对任务结果进行返回
如:
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
···
}
而在该类中我们需要重写AsyncTask的方法。常用的有四个:
(1)onPreExecute()
后台任务执行前调用,进行界面的初始化操作,比如显示进度对话框
(2)onInBackground(Params…)
该方法在子线程中完成,用于处理所有的耗时任务,任务完成可使用return语句将执行结果返回,如果像将耗时任务进度反馈给主线程,可调用publishProgress(Params…)方法来完成
(3)onProgressUpdate(Progress…)
当后台任务调用publishProgress()方法后,该方法就会被调用,,该方法中携带的参数是后台任务中传递出来的,该方法可以对UI进行操作,利用参数中的数值对当前界面元素进行更新。
(4)onPostExecute(Result)
后台任务执行完毕并通过returen返回后,该方法就会被调用,可利用返回的数据进行UI操作,比如提醒任务执行的结果以及关闭进度条对话框,
启动该AsyncTask异步机制的方法为,new +子类+().execute();
二、服务的基本用法
创建服务的方法:
new --------Service ------Service
重写父类的三个方法:
(1)onCreate():服务创建时调用
(2)onStartCommand():服务启动时调用
(3)onDestroy():服务销毁时调用
服务为四大组建需要在AndroidMainifest.xml中注册。
我们新建一个ServiceTest项目,在java包中新建一个服务为MyService,在activty_main.xml中定义两个按钮,用于启动和停止服务:
<?xml version="1.0" encoding="utf-8"?>
<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:id="@+id/start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:hint="启动服务"
/>
<Button
android:id="@+id/stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="停止服务"
android:textSize="20dp"/>
</LinearLayout>
在主函数中处理两个按钮点击事件,通过Context类中的startService()和stopService()对服务进行启动和停止,如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = findViewById(R.id.start_service);
Button stopService = 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;
}
}
}
我们可以在MyService的任意位置调用stopSelf()让服务停下来。
那么怎么让服务去做什么呢?完成什么样的任务?
我们需要onBinder()方法内构建我们的任务对象。例如,我们需要让后台去下载一个文件,我们需要构建一个Binder对象来下载,修改MyService中的内容:
·······································
private DownloadBinder mbinder = new DownloadBinder();
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mbinder;
}
class DownloadBinder extends Binder {
public void startDownload(){
Log.d("TAG","开始下载中...");
}
public void stopDownload(){
Log.d("TAG","停止下载...");
}
public int getProgress(){
Log.d("TAG"," 得到下载进度...");
return 0;
}
}
··································
当活动与服务绑定后,调用服务里的Binder提供的方法,在MainActivity中实现:
······························
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);
Button startService = findViewById(R.id.start_service);
Button stopService = findViewById(R.id.stop_service);
Button unbindService = findViewById(R.id.unbind_service);
Button onbindService = findViewById(R.id.onbind_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
onbindService.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.onbind_service:
Intent bindIntent = new Intent(this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
unbindService(connection);
break;
default:
break;
}
}
·················
在oncreate()方法中添加一个后台通知,将MyService编程一个前台服务。如下所示:
//服务创建时调用
public void onCreate(){
super.onCreate();
Log.d(TAG,"服务创建中...");
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("This is content title")
.setContentText("This is content")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pi)
.build();
startForeground(1,notification);
}
对于耗时操作中需要用到开启子线程和结束服务的方法,android提供了一个异步会自动停止的服务的类IntentService类。这里我们新建一个MyIntentService类:
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService"); //调用父类的有参构造函数
}
@Override
protected void onHandleIntent(Intent intent) {
//打印当前线程的id
Log.d("MyIntentService","Thread id is " +Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService","销毁执行中");
}
}
MainActivity中添加以下逻辑:
case R.id.start_intent_service:
Log.d("MainActivity","Thread id is "+Thread.currentThread().getId());
Intent intent = new Intent(this,MyIntentService.class);
startService(intent);
break;
运行后发现:
程序运行完成后,自动销毁。