目前有Android系统端有水印的需求,通过源码搜索,发现在系统中自带水印功能,下面看下水印的逻辑:
流程
- 代码路径
水印类的实现位置在原生的代码路径中:/frameworks/base/services/core/java/com/android/server/wm/Watermark.java
- 调用的地方
在 WindowManagerService.java中定义了局部变量:
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
CircularDisplayMask mCircularDisplayMask;
EmulatorDisplayOverlay mEmulatorDisplayOverlay;
当 SystemService启动时候,执行了
private void startOtherServices() {
final Context context = mSystemContext;
VibratorService vibrator = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
IpSecService ipSecService = null;
NetworkStatsService networkStats = null;
...
wm.onInitReady(); //这里对wmservice进行了初始化准备
...
}
回到WindowManagerSerive中的onInitReady函数中:
public void onInitReady() {
initPolicy();
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
openSurfaceTransaction();
try {
createWatermarkInTransaction();
} finally {
closeSurfaceTransaction("createWatermarkInTransaction");
}
showEmulatorDisplayOverlayIfNeeded();
}
createWatermarkInTransaction 这个函数就是用来创建水印实例的。
void createWatermarkInTransaction() {
if (mWatermark != null) {
return;
}
File file = new File("/system/etc/setup.conf");
FileInputStream in = null;
DataInputStream ind = null;
try {
in = new FileInputStream(file);
ind = new DataInputStream(in);
String line = ind.readLine();
if (line != null) {
String[] toks = line.split("%");
if (toks != null && toks.length > 0) {
// TODO(multi-display): Show watermarks on secondary displays.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
toks);
}
}
} catch (FileNotFoundException e) {
} catch (IOException e) {
} finally {
if (ind != null) {
try {
ind.close();
} catch (IOException e) {
}
} else if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
}
其中核心文件就是 /system/etc/setup.conf
,通过读取该配置文件,将数据内容作为参数初始化Watermark。mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,toks);
所有的实现都在Watermark.java
文件中。查看该文件可以看出,对传入的参数进行了编码转换,所以输入的内容要使用进制的来进行转换:
StringBuilder builder = new StringBuilder();
int len = mTokens[0].length();
len = len & ~1;
for (int i=0; i<len; i+=2) {
int c1 = mTokens[0].charAt(i);
int c2 = mTokens[0].charAt(i+1);
if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + ;
else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + ;
else c1 -= '0';
if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + ;
else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + ;
else c2 -= '0';
builder.append((char)(-((c1*)+c2)));
}
而显示的逻辑其实就是增加了一个Layer:
try {
ctrl = dc.makeOverlay()
.setName("WatermarkSurface")
.setSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
ctrl.setLayerStack(mDisplay.getLayerStack());
ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*);
ctrl.setPosition(0, 0);
ctrl.show();
mSurface.copyFrom(ctrl);
} catch (OutOfResourcesException e) {
}
绘制时机,WindowAnimator.java中查看
if (mService.mWatermark != null) {
mService.mWatermark.drawIfNeeded();
}