NDK22_Am命令原理分析和NDK层实现gif图片播放

NDK开发汇总

一 Am命令源码分析

1日常应用

双进程守护和卸载监听都使用到了AM命令,应用卸载后的访问网页,命令拉起:

execlp("am", "am", "start","--user","0","-a", "android.intent.action.VIEW", "-d",
                   "http://www.baidu.com", NULL);
//execlp 是调用命令的函数

在有些手机上如三星fork不出来线程,小米修改了Am的源码导致重新拉起的服务无效

2 命令参数如何解析

  • Android源码包名路径:
 framework.base.cmds.am.src.com.android.commands.am.Am.java
  • Am.java是可执行文件,有main函数
public static void main(String[] args){
	(new Am()).run(args);
}	

3 参数检测

  • Am中main调用的run方法中:
  if (args.length < 1) {
            onShowUsage(System.out);
            return;
        }
  • adb shell 进入手机
  • am 会打印命令提示,而这些内容和Am.java里面onShowUsage打印的一样

4 run方法

run调用了父类BaseCommand的抽象方法onRun(),在子类Am中实现了

  • onRun方法
    ->nextArgRequired()找到下一个参数

以开启网页和打电话的命令为例

am start -a andorid.intent.action.VIEW -d http://baidu.com

am start -a android.intent.action.CALL -d 10086

nextArgRequired返回的是start ,

  • 接着调用 runStart() 开始拼接Intent

5 runStart

//intent am 开启四大组件 Intent
Intent intent = makeIntent(UserHandle.USER_CURRENT);

6 makeIntent 封装好参数 到新生成的Intent中

命令行的 -d 对应Intent的action
//nextOption 移动到下一个参数

while((opt = nexOption())! = null){
	if(opt.equals("-a")){
		// action
		intent.setAction(nextArgRequired())
	}else if(otp.equals("-d")){
		data = Uri.parse(nextArgRequired());
		if(intent == baseIntent){
			hasItentInfo = true;
		}
	}else
	...
	intent.setDataAndType(data,type);
}

返回的Intent具备了uri、action ,能够开启一个组件(包括service、broadcast)

7 如何跨进程启动四大组件

返回到runStart中

//aidl文件 
private IActivityManger mAm;

void runStart(){

// pm 安装 开启组件 
IpacekageManager pm = IpacekageManager.Stub.asInterface(ServcieManager.getService(“package”));
//查询组件 Activity 注册
List<ResovleInfo> activities = pm.queryIntentActivities(intent,mimeType,0,mUserId);
//才能进行下一步跳转

...
//开启组件
res = mAm.startActivityAsUser(null,null,intent,mimeType,null,null,0,mStartFlags,profilerInfo,null,mUserId);
}

二 gif编码原理

gif编码原理

图形控制扩展块(Graphic Control Extension)

  • 固定值:0xF9
  • 作用:用来跟踪下一帧的信息和渲染形式

注释扩展块

  • 固定值0xFE
  • 作用 :可以用来记录图形、版权、描述等任何的非图形和控制的纯文本数据

图形文本扩展块

  • 固定值0x01
  • 作用:控制绘制的参数,比如左边界偏移量

应用程序扩展

  • 固定值 0xFF
  • 作用:这是提供给应用程序自己使用的,应用程序可以在这里定义自己的标识、信息。可以做到当前app所生成的gif只能由我这个app打开

a r g b 一个像素 4 个字节
a 24位 r 《16 位 g <位 b

三 Android常见的gif播放方式

Java方式: Movie类
创建Movie实例,绘制每一帧图片来达到Gif动态效果。
缺点: 部分Gif图片不能自适应大小,播放速度比实际播放速度快,如果要显示的gif过大,还会出现OOM的问题。

GifView
GifView --》GifHelper gif文件 --》编码—》解析 --》播放
Glide 也是采用GifHelper的方式
缺点:OOM的问题

四 利用系统源码实现gif播放

绘制原理

  • 利用像素算法遍历绘制图形
    优点:占用内存小

1 实现步骤

  1. 加载gif图片
  2. 获取gif图片的信息,包括 宽,高 帧数 每帧播放时长 当前播放的位置
  3. 每一帧播放

2 主要代码

第一步 引入安卓系统加载cif图片的c文件
在这里插入图片描述
第二步 JNI加载解析git图片
native-lib.cpp

#include <jni.h>
#include <string>
#include "gif_lib.h"
#include <android/log.h>
#include <android/bitmap.h>
#include <malloc.h>

