Service-服务与线程(第一行代码)包含线程与handler的总结,服务与活动的通信

* 注意!!!

* BroadcastReceiver生命周期只有十秒左右,
* 如果在onReceiver内做超过十秒内的事情,就会报错
* 应该通过发送Intent给service,由service来完成
* 不建议在广播里开启子线程

包含线程的总结:

服务(Service)是Android 中实现程序后台运行的解决方案,它非常适合用于去执行那
些不需要和用户交互而且还要求长期运行的任务

不过需要注意的是,服务并不是运行在一个独立的进程当中的,而是依赖于创建服务
时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停
止运行。 

另外,也不要被服务的后台概念所迷惑,实际上服务并不会自动开启线程,所有的代码
都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这
里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。如:

new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
}
}).start();

和许多其他的GUI 库一样,Android 的UI 也是线程不安全的。也就是说,如果想要更
新应用程序里的UI 元素,则必须在主线程中进行,否则就会出现异常。(在子线程中对UI控件进行操作就报错)

但是有些时候,我们必须在子线程里去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI 控件,这该如何是好呢?
对于这种情况,Android 提供了一套异步消息处理机制,完美地解决了在子线程中进行UI 操作的问题。如下:

public class MainActivity extends Activity implements OnClickListener {
   public static final int UPDATE_TEXT = 1;
   private TextView text;
   private Button changeText;
   private Handler handler = new Handler() {
     public void handleMessage(Message msg) {//重写父类的handleMessage 方法,在这里对具体的Message进行处理
       switch (msg.what) {
         case UPDATE_TEXT:
           // 在这里可以进行UI操作
          text.setText("Nice to meet you");//注意这里还是在主线程中修改的 是异步
           break;
        default:
           break;
     }
   }};
  ......//省略按钮
   @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对象发送出去   Handler就会收到这条Message,
           }
         }).start();
        break;
      default:
       break;  
    }
  }
 }

这样就已经掌握了Android 异步消息处理的基本用法,使用这种机制就可以出色地解决掉在子线程中更新UI 的问题。

解析异步消息处理机制
Android 中的异步消息处理主要由四个部分组成,Message、Handler、MessageQueue 和
Looper。其中Message 和Handler 在上一小节中我们已经接触过了,而MessageQueue 和Looper
对于你来说还是全新的概念,下面我就对这四个部分进行一下简要的介绍。
1. Message
Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线
程之间交换数据。上一小节中我们使用到了Message 的what 字段,除此之外还可以使
用arg1 和arg2 字段来携带一些整型数据,使用obj 字段携带一个Object 对象。
2. Handler
Handler 顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消
息一般是使用Handler 的sendMessage()方法,而发出的消息经过一系列地辗转处理后,
最终会传递到Handler 的handleMessage()方法中。
3. MessageQueue
MessageQueue 是消息队列的意思,它主要用于存放所有通过Handler 发送的消息。
这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue
对象。
4. Looper
Looper 是每个线程中的MessageQueue 的管家,调用Looper 的loop()方法后,就会
进入到一个无限循环当中,然后每当发现MessageQueue 中存在一条消息,就会将它取
出,并传递到Handler 的handleMessage()方法中。每个线程中也只会有一个Looper 对象。
了解了Message、Handler、MessageQueue 以及Looper 的基本概念后,我们再来对异步
消息处理的整个流程梳理一遍。首先需要在主线程当中创建一个Handler 对象,并重写
handleMessage()方法。然后当子线程中需要进行UI 操作时,就创建一个Message 对象,并
通过Handler 将这条消息发送出去。之后这条消息会被添加到MessageQueue 的队列中等待
被处理,而Looper 则会一直尝试从MessageQueue 中取出待处理消息,最后分发回Handler
的handleMessage()方法中。由于Handler 是在主线程中创建的,所以此时handleMessage()方
法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行UI 操作了。整个异步
消息处理机制的流程示意图如图9.4 所示。

