Activity与Service之间交互(通过Binder对象和广播)

android开发中,作为4大组件的service在开发中经常会使用到。activity用于在前台展示,service用于在后台下载,很多时候,我们的activity和service之间需要进行相应的交互,activity需要调用service里面的方法实现某些功能,service需要调用activity的方法,实现界面更新等的交互。

一般在Activity中启动后台Service,通过Intent来启动,Intent中我们可以传递数据给Service,而当我们Service执行某些操作之后想要更新UI线程,我们应该怎么做呢?我们通过一个下载的小例子来理解它们通信的方式

效果
ibinder.gif

【通过Binder对象】

1.Activity通过Intent向服务发送消息,通过调用bindService(Intent service, ServiceConnection conn,int flags)并绑定,此时我们可以得到一个Service的一个对象实例,我们就可以调用其公开的方法。通过IBinder拿到Service的引用调用其公开的方法。

2.核心总结下来就是service中有个类部类继承Binder,然后提供一个公有方法,返回当前service的实例。 activity通过bindService来开启一个service,通过onServiceConnected方法,获取IBinder实例,然后再通过IBinder实例来获取service实例,这样,我们得到了service的实例,那么我们的activity就可以随心所欲的使用它里面的各种方法来操作它了。如果要主动通知Activity,我们可以利用回调方法。

3.放出代码~

布局就不放了,就是一个小demo,有一个button,一个progressbar,一个textview
首先要写一个service的类 ,我们命名为MyService
public class MyService extends Service {
    //进度条最大值
    public static final int MAX_PROGRESS = 100;
    // 进度条的当前进度值
    public int currentProgress=0;
    /**
     * 更新进度的回调接口
     */
    private OnProgressListener onProgressListener;
    //开启下载任务
    public void startDownloadTask(final String url){

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    HttpURLConnection connection ;
                    InputStream input;
                    OutputStream output;
                    URL downloadUrl = new URL(url);
                    connection = (HttpURLConnection) downloadUrl.openConnection();
                    connection.connect();

                    input=connection.getInputStream();
                    output=new FileOutputStream("/sdcard/new.apk");
                    int fileLength = connection.getContentLength();
                    byte data[] = new byte[2048];
                    long total = 0;
                    int count;
                    while ((count = input.read(data)) != -1) {
                        total += count;
                        // publishing the progress....
                        currentProgress=(int) (total * 100 / fileLength);//进度发生变化通知调用方
                        if(onProgressListener != null){
                            onProgressListener.onProgress(currentProgress);
                        }
                        output.write(data, 0, count);
                    }
                    output.flush();
                    output.close();
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }
    //提供一个对外的获得的progress的值
    public int getProgress(){
        return currentProgress;
    }
    /**
     * 注册回调接口的方法,供外部调用
     */
    public void setOnProgressListener(OnProgressListener onProgressListener) {
        this.onProgressListener = onProgressListener;
    }
    public MyService() {
    }

    /**
     * 返回一个Binder对象
     */
    @Override
    public IBinder onBind(Intent intent) {
            return  new MyBinder();
    }
    //1.service中有个类部类继承Binder,然后提供一个公有方法,返回当前service的实例。
    public class  MyBinder extends Binder{
        public MyService getService(){
            return MyService.this;
        }

    }
}

对于这个类提供的几点说明:
1.新建一个内部类MyBinder继承Binder,提供一个公有的方法返回当前Service的实例
2.在onbind()方法中返回我们1中的Binder对象,activity也是通过绑定服务bindservice,并在onServiceConnected中获取IBinder实例,然后再通过IBinder实例来获取service实例。
3.剩下的方法我们直接在activity中调用即可

