AndroidAndroid JNIAndroid知识

通过jni调用C函数实现Android毛玻璃效果

2017-05-02  本文已影响331人  简祖明

之前项目中,有个需求是对Activtiy背景进行虚化,实现毛玻璃效果。实现的大体思路:

  1. 对手机屏幕进行截屏处理,获得截屏图片的bitmap;
  2. 对bitmap进行相应的config处理和缩放像素处理;
  3. 通过高斯模糊算法对处理后的bitmap进行虚化;
  4. 将虚化后的bitmap作为背景图展示。

虽然实现了,但由于效果不佳或是影响性能,最终选择放弃。最近闲的没事,准备将这件事给完成。
因为之前对bitmap的算法处理是通过java代码实现的,如果想提高性能或是减少处理时间,达到秒开的效果,必须设置虚化的程度低,但这样效果并不好看,但只是一味追求效果,由于java代码运行比较慢,在处理时间上必定会有延迟,总之就是有性能没效果,有效果没性能的矛盾,体验十分不佳,网上也给出过方案是通过c的方式实现,今天就来完成,随便回顾一遍jni的流程,长时间不碰全忘...

配置NDK,生成.so动态库

  1. build.gradle文件下,添加ndk配置:
android {
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 25
        ...
        ndk {
            moduleName "blur_lib"   // 动态库名称
            abiFilters "armeabi", "armeabi-v7a", "x86" // 相应的架构平台
            ldLibs "log" //log输出
            ldLibs "jnigraphics" //graphic相关jni
        }
    }
   ...
}

项目的gradle.properties添加支持NDKandroid.useDeprecatedNdk=true
local.properties添加ndk-bundle路径
ndk.dir=C\:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk\\ndk-bundle

  1. 创建NativeHelper类如下:
public class NativeHelper {
    static {
        System.loadLibrary("blur_lib");
    }
    // 参数r为对bitmap虚化的程度范围
    static native void blurBitmap(Object bitmap, int r);
}

在Terminal中cd到java目录下生成.h头文件,方便得到c中的类名,输入命令行:
javah -jni com.pecoo.blurjnidemo.NativeHelper

  1. main下创建一个jni folder,里面创建.c/c++和.h头文件,高斯算法代码粘进来,并在.h头文件进行相应的方法申明。
  2. 再创建一个c/c++文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_pecoo_blurjnidemo_NativeHelper */
#ifndef _Included_com_pecoo_blurjnidemo_NativeHelper
#define _Included_com_pecoo_blurjnidemo_NativeHelper
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_pecoo_blurjnidemo_NativeHelper
 * Method:    blurBitmap
 * Signature: (Ljava/lang/Object;I)V
 */
 #include <android/log.h>
 #include <android/bitmap.h> 
 #include "stackblur.h"   // 在第一步中创建的.h头文件,下面可以调用里面的方法

// log宏定义
 #define TAG "Native_Blur_Jni"
 #define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)

JNIEXPORT void JNICALL Java_com_pecoo_blurjnidemo_NativeHelper_blurBitmap
  (JNIEnv *env, jclass obj, jobject bitmapIn, jint r)
  {
      AndroidBitmapInfo infoIn;
      void *pixels;
     // 获取bitmap的信息
      if (AndroidBitmap_getInfo(env, bitmapIn, &infoIn) != ANDROID_BITMAP_RESULT_SUCCESS) {
          LOG_D("AndroidBitmap_getInfo failed!");
          return;
      }
      // 检测bitmap是不是这两种格式,因为算法中只有对这两种图片会做处理
      if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888 &&
          infoIn.format != ANDROID_BITMAP_FORMAT_RGB_565) {
          LOG_D("Only support ANDROID_BITMAP_FORMAT_RGBA_8888 and ANDROID_BITMAP_FORMAT_RGB_565");
          return;
      }
      // 锁定图片
      if (AndroidBitmap_lockPixels(env, bitmapIn, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
          LOG_D("AndroidBitmap_lockPixels failed!");
          return;
      }
      // 得到宽高
      int h = infoIn.height;
      int w = infoIn.width;
      if (infoIn.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
          // 调用stackblur.c中的blur_ARGB_8888()或blur_RGB_565()
          pixels = blur_ARGB_8888((int *) pixels, w, h, r);
      } else if (infoIn.format == ANDROID_BITMAP_FORMAT_RGB_565) {
          pixels = blur_RGB_565((short *) pixels, w, h, r);
      }
      // 对应上面的AndroidBitmap_lockPixels()
      AndroidBitmap_unlockPixels(env, bitmapIn);
  }
#ifdef __cplusplus
}
#endif
#endif

5.生成.so动态库:Build->Rebuild Project完成后,会在build文件下生成相应平台的.so。复制到main下的jniLibs中
若失败试试
D:\workspace\ndk\NDKDemo\myapplication\src\main>javah -d jni -classpath C:\Users\Administrator\AppData\Local\Android\Sdk\platforms\android-20\android.jar;..\..\build\intermediates\classes\debug com.pecoo.myapplication.NativeUtils

虚化图片,实现效果:

对于截屏,获取到截屏的bitmap步骤这儿就忽略了,直接拿张资源图片进行处理,显示在界面上

// 获得bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.blur_img);
// 获得图片的宽高
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// 设置想要的大小
Display display = getWindowManager().getDefaultDisplay();
int newWidth = display.getWidth();
int newHeight = display.getHeight();
// 计算缩放比例
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Log.d(TAG, "scaleWidth:" + scaleWidth);
Log.d(TAG, "scaleHeight:" + scaleHeight);
// 取得想要缩放的matrix参数
Matrix matrix = new Matrix();
// matrix.postScale(scaleWidth, scaleHeight);
// 实现模糊效果之前,这里可对bitmap进行更大缩放,减少像素点还可提高性能
float scaleFactor = 10;
float scale = 1f / scaleFactor;
matrix.postScale(scale, scale);
// 得到新的图片
Bitmap newbm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix,true);
bitmap.recycle();

子线程中调用处理bitmap的本地函数

    private void blur(final Bitmap bitmap, final int radius) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                super.run();
                // blurNatively()主要是检查图片是否为ARGB8888和RGB565,如果是则调用 NativeHelper中的本地方法blurBitmap()
                final Bitmap ret = blurNatively(bitmap, radius,true);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mImageView.setBackground(new BitmapDrawable(getResources(), ret));
                    }
                });
            }
        };
        thread.start();
    }

最后把 github地址发一下,仅供参考。

上一篇 下一篇

猜你喜欢

热点阅读