Android 服务Service开发

前言

  服务是Android中的一个应用,它在后台运行,不需要与用户有任何的交互。例如,当使用一个应用的时候,你希望同时可以在后台播放音乐。这时,在后台播放音乐的代码不需要与用户交互;因此,它可以作为一个服务运行。同时,当应用不需要提供用户界面(UI)的时候,服务也是理想的选择。对于这种情况由一个很好的示例应用是持续记录设备的地理坐标。这时,可以编写一个服务在后台运行。

Service的分类

  • 本地服务Local Service:用于应用程序内部;
  • 远程服务Remote Service:用于Android系统内部的应用程序之间的进程通信。

Service启动过程

这里写图片描述

  附上一张原图片

这里写图片描述

区别

startService启动

   startService方法启动的Service和调用者Activity之间没有任何关联,即使Activity已经退出了,Service仍然可以继续运行,而且调用者和Service之间无法进行数据交换和通信。如果需要停止Service的运行,只能通过调用Context类的stopService方法,或者由Service本身调用其stopSelf方法。当第一次启动Service方法的时候,系统会调用onCreateonStart两个方法;当停止Service服务的时候,系统会调用onDestory方法;如果Service已经启动了,在启动同一个服务,系统只会调用onStart这个方法了。

bindService启动

  此方法调用Service时,调用者Activity与Service绑定在一起,如果Activity退出,则Service也随之退出,而且调用者Activity与Service之间可以进行数据交换或者通信。

bindService方法参数说明
Intent service是指向需要绑定服务的Intent。
ServiceConnection conn是一个接口,重写其中的onServiceConnected()方法可以通过Binder类来达到Activity与Service之间传递数据的目的。
int flags用来标明绑定中的操作,不想指定时设置为0即可,通常设置为BIND_AUTO_CREATE,这样就会在Service不存在的时候创建一个。

说明:需要注意的是,ServiceConnectiononServiceDisconnected()方法仅在服务非正常中断的时候调用,如果正常解除Service绑定不会调用该语句。

两种方法混合使用的启动过程

  1. onCreate
  2. onStart
  3. onBind
  4. onUnbind
  5. onStop
  6. onDestory

  1. onCreate
  2. onBind
  3. onStart
  4. onStop
  5. onUnbind
  6. onDestory

在服务中执行长时间运行的任务

  MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onStartService(View view) {
        startService(new Intent(getApplicationContext(), MyService.class));
    }

    public void onStopService(View view) {
        stopService(new Intent(getApplicationContext(), MyService.class));
    }
}

  activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="link_work.myapplication.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/startService"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onStartService"
            android:text="@string/startService" />

        <Button
            android:id="@+id/stopService"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onStopService"
            android:text="@string/stopService" />

    </LinearLayout>
</LinearLayout>

这里写图片描述

  这个实例演示了最简单的一个Service。服务本身不做任何有用的工作,当然它只是为了说明如何创建一个服务。
  首先定义一个继承于Service基类的子类。所有的服务都继承于Service类:

public class MyService extends Service {

}

  在MyService类中,实现了三个方法:

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent arg0) {
        ...
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
    }

    @Override
    public void onDestroy() {
        ...
    }
}
方法说明
onBind()该方法使你能够将一个Activity绑定到一个服务上。反过来Activity也能够直接访问服务中的成员和方法。
onStartCommand()该方法会在显式地使用startService方法启动服务时被调用。该方法表示服务启动,你可以在该方法中编写任何想要为服务执行的任务。
onDestory()该方法会显示地使用stopService()方法停止服务时被调用。在该方法中清理服务使用的资源。

  要启动一个服务,调用startService()方法:

startService(new Intent(getApplicationContext(), MyService.class));

  要停止一个服务,调用StopService()方法:

stopService(new Intent(getApplicationContext(), MyService.class));

  先在中AndroidManifest.xml添加<service android:name=".Service.MyService" />

  MyService

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
        try {
            int result =
                    downLoadFile(new URL("http://www.amazon.com/somefile.pdf"));
            Toast.makeText(getBaseContext(), "Downloaded " + result + " bytes",
                    Toast.LENGTH_LONG).show();
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return START_STICKY;
    }

    private int downLoadFile(URL url) {
        try {
            //---模拟下载---
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //---随意返回一个任意值100---
        return 100;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
    }
}

