Android Service講解 和 aidl 實現
代碼下載 Android Service講解 和 aidl 實現
一、Android Service
1.建立一個service
service和activity很相識,只是service在后台運行,activity在前台運行,他們都屬於同一個同一個線程里,都屬於UI線程,所以service和Thread是完全不一樣的東西。一些耗時的操作在Service里運行也要開辟新的線程。
新建一個自己的service,只需要繼承系統Service就行了,看下面代碼:
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate() called");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand() called");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() called");
return stub;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind() called");
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy() called");
}
這樣就建好了一個service,service建好了了,現在也並不能直接啟動,也要像activity一樣注冊進AndroidManifest.xml中才能,運行它。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mangues.com.aidl_service">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".AIDLService">
</service>
</application>
</manifest>
2、啟動service
現在我們建好了service,也注冊進AndroidManifest.xml中怎么才能運行他呢。
有兩種方式可以運行:startService 和 bindService
這兩種方式的區別:
執行startService時,Service會經歷onCreate->onStartCommand。當執行stopService時,直接調用onDestroy方法。調用者如果沒有stopService,Service會一直在后台運行,下次調用者再起來仍然可以stopService。
執行bindService時,Service會經歷onCreate->onBind。這個時候調用者和Service綁定在一起。調用者調用unbindService方法或者調用者Context不存在了(如Activity被finish了),Service就會調用onUnbind->onDestroy。這里所謂的綁定在一起就是說兩者共存亡了。
多次調用startService,該Service只能被創建一次,即該Service的onCreate方法只會被調用一次。但是每次調用startService,onStartCommand方法都會被調用。Service的onStart方法在API 5時被廢棄,替代它的是onStartCommand方法。
第一次執行bindService時,onCreate和onBind方法會被調用,但是多次執行bindService時,onCreate和onBind方法並不會被多次調用,即並不會多次創建服務和綁定服務。
2.1、startService
和startActivity一樣使用就行了,停止的時候直接調用stopService 就行了。
@Override
public void onClick(View view) {
Intent startIntent = new Intent(this, AIDLService.class);
switch (view.getId()){
case R.id.btn_start:
startService(startIntent);
break;
case R.id.btn_stop:
stopService(startIntent);
break;
case R.id.btn_bind:
break;
case R.id.btn_unbind:
break;
}
}
多次點擊start時,看打印日志
證明了
多次調用startService,該Service只能被創建一次,即該Service的onCreate方法只會被調用一次。但是每次調用startService,onStartCommand方法都會被調用。
2.1、bindService
1.這時候綁定的service會和該activity共存亡。不會單獨存在。
2.利用該種方法綁定的service可以和activity交互,不單單只是啟動。
看代碼:
package mangues.com.aidl_service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.Date;
import mangues.com.aidl.IPerson;
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate() called");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onBind() onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() called");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind() called");
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy() called");
}
class MyBinder extends Binder {
public String getMyBinder() {
Log.d(TAG, "MyBinder() called");
return new Date().getTime()+""; //獲取時間
}
}
}
利用binder 把數據傳出service給activity獲取到,只要寫個內部類 繼承Binder,通過binder寫個方法 在利用onBind把這個binder return出去,activity就可以獲得這個binder進而得到方法getMyBinder,獲得service中數據
Activity代碼:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button mBtnBind;
private Button mBtnUnBind;
private AIDLService.MyBinder myBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (AIDLService.MyBinder) service;
String time = myBinder.getMyBinder();
Log.i("MainActivity",time);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init(){
mBtnBind = (Button)findViewById(R.id.btn_bind);
mBtnUnBind = (Button)findViewById(R.id.btn_unbind);
mBtnBind.setOnClickListener(this);
mBtnUnBind.setOnClickListener(this);
}
@Override
public void onClick(View view) {
Intent startIntent = new Intent(this, AIDLService.class);
switch (view.getId()){
case R.id.btn_bind:
Intent bindIntent = new Intent(this, AIDLService.class);
//這里傳入BIND_AUTO_CREATE表示在Activity和
//Service建立關聯后自動創建Service,這會使得
//MyService中的onCreate()方法得到執行,但
//onStartCommand()方法不會執行。
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.btn_unbind:
unbindService(connection);
break;
}
}
}
點擊開始和結束 日志如下:
看第四行:activity 獲取到了service中數據
3、注銷service
第一種、startService stopService
第二種、bindService unBindService
上面都好理解,那么如果我們既點擊了startService按鈕,又點擊了bindService按鈕會怎么樣呢?
這個時候你會發現,不管你是單獨點擊stopService按鈕還是unbindService按鈕,Service都不會被銷毀,必要將兩個按鈕都點擊一下,Service才會被銷毀。也就是說,點擊Stop Service按鈕只會讓Service停止,點擊Unbind Service按鈕只會讓Service和Activity解除關聯,一個Service必須要在既沒有和任何Activity關聯又處理停止狀態的時候才會被銷毀。
看日志:
只執行stopService 或者 unbindService 根本沒有注銷
只有全部執行才會 注銷
4.遠程service
只要在service中注冊信息中加上 android:process=":remote" 就行了
<service android:name=".AIDLService"
android:process=":remote">
</service>
遠程service有什么用呢?
遠程service的作用只是重新建立一個新進程執行,可以獨立出去。其他app可以調用這個service。因為是一個新的進程,所以也不能用bind來建立關聯了。
可以用新的方式來建立關系就是下面要講的aidl技術。
二、aidl實現
1.首先我建立2個app工程,通過aidl實現一個app調用另一個app的service
目錄結構如下:
service提供端app
利用aidl調用service的app
2.在兩個app中都建立一個文件 IPerson.aidl注意 包名 要相同
IPerson.aidl只是一個接口文件,用來aidl交互的,建立好之后gradle一下就行了,studio會自動創建需要的文件,具體什么文件你不用管,我們用不到,你在代碼里可以看到