也就从子线程进入到了主线程,从不能更新UI 变成了可以更新UI,整个异步消息处理的核心思想也就是如此。

服务的基本用法

定义两个按钮执行开启与关闭服务,如下:

public class MainActivity extends Activity {
    private Button btn_start;
    private Button btn_stop;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_start = (Button) findViewById(R.id.btn_start);
        btn_stop = (Button) findViewById(R.id.btn_stop);
        btn_start.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MainActivity.this,MyService.class);
                startService(intent);//开启服务
            }
        });
        btn_stop.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MainActivity.this,MyService.class);
                stopService(intent);//关闭服务
            }
        });
    }
}

服务的定义:

/**
 * 定义服务类
 * 需要在AndroidManifest.xml中注册
 *  <service android:name=".MyService"></service>
 */
public class MyService extends Service{
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override//会在服务创建的时候调用
    public void onCreate() {
        System.out.println("服务-->onCreate");
        super.onCreate();
    }
    @Override//会在每次服务启动的时候调用
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("服务-->onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override//在服务销毁的时候调用
    public void onDestroy() {
        System.out.println("服务-->onDestroy");
        super.onDestroy();
    }
}

关闭服务的方法:1.调用 stopService(intent) 2.在服务类内部调用stopSelf()方法

运行顺序:第一次开启服务会执行onCreate方法,onStartCommand方法,再开启会执行onStartCommand方法(和活动类似),

取消服务的时候会调用onDestroy方法.在没开启服务的时候去关闭服务不会出现异常,也不会调用onDestroy方法(有别于bindService)

以上为服务的基本用法:可是上例代码中的活动与服务不能进行通信,

那么有没有什么办法能让活动和服务的关系更紧密一些呢?例如在活动中指挥服务去
干什么,服务就去干什么。当然可以,这就需要借助我们刚刚忽略的onBind()方法了。

/**
 * 实现服务与活动的通信(让活动指定服务做某些事)
 * @author Administrator
 *
 */
public class BinderServiceTest extends Activity {
    private Button btn_bindService;
    private Button btn_unbindService;

    public MyService.DownloadBinder downloadBinder;
    public final ServiceConnection connection=new ServiceConnection() {
        @Override//服务失去连接的时候调用(解除绑定的时候不会调用,只有在进程挂了,服务被异常杀死才会调用)
        public void onServiceDisconnected(ComponentName name) {
            System.out.println("按了单击事件的取消服务");//取消绑定不会调用此方法,只会调用onDestroy方法
        }
        @Override//服务连接的时候调用(绑定的时候调用),当被另一个对象绑定该服务的时候也会执行该方法
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("按了单击时间的绑定服务");//先执行OnCreate方法,再执行onBind方法,再执行该方法
            downloadBinder=(DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();//调用服务中的方法
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //绑定服务与取消绑定服务,实现活动与服务的通信
        btn_bindService = (Button) findViewById(R.id.btn_bindService);
        btn_unbindService = (Button) findViewById(R.id.btn_unbindService);
        btn_bindService.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(BinderServiceTest.this,MyService.class);
                bindService(intent, connection, BIND_AUTO_CREATE);//绑定服务
                //BIND_AUTO_CREATE 表示在活动和服务进行绑定后自动创建服务。
                //这会使得MyService 中的onCreate()方法得到执行,但onStartCommand()方法不会执行。
            }
        });
        btn_unbindService.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(connection);//取消绑定
            }
        });
    }
}

当点击绑定服务按钮时,先执行先执行OnCreate方法,再执行onBind方法,再执行onServiceConnected方法,当再点击绑定按钮时不会执行任何操作。按取消绑定时执行onDestroy()方法,在没绑定的前提下去取消绑定会出现异常

MyService类:

/**
 * 定义服务类
 * 需要在AndroidManifest.xml中注册
 *  <service android:name=".MyService"></service>
 */