这里写图片描述

  注意:请注意在点击开启服务的之后,按钮会由1秒多的卡顿(按钮点击特效会有卡顿感)。


  在本示例中,服务调用downloadFile()方法模拟从给定的URL中下载一个文件。该方法返回下载的字节总数(这里硬编码为100)。为了模拟当下载文件时服务所经历的延时,这里使用Thead.Sleep()方法将服务暂停5秒钟(5000毫秒)。
  当启动服务的时候,注意Activity会有5秒钟的停顿。这是从网络下载文件所花的时间。在这段时间中,整个Activity没有任何响应,这也演示了一个非常重要的知识点:服务和Activity运行在相同的线程上。在这种情况下,因为服务停顿5秒钟,所以Activity也同样停顿5秒钟。


  也就是说,对于一个长时间运行的服务来说,必须将所有耗时代码放在一个独立的线程中,这样就不会影响调用它的应用。

在服务中创建异步执行任务

  MyService

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
        try {
            new DoBackgroundTask().execute(
                    new URL("http://www.amazon.com/somefiles.pdf"),
                    new URL("http://www.wrox.com/somefiles.pdf"),
                    new URL("http://www.google.com/somefiles.pdf"),
                    new URL("http://www.learn2develop.net/somefiles.pdf"));
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return START_STICKY;
    }

    private int downloadFile() {
        try {
            //---模拟下载停顿---
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 随意返回一个下载文件的大小。
        return 100;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
    }

    @SuppressLint("StaticFieldLeak")
    private class DoBackgroundTask extends AsyncTask<URL, Integer, Long> {
        @Override
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalBytesDownloaded = 0;
            for (int i = 0; i < count; i++) {
                totalBytesDownloaded += downloadFile();
                // 在下载中不断更新进度条
                publishProgress((int) (((i + 1) / (float) count) * 100));
            }
            return totalBytesDownloaded;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            Log.d("Downloading files",
                    String.valueOf(progress[0]) + "% downloaded");
            Toast.makeText(getBaseContext(),
                    String.valueOf(progress[0]) + "% downloaded",
                    Toast.LENGTH_LONG).show();
        }

        @Override
        protected void onPostExecute(Long result) {
            Toast.makeText(getBaseContext(),
                    "Downloaded " + result + " bytes",
                    Toast.LENGTH_LONG).show();
            // 当后台线程完成执行之后,需要手动调用stopSelf()方法停止服务。
            // 该方法类似于调用stopService()方法停止服务。
            stopSelf();
        }
    }
}

这里写图片描述

  单击完开启服务按钮之后,Toast类显示信息指示下载的完成进度。你可以看到四条信息:25%、50%、75%、100%。


  本示例说明了一个在服务中执行异步任务的方法。该方法通过创建一个继承于AsyncTask类的内部类。AsyncTask方法能够在不需要手动处理线程和执行者的情况下在后台执行操作。

方法说明
doInBackground()这个方法接收一个数组作为参数,数组类型为早前制定的第一个类型,本示例中为URL类型。该方法可以在后台线程中执行,因此它就是防止耗时代码的地方。要报告任务的进度,可以调用publishProgress()方法,它会调用下一个方法,onProgressUpdate()
onProgressUpdate()该方法在UI线程中调用,当调用publishProgress()方法的时候就会调用该方法。它接受一个数组作为参数。使用这个方法可以为用户汇报后台任何的进度。
onPostExecute()这个方法在UI线程中调用,当doInBackground()方法结束执行的时候就会调用该方法。

在服务中执行重复任务

  MyService

public class MyService extends Service {
    static final int UPDATE_INTERVAL = 1000;
    int counter = 0;
    private Timer timer = new Timer();

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        doSomethingRepeatedly();
        try {
            new DoBackgroundTask().execute(
                    new URL("http://www.amazon.com/somefiles.pdf"),
                    new URL("http://www.wrox.com/somefiles.pdf"),
                    new URL("http://www.google.com/somefiles.pdf"),
                    new URL("http://www.learn2develop.net/somefiles.pdf"));
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return START_STICKY;
    }

