精华内容
下载资源
问答
  • 我在一个APP里通过bindService启动了Service,并且在Service的onbind方法里开启了一个线程 ...所以Service到底是什么? 看来只有一个实例对象,顺便问它到底是运行在哪个主线程的,两个APP都调用到了
  • 安卓Service(一)

    2018-03-21 16:56:53
    什么安卓Service Service是安卓四大组件之一和Activity相当。 Service是可以长时间运行在后台的,是不可见、没有Ui界面的组件。 Service是运行在主线程中的。 Service是可以跨进程调用。 二.为什么要有...

    一.什么是安卓Service
    Service是安卓四大组件之一和Activity相当。
    Service是可以长时间运行在后台的,是不可见、没有Ui界面的组件。
    Service是运行在主线程中的。
    Service是可以跨进程调用。
    二.为什么要有Service
    在实际运营在,绝大多数项目都有在后台运行的需求(如上传,下载),这时就需要通过Service在后台指定完成任务。
    三.如何使用Service
    这里先介绍startService方式
    1.新建类继承Servic
    2.重写onCreate方法
    3.实现onBind方法
    4.重写onStartCommand方法
    5.重写onDestroy方法
    6.在AndroidManifest中注册Service
    7.在有Context环境中通过startService启动Service
    8.在有Context环境中通过stopService停止Service
    四.代码展示
    先创个类继承Servic

    package com.example.ll.storeapplication;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.support.annotation.Nullable;
    import android.util.Log;
    
    /**
     * Created by ll on 2018/3/21.
     */
    
    public class MyService extends Service {
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        //创建(只创建一次)
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e("Service", "onCreate");
        }
        //开始(可以进行无数次)
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            //打印线程名字
            Log.e("Service" + Thread.currentThread().getName(), "onStartCommand");
            //进行一个9秒计时进程
            //主线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10; i++) {
                        try {
                            Log.e("Service" + Thread.currentThread().getName(), i + "*****");
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            return super.onStartCommand(intent, flags, startId);
        }
    
        //结束(只结束一次)
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.e("Service" + Thread.currentThread().getName(), "onDestroy");
        }
    }
    

    然后写xml界面

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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"
        android:orientation="vertical"
        tools:context="com.example.ll.storeapplication.Main2Activity">
    
        <Button
            android:id="@+id/start_btn"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="启动Service" />
    
        <Button
            android:id="@+id/stop_btn"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="停止Service" />
    </LinearLayout>
    

    最后是Main2Activity代码

    package com.example.ll.storeapplication;
    
    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    
    public class Main2Activity extends AppCompatActivity implements View.OnClickListener{
    private Button qdbtn;
    private  Button tzbtn;
    private Intent intent;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main2);
            bindId();
        }
    
        private void bindId() {
            qdbtn=findViewById(R.id.start_btn);
            tzbtn=findViewById(R.id.stop_btn);
            qdbtn.setOnClickListener(this);
            tzbtn.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View view) {
        switch (view.getId()){
            case R.id.start_btn:
                 intent=new Intent(Main2Activity.this,MyService.class);
                startService(intent);
                break;
            case R.id.stop_btn:
                stopService(intent);
                break;
        }
        }
    }
    

    当然不要忘了在AndroidManifest加一句话

      <service android:name=".MyService"/>
    展开全文
  • 什么办法可以让安卓Service在关闭进程后继续运行?最好不要重启Service
  • 关于安卓Service相信很多安卓开发者都听说过,作为安卓四大组件之一,即使不经常用也应该听说过,但并不是每一个人都掌握的特别详细,全面。那么今天我将带大家全面了解一下Service.希望对您有所帮助。 什么是...

    版权声明:本文出自汪磊的博客,转载请务必注明出处。  

    关于安卓Service相信很多安卓开发者都听说过,作为安卓四大组件之一,即使不经常用也应该听说过,但并不是每一个人都掌握的特别详细,全面。那么今天我将带大家全面了解一下Service.希望对您有所帮助。

    什么是Service? 

    先来看一下官方定义:

    Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.

    翻译过来就是:Service是一个没有用户界面的在后台运行执行耗时操作的应用组件。其他应用组件能够启动Service,

    并且当用户切换到另外的应用场景,Service将持续在后台运行。另外,一个组件能够绑定到一个service与之交互(IPC机制),

    例如,一个service可能会处理网络操作,播放音乐,操作文件I/O或者与内容提供者(content provider)交互,所有这些活动都是在后台进行。

    简单说Service就是一个不依附界面可以在后台长期执行耗时操作的组件。

          

     

    Service基本用法

    接下来了解一下Service的启动以及生命周期,下面通过一个简单实例来学习一下。

    新建一个MyService继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法,如下所示:

        

     1 public class MyService extends Service {
     2 
     3     @Override
     4     public IBinder onBind(Intent arg0) {
     5         return null;
     6     }
     7     
     8     @Override
     9     public void onCreate() {
    10         Log.i("WLService", "onCreate"); 
    11         super.onCreate();
    12     }
    13     
    14     @Override
    15     public int onStartCommand(Intent intent, int flags, int startId) {
    16         Log.i("WLService", "onStartCommand"); 
    17         return super.onStartCommand(intent, flags, startId);
    18     }
    19     
    20     @Override
    21     public void onDestroy() {
    22         Log.i("WLService", "onDestroy"); 
    23         super.onDestroy();
    24     }
    25 
    26 }

     

    然后打开项目布局文件,添加启动,关闭Service按钮,如下:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical"
     6     tools:context=".MainActivity" >
     7 
     8     <Button
     9         android:layout_width="fill_parent"
    10         android:layout_height="wrap_content"
    11         android:onClick="start"
    12         android:text="开启服务" />
    13 
    14     <Button
    15         android:layout_width="fill_parent"
    16         android:layout_height="wrap_content"
    17         android:onClick="stop"
    18         android:text="停止服务" />
    19 
    20 </LinearLayout>

     

    Service作为四大组件之一,我们还需要在清单文件中注册一下,如下:

     1 <service android:name="com.wl.servicelife.MyService"></service> 

       

    然后我们在MainActivity中编写启动,关闭服务代码。如下:

     1 public class MainActivity extends Activity {
     2 
     3     @Override
     4     protected void onCreate(Bundle savedInstanceState) {
     5         super.onCreate(savedInstanceState);
     6         setContentView(R.layout.activity_main);
     7     }
     8 
     9     public void start(View view) {
    10         Intent intent = new Intent(this, MyService.class);
    11         // 通知框架开启服务。
    12         startService(intent);
    13     }
    14 
    15     public void stop(View view) {
    16         Intent intent = new Intent(this, MyService.class);
    17         stopService(intent);
    18     }
    19 
    20     @Override
    21     protected void onDestroy() {
    22         Log.i("WLService", "MainActivity onDestroy"); 
    23         super.onDestroy();
    24     }
    25 }

     

    在Start Service按钮的点击事件里,我们构建出了一个Intent对象,调用startService()方法来启动MyService。

    然后在Stop Serivce按钮的点击事件里,我们同样构建出了一个Intent对象,调用stopService()方法来停止MyService。

    基本的项目搭建完成,现在我们运行项目,点击开启服务按钮,会看到打印如下:

    当我们再次点击开启服务按钮,会看到打印如下:只有"onStartCommand"会打印出来

    当我们点击停止服务按钮,会看到打印如下:

     到现在为止相信你对Service有了最基础的了解.接下来,我们还需要了解怎么调用服务里面的方法。  

    调用服务里面的方法

    有些同学可能会说调用服务里面的方法还不简单吗,new一个对象获得引用,然后不久可以调用了吗?这样可以吗?我们可以自己测试一下。

    首先我们在服务里面添加一个方法,如下:

    1     public void methodInService(){
    2         
    3         Toast.makeText(this, "ClearHeart", Toast.LENGTH_SHORT).show();
    4     }

    很简单,我们只是弹出一个吐司。

    然后在布局文件加入如下按钮:

    1     <Button
    2         android:layout_width="fill_parent"
    3         android:layout_height="wrap_content"
    4         android:onClick="call"
    5         android:text="调用服务里面的方法" />

    接下来我们编写代码调用服务里面的方法:

    1     // 调用服务里面的方法
    2     public void call(View view) {
    3 
    4         MyService myService = new MyService();
    5         myService.methodInService();    
    6     }

    运行程序点击按钮调用服务里面的方法我们会发现程序崩溃,报如下错误:

     1 08-13 10:38:41.114: D/AndroidRuntime(2431): Shutting down VM
     2 08-13 10:38:41.115: E/AndroidRuntime(2431): FATAL EXCEPTION: main
     3 08-13 10:38:41.115: E/AndroidRuntime(2431): Process: com.wl.service, PID: 2431
     4 08-13 10:38:41.115: E/AndroidRuntime(2431): java.lang.IllegalStateException: Could not execute method of the activity
     5 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.view.View$1.onClick(View.java:4007)
     6 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.view.View.performClick(View.java:4756)
     7 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.view.View$PerformClick.run(View.java:19749)
     8 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.os.Handler.handleCallback(Handler.java:739)
     9 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.os.Handler.dispatchMessage(Handler.java:95)
    10 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.os.Looper.loop(Looper.java:135)
    11 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.app.ActivityThread.main(ActivityThread.java:5221)
    12 08-13 10:38:41.115: E/AndroidRuntime(2431):     at java.lang.reflect.Method.invoke(Native Method)
    13 08-13 10:38:41.115: E/AndroidRuntime(2431):     at java.lang.reflect.Method.invoke(Method.java:372)
    14 08-13 10:38:41.115: E/AndroidRuntime(2431):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
    15 08-13 10:38:41.115: E/AndroidRuntime(2431):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
    16 08-13 10:38:41.115: E/AndroidRuntime(2431): Caused by: java.lang.reflect.InvocationTargetException
    17 08-13 10:38:41.115: E/AndroidRuntime(2431):     at java.lang.reflect.Method.invoke(Native Method)
    18 08-13 10:38:41.115: E/AndroidRuntime(2431):     at java.lang.reflect.Method.invoke(Method.java:372)
    19 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.view.View$1.onClick(View.java:4002)
    20 08-13 10:38:41.115: E/AndroidRuntime(2431):     ... 10 more
    21 08-13 10:38:41.115: E/AndroidRuntime(2431): Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
    22 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.content.ContextWrapper.getResources(ContextWrapper.java:85)
    23 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.widget.Toast.<init>(Toast.java:101)
    24 08-13 10:38:41.115: E/AndroidRuntime(2431):     at android.widget.Toast.makeText(Toast.java:250)
    25 08-13 10:38:41.115: E/AndroidRuntime(2431):     at com.wl.service.MyService.methodInService(MyService.java:38)
    26 08-13 10:38:41.115: E/AndroidRuntime(2431):     at com.wl.service.MainActivity.call(MainActivity.java:37)
    27 08-13 10:38:41.115: E/AndroidRuntime(2431):     ... 13 more

     

    定位到弹出吐司那一句代码有空指针异常,通过简单分析可以确定this是空,那么为什么this是空呢?

    这里需要解释一下,服务(Service)只能由系统创建,而不能通过new MyService()方式自己创建,如果我们自己创建服务对象,这样创建出来的只是一个普通类。Service的创建只能由系统来创建完成,而不能我们自己创建,只有系统框架创建的Service才能将应用上下文传递给Service。

    那我们如何调用Service内部方法呢?这里我们需要另一种方式开启服务,bind方式开启服务。

    接下来我们新建一个工程,新建MyService类继承系统Service类,代码如下:

     1 public class MyService extends Service {
     2 
     3     private static final String TAG = "WLHeart";
     4 
     5     @Override
     6     public IBinder onBind(Intent arg0) {
     7         Log.i(TAG, "onBind");
     8         return new MiddlePerson();
     9     }
    10 
    11     @Override
    12     public boolean onUnbind(Intent intent) {
    13         Log.i(TAG, "onUnbind");
    14         return super.onUnbind(intent);
    15     }
    16 
    17     @Override
    18     public void onCreate() {
    19         Log.i(TAG, "onCreate");
    20         super.onCreate();
    21     }
    22 
    23     @Override
    24     public int onStartCommand(Intent intent, int flags, int startId) {
    25         Log.i(TAG, "onStartCommand");
    26         return super.onStartCommand(intent, flags, startId);
    27     }
    28 
    29     @Override
    30     public void onDestroy() {
    31         Log.i(TAG, "onDestroy");
    32         super.onDestroy();
    33     }
    34 
    35     /**
    36      * 这是服务里面的一个方法
    37      */
    38     public void methodInService() {
    39         Toast.makeText(this, "WLHeart", 0).show();
    40     }
    41 
    42     private class MiddlePerson extends Binder implements IMiddleBind {
    43 
    44         public void callMethodInService(int money) {
    45 
    46             methodInService();
    47         }
    48     }
    49 }

    可见我们新增了一个私有内部类MiddlePerson继承Binder并且实现IMiddleBind接口,实现IMiddleBind接口中定义的方法,并调用服务中方法,

    重写服务的onUnbind方法,返回内部类MiddlePerson的实例对象。

    IMiddleBind代码如下:

    1 public interface IMiddleBind {
    2     
    3     public void callMethodInService();
    4 }

    然后我们修改布局文件,很简单不必过多解释了,如下:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical"
     6     tools:context=".MainActivity" >
     7 
     8     <Button
     9         android:layout_width="fill_parent"
    10         android:layout_height="wrap_content"
    11         android:onClick="bind"
    12         android:text="绑定服务" />
    13 
    14     <Button
    15         android:layout_width="fill_parent"
    16         android:layout_height="wrap_content"
    17         android:onClick="unbind"
    18         android:text="解除绑定服务" />
    19 
    20     <Button
    21         android:layout_width="fill_parent"
    22         android:layout_height="wrap_content"
    23         android:onClick="call"
    24         android:text="调用服务里面的方法" />
    25 
    26 </LinearLayout>

    接下来我们修改MainActivity中代码,绑定服务并且调用其中代码:

     1 public class MainActivity extends Activity {
     2     private static final String TAG = "WLHeart";
     3     private MyConn conn;
     4     private IMiddleBind middleBinder;
     5 
     6     @Override
     7     protected void onCreate(Bundle savedInstanceState) {
     8         super.onCreate(savedInstanceState);
     9         setContentView(R.layout.activity_main);
    10     }
    11 
    12     // 绑定服务
    13     public void bind(View view) {
    14         Intent intent = new Intent(this, MyService.class);
    15         conn = new MyConn();
    16         bindService(intent, conn, BIND_AUTO_CREATE);
    17     }
    18 
    19     // 解除绑定服务
    20     public void unbind(View view) {
    21         unbindService(conn);
    22     }
    23 
    24     @Override
    25     protected void onDestroy() {
    26         Log.i(TAG, "activity,onDestroy");
    27         middleBinder = null;
    28         super.onDestroy();
    29     }
    30 
    31     // 调用服务里面的方法。
    32     public void call(View view) {
    33         // 
    34         if(null != middleBinder){
    35             middleBinder.callMethodInService();
    36         }
    37     }
    38 
    39     private class MyConn implements ServiceConnection {
    40         //
    41         @Override
    42         public void onServiceConnected(ComponentName name, IBinder service) {
    43             Log.i(TAG, "onServiceConnected");
    44             middleBinder = (IMiddleBind) service;
    45         }
    46 
    47         // 当服务失去连接的时候调用
    48         @Override
    49         public void onServiceDisconnected(ComponentName name) {
    50 
    51         }
    52     }
    53 }

    可以看到我们新建一个MyConn的内部类实现ServiceConnection接口,在里面重写了onServiceConnected()方法和onServiceDisconnected()方法,

    这两个方法分别会在调用者与Service建立关联和解除关联的时候调用。在onServiceConnected()方法中我们将返回的service参数转型为IMiddleBind类型,这里为什么可以转型呢?大家注意到在MyService的onBind方法中我们返回了其内部类MiddlePerson的实例,

    MiddlePerson继承自Binder类,Binder类点进系统源码我们可以看到其实现IBinder接口,而onServiceConnected(ComponentName name, IBinder service)方法第二个参数正是IBinder类型,

    到此,大家应该明白了吧。

    简单总结一下:在调用者与Service绑定成功的时候会调用onServiceConnected()方法,此方法中第二个参数就是我们在onBind方法中返回的

    MiddlePerson实例,好了到此为止我们就可以获取Service内部类的实例引用了,通过这个实例引用我们就可以调用其内部方法了。

        

    我们运行程序实验一下,运行程序,点击绑定服务按钮,打印如下:

    点击调用服务里面方法按钮,打印如下:

     

    
    

    点击解除绑定按钮,打印如下: 

    到此为止是不是对绑定服务方式开启服务并且调用其中方法有了一步初步认识,似乎过程又有些小复杂,我们总结一下绑定本地服务调用方法的步骤:

              

    1.在服务的内部创建一个内部类 提供一个方法,可以间接调用服务的方法      

    private class MiddlePerson extends Binder implements IMiddleBind

    2.实现服务的onbind方法,返回的就是中间人 MiddlePerson     

    1     @Override
    2     public IBinder onBind(Intent arg0) {
    3         Log.i(TAG, "onBind");
    4         return new MiddlePerson();
    5     }

    3.在activity 绑定服务。bindService();

    1     // 绑定服务
    2     public void bind(View view) {
    3         Intent intent = new Intent(this, MyService.class);
    4         conn = new MyConn();
    5         bindService(intent, conn, BIND_AUTO_CREATE);
    6     }


    4.在服务成功绑定的时候 会执行一个方法 onServiceConnected 传递过来一个 IBinder对象
    5.强制类型转化 调用接口里面的方法。

     1 private class MyConn implements ServiceConnection {
     2         //
     3         @Override
     4         public void onServiceConnected(ComponentName name, IBinder service) {
     5             Log.i(TAG, "onServiceConnected");
     6             middleBinder = (IMiddleBind) service;
     7         }
     8 
     9         // 当服务失去连接的时候调用
    10         @Override
    11         public void onServiceDisconnected(ComponentName name) {
    12 
    13         }
    14     }

    遵循以上5部就可以绑定本地服务并且调用服务种方法,是不是很简单呢。

     

    绑定服务调用服务中方法注意点 

     有些同学有没有想过可不可以绑定服务之后立刻调用服务中方法呢?我们可以试验一下,将MainActivity代码改为如下:

     1 public class MainActivity extends Activity {
     2     private static final String TAG = "WLHeart";
     3     private MyConn conn;
     4     private IMiddleBind middleBinder;
     5 
     6     @Override
     7     protected void onCreate(Bundle savedInstanceState) {
     8         super.onCreate(savedInstanceState);
     9         setContentView(R.layout.activity_main);
    10     }
    11 
    12     // 绑定服务
    13     public void bind(View view) {
    14         Intent intent = new Intent(this, MyService.class);
    15         conn = new MyConn();
    16         bindService(intent, conn, BIND_AUTO_CREATE);
    17         middleBinder.callMethodInService();
    18     }
    19 
    20     // 解除绑定服务
    21     public void unbind(View view) {
    22         unbindService(conn);
    23     }
    24 
    25     @Override
    26     protected void onDestroy() {
    27         Log.i(TAG, "activity,onDestroy");
    28         middleBinder = null;
    29         super.onDestroy();
    30     }
    31 
    32     // 调用服务里面的方法。
    33     public void call(View view) {
    34         // 
    35 
    36     }
    37 
    38     private class MyConn implements ServiceConnection {
    39         //
    40         @Override
    41         public void onServiceConnected(ComponentName name, IBinder service) {
    42             Log.i(TAG, "onServiceConnected");
    43             middleBinder = (IMiddleBind) service;
    44         }
    45 
    46         // 当服务失去连接的时候调用
    47         @Override
    48         public void onServiceDisconnected(ComponentName name) {
    49 
    50         }
    51     }
    52 }

    可见我们只是将逻辑改为绑定完服务立刻调用其方法,运行程序,点击绑定服务按钮会发现报如下错误:

     1 08-14 14:47:21.311: E/AndroidRuntime(1890): FATAL EXCEPTION: main
     2 08-14 14:47:21.311: E/AndroidRuntime(1890): Process: com.wl.bindservice, PID: 1890
     3 08-14 14:47:21.311: E/AndroidRuntime(1890): java.lang.IllegalStateException: Could not execute method of the activity
     4 08-14 14:47:21.311: E/AndroidRuntime(1890):     at android.view.View$1.onClick(View.java:4007)
     5 08-14 14:47:21.311: E/AndroidRuntime(1890):     at android.view.View.performClick(View.java:4756)
     6 08-14 14:47:21.311: E/AndroidRuntime(1890):     at android.view.View$PerformClick.run(View.java:19749)
     7 08-14 14:47:21.311: E/AndroidRuntime(1890):     at android.os.Handler.handleCallback(Handler.java:739)
     8 08-14 14:47:21.311: E/AndroidRuntime(1890):     at android.os.Handler.dispatchMessage(Handler.java:95)
     9 08-14 14:47:21.311: E/AndroidRuntime(1890):     at android.os.Looper.loop(Looper.java:135)
    10 08-14 14:47:21.311: E/AndroidRuntime(1890):     at android.app.ActivityThread.main(ActivityThread.java:5221)
    11 08-14 14:47:21.311: E/AndroidRuntime(1890):     at java.lang.reflect.Method.invoke(Native Method)
    12 08-14 14:47:21.311: E/AndroidRuntime(1890):     at java.lang.reflect.Method.invoke(Method.java:372)
    13 08-14 14:47:21.311: E/AndroidRuntime(1890):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
    14 08-14 14:47:21.311: E/AndroidRuntime(1890):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
    15 08-14 14:47:21.311: E/AndroidRuntime(1890): Caused by: java.lang.reflect.InvocationTargetException
    16 08-14 14:47:21.311: E/AndroidRuntime(1890):     at java.lang.reflect.Method.invoke(Native Method)
    17 08-14 14:47:21.311: E/AndroidRuntime(1890):     at java.lang.reflect.Method.invoke(Method.java:372)
    18 08-14 14:47:21.311: E/AndroidRuntime(1890):     at android.view.View$1.onClick(View.java:4002)
    19 08-14 14:47:21.311: E/AndroidRuntime(1890):     ... 10 more
    20 08-14 14:47:21.311: E/AndroidRuntime(1890): Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'void com.wl.bindservice.IMiddleBind.callMethodInService()' on a null object reference
    21 08-14 14:47:21.311: E/AndroidRuntime(1890):     at com.wl.bindservice.MainActivity.bind(MainActivity.java:28)
    22 08-14 14:47:21.311: E/AndroidRuntime(1890):     ... 13 more

    报空指针异常,定位到middleBinder.callMethodInService();发生空指针,明显是middleBinder为空,那为什么会是空呢?我们不是已经绑定服务了吗?

    原来绑定服务的过程是异步过程,这个过程可能持续几十或者几百毫秒,而middleBinder只有在调用完onServiceConnected才会初始化完成,

    这样就不难理解为什么会报空指针异常了吧,希望各位使用过程中会注意一下这个地方。

       

    两种开启服务方法的区别        

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

     

    混合方式开启服务

    如果我们即想服务长期在后台运行又想调用其中方法怎么办呢?很简单,我们可以采取混合调用服务的方法开启服务

    即我们先调用start方式开启服务保证服务在后台长期运行,在调用bind方式绑定服务保证调用服务里面方法,这样子即可。

    那我们如何关闭服务呢?我们知道start方式开启服务我们只需要调用stopservice()即可关闭服务,

     bind方式开启服务我们只需调用unbind即可关闭服务 ,其实混合方法开启服务我们只需要先调用unbind解除绑定,

    再调用stopservice停止服务即可。混合方式调用服务完整过程如下:

          1.start方式开启服务(保证服务长期后台运行)
          2.bind方式绑定服务(保证调用服务的方法)
          3.unbind解除绑定服务
          4.stopService停止服务

     

    好了,到此为止我们算是对服务有了一个比较基础的全面的认识,掌握这些相信能解决平时开发中大部分的问题了。

    下一篇(中篇)会带大家了解一下安卓IPC(进程间通讯)机制,最后(下篇)详细了解安卓Binder机制

     

    转载于:https://www.cnblogs.com/leipDao/p/7347320.html

    展开全文
  • 安卓016Service

    2016-04-18 20:32:18
    一、 什么是 Service Service Android 中四大组件乊一,在 Android 开发中起到非常重要的作用。 Service(服务)一个没有用户界面的在后台运行执行“耗时操作”的应用组件。其 他应用组件能够启劢 Service,...

    一、  什么是 Service
    Service 是 Android 中四大组件乊一,在 Android 开发中起到非常重要的作用。
    Service(服务)是一个没有用户界面的在后台运行执行“耗时操作”的应用组件。其
    他应用组件能够启劢 Service,并且当用户切换到另外的应用场景,Service 将持续在
    后台运行。另外,一个组件能够绑定到一个 service 不乊交互(IPC 机制),例如,一
    个 service 可能会处理网络操作,播放音乐,操作文件 I/O 戒者不内容提供者

    (content provider)交互,所有这些活劢都是在后台迚行。


    二、  定义册 和注册 Service  类
    1、  写一个类基础系统的 Service 类。

    2、  在 AndroidManifest 文件中注册该服务


    三、  启动 Service
    共有两种启劢服务的方法:
    1、  startService(intent);
    既可以用显示的,也可以用隐式
    音乐播放
    2、  bindService(intent, conn, flags)
    |--Intent:要绑定的服务意图
    |--conn: ServiceConnection:可以接收服务里面传过来数据
    |--flags: Context.BIND_AUTO_CREATE 表明只要绑定存在,就自劢建立
    Service
    两种启劢 Service 方式的应用场景
    1.通过 startservice 开启的服务.一旦服务开启, 这个服务和开启他的调用者乊间就没有
    任何的关系了.
    调用者丌可以访问 service 里面的方法. 调用者如果被系统回收了戒者调用了
    ondestroy 方法, service 还会继续存在
    2.通过 bindService 开启的服务,服务开启乊后,调用者和服务乊间 还存在着联系 ,

    一旦调用者挂掉了.service 也会跟着挂掉 .


    四、 Service  生命周期
    不同的启动方式,Service 回调的声明周期方法也不一样。
    第二张图
    1). 被启动的服务的生命周期:
    如果一个 Service 被某个 Activity 调用 Context.startService 方法启劢,那么丌管
    是否有 Activity 使用 bindService 绑定戒 unbindService 解除绑定到该 Service,该
    Service 都在后台运行。
    如果一个 Service 被 startService 方法多次启劢,那么 onCreate 方法只会调用一
    次,onStartCommand 将会被调用多次(对应调用 startService 的次数),并且系统只
    会创建 Service 的一个实例(因此你应该知道只需要一次 stopService 调用)。该
    Service 将会一直在后台运行,而丌管对应程序的 Activity 是否在运行,直到被调用
    stopService,戒自身的 stopSelf 方法。当然如果系统资源丌足,android 系统也可能结
    束服务。
    2). 被绑定的服务的生命周期:
    如果一个 Service 被某个 Activity 调用 Context.bindService 方法绑定启劢,丌管调
    用 bindService 调用几次,onCreate 和 onBind 方法都只会调用一次,同时
    onStartCommand 方法始终丌会被调用。当连接建立之后,Service 将会一直运行,
    除非调用 Context.unbindService 断开连接戒者乊前调用 bindService 的 Context 丌存
    在了(如 Activity 被 finish 的时候),系统将会自劢停止 Service,对应 onDestroy 将
    被调用。
    3). 被启动又被绑定的服务的生命周期:
    如果一个 Service 又被启劢又被绑定,则该 Service 将会一直在后台运行。并且丌
    管如何调用,onCreate 始终只会调用一次,对应 startService 调用多少次,Service
    的 onStartCommand 便会调用多少次。调用 unbindService 将丌会停止 Service,而必
    须调用 stopService 戒 Service 的 stopSelf 来停止服务。
    4). 当服务被停止时清除服务:
    当一个 Service 被终止(1、调用 stopService;2、调用 stopSelf;3、丌再有绑定
    的连接(没有被启劢))时,onDestroy 方法将会被调用,在这里你应当做一些清除工
    作,如停止在 Service 中创建并运行的线程。
    注意: 1、使用 startService 的时候,一般在 onStartCommand()中写主要的逻辑代
    码,但是 Service 运行在主线程中,所以 Service 本身丌能做耗时操作。如果做耗时操

    作戒访问网络可以使用异步任务、子线程、来处理。


    【特别注意】:
    1、你应当知道在调用 bindService 绑定到 Service 的时候,你就应当保证在某处
    调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自劢解除,并且
    Service 会自劢停止);
    2、你应当注意 使用 startService 启劢服务乊后,一定要使用 stopService 停止服
    务,丌管你是否使用 bindService;
    3、同时使用 startService 不 bindService 要注意到,Service 的终止,需要
    unbindService 不 stopService 同时调用,才能终止 Service,丌管 startService 不
    bindService 的调用顺序,如果先调用 unbindService 此时服务丌会自劢终止,再调用
    stopService 乊后服务才会停止,如果先调用 stopService 此时服务也丌会终止,而再
    调用 unbindService 戒者 乊前调用 bindService 的 Context 丌存在了(如 Activity 被
    finish 的时候)乊后服务才会自劢停止;
    4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你
    的 Activity 如果会自劢旋转的话,旋转其实是 Activity 的重新创建,因此旋转乊前的使
    用 bindService 建立的连接便会断开(Context 丌存在了),对应服务的生命周期不上
    述相同。
    5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了
    onStartCommand,丌过乊前的 onStart 任然有效。这意味着,如果你开发的应用程序
    用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而丌是
    onStart。
    绑定分两种:
    1、  本地绑定:同一个应用程序迚行绑定
    2、  远程绑定:跨迚程的绑定。 A 应用去绑定 B 应用的服务。

    AIDL(android interface definded lanugage)迚行绑定


    五、  服务的混合启动
    startService 和 bindService 混合启劢时的生命周期方法的调用
    混合启劢:
    1、  对同一个 ServiceConnetion 对象,多次重复绑定,onCreate 和 onBind 方法只
    会调用一次。
    2、  如果 unbindService 成功乊后(并且这个方法返回 true),Service 没有被
    destroy,那么当再次绑定服务的时候,丌会调用 onCreate 和 onBind 方法,而

    是调用 onRebind 方法。


    六、  粘性 Service
    在 startService()的时候 onStartCommand()返回值是 int,表示的服务是否为
    粘性的。有四种返回值:
    1)START_STICKY:sticky 的意思是“粘性的”。使用这个返回值时,我们启劢的服
    务跟应用程序"粘"在一起,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系
    统会自动重启该服务。当再次启劢服务时,传入的第一个参数将为 null;
    2)START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完
    onStartCommand 后,服务被异常 kill 掉,系统丌会自劢重启该服务。
    3)START_REDELIVER_INTENT:重传 Intent。使用这个返回值时,如果在执行完
    onStartCommand 后,服务被异常 kill 掉,系统会自劢重启该服务,并将 Intent 的值传

    入。


    七、 IntentService
    IntentService 是继承于 Service 并处理异步请求的一个类,在 IntentService 内有
    一个工作线程来处理耗时操作,启 劢 IntentService 的方式和启劢传统 Service 一样,
    同时,当任务执行完后,IntentService 会自动停止,而不需要我们去手动控 制。另
    外,可以启劢 IntentService 多次,而每一个耗时操作会以工作队列的方式在
    IntentService 的 onHandleIntent 回 调方法中执行,并且,每次只会执行一个工作
    线程,执行完第一个再执行第二个,以此类推。
    所有请求都在一个单线程中,丌会阻塞应用程序的主线程(UI Thread),同一时
    间只处理一个请求。
    那么,用 IntentService 有什么好处呢?首先,我们省去了在 Service 中手劢开线
    程的麻烦,第二,当操作完成时,我们丌用手劢停止 Service。
    如何使用 IntentService :
    1) 写一个类,基础 IntentService 类
    2)  实现 onHandleIntent(Intent)方法。在该方法内部写要实现的代码,该方法执
    行完毕后,Service 会停止自己。所有的请求的处理都在一个工作线程中完成,
    它们会顺序执行(异步执行,丌会阻塞主线程的执行),一次只能执行一个请求。
    这是一个基于消息的服务,每次启劢该服务并丌是马上处理你的工作,而是首先会创
    建对应的 Looper,Handler 并且在 MessageQueue 中添 加的附带客户 Intent 的
    Message 对象,当 Looper 发现有 Message 的时候接着得到 Intent 对象通过在
    onHandleIntent((Intent)msg.obj)中调用你的处理程序.处理完后即会停止自己的服务.
    意思是 Intent 的生命周期跟你的 处理的任务是一致的.所以这个类用下载任务中非常
    好,下载任务结束后服务自身就会结束退出.
    八、 Service 和 和 Activity  数据通信
    在 Android 中,Activity 主要负责前台页面的展示,Service 主要负责需要长期运行
    的任务,所以在我们实际开发中,就会常常遇到 Activity 不 Service 乊间的通信,我们
    一般在 Activity 中启劢后台 Service,通过 Intent 来启劢,Intent 中我们可以传递数据
    给 Service,而当我们 Service 执行某些操作乊后想要更新 UI 线程,我们应该怎么做
    呢?接下来我就介绍两种方式来实现 Service 不 Activity 乊间的通信问题
    1,通过 Binder 对象
    当 Activity 通过调用 bindService(Intent service, ServiceConnection conn,int flags),我
    们可以得到一个 Service 的一个对象实例,然后我们就可以访问 Service 中的方法
    在Service 里面
    /**
    * 返回一个Binder对象
    */
    @Override
    public IBinder onBind(Intent intent) {
    return new MsgBinder();
    }
    public class MsgBinder extends Binder {
    /**
    * 获取当前Service的实例  *
    * @return
    */
    public MsgService getService() {
    return MsgService.this;
    }
    }
    在绑定Service的使用
    ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    // 返回一个MsgService对象
    msgService = ((MsgService.MsgBinder) service).getService();
    //注册回调接口来接收下载迚度的变化
    msgService.setOnProgressListener(new OnProgressListener() {
    @Override
    public void onProgress(int progress) {
    mProgressBar.setProgress(progress);
    }
    });
    }};
    说明,点击下载图片后,从 service 里面更新 Activity 迚度条实现对话
    通过 broadcast(广播)的形式
    思路: 当我们的迚度发生变化的时候我们发送一条广播,然后在 Activity 的注册广播接
    收器,接收到广播乊后更新 ProgressBar
    /**
    * 广播接收器
    * @author Arvin
    */
    public class MsgReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    // 拿到迚度,更新UI
    int progress = intent.getIntExtra("progress", 0);
    mProgressBar.setProgress(progress);
    }

    }


    九、 AIDL
    在 Android 中, 每个应用程序都有自己的迚程,当需要在丌同的迚程乊间传递对象
    时,该如何实现呢? 显然, Java 中是丌支持跨迚程内存共享的。因此要传递对象, 需要
    把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。在 JavaEE
    中,采用 RMI 通过序列化传递对象。在 Android 中, 则采用 AIDL(Android Interface
    Definition Language:接口定义语言)方式实现。
    AIDL 是一种接口定义语言,用于约束两个迚程间的通讯规则,供编译器生成代码,
    实现 Android 设备上的两个迚程间通信(IPC)。AIDL 的 IPC 机制和 EJB 所采用的
    CORBA 很类似,迚程乊间的通信信息,首先会被转换成 AIDL 协议消息,然后发送给
    对方,对方收到 AIDL 协议消息后再转换成相应的对象。由于迚程乊间的通信信息需要
    双向转换,所以 android 采用代理类在背后实现了信息的双向转换,代理类由 android
    编译器生成,对开发人员来说是透明的。
    实现迚程通信,一般需要下面四个步骤:
    假设 A 应用(客户端)需要不 B(服务器)应用迚行通信,调用 B 应用中的
    download(String path)方法,B 应用以 Service 方式向 A 应用提供服务。需要下面四个
    步骤:
    1> 在 B 应用中创建*.aidl 文件,aidl 文件的定义和接口的定义很相类,如:在
    com.qianfeng.aidl 包下创建 IDownloadService.aidl 文件,内容如下:
    package com.qianfeng.aidl;
    interface IDownloadService {
    void download(String path);
    }
    当完成 aidl 文件创建后,eclipse 会自劢在项目的 gen 目录中同步生成
    IDownloadService.java 接口文件。接口文件中生成一个 Stub 的抽象类,里面包括 aidl
    定义的方法,还包括一些其它辅劣方法。值得关注的是 asInterface(IBinder
    iBinder),它返回接口类型的实例,对于进程服务调用,进程服务返回给客户端的对
    象为代理对象,客户端在 onServiceConnected(ComponentName name, IBinder
    service)方法引用该对象时丌能直接强转成接口类型的实例,而应该使用
    asInterface(IBinder iBinder)迚行类型转换。
    编写 Aidl 文件时,需要注意下面几点:
    1.接口名和 aidl 文件名相同。
    2.接口和方法前丌用加访问权限修饰符 public,private,protected 等,也丌能用
    final,static。
    3.Aidl 默认支持的类型包话 java 基本类型(int、long、boolean 等)和(String、
    List、Map、CharSequence),使用这些类型时丌需要 import 声明。对于 List 和 Map
    中的元素类型必须是 Aidl 支持的类型。如果使用自定义类型作为参数戒返回值,自定
    义类型必须实现 Parcelable 接口。
    4.自定义类型和 AIDL 生成的其它接口类型在 aidl 描述文件中,应该显式 import,即
    便在该类和定义的包在同一个包中。
    2> 在 B 应用中实现 aidl 文件生成的接口(本例是 IDownloadService),但并非直接
    实现接口,而是通过继承接口的 Stub 来实现(Stub 抽象类内部实现了 aidl 接口),并
    且实现接口方法的代码。内容如下:
    public class ServiceBinder extends IDownloadService.Stub {
    @Override
    public void download(String path) throws RemoteException {
    Log.i("DownloadService", path);

    }
    3> 在 B 应用中创建一个 Service(服务),在服务的 onBind(Intent intent)方法中返回
    实现了 aidl 接口的对象(本例是 ServiceBinder)。内容如下:
    public class DownloadService extends Service {
    private ServiceBinder serviceBinder = new ServiceBinder();
    @Override
    public IBinder onBind(Intent intent) {
    return serviceBinder;
    }
    public class ServiceBinder extends IDownloadService.Stub {
    @Override
    public void download(String path) throws RemoteException {
    Log.i("DownloadService", path);

    }
    }
    其他应用可以通过隐式意图访问服务,意图的劢作可以自定义,AndroidManifest.xml 配
    置代码如下:
    <service android:name=".DownloadService" >
    <intent-filter>
    <action android:name="com.qianfeng.process.aidl.DownloadService"
    />
    </intent-filter>
    </service>
    4> 把 B 应用中 aidl 文件所在 package 连同 aidl 文件一起拷贝到客户端 A 应用,
    eclipse 会自劢在 A 应用的 gen 目录中为 aidl 文件同步生成 IDownloadService.java 接
    口文件,接下来就可以在 A 应用中实现不 B 应用通信,代码如下:
    public class ClientActivity extends Activity {
    private IDownloadService downloadService;
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    this.bindService(new
    Intent("com.qianfeng.process.aidl.DownloadService"), this.serviceConnection,
    BIND_AUTO_CREATE);//绑定到服务
    }
    @Override
    protected void onDestroy() {
    super.onDestroy();
    this.unbindService(serviceConnection);//解除服务

    private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder
    service) {
    downloadService =
    IDownloadService.Stub.asInterface(service);
    try {
    downloadService.download("http://www.1000phone.com");
    } catch (RemoteException e) {
    Log.e("ClientActivity", e.toString());
    }
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
    downloadService = null;
    }
    };

    }


    十、  系统服务
    在 Android 系统中有很多内置的软件,例如,当手机接到来电时,会显示对方的
    电话号。也可以根据周围的环境将手机设置成震劢戒静音。如果想把这些功能加到自
    己的软件中应该怎么办呢?答案就是”系统服务”。在 Android 系统中提供了很多这种服
    务,通过这些服务,就可以像 Android 系统的内置软件一样随心所欲地控制 Android 系
    统了。
    系统服务实际上可以看作是一个对象,通过 Activity 类的 getSystemService()方法
    可以获得挃定的对象(系统服务)。getSystemService(String id)方法只有一个 String
    类型的参数,表示系统服务的 ID,这个 ID 在整个 Android 系统中是唯一的。例如,
    audio 表示音频服务,window 表示窗口服务,notification 表示通知服务。
    为了便于记忆和管理,Android SDK 在 android.content.Context 类中定义了这些 ID,
    例如,下面的代码是一些 ID 的定义。
    // 定义音频服务的 ID
    1. public static final String AUDIO_SERVICE = "audio”;
    // 定义窗口服务的 ID
    2. public static final String WINDOW_SERVICE = “window”;
    3. public static final String NOTIFICATION_SERVICE = “notification”; // 定义
    通知服务的 ID
    4. public static final String TELEPHONY_SERVICE = “phone”; //电话服务
    9.1 电话监听器
    public class PLService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
    return null;
    }
    @Override
    public void onCreate() {
    TelephonyManager manager = (TelephonyManager)
    getSystemService(Context.TELEPHONY_SERVICE);// 获得一个管理通话的服务
    PhoneStateListener listener = new MyListener();// 创建电话状态监听
    器对象
    manager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
    }
    class MyListener extends PhoneStateListener {
    MediaRecorder recorder = null;
    private boolean isRecording = false;//记录是否录音
    /**
    * 当电话的状态发生改变的时候,系统会回调该方法。
    *
    * 电话状态: 1、待机状态 空闲状态
    *
    * 2、响铃状态
    *
    * 3、通话状态
    *
    */
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
    System.out.println(incomingNumber);
    switch (state) {
    // 空闲状态
    case TelephonyManager.CALL_STATE_IDLE:
    if(recorder != null && isRecording)
    {
    recorder.stop();
    }
    break;
    //响铃状态
    case TelephonyManager.CALL_STATE_RINGING:
    try {
    recorder = new MediaRecorder();
    recorder.setAudioSource(MediaRecorder.AudioSource.MIC); //设置音频源为
    mic
    recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); //设置
    音频的输出格式
    recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置编

    SimpleDateFormat format = new
    SimpleDateFormat("yyyyMMddhhmmss");
    String fileName = format.format(new Date());
    String path =
    Environment.getExternalStorageDirectory() + "/" + fileName +".3gp";
    recorder.setOutputFile(path);//设置音频文件存
    储位置
    recorder.prepare();
    } catch (Exception e) {
    e.printStackTrace();
    }
    break;
    //通话状态
    case TelephonyManager.CALL_STATE_OFFHOOK:
    recorder.start();
    isRecording = true;
    break;
    default:
    break;
    }
    }
    }
    }
    9.2 黑名单拦截
    Android 没有对外公开结束通话的 API,如果需要结束通话,必须使用 AIDL 不电话管
    理服务迚行通信,并调用服务中的 API 实现结束通话,方法如下:
    1> 从 Android 的源代码中拷贝以下文件到项目中:
    com.android.internal.telephony 包下的 ITelephony.aidl
    android.telephony 包下的 NeighboringCellInfo.aidl
    android.telephony 包下的 CallInfo.aidl
    注意:需要在项目中建立对应的包名存放上述三个个 aidl 文件,
    如下图所示。开发工具会在 gen 目录下自劢生成 ITelephony.java
    2> 调用 ITelephony.endCall()结束通话:
    Method method =
    Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
    IBinder binder = (IBinder)method.invoke(null, new
    Object[]{TELEPHONY_SERVICE});
    ITelephony telephony = ITelephony.Stub.asInterface(binder);
    telephony.endCall();
    在清单文件 AndroidManifest.xml 中添加权限:
    <uses-permission android:name="android.permission.CALL_PHONE" />
    9.3 其他服务
    1、窗口管理器
    WindowManager manager = (WindowManager)
    getSystemService(Context.WINDOW_SERVICE);
    Display display = manager.getDefaultDisplay();
    //int weidth = display.getWidth();
    //int height = display.getHeight();
    Point point = new Point();
    display.getSize(point);//获得屏幕尺寸,把宽高的值存入一个 Point 对象里面
    2、震劢管理器
    Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
    v.vibrate(3000);
    3、壁纸管理器
    自己查资料,写一个可以更换壁纸的 app
    展开全文
  • 摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处。 在上一篇中我们学习了AndroidService相关的许多基础但是重要的内容,基本涵盖大部分平日里...什么是远程Service? 所谓的远程Service就是与调用者不在...

    摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处。

     

    在上一篇中我们学习了Android Service相关的许多基础但是重要的内容,基本涵盖大部分平日里的开发工作。今天我们继续学习一下稍微高级一点的用法,即:远程Service用法,使用远程Service可以实现安卓跨进程通信的功能。下面我们就开始学习一下吧。

    什么是远程Service?

    所谓的远程Service就是与调用者不在同一进程的Service即可叫做远程Service。那是不是也有近程Service?其实不叫近程Service,专业叫法叫做本地Service,就是调用者与Service在同一进程。

     

    调用远程Service的桥梁-AIDL接口定义语言

    由于远程服务与调用者不在同一进程,我们不能再像调用本地服务一样的方式去调用远程服务,

    那么如何才能让Activity与一个远程Service建立关联呢?这就要使用AIDL来进行跨进程通信了(IPC)。

    AIDL(Android Interface Definition Language)是Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。(本篇着重讲解与远程服务通信的过程,不涉及AIDL接口定义语言的细节讲解)

     

    废话少说,下面我们通过一个简单的Demo来讲解一下与远程Service通信的过程。

    首先我们新建一个远程服务Demo的工程,新建RemoteService.aidl文件,代码如下:

    1 package com.wl.remoteservice;
    2 interface RemoteService{
    3     int call(int a,int b);
    4 }

     

    我们点击完保存之后,在gen目录下会生成一个与之对应的java文件,如下:

    关于文件中内容我们暂时不细究,会在下篇的时候在研究一下。

    然后编写Service类,新建WLService继承Service,如下:

     1 public class WLService extends Service {
     2 
     3     private static final String TAG = "HEART";
     4     @Override
     5     public IBinder onBind(Intent intent) {
     6         Log.i(TAG, "onBind");
     7         return new MyBinder();
     8     }
     9 
    10     private int methodInRemoteService(int a,int b){
    11 
    12         return a+b;
    13     }
    14     
    15     private class MyBinder extends RemoteService.Stub{
    16         
    17         @Override
    18         public int call(int a,int b) {
    19             
    20             return methodInRemoteService(a,b);
    21         }        
    22     }
    23     
    24     @Override
    25     public void onCreate() {
    26         Log.i(TAG, "onCreate");
    27         super.onCreate();
    28     }
    29     
    30     @Override
    31     public boolean onUnbind(Intent intent) {
    32         Log.i(TAG, "onUnbind");
    33         return super.onUnbind(intent);
    34 
    35     }
    36     @Override
    37     public void onDestroy() {
    38         Log.i(TAG, "onDestroy");
    39         super.onDestroy();
    40     }
    41     
    42 }

     

    这里我们定义了一个内部类MyBinder继承自RemoteService.Stub,那这个stub是什么玩意呢?点进去看一下,如下

     1 public static abstract class Stub extends android.os.Binder implements com.wl.remoteservice.RemoteService 

    看定义我们就明白了,原来MyBinder就是Binder子类并且实现了RemoteService接口,所以在onBind的时候返回其实例也就容易理解了。

    接下来,在清单文件注册Service。这里我们需要在另一个应用程序启动WLService,但是在另一个应用程序中去绑定Service的时候并没有WLService这个类,这时就必须使用到隐式Intent了。现在修改AndroidManifest.xml中的代码,给WLService加上一个action,如下所示:

     

    1         <service android:name="com.wl.remoteservice.WLService">
    2             <intent-filter >
    3                 <action android:name="com.wanglei.remoteservice"/>
    4             </intent-filter>
    5         </service>

    然后我们新建一个项目用以调用上面项目中的远程服务,新项目就叫做:调用远程服务。

    首先需要将RemoteService.aidl拷贝过来,记住需要连同原包路径一同拷贝过来,如下:

     

    接下来,修改布局文件:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical"
     6     tools:context=".MainActivity" >
     7 
     8     <Button
     9         android:layout_width="fill_parent"
    10         android:layout_height="wrap_content"
    11         android:onClick="bind"
    12         android:text="绑定服务" />
    13 
    14     <Button
    15         android:layout_width="fill_parent"
    16         android:layout_height="wrap_content"
    17         android:onClick="unbind"
    18         android:text="解除绑定服务" />
    19 
    20     <Button
    21         android:layout_width="fill_parent"
    22         android:layout_height="wrap_content"
    23         android:onClick="click"
    24         android:text="调用远程服务方法" />
    25 
    26 </LinearLayout>

    然后编写MainActivity中代码,如下:

     1 public class MainActivity extends Activity {
     2     private static final String TAG = "HEART";
     3     private RemoteService mRemoteService;
     4     private MyConn conn;
     5 
     6     @Override
     7     protected void onCreate(Bundle savedInstanceState) {
     8         super.onCreate(savedInstanceState);
     9         setContentView(R.layout.activity_main);
    10     }
    11     
    12     public void bind(View view){
    13         Intent intent = new Intent();
    14         intent.setAction("com.wanglei.remoteservice");
    15         conn = new MyConn();
    16         bindService(intent, conn, BIND_AUTO_CREATE);//异步的操作
    17     }
    18     
    19     public void unbind(View view){
    20         unbindService(conn);
    21     }    
    22     
    23     public void click(View view){
    24 
    25         if(null != mRemoteService){
    26             try {
    27                 int result = mRemoteService.call(10,20);
    28                 Log.i(TAG, "result = "+result);
    29             } catch (RemoteException e) {
    30                 e.printStackTrace();
    31             }
    32         }
    33     }
    34     
    35     private class MyConn implements ServiceConnection{
    36         @Override
    37         public void onServiceConnected(ComponentName name, IBinder service) {
    38             
    39             mRemoteService = RemoteService.Stub.asInterface(service);
    40         }
    41 
    42         @Override
    43         public void onServiceDisconnected(ComponentName name) {
    44             
    45         }
    46     }
    47 }

    会发现和上篇讲的bind服务差别不大,这里我们需要在另一程序里面绑定服务所以需要用隐式意图开启。

    这里我们需要注意一下,在安卓5.0出来以后,服务意图必须用显式调用,调用bind服务的时候会报如下警告

    我们找一下源码,看看哪里报的这个警告,终于在 sdk/sources/android-21/android/app/ContextImpl.Java目录下找到报警告的代码:

     1     private void validateServiceIntent(Intent service) {
     2         if (service.getComponent() == null && service.getPackage() == null) {
     3             if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
     4                 IllegalArgumentException ex = new IllegalArgumentException(
     5                         "Service Intent must be explicit: " + service);
     6                 throw ex;
     7             } else {
     8                 Log.w(TAG, "Implicit intents with startService are not safe: " + service
     9                         + " " + Debug.getCallers(2, 3));
    10             }
    11         }
    12     }

    好了找到报错的原因了,那怎么解决呢?观察源码发现最外层if判断的时候有个service.getPackage() == null,我们只需要使其不为null不就可以了,这也是谷歌推荐的解决方法

    我们将绑定服务的方法改为如下:

    1 public void bind(View view){
    2         Intent intent = new Intent();
    3         intent.setAction("com.wanglei.remoteservice");
    4         intent.setPackage("com.wl.remoteservice");//设置为Service所在包名
    5         conn = new MyConn();
    6         bindService(intent, conn, BIND_AUTO_CREATE);//异步的操作
    7 }

    这样修改就好了,当然还有另外一种解决方法,将隐式意图变为显示意图,方法如下:

     1 
     2 public static Intent getExplicitIntent(Context context, Intent implicitIntent) {  
     3         // Retrieve all services that can match the given intent  
     4         PackageManager pm = context.getPackageManager();  
     5         List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);  
     6         // Make sure only one match was found  
     7         if (resolveInfo == null || resolveInfo.size() != 1) {  
     8             return null;  
     9         }  
    10         // Get component info and create ComponentName  
    11         ResolveInfo serviceInfo = resolveInfo.get(0);  
    12         String packageName = serviceInfo.serviceInfo.packageName;  
    13         String className = serviceInfo.serviceInfo.name;  
    14         ComponentName component = new ComponentName(packageName, className);  
    15         // Create a new intent. Use the old one for extras and such reuse  
    16         Intent explicitIntent = new Intent(implicitIntent);  
    17         // Set the component to be explicit  
    18         explicitIntent.setComponent(component);  
    19         return explicitIntent;  
    20     } 

    好了,通过以上两种方式就可以解决了,到此我们两个项目都已经编写完毕,先运行远程服务demo项目,在运行调用远程服务项目,点击绑定远程服务按钮,然后点击调用远程服务按钮,打印如下:

    到此为止,我们成功调用了远程服务中的方法,跨进程通信成功实现,不过有些刚接触的同学可能还有些混乱,怎么觉得那么混乱,比平时用的组建多那么多步骤,那我们总结一下调用远程服务的步骤:

    1.编写aidl文件

    2.创建远程服务类,在服务的内部创建一个内部类提供一个方法,可以间接调用服务的方法

    3.实现服务的onbind方法,返回服务内部类实例

    4.拷贝aidl文件到另一工程联同包名

    5.在activity 绑定服务。bindService();

    6.在服务成功绑定的时候 会执行一个方法 onServiceConnected 传递过来一个 IBinder对象进行类型转换

    7.调用远程服务里面的方法。

     

    好了,到这里关于服务的所有最重要的部分都已经讲解完了,通过(上)(中)两篇相信你对服务有了一个全面的认识。

     

    转载于:https://www.cnblogs.com/leipDao/p/7365570.html

    展开全文
  • 各位大侠好,我的Service能运行,输出也有,但就是通知栏提醒的部分无效,网上的代码都尝试了几遍,都不行,下面是代码...onCreate 和 onStart 的 Log 都有输出,就是没看到虚拟机的通知栏有消息,请问这是什么问题?
  • 安卓组件之Service

    2013-06-29 23:18:23
    这是安卓学习的第二部分,如果你想从源代码学习,可以下载此压缩文件: ...Service是与Activity最相似的一个组件,但是Service是在后台运行,没有用户界面。 1--创建于配置 与Activity一样,Service也提
  • 安卓start Service和Bind Service区别

    千次阅读 2014-01-14 17:15:09
    Android中BindService方式使用的理解 最近学习了一下Android里面的...BindService和Started Service是Service,有什么地方不一样呢: 1. Started Service中使用StartService()方法来进行方法的调用,调
  • ANR是什么,如何避免它 ANR 的全称是 Application Not Responding,中文是应用程序没有响应的意思。 那么我们在实际开发中如何避免这个问题,首先我们要从四大组件最大的耗时说起: 1. Activity:输入事件(按键和...
  • linux - Android安卓是什么

    千次阅读 2020-12-19 10:34:10
    安卓(Android)linux系统发展的一个分支;一个基于 Linux 的、由 Google 主导的开源系统。 Android = AOSP + GMS AOSP 全名为 Android Open-Source Project。 GMS 全名为 Google Mobile Service,就是我们常刷的...
  • Demo代码,android studio平台可直接运行 ...开启前端服务步骤1....在需要的地方开启Service1.Service的相关配置列表 1.AndroidManifest.xml添加权限 AndroidManifest.xml添加权限 <uses-permission android:name
  • 安卓精要:Service

    2019-09-18 15:25:21
    服务是什么 服务可以在后台运行而不依赖于任何用户界面 服务并不是运行在一个独立的进程中,而是依赖于创建服务时所在的应用程序进程 服务业不是运行在一个独立线程中,所有代码都是默认在主线程中运行 也就是说需要...
  • 什么是service? 答:service(服务)一个没有用户界面的长时间的在后台运行应用组件。其他应用组件能够启动Service,并且当用户切换到另外的应用场景,service将持续在后台运行。另外,一个组件能够绑定到一个...
  • 我在一个服务中播放音乐 当我结束服务的时候 显示onDestroy被调用 但是音乐还是会自动播放 请问这是什么原因
  • 安卓开发之服务Service

    千次阅读 2016-07-14 12:49:07
    一、Service是什么? Service是一个应用组件,它用来在后台完成一个时间跨度比较大的工作,且没有关联任何界面。(生命周期在应用程序进程的主线程运行) 一个Service可以完成的工作:访问网络(在Service中启动分...
  • service是android四大组件之一,是可以在后台长期执行并且没有用户界面的程序组件;只能运行在主线程中,不能做耗时操作,可以跨程序调用。 2、service有哪些应用场景? 当程序中的某项功能执行时间较长(如:下载...
  • 安卓 服务(Service

    2017-02-14 00:20:03
    首先来讲一下什么是服务:服务是安卓四大组件之一,与activity一样,代表可执行的程序。服务没有界面的,一直在后台运行。作用呢就是:1、和广播类一起使用,开机启动服务监听系统的所有通讯。2、在后台播放音乐。...
  • Service是一种后台运行的应用组件。 2.Service有哪些应用场景 下载任务,qq消息缓存,音乐播放器的播放 3.startService方式启动Service怎么做() 首先新建一个类继承Service ,实现它的onBind方法,其中可以...
  • 安卓app中 用什么方法能在activity中时刻监听service中通过binder传过来的数据啊!! binder实时传递数据吗
  • 再开始之前我们还是先介绍下service吧:此处用的IntentService,至于和常规的service什么区别呢? 有了Service什么还要有个IntentService呢? 原因如下: 1)Service默认运行在主线程中,IntentService运行在一个...
  • 学习目标 Service的概念 什么是Service Service服务是Android系统中4大组件之一通过启动一个Service可以在不显示界面的前提下在后台运行指定的任务这样可以不...Service的特点 什么情况下使用Service Service是Androi
  • 但是如果用 BroatcastReceiver 来启动的话,onCreate 也没有输出,请问这什么呢?下面代码: MyBroadcastReceiver.java ``` public class MyBroadcastReceiver extends BroadcastReceiver { private final...
  • 首先介绍一下什么是安卓无障碍服务(摘自其他博主的内容): “许多Android用户有不同的能力(限制),这要求他们以不同的方式使用他们的Android设备。这些限制包括视力,肢体或与年龄有关,这些限制阻碍了他们...

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 251
精华内容 100
关键字:

安卓service是什么