Android Service 与 Activity使用Pending Intent通信
service使用pending intent返回结果给客户端
我们使用一个activity作为一个客户端,来通过startService()的方法启动一个服务,这个服务的功能很简单,就是去访问客户端指定的Url,然后返回这个url对应的页面的源代码的字符数量。这个例子中,我们是不允许这个服务被绑定的。即我们在onBind()方法中返回null。那么好了,现在我们这个服务不可以被绑定,只可以通过startService()的方法启动,那么此时服务和客户端的通信方式就只有一种了,即通过Pending Intent了。
1.首先自定义我们的服务类:MyService
public class MyService extends Service { private ServiceHandler mHandler; //内部类 private final class ServiceHandler extends Handler{ public ServiceHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if(msg.what == 0x1234){ Log.v("MyService","handleMessage()被调用"); int charNum = ((String)(msg.obj)).length(); Bundle bundle = msg.getData(); PendingIntent client = bundle.getParcelable("receiver"); Log.v("MyService","字符数为" + charNum); Intent intent = new Intent(); intent.putExtra("CHARNUM",charNum); try { client.send(getApplicationContext(),0,intent); Log.v("MyService","广播发送完成"); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } } } } @Override public void onCreate() { super.onCreate(); mHandler = new ServiceHandler(this.getMainLooper()); Log.v("MyService","onCreate()被调用"); } @Override public int onStartCommand(final Intent intent, int flags, final int startId) { Log.v("MyService","onStartCommand()被调用"); final String urlString = intent.getStringExtra("URL"); new Thread(){ @Override public void run() { try { String result = null; BufferedReader in = null ; URL url = new URL(urlString); URLConnection conn = url.openConnection(); conn.connect();//建立连接 in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line = null; while ((line = in.readLine()) != null ){ result += " " + line; } Log.v("URL请求结果",result); //发送消息 Message msg = new Message(); msg.what = 0x1234; msg.arg1 = startId; msg.obj = result; msg.setData(intent.getExtras()); mHandler.sendMessage(msg); } catch (IOException e) { e.printStackTrace(); } } }.start(); return super.onStartCommand(intent, flags, startId); } @Nullable @Override public IBinder onBind(Intent intent) { Log.v("MyService","onBind()被调用"); return null; } }
对于每一个startService()请求,我们都开启了一个新的线程去访问指定的额URL,并对字符串计数。服务里面最重要的便是自定义的Handler里面的handleMessage()方法了,这个方法里面我们取出client传给服务的intent,然后在这个intent里面取出序列化的pendingintent,然后再将其反序列化成为pending intent对象。最后理由这个客户端定义好的pending intent,给广播接受者发送广播,当然广播内容便是我们需要返回给客户端的的结果了。
2.MainActivity类
public class MainActivity extends AppCompatActivity implements View.OnClickListener,MyBroadcastReceiver.onServiceResultReturnListener{ Button btnStartService,btnBindService,btnStartServiceForResult; MyBroadcastReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnStartServiceForResult = (Button)findViewById(R.id.id_btn_startServiceForResult); btnStartServiceForResult.setOnClickListener(this); //初始化广播接受者 initBroadcastReceiver(); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.id_btn_startServiceForResult: Intent sfIntent = new Intent(this,MyService.class); Intent broadIntent = new Intent(); broadIntent.setAction("cn.edu.dlut.receiver"); PendingIntent pIntent = PendingIntent.getBroadcast(this,0,broadIntent,0); Bundle bundle = new Bundle(); bundle.putParcelable("receiver",pIntent); sfIntent.putExtras(bundle); sfIntent.putExtra("URL","http://www.baidu.com"); startService(sfIntent); Log.v("MainActivity","startService()方法被调用"); break; } } //初始化广播接收者 private void initBroadcastReceiver(){ receiver = new MyBroadcastReceiver(this); this.registerReceiver(receiver,new IntentFilter("cn.edu.dlut.receiver")); Log.v("MainActivity","广播接受者动态注册初始化完成"); } @Override protected void onDestroy() { super.onDestroy(); this.unregisterReceiver(receiver); } @Override public void onServiceResultReturn(Intent intent) { int charNum = intent.getIntExtra("CHARNUM",0); Toast.makeText(this,"字符数为" + charNum,Toast.LENGTH_SHORT).show(); } } //自定义广播接受者 class MyBroadcastReceiver extends BroadcastReceiver{ //回调接口 public interface onServiceResultReturnListener{ void onServiceResultReturn(Intent intent); } private onServiceResultReturnListener client; public MyBroadcastReceiver(onServiceResultReturnListener client){ this.client = client; } @Override public void onReceive(Context context, Intent intent) { Log.v("MyBroadcaseReceiver","onReceive()方法被调用"); this.client.onServiceResultReturn(intent); } }
demo程序源代码,已上传至git上面https://github.com/Spground/ServiceDemo
3.总结
利用pending intent作为started service(即用startService()启动的服务)与client通信的步骤大致如下:
1.实现自己的Service类,并取得client传递过来的intent,因为这个intent里面包含我们序列化的pending intent。
2.自定义广播接受者,并在client端动态注册广播接收者(静态注册也可以,只要广播接受者收到消息后会通知到client)
3.定义好client与广播接受者通信的回调接口
4.在client端调用startService()方法启动服务的时候,讲pending intent准备好,并将其序列化作为Extras放进入启动服务的intent中.
总的来说,使用这种方式来进行服务 和 client通信是十分繁琐的,后面有绑定服务运行方式,会在绑定成功以后给client一个实现了IBinder的实例,
通过让服务实现IBinder接口,便可以将服务的实例的引用作为返回值传递给client,client便可以用这个接口与服务通信了,当然也还是可用使用广播的形式让服务与client通信。