android开发笔记之app应用间接使用frameworks接口

背景

公司平台化开发,一个重要的地方是上层应用可以兼容不同的芯片平台。比如上层camera应用可以兼容Mtk和Qcom等不同平台。这个我们公司的camera不知道他们从哪里搞来了一个中间键的实现方法。说白一点,就是统一在hal层通过中间键给上层camera发送固定格式的消息,然后上层camera通过解析此消息来实现具体的逻辑功能。好高大上啊!

问题

然后,我被抽调支持camera的工作。在android Go (8.1.0)有一个柔光灯的功能没有实现,说要从android 7.0上来移植。正好,我刚刚移植了一个自拍镜像的功能,好吧,这个任务就这么缘份的分到了我名下。我开始以为这是一个简单的移植,应该问题不大。一开工,发现这个具体的实现是在中间键上来实现,而此android Go (8.1.0)软件上,我们不打算移植中间键,影像部门给的解释是说移植此中间键的工作量特别大,要一个熟悉此模块的工程师一个星期才能移植成功(我们心里冷笑一下吧,这个可以骗骗老板,请不要来骗我)。所以要求我在不实现中间键的情况,使用MTK平台的方式来实现此功能。并且,他们还给了一个传说中的备份方案说可以实现,然后此工作就看我了,小伙子加油,我们会为你鼓劲的噢。

柔光灯的功能简单说明:
当我们使用前置camera时,如果发现当前环境的光线比较暗时,开启前置flash,如果发现当前环境的光线比较明时,关闭前置flash。

具体问题分析

那我们先看看此问题在代码上的实现吧:

在frameworks/base/core/java/android/hardware/Camera.java文件中,接受到hal层发送的识别当前环境的值(msg.arg2),此值是通过消息MTK_CAMERA_MSG_EXT_NOTIFY_ASD来发送的:

private void handleExtNotify(Message msg, Camera camera) {
        switch (msg.arg1) {
        case MTK_CAMERA_MSG_EXT_NOTIFY_ASD:
            if (mAsdCallback != null) {
                mAsdCallback.onDetected(msg.arg2);
                return;
            }
            break;

我们可以查看mAsdCallback,发现其为一个接口AsdCallback对象。:

// ASD
private Camera.AsdCallback mAsdCallback;
.....................................
//ASD
/**
* @hide An interface which contains a callback for the auto scene detection
*/
public interface AsdCallback {
        /**
         * @param scene the scene detected
         */
        void onDetected(int scene);
}

/**
* @param cb the callback to run
* @hide Registers a callback to be invoked when auto scene is detected
*/
public final void setAsdCallback(AsdCallback cb) {
        mAsdCallback = cb;
}

好吧,这就是我们现在有的所有信息,开始干活吧。

当然,立刻有大神会说,你太菜了,这个太简单了,在camera应用中要实现具体功能的地方实现此接口就可以了。

是的,原理的确如此。但是我们上面说了,我们上层应用是做成平台化,也就是与平台无关,也就是此应用的编译不参与整个项目的编译,应用会在Android Studio中直接生成APK,不和平台的代码产生任何交集。所以,你说的方法是做不了的。

下面就开始介绍传说中的备份方案吧:
也就是直接在应用中开一个服务,一直监听此hal层传上来的值,如果此值发生变化,就进行对应的操作。
我和他们交流了一下,最后他们自己都不好意思的说此方法不好,哥哥,请不要出嫂主意。但是,这是部门合作,他们给领导汇报工作说给了备份的方案,然后他们领导还在群里说他们已经给了方案,为什么你搞不定,你这个废物,好吧,哥,你厉害,我菜。

功能实现

直接来调用此接口,肯定是不行的,要想办法。

和周围一堆人讨论了一圈,各路大神各种神通,各种仙技,但是最后实现此功能的一定是你自己,所以这要求自己对各种主意有精准的识别和判断,不要被带坑里去了,最后的事实也是证明,大部分人的主意连边都没有粘。

我们要记住:
android本身的代码就是最好的老师,一定要以android本身的代码为导向原则

我们要记住:
我们是二次开发,所以,当前的代码也是有充分的参考价值。

好吧,经过讨论和分析现有的代码,我发现camera应用有一个笑容识别功能,也是调用frameworks下的接口,好吧, 我们就仿造此方法来实现此功能。

笑容识别功能的关键思想是不直接调用此接口,而是先提供一个重新包装后的AsdCallback 接口的jar包给上层camera应用使用。

好吧,那我们先来做一个jar包吧。

生产上层应用使用的jar包

第一步,打开Eclipse,新建一个android应用工程。
File–New–Android Application Project–新建一个MTKCameraAsd项目—next–取消Create Activity和Create custom launcher icon,选中Mark this project as library。

第二步,新建MTKCameraAsd.java 文件.
此类MTKCameraAsd有一个类AsdCallbackMTK实现接口Camera类中的接口AsdCallback,还新建了一个接口AsdCallbackMTKInterface,此接口为上层应用中使用的要实现的接口.还有一个setAsdCallback方法,此方法主要功能是为调用Camera类中的方法setAsdCallback.

package com.tinno.MTkCamera;

import android.hardware.Camera;
import android.hardware.Camera.AsdCallback;

public class MTKCameraAsd {

    public MTKCameraAsd() {

    }

    private MTKCameraAsd.AsdCallbackMTK asdCallbackMTK;
    private MTKCameraAsd.AsdCallbackMTKInterface asdCallbackMTKInterface;

    public interface AsdCallbackMTKInterface {
     void onDetected(int scene);
   }

    private final class AsdCallbackMTK implements AsdCallback {
        private AsdCallbackMTK() {
        }

        public void onDetected(int scene){
            if(MTKCameraAsd.this.asdCallbackMTKInterface != null) {
                MTKCameraAsd.this.asdCallbackMTKInterface.onDetected(scene);
            }
        }
    }

    public void setAsdCbInterfaceListener(MTKCameraAsd.AsdCallbackMTKInterface as) {
        this.asdCallbackMTKInterface = as;
    }

    public void setAsdCallback(Camera camera) {
        if(camera != null) {
            if(this.asdCallbackMTK == null) {
                this.asdCallbackMTK = new MTKCameraAsd.AsdCallbackMTK();
            }

            camera.setAsdCallback(this.asdCallbackMTK);
        }

    }

}

AndroidManifest.xml文件:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tinno.MTKAsdHelpLib"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="23"
        android:targetSdkVersion="23" />

manifest>

到此,我们会发现Eclipse报错,因为其不识别Camera类中的接口AsdCallback.没有关系,这主要是因为android标注接口中没有接口导致的错误,我们可以找到编译工程的项目out目录下,找到对应的Camera类的jar包,装其导入到Eclipse中.

第三步:查找jar包和导入到Eclipse的项目中

具体查找jar包和导入到Eclipse的项目中的方法可以查看如下的参考资料:
2.Android之导入源码到eclipse中以及单模块调试
http://blog.csdn.net/way_ping_li/article/details/10494925
4.Android学习之往系统应用中添加framework层的jar包
http://blog.csdn.net/feishangbeijixing/article/details/44156945

我说一下我的方法:

grep -rni   --include=*.jar  "AsdCallback"  ./out/
匹配到二进制文件 ./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar

一行命令定位到,事实上,./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar文件,也是framework的jar包.

然后我们将其导入到Eclipse的项目中.
方法为:
在eclipse的Android项目中,选择项目–右键–选择属性–Java Build Path–Libraries–Add Library–User Library–Next–UserLibraries–User Libraries–New–新建一个User Library–点击刚新建的user library–Add JARS把Jar包加入到建立的User Library中–最后点击OK。回到Java Build Path界面–Order and Export,把所建立的User Libraries移到android sdk的上面

第四步:使用Eclipse将此应用生成一个jar包.

这个非常简单,具体的实现过程可以参考:
1.使用eclipse工程导出jar包
http://blog.csdn.net/changqing5818/article/details/52062790

在应用中使用此jar包

第一步:导入此jar包到Android Studio中.
具体的实现方法请参考:
3.Android studio如何导入jar包
https://jingyan.baidu.com/article/d621e8da41135e2865913fb1.html

第二步:实现其具体的逻辑功能.

那么我们在上层应用中的关键实现方法为:
关键代码一:
在CameraManager.java文件中:

//导入类MTKCameraAsd
import com.tinno.MTkCamera.MTKCameraAsd;
........................................................
//定义一个类MTKCameraAsd的对象mMTKCameraAsd
private MTKCameraAsd mMTKCameraAsd = new MTKCameraAsd();
........................................................
//在方法中调用对象mMTKCameraAsd的仅有的二个公开接口
private void setAsdCb(int id) {
    //调用对象mMTKCameraAsd的setAsdCbInterfaceListener方法,设置接口对象mAsdCb,此接口对象mAsdCb为我们具体的实现逻辑操作的回调接口.
    mMTKCameraAsd.setAsdCbInterfaceListener(mAsdCb);
    //调用对象mMTKCameraAsd的setAsdCallback方法,给camera设置接口
    mMTKCameraAsd.setAsdCallback(mCamera[id]);
}
........................................................
//定义一个asdCallback对象mAsdCb
private asdCallback mAsdCb = new asdCallback();
//定义一个类AsdCallbackMTKInterface,其实现接口AsdCallbackMTKInterface.而此类AsdCallbackMTKInterface又将其具体的回调推迟到了接口onAsdListener中.其实,我们也可以完全在这实现具体的逻辑,不再使用onAsdListener回调接口.只是此功能需要要别的文件实现具体的逻辑,所以才又使用了一次onAsdListener回调接口

private class asdCallback implements MTKCameraAsd.AsdCallbackMTKInterface {
        public void onDetected(int scene) {
            // TODO Auto-generated method stub
            if(onAsdListener != null) {
                onAsdListener.onDetected(scene);
            }
        }
    }
........................................................
//我们定义推迟回调的接口OnAsdListener对象onAsdListener
public ICamera.OnAsdListener onAsdListener=null;
........................................................
//我们再在对应的地方将赋值为其它文件的实现此推迟回调的接口OnAsdListener的对象
onAsdListener = (ICamera.OnAsdListener)msg.obj;
........................................................

在ICamera.java定义一个接口,此接口为推迟的回调接口.

    public interface OnAsdListener {
        void onDetected(int scene);
    }

我们真正实现此推迟的回调接口的具体逻辑为:
PhotoModule.java

..........................................................................
private final ActorAsdCallback mAsdCallback = new ActorAsdCallback();
//定义类ActorAsdCallback,此类ActorAsdCallback实现推迟的回调接口OnAsdListener 
private final class ActorAsdCallback implements ICamera.OnAsdListener {
        public void onDetected(int scene) {
             //此处才是真正实现上层监听回调接口的具体逻辑的地方
             .....................
        }
}
..........................................................................
//将具体的mAsdCallback接口赋值给CameraManager.java文件中的对象onAsdListener,使其调用起来
public void startAsdDetection() {
   //
   mCameraDevice.setAsdCallback(mAsdCallback);
}

..........................................................................

至此,功能完成,打完收工.

再回首

我们再回过头来看一看,为什么我们这样就可以调用Framework中Camera类的接口AsdCallback呢?

不着急,我们先在上层camera应用中查看一下AsdCallback关键字,我们会发现,在整个上层应用中,竟然没有搜索到使用接口AsdCallback.

这,就是这个方法为什么我们这样就可以调用Framework中Camera类的接口AsdCallback的秘密.因为我们根本上应用中没有使用此接口,所以当然就不会报错了.

那么,我们为什么在上层应用中没有使用此接口AsdCallback而又实现了此功能呢?奥秘就在我们生成为上层应用使用的jar包的项目文件MTKCameraAsd.java中.

请你再回去看MTKCameraAsd.java文件,能发现什么不?

奥秘就是我们使用了一个中间类AsdCallbackMTK,此类实现接口AsdCallback,将类将MTKCameraAsd和接口AsdCallbackMTKInterface关联,并且对外只提供公开的可以操作Camera和AsdCallbackMTKInterface的接口,但是我们不提供公开的接口来使用AsdCallback这个类.也就是重新包装AsdCallback,将其对外隐藏.

心里的话:

说实在话,做这个东东,坑太多,真是让人非常痛苦,领导只有拼命的催进度,心里还觉得你特别的菜,外人完全不知道此实现的难度,只有冷暖自知.

但是,说实话,做完这个,解答了我多年前的几个疑惑,还是有收获的.。

但是,悲剧在于我现在根本对这些东东没有兴趣了。没劲!

场景光线的值的配置参数

此场景光线的值的识别配置参数为

在文件vendor/mediatek/proprietary/custom/mt6580/hal/camera/camera_custom_asd.cpp

ASDDataOut1->u1ScoreThrNight
ASDDataOut2->s2EvLoThrNight
ASDDataOut2->s2EvHiThrNight

vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6580/v1/hal/client/CamClient/FD/Asd/AsdClient.cpp


在hal层更新此当前环境的值u4Scene的方法:

void
AsdClient::
update(MUINT8 * OT_Buffer, MINT32 a_Buffer_width, MINT32 a_Buffer_height, MINT32 a_FaceNum)
{
...................................
    u4Scene = mSceneCur;

    MY_LOGD("u4Scene:%d ", u4Scene);

    if  (1)
    {
        mpCamMsgCbInfo->mNotifyCb(
            MTK_CAMERA_MSG_EXT_NOTIFY,
            MTK_CAMERA_MSG_EXT_NOTIFY_ASD,
            u4Scene,
            mpCamMsgCbInfo->mCbCookie
        );
    }
..............................
}

安装使用jar包生成的APK报错的处理

如果报此错误:

adb install ApeCamera48-debug.apk 
Failed to install ApeCamera48-debug.apk: Failure [INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: Failed to parse /data/app/vmdl1065641074.tmp/base.apk: Corrupt XML binary file]

解决原因,是你的AndroidManifest.xml文件不对.

解决方法:
对应修改你的AndroidManifest.xml文件

参考资料

1.使用eclipse工程导出jar包
http://blog.csdn.net/changqing5818/article/details/52062790
2.Android之导入源码到eclipse中以及单模块调试
http://blog.csdn.net/way_ping_li/article/details/10494925
3.Android studio如何导入jar包
https://jingyan.baidu.com/article/d621e8da41135e2865913fb1.html
4.Android学习之往系统应用中添加framework层的jar包
http://blog.csdn.net/feishangbeijixing/article/details/44156945

你可能感兴趣的:(android开发笔记)