IPerson.aidl代碼
package mangues.com.aidl;
interface IPerson {
String greet(String someone);
}
3.在aidl_service 中建立AIDLService
這個IPerson.Stub 就是通過IPerson.aidl 自動生成的binder 文件,你實現下,然后onBind return出去就好了,就和Android Service實現和activity交互一樣。
代碼:
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
IPerson.Stub stub = new IPerson.Stub() {
@Override
public String greet(String someone) throws RemoteException {
Log.i(TAG, "greet() called");
return "hello, " + someone;
}
};
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate() called");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onBind() onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() called");
return stub;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind() called");
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy() called");
}
}
4.aidl_service MainActivity 中啟動這個service
簡單點就不寫關閉什么的了;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent startIntent = new Intent(this, AIDLService.class);
startService(startIntent);
}
在AndroidManifest.xml注冊
<service android:name=".AIDLService"
android:process=":remote">
<intent-filter>
<action android:name="android.intent.action.AIDLService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
android.intent.action.AIDLService 字段隱形綁定這個service,獲取數據。
5.aidl_client 中綁定aidl_service service 獲取數據
代碼:
public class MainActivity extends AppCompatActivity {
private IPerson person;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("ServiceConnection", "onServiceConnected() called");
person = IPerson.Stub.asInterface(service);
String retVal = null;
try {
retVal = person.greet("scott");
} catch (RemoteException e) {
e.printStackTrace();
}
Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
//This is called when the connection with the service has been unexpectedly disconnected,
//that is, its process crashed. Because it is running in our same process, we should never see this happen.
Log.i("ServiceConnection", "onServiceDisconnected() called");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent mIntent = new Intent();
mIntent.setAction("android.intent.action.AIDLService");
Intent eintent = new Intent(getExplicitIntent(this,mIntent));
bindService(eintent, conn, Context.BIND_AUTO_CREATE);
}
public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
和Android Service 中學習的調用MyBinder獲取service中數據一樣,這邊只是吧MyBinder 改成了aidl定義的接口IPerson 本質上還是一個Binder
因為android 5.0 不允許隱形啟用service 所有用getExplicitIntent轉一下
好了現在看下效果:
