android 跨应用启动/绑定Service && aidl

  以前写了一篇文章是关于同一个应用中有一个Activity 和 一个 Service,然后在AndroidManifest.xml文件中 将service的
  android:process设置为":remote" , 这里要强调一下带“:”和不带“:”时的属性, android:process=":remote",代表在应用
  程序里,当需要该service时,会自动创建新的进程,意思就是说你配的service会在另外一个进程中运行,而如果是
android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
上次虽然同一个应用中实现了Activity和Service的夸进程通信,但是感觉不爽,毕竟是在同一应用中,这次实现一个两个应用中的跨进程使用aidl通信。
1 首先在创建一个应用工程MyApplication4,作用就是创建一个继承自Service的一个服务类,然后再创建一个aidl 文件。
Myservice.java
package com.example.lsw.myapplication;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(MainActivity.TAG,"onBind");
        // TODO: Return the communication channel to the service.
       return new MyBinder();
    }

    @Override
    public void onCreate() {
        Log.d(MainActivity.TAG,"onCreate");
        super.onCreate();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(MainActivity.TAG,"onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.d(MainActivity.TAG,"onDestroy");
        super.onDestroy();
    }

    public class MyBinder extends IMyAidlInterface.Stub{
        @Override
        public String getMusicName() throws RemoteException {
            return "明天会更好";
        }
    }
}

这里也强调一个知识点,启动Service一共有两种方法,一个是StartService,这种启动方式一般启动的Service和当前启动它的组件就没有任何关系了,和这个组件处于不同的进程了。 另一种启动方式就是bindService这种方法,这种启动方式Service和绑定它的组件共存亡。(具体情况大家可以去查看service的启动方式)。

IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.example.lsw.myapplication;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    String getMusicName();
}

这里就是定义一个接口,在MyService中定义一个class 去继承这个接口然后,实现这个接口的方法,之后再通过onBind()方法将这个继承接口类的对象返回给绑定的应用中,实现可以访问到MyService内部方法的机制。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.lsw.myapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <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=".MyService"
            android:enabled="true"
            android:exported="true">
        </service>
    </application>

</manifest>

这里强调一下属性 android:exported="true"表示这个进程中的MyService服务允许外接访问。

可以看到这了的aidl包名为 com/example/lsw/myapplication
在这里插入图片描述

2 Client 端
(1) 重新创建一个应用工程MyApplication5,主要任务就是在Mainactivity.java中点击button1去绑定MyApplication4中的MyService,然后点击button2调用 aidl中的getMusicName()方法。
Mainctivity.java

package com.example.lisiwei.myapplication;

import android.content.ComponentName;
import android.content.Context;
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 android.widget.Toast;

import com.example.lsw.myapplication.IMyAidlInterface;

public class MainActivity extends AppCompatActivity {

    private Button m_startButton = null;
    private Button m_getMusicButton = null;
    private ServiceConnection m_serviceConnection = null;
    private IMyAidlInterface m_iMyAidlInterface = null;
    private Context m_context = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init() {
        m_context = this;
        m_startButton = findViewById(R.id.startButton);
        m_getMusicButton = findViewById(R.id.getMusicButton);
        m_serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d("lisiwei","onServiceConnected");
                if (null == m_iMyAidlInterface) {
                    m_iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };

        m_startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("lisiwei","m_startButton setOnClickListener");
                Intent intent = new Intent();
                // 这里注意这两个参数第一个是Myservice的包名也就是application4的包名,第二个是包名+类型,
                intent.setComponent(new ComponentName("com.example.lsw.myapplication","com.example.lsw.myapplication.MyService"));
                bindService(intent,m_serviceConnection,Context.BIND_AUTO_CREATE);
            }
        });

        m_getMusicButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Log.d("lisiwei","m_getMusicButton setOnClickListener + " + getMusicName());
                Toast.makeText(m_context,getMusicName(),Toast.LENGTH_LONG).show();
            }
        });

    }

    private String getMusicName() {
        String name = null;
        if ( null != m_iMyAidlInterface) {
            try {
                name = m_iMyAidlInterface.getMusicName();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return name;
    }
}

这里需要强调4个点:
1 因为我们需要去绑定application4中的service就涉及到了Intent启动service,自从Android 5.0之后就不允许隐式启动intent 启动service了,只能显示intent启动service。
2 就是比较坑的一个地方,这里感谢同事的指导,就是要将application4中的aidl文件copy到application5中,而且两个应用中的aidl要处于同一个目录下。
3 这个又是一个坑逼的地方,我要通过application5(client)去绑定application4中service,结果必须要先启动application4的工程,注意这里只是启动了工程,并没有启动application4中的service,service还是由application5中点击button来绑定。你可以看log,是点击button之后application4中的service还是第一次走onCreate方法,那就证明application4只是启动了它自身的工程,并没有启动服务,服务还是由application5绑定之后启动的。
4 为什么不在application5中启动Myservice的时候又通过m_iMyAidlInterface去调用getMusicName()方法那?因为绑定是一个异步执行的过程,如果绑定之后立马去使用m_iMyAidlInterface这个时候m_iMyAidlInterface还可能是null。

这里这个项目的路径名和application4不一样,但是aidl的路径名一样 都为com/example/lsw/myapplication
在这里插入图片描述

具体代码路径: https://download.csdn.net/download/lisiwei1994/10946447

发布了14 篇原创文章 · 获赞 4 · 访问量 3514

猜你喜欢

转载自blog.csdn.net/lisiwei1994/article/details/86717483