    private void doSomethingRepeatedly() {
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                Log.d("MyService", String.valueOf(++counter));
            }
        }, 0, UPDATE_INTERVAL);
    }

    private int downloadFile() {
        try {
            //---模拟下载延时---
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 100;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (timer != null) {
            timer.cancel();
        }
        Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
    }

    @SuppressLint("StaticFieldLeak")
    private class DoBackgroundTask extends AsyncTask<URL, Integer, Long> {
        @Override
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalBytesDownloaded = 0;
            for (int i = 0; i < count; i++) {
                totalBytesDownloaded += downloadFile();
                //---calculate percentage downloaded and
                // report its progress---
                publishProgress((int) (((i + 1) / (float) count) * 100));
            }
            return totalBytesDownloaded;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            Log.d("Downloading files",
                    String.valueOf(progress[0]) + "% downloaded");
            Toast.makeText(getBaseContext(),
                    String.valueOf(progress[0]) + "% downloaded",
                    Toast.LENGTH_LONG).show();
        }

        @Override
        protected void onPostExecute(Long result) {
            Toast.makeText(getBaseContext(),
                    "Downloaded " + result + " bytes",
                    Toast.LENGTH_LONG).show();
            stopSelf();
        }
    }
}

这里写图片描述

  在本例中,创建了一个Timer对象,并在自定义的doSomethingRepeatedly()方法中调用Timer对象的scheduleAtFixedRate()方法:

private void doSomethingRepeatedly() {
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                Log.d("MyService", String.valueOf(++counter));
                }
        }, 0, UPDATE_INTERVAL);
}

  向scheduleAtFixedRate()方法中传入了一个TimerTask类的实例,从而可以在run()方法中重复执行一段代码。scheduleAtFixedRate()方法的第二个参数指定了第一次执行前的等待时间,以毫秒为单位。第三个参数指定了后续执行的时间间隔,以毫秒为单位。
  以上示例代码实际上每秒打印计数器的数值(1000毫秒)。服务会重复打印计数器的数值直到服务被终止。

@Override
public void onDestroy() {
        super.onDestroy();
        if (timer != null) {
            timer.cancel();
    }
    Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
}

  对于scheduleAtFixedRate()方法来说,它会固定时间间隔执行任务,不管每次任务会消耗多长时间。
  同样需要注意的是,在onStartCommand()方法中直接调用doSomethingRepeatedly(),而不需要将它封装在AsyncTask类的子类中。这是因为TimerTask类中自己实现了Runnable接口,能够允许它在独立的线程上运行。

使用IntentService在独立的线程中执行异步任务

  使用Service的时候,需要时刻注意的就是,当服务结束执行一个任务以后,它应该立即停止从而可以释放宝贵的资源。这就是为什么当一个任务结束以后需要调用stopSelf()方法停止服务的原因。遗憾的是,当任务完成以后,很多开发者经常忘记终止服务。为了方便地创建一个异步运行任务的服务,并且当任务结束的时候自动终止,可以使用IntentService类。
  作为服务的基类,IntentService类根据需求处理异步请求。启动它的方法与普通服务相同;但是它会在一个工作线程中执行它的任务并且当任务完成时它会自动终止

  MyIntentService

public class MyIntentService extends IntentService {

    public MyIntentService() {
        // Intent Service的名称
        super("MyIntentServiceName");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            int result =
                    downloadFile(new URL("http://www.amazon.com/somefile.pdf"));
            Log.d("IntentService", "Downloaded " + result + " bytes");
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    private int downloadFile(URL url) {
        try {
            //---模拟下载---
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 100;
    }
}

这里写图片描述

  onHandleIntent()方法就是放置需要在独立线程中执行的代码的位置,比如从服务器上下载文件。当代码结束执行后,线程会被终止而且服务也会自动停止。

在服务和Activity之间通信

  MyIntentService

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentServiceName");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            int result =
                    downloadFile(new URL("http://www.amazon.com/somefile.pdf"));
            Log.d("IntentService", "Downloaded " + result + " bytes");
            //---send a broadcast to inform the activity
            // that the file has been downloaded---
            Intent broadcastIntent = new Intent();
            broadcastIntent.setAction("FILE_DOWNLOADED_ACTION");
            getBaseContext().sendBroadcast(broadcastIntent);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    private int downloadFile(URL url) {
        try {
            //---模拟下载---
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 100;
    }
}

  MainActivity

BroadcastReceiver的生命周期: 一个广播接收器对象只在onReceive(Context context, Intent intent)方法的执行期间有效,所以广播接收器的生命周期就是onReceive()方法的执行过程。一旦代码从该方法中返回,系统就认为这个广播接收器对象已经无效了。
注意:由于一定执行完onReceive()方法中的代码,系统就会清除这个接收器,所以一般不把需要等待回调的操作放在这里执行,否则当回调操作返回的时候,这个接收器可能已经不存在了。

public class MainActivity extends AppCompatActivity {
    private BroadcastReceiver intentReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Toast.makeText(getApplicationContext(), "File downloaded.",
                        Toast.LENGTH_LONG).show();
            }
        };
    }

    public void onStartIntentService(View view) {
        startService(new Intent(getApplicationContext(), MyIntentService.class));
    }

    public void onStopIntentService(View view) {
        stopService(new Intent(getApplicationContext(), MyIntentService.class));
    }

    @Override
    public void onResume() {
        super.onResume();
        //---intent to filter for file downloaded intent---
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("FILE_DOWNLOADED_ACTION");
        //---register the receiver---
        registerReceiver(intentReceiver, intentFilter);
    }
    @Override
    public void onPause() {
        super.onPause();
        //---unregister the receiver---
        unregisterReceiver(intentReceiver);
    }
}

  单击打开Intent服务按钮,大约5秒钟后,Toast类就会弹出一条消息表示文件下载完成。

