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 += "\n" + 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通信。