Android学习之Service(下)
概述
在Android学习之Service(上)中学习了Service的基本使用方式,接下来需要研究一下使用Service实现跨进程通信的功能。
配置远程Service
要实现Service的跨进程通信就需要提到远程Service。远程Service和普通的Service注册方式是一样的,只不过是在注册时,设定android:process属性为:remote,这个“:remote”并不是固定这样写的可以随意写,例如,包名是com.servicedemo那么,不设定”:remote”的时候,默认的进程名称为com.servicedemo,当设置该属性后,就变成了com.servicedemo:remote。
除此之外,还需要添加一个action属性,记得在Android学习之Service(上) 中我的启动服务是这样写的final Intent intent = new Intent(this, StartService.class);
其中StartService就是我的自定义服务类,那么,如果是其他应用程序的话,其他程序中是不可能存在StartService这个类的,所以就要使用action属性,用Intent的隐式调用,具体代码如下:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.servicedemo"> <application> ............. <service android:name=".service.StartService" android:process=":remote" > <intent-filter> <action android:name="com.servicedemo.CalculateService"/> intent-filter> service> application>manifest>
编写AIDL
解决了配置文件后,又看到一个新的东西,AIDL(Android接口定义语言),它实现了让Activity与一个远程Service建立关联,这里需要定一个.aidl文件由于我用的是Android Studio开发工具,作为新手着实费了一番周折,下面做一个记录。
首先,.aidl文件是单独放在aidl文件夹下,当新建一个.aidl的文件的时候就会自动生成这样一个文件夹,既然是自动的就直在app上新建一个AIDL就好了方法是,选中app->右键->New->AIDL->AIDL File 然后输入名字即可这里是ADLIService.aidl,如下图:
这里基本没什么问题,.aidl文件的内容如下:
// ADLIService.aidlpackage com.servicedemo;interface ADLIService { int addCalculate(int a, int b); int factorialCalculate(int num);}
这就是一个接口,里边添加了两个需要子类去实现的方法,分别用来在服务启动的时候进行加法计算和阶乘运算。这一步完成后使用Eclipse就会直接生成一个与.aidl名字一样的.java文件,但是,使用Android Studio就是自动的,我起初以为是目录结构不一样,生成以后我又不知道,然后强行使用,发现没有这个类,次法行不通。百度后发现Android Studio需要Build->ReBuild Project一下就好了,但是,并不是直接可见的,需要改变一下目录结构,如下:
使用AIDL
维护好这些后就可以在自定义的Service中使用这个接口了,这里还是用上一篇代码中的例子工程,代码如下:
package com.servicedemo.service;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import com.servicedemo.ADLIService;public class StartService extends Service { ADLIService.Stub mBinder = new ADLIService.Stub() { @Override public int addCalculate(int a, int b) throws RemoteException { return a + b; } @Override public int factorialCalculate(int num) throws RemoteException { int result = 1; do { result *= num; } while (num-- > 1); return result; } }; @Override public IBinder onBind(Intent intent) { return mBinder; }}
将上个例子中没有用的代码去掉,留下有用的部分,首先是创建一个提供给obBind()返回的对象,这个对象就是刚刚定义的AIDL接口,这个对象是通过new ADLIService.Stub()得到的,而Stub是IBinder的子类。在创建对象的同时就可以像上边的代码中一样,实现那两个接口中的方法。
使用AIDL
现在要实现的是跨进程通信,所以,首先是要有两个程序不是。这里先不急,先看看这个服务运行起来是什么样的,代码基本和之前的代码基本一样,就修改了ServiceConnection对象是重写的方法,如下所示:
package com.servicedemo.activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import com.servicedemo.ADLIService;import com.servicedemo.R;import com.servicedemo.service.StartService;public class MainActivity extends AppCompatActivity { private Button mStart, mStop, mBind, mUnBind; // 改变一:自定义的AIDL接口对象 private ADLIService mADLIService; private ServiceConnection connection = new ServiceConnection() { /** * 与Service断开连接后调用 */ @Override public void onServiceDisconnected(ComponentName name) { } /** * 与Service连接后调用 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 改变二:// 得到自定义的AIDL接口对象 mADLIService = ADLIService.Stub.asInterface(service); try { // 使用服务的方法 int result = mADLIService.addCalculate(10, 10); Log.d("Service_remote", "add = " + result); result = mADLIService.factorialCalculate(5); Log.d("Service_remote", "factorial = " + result); } catch (RemoteException e) { e.printStackTrace(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initUI(); } private void initUI() { mBind = (Button) findViewById(R.id.btn_bind_service); final Intent intent = new Intent(this, StartService.class); mBind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { bindService(intent, connection, BIND_AUTO_CREATE); } }); }}
与之前的代码比去掉了一些用不到的按钮事件只看绑定Service事件。当点击”绑定Service”的时候就会将计算结果传回来,计算过程就是在之前Service中实现的,效果如下:
知道了Service的正常使用和运行效果,再看看跨进程的效果,首先,在创建一个工程ServiceClient,然后,将先前工程中的aidl文件夹整个拷贝到ServiceClient工程下与java文件夹同级即可(可以直接到目录里复制和粘贴),如下所示:
然后,到ServiceClient工程,ReBuild一下。至此已完成多半,剩下的就是给界面添加按钮,然后,设置点击事件,代码如下:
package com.serviceclient;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import com.servicedemo.ADLIService;public class MainActivity extends AppCompatActivity { private Button mBind; private ADLIService mADLIService; // 与之前一样创建ServiceConnection对象,并称写两个监听方法 private ServiceConnection connection = new ServiceConnection() { /** * 与Service断开连接后调用 */ @Override public void onServiceDisconnected(ComponentName name) { } /** * 与Service连接后调用 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { mADLIService = ADLIService.Stub.asInterface(service); try { int result = mADLIService.addCalculate(30, 10); Log.d("Service_remote", "add = " + result); result = mADLIService.factorialCalculate(10); Log.d("Service_remote", "factorial = " + result); } catch (RemoteException e) { e.printStackTrace(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initUI(); } private void initUI() { mBind = (Button) findViewById(R.id.btn_bind_service); // 注意,要与启动的Service的action属性一致 final Intent intent = new Intent("com.servicedemo.CalculateService"); mBind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { bindService(intent, connection, BIND_AUTO_CREATE); } }); }}
与之前项目中MainActivity.java是一模一样的代码,所不同是Intent在创建的时候传递的是action属性的值,这种方式属于隐性调用,其他的代码没有区别。实验效果,就是通过点击新创建app的按钮,去启动之前项目中的服务,将参数拿到远端Service去计算,得到一个计算结果,然后打印。
注意final Intent intent = new Intent("com.servicedemo.CalculateService");
这种写法只是用于5.0以前,5.0以后就不能这样隐性调用了,要改成如下代码:
private void initUI() { mBind = (Button) findViewById(R.id.btn_bind_service); final Intent intent = new Intent(); // 还是设定action intent.setAction("com.servicedemo.CalculateService"); // 这里要明确设定要启动的服务的包名(程序名) intent.setPackage("com.servicedemo"); mBind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { bindService(intent, connection, BIND_AUTO_CREATE); } }); }
这样就可以实现远程启动服务了,至此,Service的学习就结束了,最终效果如下:
上面的代码传递就是Java的基础类型,例如,传个整型做加减乘除什么的,远程服务所传递的参数基本也就是这些还有Map和List什么的,如果想传递一些自定义类型,这就必须要让这个类去实现Parcelable接口,并且要给这个类也定义一个同名的AIDL文件,我的理解就是将自动生成的那个.java文件改成手写的,这个类实现Parcelable接口接口以了,我大概创建了一下,还是挺复杂的(功力不足),回头用的时候再说吧!
以上是关于Service学习和理解!
更多相关文章
- Android(安卓)自定义View - 启航 一般View定义
- Android(安卓)更新UI的两种方法——handler和runOnUiThread() -
- Android(安卓)APP之间共享SharedPreference
- Android(安卓)SDK更新后 ADT R17 E/AndroidRuntime : java.lang.
- Android(安卓)JNI 实例
- 下载android sdk更新包离线安装解决方案
- Android使用Aidl实现跨进程通信
- Android性能测试工具(一)之Emmagee
- Android(安卓)Native层开发Camera应用的方法