这里写图片描述

  当服务的执行任务完成之后,想要通知Activity,可以使用sendBroadcast()方法广播一个Intent对象:

@Override
protected void onHandleIntent(Intent intent) {
        try {
            int result =
            downloadFile(new URL("http://www.amazon.com/somefile.pdf"));
            Log.d("IntentService", "Downloaded " + result + " bytes");
            //---send a broadcast to inform the activity
            // that the file has been downloaded---
            Intent broadcastIntent = new Intent();
            broadcastIntent.setAction("FILE_DOWNLOADED_ACTION");
            getBaseContext().sendBroadcast(broadcastIntent);
    } catch (MalformedURLException e) {
            e.printStackTrace();
    }
}

  被广播的Intent对象的动作被设置为FILE_DOWNLOADED_ACTION,这意味着所有监听该Intent的Activity将会被调用。也就是说,在MainActivity文件中,需要使用registerReceiver()方法监听IntentFilter类中的Intent对象。

@Override
public void onResume() {
        super.onResume();
        //---intent to filter for file downloaded intent---
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("FILE_DOWNLOADED_ACTION");
        //---register the receiver---
        registerReceiver(intentReceiver, intentFilter);
}

  当接收到Intent后,它会调用已经定义的BroadcastReceiver类的实例:

    private BroadcastReceiver intentReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Toast.makeText(getApplicationContext(), "File downloaded.",
                        Toast.LENGTH_LONG).show();
            }
        };
    }

将Activity与服务绑定

  MainActivity

public class MainActivity extends AppCompatActivity {
    int notificationID = 1;
    MyService serviceBinder;
    Intent i;
    private BroadcastReceiver intentReceiver;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(
                ComponentName className, IBinder service) {
            //—-called when the connection is made—-
            serviceBinder = ((MyService.MyBinder) service).getService();
            try {
                //---assign the URLs to the service through the
                // serviceBinder object---
                serviceBinder.urls = new URL[]{
                        new URL("http://www.amazon.com/somefiles.pdf"),
                        new URL("http://www.wrox.com/somefiles.pdf"),
                        new URL("http://www.google.com/somefiles.pdf"),
                        new URL("http://www.learn2develop.net/somefiles.pdf")};
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            startService(i);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            //---当service为null的时候回调---
            serviceBinder = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Toast.makeText(getApplicationContext(), "File downloaded.",
                        Toast.LENGTH_LONG).show();
            }
        };
    }

    public void onContentProvider(View view) {
        startActivity(new Intent(this, Main2Activity.class));
    }

    public void onMyCP(View view) {
        startActivity(new Intent(this, Main3Activity.class));
    }

    public void onStartService(View view) {
//        startService(new Intent(getApplicationContext(), MyService.class));
        i = new Intent(MainActivity.this, MyService.class);
        bindService(i, connection, Context.BIND_AUTO_CREATE);
    }

    public void onStopService(View view) {
        stopService(new Intent(getApplicationContext(), MyService.class));
    }

    public void onStartIntentService(View view) {
        startService(new Intent(getApplicationContext(), MyIntentService.class));
    }

    public void onStopIntentService(View view) {
        stopService(new Intent(getApplicationContext(), MyIntentService.class));
    }

    @Override
    public void onResume() {
        super.onResume();
        //---intent to filter for file downloaded intent---
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("FILE_DOWNLOADED_ACTION");
        //---register the receiver---
        registerReceiver(intentReceiver, intentFilter);
    }

    @Override
    public void onPause() {
        super.onPause();
        //---unregister the receiver---
        unregisterReceiver(intentReceiver);
    }
}