public class MyService extends Service{
    private DownloadBinder binder=new DownloadBinder();
    class DownloadBinder extends Binder{
        public void startDownload(){//自定义方法,实现活动与服务的模拟
            System.out.println("开启下载");
        }
        public int getProgress(){
            System.out.println("获取下载进度");
            return 0;
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("返回binder对象");
        return binder;//实现通信的关键
    }
    @Override//会在服务创建的时候调用
    public void onCreate() {
        System.out.println("服务-->onCreate");
        super.onCreate();
    }
    @Override//会在每次服务启动的时候调用
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("服务-->onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override//在服务销毁的时候调用
    public void onDestroy() {
        System.out.println("服务-->onDestroy");
        super.onDestroy();
    }
}

 

onstartCommad的返回值的概念
/* Service.START_STICKY //粘性服务 当服务意外被杀死的时候,可以自动重启,重新启动的时候,传入空的值
*Service.START_NOT_STICKY //非粘性服务 当服务意外被杀死的时候,不会自动重启
*Service.START_REDELIVER_INTENT //当服务被意外杀死的时候,重新启动的时候,会通过intent传入保存的值,恢复以前的状态

 

服务的生命周期

前面我们使用到的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()方法才会执行。

两种方式的对比:

一、采用start的方式开启服务
生命周期如下:
onStart()过时了
开启服务: onCreate()--> onStartCommand() ---> onDestory();

如果服务已经开启,不会重复的执行onCreate(), 而是会调用onStart()和 onStartCommand();

服务停止的时候 onDestory().

服务只会被停止一次


二、服务还有一种开启方式,绑定的方式开启服务。
onCreate() --->onBind();--->onunbind()-->onDestory();
绑定服务不会调用onstart或者onstartcommand方法;

混合调用的服务的生命周期:步骤必须如下,不然容易出异常
服务长期后台运行,又想调用服务的方法:
1.start方式开启服务(保证服务长期后台运行)
2.bind方式绑定服务(保证调用服务的方法)
3.unbind解除绑定服务
4.stopService停止服务。

 

三、两种开启服务方法的区别。

start方式开启服务。 一旦服务开启跟调用者(开启者)就没有任何关系了。
开启者退出了,开启者挂了,服务还在后台长期的运行。
开启者没有办法去调用服务里面的方法。

bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。  不求同时生,但求同时死。
开启者可以调用服务里面的方法。

 

使用前台服务:因为服务的系统优先级比较低,当系统出现内存不足的时候就有可能回收掉正在后台运行的服务,如果你希望服务可以一直保持运行状态,而不会

由于系统的内存不足而被回收,就可以考虑使用前台服务

前台服务和普通服务最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的
效果。当然有时候你也可能不仅仅是为了防止服务被回收掉才使用前台服务的,有些项目由于特殊的需求会要求必须使用前台服务,比如说墨迹天气。

前台服务代码如下:

/**
 * 前台服务
 */
public class ForegroudService extends Service{

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //通知栏的设置
        Notification.Builder builder=new Notification.Builder(this);
        builder.setTicker("瞬间通知");
        builder.setContentTitle("标题");
        builder.setContentText("内容");
        builder.setSmallIcon(R.drawable.ic_launcher);
        Intent intent=new Intent(this,MainActivity.class);
        builder.setContentIntent(PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));//点击服务跳转到MainActivity
        startForeground(1, builder.getNotification());//开启前台服务
        System.out.println("前台服务的创建");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
    }
    
}

可以看到,这里只是修改了 onCreate()方法中的代码,这次在构建出 Notification 对
象后并没有使用 NotificationManager 来将通知显示出来,而是调用了 startForeground()方法。
这个方法接收两个参数,第一个参数是通知的 id,类似于 notify()方法的第一个参数,第二
个参数则是构建出的 Notification 对象。调用 startForeground()方法后就会让 MyService 变成
一个前台服务,并在系统状态栏显示出来。(还是以startService来开启服务)

