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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章