Android开发

Android 系统开发提供Api供三方应用(二、实现系统添加接

2021-10-18  本文已影响0人  MrDemo

我们一起来开发一款对外SDK,可以让第三方应用调用需要系统级权限的接口,并且调用方式比Google的原生接口还要人性化,不需要Context,随时随地都可以调用。

一、开发流程简要

接口封装流程第一步实现.png

二、撰写基础代码

(1)、创建属于自己的文件夹

在系统的frameworks/base/core/java/android/里面创建文件夹htyera/wp/(个人测试使用,如有雷同侵权,纯属巧合,请通知修改)

(2)、创建AIDL文件

创建aidl文件(ILookMessage.aidl),添加了两个方法,一个setValue方法,一个getValue方法,代码如下

package android.htyera.wp;
 interface ILookMessage {
   void setValue(String message);
   String getValue();
 }

我是通过Android Studio创建的,如果跟我一样千万别忘记修改包名,否则编译的时候会提示找不到文件的错误。

(3)、配置aidl文件

虽然我们创建了aidl文件,但是编译Android的时候是不会编译到这个文件的,如果要用编译到对应的文件,需要修改frameworks/base/Android.mk文件,在对应的LOCAL_SRC_FILES宏中添加对应的文件即可,代码如下

LOCAL_SRC_FILES += \
    core/java/android/htyera/wp/ILookMessage.aidl \

我是第一次本着创建一个完成一个的原则,马上就执行了对文件的编译,在make之前一定要执行make update-api的命令,更新一下api,否则一定会报错的。切记。(注:执行完该命令后,发现frameworks/base/api/current.txt文件有更新,可以看到,文件中增加了一个WRTSZ_SERVICE 的常量和一个com.wrtsz.api的package,该package中就有对应的接口声明。)

(4)、编写系统级服务

创建LookMessageService.java并且继承ILookMessage.Stub实现里面的所有方法,(别忘改包名,别忘改报名,别忘改包名)代码如下:

package android.hytera.wp;
import android.os.RemoteException;
/**
 * this is my SecondService for system
 * test aidl file
 */
public class LookMessageService extends ILookMessage.Stub {
    public String value = "I am a value";

    @Override
    public void setValue(String message) throws RemoteException {
        this.value = message;
    }

    @Override
    public String getValue() throws RemoteException {
        return value;
    }
}

(5)、定义Java接口类

在注册之前我们先在frameworks/base/core/java/android/content/Context.java中添加一个常量
public static final String LOOK_MESSAGE_WP = "lookmessagewp";
添加常量的目的:就是给服务起一个名字,好比我们再应用层使用服务的时候注册的名字一样,以后使用这个服务的时候都用这个名字就可以了。


123.png

创建java文件(LookMessageManager.java),这个类就是暴露给其它程序使用的Manager接口类,添加的方法跟ILookMessage里面一样就可以了,构造方法有很多种,自己定义吧。

package android.hytera.wp;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;

public class LookMessageManager {
    private static String TAG = "hty_wp_LookMessageManager";
    private ILookMessage mService;
    private static LookMessageManager mInstance = null;

    public LookMessageManager() {
        mService = ILookMessage.Stub.asInterface(ServiceManager.getService(Context.LOOK_MESSAGE_WP));
    }

    public static synchronized LookMessageManager getInstance() {
        if (mInstance == null) {
            synchronized (LookMessageManager.class) {
                if (mInstance == null) {
                    mInstance = new LookMessageManager();
                }
            }
        }
        return mInstance;
    }

    public void setValue(String message) {
        try {
            mService.setValue(message);
        } catch (RemoteException e) {
            Log.e(TAG, "setValue fail.");
        }
    }

    public String getValue() {
        try {
            return mService.getValue();
        } catch (RemoteException e) {
            Log.e(TAG, "getValue fail.");
            return "";
        }
    }
}

此时先不要运行编译,因为运行编译一定会报错的,为什么呢,因为我们ServiceManager.getService(Context.LOOK_MESSAGE_WP)去找这个Service,而系统根本都不知道这是个啥,所以接下来我们要执行注册服务。

(6)、注册服务

仅仅将服务写好是不够的,系统中的每个原生服务都需要注册,相当于给系统留一个备案,方便需要使用的时候快速查找,否则即使编译通过,也是无法找到这个服务的。
在frameworks/base/core/java/android/app/SystemServiceRegistry.java文件中添加如下注册代码:

 registerService(Context.LOOK_MESSAGE_WP, LookMessageManager.class,
                new StaticServiceFetcher<LookMessageManager>() {
                    @Override
                    public LookMessageManager createService() {
                        return LookMessageManager.getInstance();
                    }});

(7)、将自定义Service 加入到SystemServer

注册完service后,系统知道有这么一个service,而我们要在开机的时候启动自己的Service,我是在SystemService中的startOtherServices中进行启动的。

                traceBeginAndSlog("start htyera_wp service");//log日志
                try {
                    Slog.i(TAG, "add htyera_wp Service");//log日志
                    // Serial port support
                    LookMessageService look = new LookMessageService();//创建service
                    ServiceManager.addService(Context.LOOK_MESSAGE_WP, look);//添加Service
                } catch (Throwable e) {
                    Slog.e(TAG, "Failure starting SerialService", e);
                }
                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

(8)、权限策略配置

system/sepolicy/service_contexts  lookmessagewp             u:object_r:lookmessagewp_service:s0
system/sepolicy/service.te type lookmessagewp_service, system_api_service, system_server_service, service_manager_type;
策略1.png 策略2.png

(9)、编译系统

1、在根目录里面执行:source build/envsetup.sh
2、在根目录里面执行:lunch
3、选择对用的编译版本
4、执行 make update-api更新接口
5、编译 make -j8 编译系统

(10)、刷机重启系统

(11)、创建应用编写调用接口类

首先创建一个项目,并且给项目创建一个moudle包,里面主要是放我们的接口类,不建议直接放app里面,比较乱,可以创建一个moudle或者自己将文件打成jar包。
其中moudle的包名一定要和我们系统源码中的LookMessageManager.java的包名一致
我之门就直接将系统中的LookMessageManager.java文件拷贝到这个包下


jar.png
package android.hytera.wp;
public class LookMessageManager {
    private static String TAG = "hty_wp_LookMessageManager";
    private static LookMessageManager mInstance = null;
    public static synchronized LookMessageManager getInstance() {
        throw new RuntimeException("API not supported!");
    }
   public void setValue(String message) {
       throw new RuntimeException("API not supported!");
    }
    public String getValue() {
        throw new RuntimeException("API not supported!");
    }
}

(11)、应用层调用

public class MainActivity extends AppCompatActivity {
    private Button getValue;
    private Button setValue;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getValue = findViewById(R.id.getValue);
        setValue = findViewById(R.id.setValue);
        getValue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("hty_wp",LookMessageManager.getInstance().getValue()+"");//获取value
            }
        });
        setValue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LookMessageManager.getInstance().setValue("我要变化");//设置value
            }
        });
    }
}

通过日子查看,我们可以看到,我们的调用成功,截止到现在我们就实现了第一步,应用程序调用framework的方法。接下来就是根据实际业务进行实现具体功能了,而且还要实现注册监听framework的变化。

上一篇下一篇

猜你喜欢

热点阅读