注意:ServiceConnection中的onServiceDisconnected()方法仅在服务非正常中断的时候调用,如果正常解除Service绑定不会调用该语句。

  MyService

public class MyService extends Service {
    public URL[] urls;
    private final IBinder binder = new MyBinder();
    public class MyBinder extends Binder {
        public MyService getService() {
            return MyService.this;
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return binder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
        new DoBackgroundTask().execute(urls);
        return START_STICKY;
    }

    private int downloadFile() {
        try {
            //---模拟下载延时---
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 100;
    }

    @SuppressLint("StaticFieldLeak")
    private class DoBackgroundTask extends AsyncTask<URL, Integer, Long> {
        @Override
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalBytesDownloaded = 0;
            for (int i = 0; i < count; i++) {
                totalBytesDownloaded += downloadFile();
                //---calculate percentage downloaded and
                // report its progress---
                publishProgress((int) (((i + 1) / (float) count) * 100));
            }
            return totalBytesDownloaded;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            Log.d("Downloading files",
                    String.valueOf(progress[0]) + "% downloaded");
            Toast.makeText(getBaseContext(),
                    String.valueOf(progress[0]) + "% downloaded",
                    Toast.LENGTH_LONG).show();
        }

        @Override
        protected void onPostExecute(Long result) {
            Toast.makeText(getBaseContext(),
                    "Downloaded " + result + " bytes",
                    Toast.LENGTH_LONG).show();
            stopSelf();
        }
    }
}

这里写图片描述

  要将Activity与服务绑定,首先必须在服务中创建一个继承于Binder类的内部类:

public class MyBinder extends Binder {
        public MyService getService() {
            return MyService.this;
    }
}

  在这个内部类中实现getService()方法,该方法返回一个服务的实例:

private final IBinder binder = new MyBinder();

  同时修改onBind()方法返回MyBind实例:

@Override
public IBinder onBind(Intent arg0) {
        return binder;
}

  在onStartCommand()方法中,使用urls数组调用execute()方法,urls数组在服务中被声明为一个公共成员:

public class MyService extends Service {
    public URL[] urls;

    ......

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
        new DoBackgroundTask().execute(urls);
        return START_STICKY;
    }
    ......
}

  接着,这个URL数组必须从Activity中直接赋值。
  在MainActivity文件中,首先声明一个服务的实例和一个Intent对象:

MyService serviceBinder;
Intent i;

  serviceBinder对象将被用来作为服务的引用,并在Activity中可以直接访问。
  然后创建一个ServiceConnection类的实例,从而可以监控服务的状态:

private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(
                ComponentName className, IBinder service) {
            //—-当连接成功的时候回调—-
            serviceBinder = ((MyService.MyBinder) service).getService();
            try {
                //---初始化URLs数组,并通过Intent传递给Service---
                serviceBinder.urls = new URL[]{
                        new URL("http://www.amazon.com/somefiles.pdf"),
                        new URL("http://www.wrox.com/somefiles.pdf"),
                        new URL("http://www.google.com/somefiles.pdf"),
                        new URL("http://www.learn2develop.net/somefiles.pdf")};
                } catch (MalformedURLException e) {
                        e.printStackTrace();
                }
                startService(i);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            //---当service为null的时候回调---
            serviceBinder = null;
    }
};

  需要实现两个方法:onServiceConnectedonServiceDisconnected

方法说明
onServiceConnected()当Activity与服务连接的时候调用。
onServiceDisconnected()当Activity与服务断开的时候调用。

  随后使用startService(i);启动服务。
  在启动服务之前,必须将Activity与该服务绑定。该内容在启动服务按钮的onClick事件函数onStartService中:

public void onStartService(View view) {
        i = new Intent(MainActivity.this, MyService.class);
        bindService(i, connection, Context.BIND_AUTO_CREATE);
}

  bindService()方法使Activity连接到服务。

参数说明
Intent service一个Intent对象。
ServiceConnection conn一个ServiceConnection对象。
int flags一个标识如何绑定服务的标志。

附录

  • Beginning Android Programming with Android Studio, 4th Edition
  • 《Android高级开发》
  • 《煮酒论 Android》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值