不过上面所将的所有服务都是在主线程中运行的如果直接在服务里去处理一些耗时的逻辑,就很容易出现 ANR(Application Not Responding)的情况。

所以这个时候就需要用到 Android 多线程编程的技术了,如下,标准的一个服务可以这样写

public int onStartCommand(Intent intent, int flags, int startId) {
  new Thread(new Runnable() {
    @Override
    public void run() {
      ...... // 这里写处理具体的逻辑
      stopSelf();//希望开启服务后自动关闭就可以这样写,处理完后停止服务 会
    }
  }).start();
  return super.onStartCommand(intent, flags, startId);
}

虽说这种写法并不复杂,但是总会有一些程序员忘记开启线程,或者忘记调用 stopSelf()
方法。为了可以简单地创建一个同步的、会自动停止的服务,Android 专门提供了一个
IntentService 类,这个类就很好地解决了前面所提到的两种尴尬,下面我们就来看一下它的
用法。
新建一个 MyIntentService 类继承自 IntentService,代码如下所示:切记 需要在xml中注册该服务

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());//这就是同步了(子线程) 运行完该函数会自动关闭服务调用onDestroy方法
  }
  @Override
  public void onDestroy() {
    super.onDestroy();
    Log.d("MyIntentService", "onDestroy executed");//这里为主线程执行的
  }
}

这里首先是要提供一个无参的构造函数,并且必须在其内部调用父类的有参构造函数
然后要在子类中去实现 onHandleIntent()这个抽象方法,在这个方法中可以去处理一些具体的
逻辑,而且不用担心 ANR 的问题,因为这个方法已经是在子线程中运行的了

Intent intentService = new Intent(this, MyIntentService.class);
startService(intentService);//这样开启就是

所以:这个类集开启线程和自动停止服务于一身

Activity与Service通信--2:

服务类如下,切记要注册

public class MyService extends Service {

    public class MyBind extends Binder{
        /**
         * 重写该方法实现活动与服务之间的通信
         * @param code 发送的标识 Binder.FIRST_CALL_TRANSACTION 立马发送
         * @param data 表示发送给服务的数据
         * @param reply 表示服务回复的数据
         * @param flags 0表示双向传递   1表示单向传递
         * @return
         * @throws RemoteException
         */
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            String string = data.readString();//读取Activity发送过来的数据
            Log.i("tag","onTransact->服务里接收的数据-"+string);
            reply.writeString("收到收到,我是回复你的数据");
            return super.onTransact(code, data, reply, flags);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBind();
    }
}

活动类:

/**
 * 利用transact(交易)方法实现activity与服务的通信,携带数据的载体为Parcel(包裹),类似于handler里的message
 * sp:该方法需要在还没有得到服务的回复的时候就需要创建出装回复数据的对象
 */
public class MainActivity extends AppCompatActivity {

    private ServiceConnection serviceConnection=new ServiceConnection() {
        @Override //绑定服务连接成功后调用
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                MyService.MyBind myBind= (MyService.MyBind) service;
                Parcel data = Parcel.obtain();//包裹的意思,类似于message,做为装数据的载体
                data.writeString("我是Activity里通过Parcel打包过来的数据");

                //创建存放 服务回复的数据的对象
                Parcel parcel = Parcel.obtain();
                //调用服务里的方法,将activity的数据传过去,并得到服务回复的数据
                myBind.transact(IBinder.FIRST_CALL_TRANSACTION,data,parcel,0);//参数说明见服务类
                //获取服务回应的数据
                String str = parcel.readString();
                Log.i("tag","activityli里收到服务的回复数据:"+str);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent=new Intent(this,MyService.class);
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);//绑定服务
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);//取消绑定
    }
}

效果如下:

 

Android中Service与多个Activity通信

posted @ 2016-04-27 22:16  ts-android  阅读(1197)  评论(0编辑  收藏  举报