#define  LOG_TAG    "david"
#define  argb(a,r,g,b) ( ((a) & 0xff) << 24 ) | ( ((b) & 0xff) << 16 ) | ( ((g) & 0xff) << 8 ) | ((r) & 0xff)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
typedef struct GifBean{
    int current_frame;
    int total_frame;
    int  *dealys;
} GifBean;
extern "C"{


//绘制一张图片
void drawFrame(GifFileType *gifFileType, GifBean *gifBean, AndroidBitmapInfo info, void *pixels) {
    //播放底层代码
//        拿到当前帧
    SavedImage savedImage = gifFileType->SavedImages[gifBean->current_frame];

    GifImageDesc frameInfo = savedImage.ImageDesc;
    //整幅图片的首地址
    int* px = (int *)pixels;
//    每一行的首地址
    int *line;

//   其中一个像素的位置  不是指针  在颜色表中的索引
    int  pointPixel;
    GifByteType  gifByteType;
    GifColorType gifColorType;
    ColorMapObject* colorMapObject=frameInfo.ColorMap;
    px = (int *) ((char*)px + info.stride * frameInfo.Top);
    for (int y =frameInfo.Top; y < frameInfo.Top+frameInfo.Height; ++y) {
        line=px;
        for (int x = frameInfo.Left; x< frameInfo.Left + frameInfo.Width; ++x) {
            pointPixel = (y - frameInfo.Top) * frameInfo.Width + (x - frameInfo.Left);
            gifByteType = savedImage.RasterBits[pointPixel];
            gifColorType = colorMapObject->Colors[gifByteType];
            line[x] = argb(255,gifColorType.Red, gifColorType.Green, gifColorType.Blue);
        }
        px = (int *) ((char*)px + info.stride);
    }





}

JNIEXPORT jlong JNICALL
Java_com_dongnao_gifplayerdemo_GifHandler_loadPath(JNIEnv *env, jobject instance, jstring path_) {
    const char *path = env->GetStringUTFChars(path_, 0);
    int err;
//用系统函数打开一个gif文件   返回一个结构体,这个结构体为句柄
    GifFileType * gifFileType=DGifOpenFileName(path, &err);

    DGifSlurp(gifFileType);
    GifBean *gifBean = (GifBean *) malloc(sizeof(GifBean));


//    清空内存地址
    memset(gifBean, 0, sizeof(GifBean));
    gifFileType->UserData=gifBean;

    gifBean->dealys = (int *) malloc(sizeof(int) * gifFileType->ImageCount);
    memset(gifBean->dealys, 0, sizeof(int) * gifFileType->ImageCount);
    gifBean->total_frame = gifFileType->ImageCount;
    ExtensionBlock* ext;
    for (int i = 0; i < gifFileType->ImageCount; ++i) {
        SavedImage frame = gifFileType->SavedImages[i];
        for (int j = 0; j < frame.ExtensionBlockCount; ++j) {
            if (frame.ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) {
                ext = &frame.ExtensionBlocks[j];
                break;
            }
        }
        if (ext) {
            int frame_delay = 10 * (ext->Bytes[2] << 8 | ext->Bytes[1]);
            LOGE("时间  %d   ",frame_delay);
            gifBean->dealys[i] = frame_delay;

        }
    }
    LOGE("gif  长度大小    %d  ",gifFileType->ImageCount);
    env->ReleaseStringUTFChars(path_, path);
    return (jlong) gifFileType;
}

JNIEXPORT jint JNICALL
Java_com_dongnao_gifplayerdemo_GifHandler_getWidth(JNIEnv *env, jobject instance, jlong ndkGif) {
    GifFileType* gifFileType= (GifFileType *) ndkGif;
    return gifFileType->SWidth;
}

JNIEXPORT jint JNICALL
Java_com_dongnao_gifplayerdemo_GifHandler_getHeight(JNIEnv *env, jobject instance, jlong ndkGif) {

    GifFileType* gifFileType= (GifFileType *) ndkGif;
    return gifFileType->SHeight;

}

JNIEXPORT jint JNICALL
Java_com_dongnao_gifplayerdemo_GifHandler_updateFrame(JNIEnv *env, jobject instance, jlong ndkGif,
                                                      jobject bitmap) {
    //强转代表gif图片的结构体
    GifFileType *gifFileType= (GifFileType *)ndkGif;
    GifBean * gifBean= (GifBean *) gifFileType->UserData;
    AndroidBitmapInfo info;
    //代表一幅图片的像素数组
    void *pixels;
    AndroidBitmap_getInfo(env,bitmap,&info);
    //锁定bitmap  一幅图片--》二维 数组   ===一个二维数组
    AndroidBitmap_lockPixels(env,bitmap,&pixels);

    // TODO
    drawFrame(gifFileType, gifBean, info, pixels);

    //播放完成之后   循环到下一帧
    gifBean->current_frame+=1;
    LOGE("当前帧  %d  ",gifBean->current_frame);
    if (gifBean->current_frame >= gifBean->total_frame-1) {
        gifBean->current_frame=0;
        LOGE("重新过来  %d  ",gifBean->current_frame);
    }
    AndroidBitmap_unlockPixels(env, bitmap);
    return gifBean->dealys[gifBean->current_frame];
}


}

第3步, 调用显示

public class GifHandler {
    private long gifAddr;

    public GifHandler(String path) {
        this.gifAddr = loadPath(path);
    }

    static {
        System.loadLibrary("native-lib");
    }

    private native long loadPath(String path);
    public native int getWidth(long ndkGif);
    public native int getHeight(long ndkGif);
    public native int updateFrame(long ndkGif, Bitmap bitmap);

    public int getWidth() {
        return getWidth(gifAddr);
    }
    public int getHeight() {
        return getHeight(gifAddr);
    }

    public int updateFrame(Bitmap bitmap) {
        return updateFrame(gifAddr,bitmap);
    }
}

public class MainActivity extends AppCompatActivity {
    Bitmap bitmap;
    GifHandler gifHandler;
    ImageView imageView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView= (ImageView) findViewById(R.id.image);
    }

    public void ndkLoadGif(View view) {
        File file=new File(Environment.getExternalStorageDirectory(),"demo.gif");
        gifHandler = new GifHandler(file.getAbsolutePath());
        Log.i("tuch", "ndkLoadGif: "+file.getAbsolutePath());
        //得到gif   width  height  生成Bitmap
        int width=gifHandler.getWidth();
        int height=gifHandler.getHeight();
        bitmap= Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        int nextFrame=gifHandler.updateFrame(bitmap);
        handler.sendEmptyMessageDelayed(1,nextFrame);

    }

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            int mNextFrame=gifHandler.updateFrame(bitmap);
            handler.sendEmptyMessageDelayed(1,mNextFrame);
            imageView.setImageBitmap(bitmap);
        }
    };
}

注意:要把显示的gif图片导入到手机对应的加载目录

3 Demo

GifPlayerDemo

猜你喜欢

转载自blog.csdn.net/baopengjian/article/details/105581843
ndk
今日推荐