OnProgressListener接口代码,通过这个接口回调当前progress值
public interface OnProgressListener {
    void onProgress(int progress);
}
Activity代码
public class MainActivity extends AppCompatActivity {
    private MyService myService;
    private ProgressBar progressBar;
    private Button button;
    private TextView textview;
    private String url="xxx";
    private ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
                 //返回一个MyService对象
                myService=((MyService.MyBinder)service).getService();
                 myService.setOnProgressListener(new OnProgressListener() {
                @Override
                public void onProgress(int progress) {
                
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                  //主线程更新UI
                            progressBar.setProgress(progress);
                            textview.setText(progress+"%");
                        }
                    });

                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button= (Button) findViewById(R.id.button);
        progressBar= (ProgressBar) findViewById(R.id.progressBar);
        textview= (TextView) findViewById(R.id.textView);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myService.startDownloadTask(url);
           }
        });
        Intent intent = new Intent(this,MyService.class);
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);

    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}

对于这个类提供的几点说明:
1.在oncreate中通过bindservice绑定服务,在 onServiceConnected中获取service实例,并通过回调返回的progress更新UI。

【通过广播】

当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新ProgressBar。

放代码:

不同于上一个service,我们新建一个service通过广播的方式向activity传递

public class MyService2 extends Service {

   public MyService2() {
   }
   //进度条最大值
   public static final int MAX_PROGRESS = 100;
   // 进度条的当前进度值
   public int currentProgress=0;
   private Intent intent = new Intent("com.test.service.RECEIVER");
   //开启下载任务
   public void startDownloadTask(final String url){

       new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   HttpURLConnection connection ;
                   InputStream input;
                   OutputStream output;
                   URL downloadUrl = new URL(url);
                   connection = (HttpURLConnection) downloadUrl.openConnection();
                   connection.connect();

                   input=connection.getInputStream();
                   output=new FileOutputStream("/sdcard/new.apk");
                   int fileLength = connection.getContentLength();
                   byte data[] = new byte[2048];
                   long total = 0;
                   int count;
                   while ((count = input.read(data)) != -1) {
                       total += count;
                       // publishing the progress....
                       currentProgress=(int) (total * 100 / fileLength);//进度发生变化通知调用方
                       //发送Action为com.test.service.RECEIVER的广播
                       intent.putExtra("progress", currentProgress);
                       sendBroadcast(intent);
                       output.write(data, 0, count);
                   }
                   output.flush();
                   output.close();
                   input.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }

           }
       }).start();
   }
   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
       //通过startservice启动服务,调用的是service的onStartCommand方法
       //此时service通过发送广播实现,activity注册广播接收器接受进度
       startDownloadTask((String) intent.getSerializableExtra("url"));
       return super.onStartCommand(intent, flags, startId);
   }

   @Override
   public IBinder onBind(Intent intent) {
       //通过bindservice绑定服务,调用的是onbind方法,此时是通过IBinder传递参数
       //这个service主要演示的通过广播传递参数 这个方法直接返回空即可
    return null;
   }
}

Activity代码

public class Main2Activity extends AppCompatActivity {
    private ProgressBar progressBar;
    private Button button;
    private TextView textview;
    private String url="http://app-distribute.oss-cn-qingdao.aliyuncs.com/default/shengji.apk";
    private Intent intent;
    private MyReceiver myReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button= (Button) findViewById(R.id.button);
        progressBar= (ProgressBar) findViewById(R.id.progressBar);
        textview= (TextView) findViewById(R.id.textView);
        //动态注册广播接收器
        myReceiver = new MyReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.test.service.RECEIVER");
        registerReceiver(myReceiver, intentFilter);

        intent=new Intent(this,MyService2.class);
        intent.putExtra("url",url);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
             //启动服务
               startService(intent);
            }
        });
    }
    /**
     * 广播接收器
     * @author len
     *
     */
    public class MyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //拿到进度,更新UI
            int progress = intent.getIntExtra("progress", 0);
            progressBar.setProgress(progress);
            textview.setText(progress+"%");
        }

    }
    @Override
    protected void onDestroy() {
        //停止服务
        stopService(intent);
        //注销广播
        unregisterReceiver(myReceiver);
        super.onDestroy();
    }
}

相比较,如果某个service要向多个activity发送,用广播的方式更好一点

Android IntentService和ResultReceiver的异步处理实现service与activity的交互

其他在学习过程中应该了解的小知识点
startService与bindService的区别
静态/动态注册广播的区别


亲,给个赞鼓励一下吧~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容